diff options
596 files changed, 122063 insertions, 0 deletions
diff --git a/skia/README.google b/skia/README.google new file mode 100644 index 0000000..dc4cb88 --- /dev/null +++ b/skia/README.google @@ -0,0 +1,23 @@ +This is a copy of the Skia source tree. In the original repository, the include +directories and the "corecg" directories are separated out. On top of + libs/graphics -> skia +we have the following mappings from source repository to our tree: + include/corecg -> skia/include/corecg + include/graphics -> skia/include + libs/corecg -> skia/corecg + +platform/* are our own files that provide extra functionality we need our +Skia to implement. + +DO NOT CHANGE THE SKIA FILES IN OUR TREE. These will be overwritten when we +sync to newer versions of Skia. The exception is platform/ + +THE EXCEPTION IS include/corecg/SkUserConfig.h which are the application's +definition of its options and environment. This file must be manually merged +with any changes in the Skia tree so that our options are preserved and we +also pick up any important changes they make. + + -- brettw@google.com, 28 December 2006 + +Patches we are tracking locally (until Skia is fixed upstream): +fix_for_1186198.diff -- eseidel, 6/4/08, BUG=1186198 diff --git a/skia/SConscript b/skia/SConscript new file mode 100644 index 0000000..6a05b81 --- /dev/null +++ b/skia/SConscript @@ -0,0 +1,195 @@ +# Copyright 2008, Google 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:
+#
+# * 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.
+# * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+Import('env')
+
+env = env.Clone()
+
+env.Prepend(
+ CPPPATH = [
+ 'include',
+ 'include/corecg',
+ 'corecg',
+ 'sgl',
+ 'picture',
+ '#..',
+ ],
+)
+
+env.Append(
+ CPPDEFINES = [
+ 'SKIA_DISABLE_SUPPORT_FOR_DECODERS',
+ ],
+
+ CCFLAGS = [
+ '/TP',
+
+ '/wd4244',
+ '/wd4267',
+ '/wd4345',
+ '/wd4390',
+ '/wd4554',
+ '/wd4800',
+ '/wd4503',
+ '/wd4819',
+ ],
+)
+
+
+input_files = [
+ 'animator/SkTime.cpp',
+ 'corecg/Sk64.cpp',
+ 'corecg/SkBuffer.cpp',
+ 'corecg/SkChunkAlloc.cpp',
+ 'corecg/SkCordic.cpp',
+ 'corecg/SkDebug.cpp',
+ 'corecg/SkDebug_stdio.cpp',
+ 'corecg/SkFloat.cpp',
+ 'corecg/SkInterpolator.cpp',
+ 'corecg/SkMath.cpp',
+ 'corecg/SkMatrix.cpp',
+ 'corecg/SkMemory_stdlib.cpp',
+ 'corecg/SkPoint.cpp',
+ 'corecg/SkRect.cpp',
+ 'corecg/SkRegion.cpp',
+ 'effects/Sk1DPathEffect.cpp',
+ 'effects/Sk2DPathEffect.cpp',
+ 'effects/SkAvoidXfermode.cpp',
+ 'effects/SkBlurDrawLooper.cpp',
+ 'effects/SkBlurMask.cpp',
+ 'effects/SkBlurMaskFilter.cpp',
+ 'effects/SkCamera.cpp',
+ 'effects/SkColorFilters.cpp',
+ 'effects/SkColorMatrix.cpp',
+ 'effects/SkColorMatrixFilter.cpp',
+ 'effects/SkCornerPathEffect.cpp',
+ 'effects/SkCullPoints.cpp',
+ 'effects/SkDashPathEffect.cpp',
+ 'effects/SkDiscretePathEffect.cpp',
+ 'effects/SkEmbossMask.cpp',
+ 'effects/SkEmbossMaskFilter.cpp',
+ 'effects/SkGradientShader.cpp',
+ 'effects/SkKernel33MaskFilter.cpp',
+ 'effects/SkLayerRasterizer.cpp',
+ 'effects/SkPaintFlagsDrawFilter.cpp',
+ 'effects/SkPixelXorXfermode.cpp',
+ 'effects/SkShaderExtras.cpp',
+ 'effects/SkTransparentShader.cpp',
+ 'effects/SkUnitMappers.cpp',
+ 'images/SkImageDecoder.cpp',
+ 'images/SkImageRef.cpp',
+ 'images/SkStream.cpp',
+ 'images/SkStream.cpp',
+ 'picture/SkPictureFlat.cpp',
+ 'picture/SkPicturePlayback.cpp',
+ 'picture/SkPictureRecord.cpp',
+ 'ports/SkFontHost_none.cpp',
+ 'ports/SkGlobals_global.cpp',
+ 'ports/SkImageDecoder_Factory.cpp',
+ 'ports/SkOSFile_stdio.cpp',
+ 'ports/SkThread_win.cpp',
+ 'sgl/SkAlphaRuns.cpp',
+ 'sgl/SkBitmap.cpp',
+ 'sgl/SkBitmapProcShader.cpp',
+ 'sgl/SkBitmapProcState.cpp',
+ 'sgl/SkBitmapProcState_matrixProcs.cpp',
+ 'sgl/SkBitmapSampler.cpp',
+ 'sgl/SkBitmapShader.cpp',
+ 'sgl/SkBlitRow_D16.cpp',
+ 'sgl/SkBlitRow_D4444.cpp',
+ 'sgl/SkBlitter.cpp',
+ 'sgl/SkBlitter_4444.cpp',
+ 'sgl/SkBlitter_A1.cpp',
+ 'sgl/SkBlitter_A8.cpp',
+ 'sgl/SkBlitter_ARGB32.cpp',
+ 'sgl/SkBlitter_RGB16.cpp',
+ 'sgl/SkBlitter_Sprite.cpp',
+ 'sgl/SkCanvas.cpp',
+ 'sgl/SkColor.cpp',
+ 'sgl/SkColorFilter.cpp',
+ 'sgl/SkColorTable.cpp',
+ 'sgl/SkDeque.cpp',
+ 'sgl/SkDevice.cpp',
+ 'sgl/SkDither.cpp',
+ 'sgl/SkDraw.cpp',
+ 'sgl/SkEdge.cpp',
+ 'sgl/SkFilterProc.cpp',
+ 'sgl/SkFlattenable.cpp',
+ 'sgl/SkGeometry.cpp',
+ 'sgl/SkGlobals.cpp',
+ 'sgl/SkGlyphCache.cpp',
+ 'sgl/SkGraphics.cpp',
+ 'sgl/SkMask.cpp',
+ 'sgl/SkMaskFilter.cpp',
+ 'sgl/SkPackBits.cpp',
+ 'sgl/SkPaint.cpp',
+ 'sgl/SkPath.cpp',
+ 'sgl/SkPathEffect.cpp',
+ 'sgl/SkPathMeasure.cpp',
+ 'sgl/SkPicture.cpp',
+ 'sgl/SkPixelRef.cpp',
+ 'sgl/SkProcSpriteBlitter.cpp',
+ 'sgl/SkPtrRecorder.cpp',
+ 'sgl/SkRasterizer.cpp',
+ 'sgl/SkRefCnt.cpp',
+ 'sgl/SkRegion_path.cpp',
+ 'sgl/SkScalerContext.cpp',
+ 'sgl/SkScan.cpp',
+ 'sgl/SkScan_Antihair.cpp',
+ 'sgl/SkScan_AntiPath.cpp',
+ 'sgl/SkScan_Hairline.cpp',
+ 'sgl/SkScan_Path.cpp',
+ 'sgl/SkShader.cpp',
+ 'sgl/SkSpriteBlitter_ARGB32.cpp',
+ 'sgl/SkSpriteBlitter_RGB16.cpp',
+ 'sgl/SkString.cpp',
+ 'sgl/SkStroke.cpp',
+ 'sgl/SkStrokerPriv.cpp',
+ 'sgl/SkTSearch.cpp',
+ 'sgl/SkTypeface_fake.cpp',
+ 'sgl/SkUtils.cpp',
+ 'sgl/SkWriter32.cpp',
+ 'sgl/SkXfermode.cpp',
+]
+
+env_p = env.Clone(
+ PCHSTOP = 'SkTypes.h',
+ PDB = 'vc80.pdb',
+)
+
+# TODO(rspangler): This step forces -Zi, but doesn't actually use it. Need to
+# fix so it doesn't override our -Z7. -Zi also causes vc80.pdb to be created
+# in the skia directory.
+# TODO(bradnelson): This step creates a skia.pch.ib_tag file to be created.
+# It's a 0-length file so likely harmless. Is this a side effect of having
+# IncrediBuild installed on the build machine?
+pch, obj = env_p.PCH(['skia.pch', 'precompiled.obj'], 'precompiled.cc')
+env_p['PCH'] = pch
+
+env.StaticLibrary('skia', input_files + [obj])
diff --git a/skia/animator/SkAnimate.h b/skia/animator/SkAnimate.h new file mode 100644 index 0000000..5500798 --- /dev/null +++ b/skia/animator/SkAnimate.h @@ -0,0 +1,43 @@ +/* libs/graphics/animator/SkAnimate.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkAnimate_DEFINED +#define SkAnimate_DEFINED + +#include "SkAnimateBase.h" +#include "SkDisplayType.h" +#include "SkIntArray.h" +#include "SkUtils.h" + +class SkAnimate : public SkAnimateBase { + DECLARE_MEMBER_INFO(Animate); + SkAnimate(); + virtual ~SkAnimate(); + virtual int components(); +#ifdef SK_DUMP_ENABLED + virtual void dump(SkAnimateMaker* ); +#endif + virtual void onEndElement(SkAnimateMaker& maker); +protected: + bool resolveCommon(SkAnimateMaker& ); + int fComponents; +private: + typedef SkAnimateBase INHERITED; +}; + +#endif // SkAnimateField_DEFINED + diff --git a/skia/animator/SkAnimate3DSchema.xsd b/skia/animator/SkAnimate3DSchema.xsd new file mode 100644 index 0000000..6b98cf7 --- /dev/null +++ b/skia/animator/SkAnimate3DSchema.xsd @@ -0,0 +1,39 @@ +<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
+ xmlns:Sk="http://www.skia.com/schema/SkAnimateSchema.xsd"
+ targetNamespace="urn:skia3D" xmlns:Sk3D="urn:skia3D">
+
+ <xs:simpleType name="Patch" >
+ <xs:restriction base="xs:string" >
+ </xs:restriction>
+ </xs:simpleType>
+
+ <xs:simpleType name="Point" >
+ <xs:restriction base="xs:string" >
+ <xs:pattern value="[+-]?([0-9]*\.[0-9]+|[0-9]+\.?)( *[ ,] *[+-]?([0-9]*\.[0-9]+|[0-9]+\.?)){2}" />
+ </xs:restriction>
+ </xs:simpleType>
+
+ <xs:element name="camera">
+ <xs:complexType >
+ <xs:attribute name="axis" type="Sk3D:Point" />
+ <xs:attribute name="hackHeight" type="Sk:Float" />
+ <xs:attribute name="hackWidth" type="Sk:Float" />
+ <xs:attribute name="location" type="Sk3D:Point" />
+ <xs:attribute name="observer" type="Sk3D:Point" />
+ <xs:attribute name="patch" type="Sk3D:Patch" />
+ <xs:attribute name="zenith" type="Sk3D:Point" />
+ <xs:attribute name="id" type="xs:ID" />
+ </xs:complexType>
+ </xs:element>
+
+ <xs:element name="patch">
+ <xs:complexType >
+ <xs:attribute name="origin" type="Sk3D:Point" />
+ <xs:attribute name="rotateDegrees" type="Sk:MemberFunction" />
+ <xs:attribute name="u" type="Sk3D:Point" />
+ <xs:attribute name="v" type="Sk3D:Point" />
+ <xs:attribute name="id" type="xs:ID" />
+ </xs:complexType>
+ </xs:element>
+
+</xs:schema>
diff --git a/skia/animator/SkAnimate3DSchema.xsx b/skia/animator/SkAnimate3DSchema.xsx new file mode 100644 index 0000000..5be2933 --- /dev/null +++ b/skia/animator/SkAnimate3DSchema.xsx @@ -0,0 +1,3 @@ +<?xml version="1.0" encoding="utf-8"?>
+<!--This file is auto-generated by the XML Schema Designer. It holds layout information for components on the designer surface.-->
+<XSDDesignerLayout />
diff --git a/skia/animator/SkAnimateActive.cpp b/skia/animator/SkAnimateActive.cpp new file mode 100644 index 0000000..0aa1279 --- /dev/null +++ b/skia/animator/SkAnimateActive.cpp @@ -0,0 +1,509 @@ +/* libs/graphics/animator/SkAnimateActive.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkAnimateActive.h" +#include "SkAnimateBase.h" +#include "SkAnimateMaker.h" +#include "SkAnimateSet.h" +#include "SkDrawGroup.h" +#ifdef SK_DEBUG +#include "SkTime.h" +#endif + +// SkActive holds array of interpolators + +SkActive::SkActive(SkApply& apply, SkAnimateMaker& maker) : fApply(apply), + fMaxTime(0), fMaker(maker), fDrawIndex(0), fDrawMax(0) { +} + +void SkActive::init() +{ + fAnimators = fApply.fAnimators; + int animators = fAnimators.count(); + fInterpolators.setCount(animators); + memset(fInterpolators.begin(), 0, animators * sizeof(SkOperandInterpolator*)); + fState.setCount(animators); + int index; + for (index = 0; index < animators; index++) + fInterpolators[index] = SkNEW(SkOperandInterpolator); + initState(&fApply, 0); +// for (index = 0; index < animators; index++) +// fState[index].bumpSave(); + SkASSERT(fInterpolators.count() == fAnimators.count()); +} + +SkActive::~SkActive() { + int index; + for (index = 0; index < fSaveRestore.count(); index++) + delete[] fSaveRestore[index]; + for (index = 0; index < fSaveInterpolators.count(); index++) + delete[] fSaveInterpolators[index]; + for (index = 0; index < fInterpolators.count(); index++) + delete fInterpolators[index]; +} + +void SkActive::advance() { + if (fDrawMax < fDrawIndex) + fDrawMax = fDrawIndex; + fDrawIndex += fAnimators.count(); +} + +void SkActive::append(SkApply* apply) { + int oldCount = fAnimators.count(); + SkTDAnimateArray& animates = apply->fAnimators; + int newCount = animates.count(); + int index; + int total = oldCount + newCount; + if (total == 0) + return; + fInterpolators.setCount(total); + memset(&fInterpolators.begin()[oldCount], 0, newCount * sizeof(SkOperandInterpolator*)); + for (index = oldCount; index < total; index++) + fInterpolators[index] = SkNEW(SkOperandInterpolator); + fAnimators.setCount(total); + memcpy(&fAnimators[oldCount], animates.begin(), sizeof(fAnimators[0]) * + newCount); + fState.setCount(total); + initState(apply, oldCount); + SkASSERT(fApply.scope == apply->scope); + for (index = 0; index < newCount; index++) { + SkAnimateBase* test = animates[index]; +// SkASSERT(fApply.scope == test->fTarget || fApply.scope->contains(test->fTarget)); + SkActive::SkState& testState = fState[oldCount + index]; + for (int inner = 0; inner < oldCount; inner++) { + SkAnimateBase* oldGuard = fAnimators[inner]; + SkActive::SkState& oldState = fState[inner]; + if (oldGuard->fTarget == test->fTarget && oldGuard->fFieldInfo == test->fFieldInfo && + testState.fBegin == oldState.fBegin) { + delete fInterpolators[inner]; + fInterpolators.remove(inner); + fAnimators.remove(inner); + testState.fSave = oldState.fSave; + if (oldState.fUnpostedEndEvent) { +// SkDEBUGF(("%8x %8x active append: post on end\n", this, oldGuard)); + fMaker.postOnEnd(oldGuard, oldState.fBegin + oldState.fDuration); + } + fState.remove(inner); + if (fApply.restore) { + int saveIndex = fSaveRestore.count(); + SkASSERT(fSaveInterpolators.count() == saveIndex); + saveIndex += inner; + do { + saveIndex -= oldCount; + delete[] fSaveRestore[saveIndex]; + fSaveRestore.remove(saveIndex); + delete[] fSaveInterpolators[saveIndex]; + fSaveInterpolators.remove(saveIndex); + } while (saveIndex > 0); + } + oldCount--; + break; + } + } + } +// total = oldCount + newCount; +// for (index = oldCount; index < total; index++) +// fState[index].bumpSave(); + SkASSERT(fInterpolators.count() == fAnimators.count()); +} + +void SkActive::appendSave(int oldCount) { + SkASSERT(fDrawMax == 0); // if true, we can optimize below quite a bit + int newCount = fAnimators.count(); + int saveIndex = fSaveRestore.count(); + SkASSERT(fSaveInterpolators.count() == saveIndex); + int records = saveIndex / oldCount; + int newTotal = records * newCount; + fSaveRestore.setCount(newTotal); + do { + saveIndex -= oldCount; + newTotal -= newCount; + SkASSERT(saveIndex >= 0); + SkASSERT(newTotal >= 0); + memmove(&fSaveRestore[newTotal], &fSaveRestore[saveIndex], oldCount); + memset(&fSaveRestore[newTotal + oldCount], 0, + sizeof(fSaveRestore[0]) * (newCount - oldCount)); + memmove(&fSaveInterpolators[newTotal], + &fSaveInterpolators[saveIndex], oldCount); + memset(&fSaveInterpolators[newTotal + oldCount], 0, + sizeof(fSaveRestore[0]) * (newCount - oldCount)); + } while (saveIndex > 0); + SkASSERT(newTotal == 0); +} + +void SkActive::calcDurations(int index) +{ + SkAnimateBase* animate = fAnimators[index]; + SkMSec duration = animate->dur; + SkState& state = fState[index]; + if (state.fMode == SkApply::kMode_immediate || state.fMode == SkApply::kMode_create) + duration = state.fSteps ? state.fSteps * SK_MSec1 : 1; +// else if (state.fMode == SkApply::kMode_hold) { +// int entries = animate->entries(); +// SkScriptValue value; +// value.fOperand = animate->getValues()[entries - 1]; +// value.fType = animate->getValuesType(); +// bool result = SkScriptEngine::ConvertTo(NULL, SkType_Int, &value); +// SkASSERT(result); +// duration = value.fOperand.fS32 * SK_MSec1; +// } + state.fDuration = duration; + SkMSec maxTime = state.fBegin + duration; + if (fMaxTime < maxTime) + fMaxTime = maxTime; +} + +void SkActive::create(SkDrawable* drawable, SkMSec time) { + fApply.fLastTime = time; + fApply.refresh(fMaker); + for (int index = 0; index < fAnimators.count(); index++) { + SkAnimateBase* animate = fAnimators[index]; + SkOperandInterpolator& interpolator = *fInterpolators[index]; + int count = animate->components(); + if (animate->formula.size() > 0) { + SkTDOperandArray values; + values.setCount(count); + bool success = animate->fFieldInfo->setValue(fMaker, &values, 0, 0, NULL, + animate->getValuesType(), animate->formula); + SkASSERT(success); + fApply.applyValues(index, values.begin(), count, animate->getValuesType(), time); + } else { + SkAutoSTMalloc<16, SkOperand> values(count); + interpolator.timeToValues(time, values.get()); + fApply.applyValues(index, values.get(), count, animate->getValuesType(), time); + } + } + drawable->enable(fMaker); + SkASSERT(fAnimators.count() == fInterpolators.count()); +} + +bool SkActive::immediate(bool enable) { + SkMSec time = 0; + bool result = false; + SkDrawable* drawable = fApply.scope; + SkMSec final = fMaxTime; + do { + bool applied = fAnimators.count() == 0; + fApply.fLastTime = time; + fApply.refresh(fMaker); + for (int index = 0; index < fAnimators.count(); index++) { + SkAnimateBase* animate = fAnimators[index]; + SkState& state = fState[index]; + if (state.fMode != SkApply::kMode_immediate) + continue; + if (state.fBegin > time) + continue; + if (time > state.fBegin + state.fDuration) + continue; + applied = true; + SkOperandInterpolator& interpolator = *fInterpolators[index]; + int count = animate->components(); + if (animate->formula.size() > 0) { + SkTDOperandArray values; + values.setCount(count); + bool success = animate->fFieldInfo->setValue(fMaker, &values, 0, 0, NULL, + animate->getValuesType(), animate->formula); + SkASSERT(success); + fApply.applyValues(index, values.begin(), count, animate->getValuesType(), time); + } else { + SkAutoSTMalloc<16, SkOperand> values(count); + interpolator.timeToValues(time, values.get()); + fApply.applyValues(index, values.get(), count, animate->getValuesType(), time); + } + } + if (enable) + drawable->enable(fMaker); + else if (applied) + result |= drawable->draw(fMaker); + time += SK_MSec1; + } while (time <= final); + return result; +} + +void SkActive::fixInterpolator(SkBool save) { + int animators = fAnimators.count(); + for (int index = 0; index < animators; index++) { + SkAnimateBase* animate = fAnimators[index]; + if (save) { // saved slots increased + animate->refresh(fMaker); + SkOperand* values = animate->getValues(); + setInterpolator(index, values); + saveInterpolatorValues(index); + } else + restoreInterpolatorValues(index); + } +} + +SkMSec SkActive::getTime(SkMSec inTime, int animatorIndex) { + fState[animatorIndex].fTicks = inTime; + return inTime - fState[animatorIndex].fStartTime; +} + +bool SkActive::initializeSave() { + int animators = fAnimators.count(); + int activeTotal = fDrawIndex + animators; + int oldCount = fSaveRestore.count(); + if (oldCount < activeTotal) { + fSaveRestore.setCount(activeTotal); + memset(&fSaveRestore[oldCount], 0, sizeof(fSaveRestore[0]) * (activeTotal - oldCount)); + SkASSERT(fSaveInterpolators.count() == oldCount); + fSaveInterpolators.setCount(activeTotal); + memset(&fSaveInterpolators[oldCount], 0, + sizeof(fSaveInterpolators[0]) * (activeTotal - oldCount)); + return true; + } + return false; +} + +void SkActive::initState(SkApply* apply, int offset) { + int count = fState.count(); + for (int index = offset; index < count; index++) { + SkState& state = fState[index]; + SkAnimateBase* animate = fAnimators[index]; +#if 0 // def SK_DEBUG + if (animate->fHasEndEvent) + SkDebugf("%8x %8x active initState:\n", this, animate); +#endif + SkOperand* from = animate->getValues(); + state.fStartTime = state.fBegin = apply->begin + animate->begin; + state.fMode = apply->mode; + state.fTransition = apply->transition; +#if 0 + state.fPickup = (SkBool8) apply->pickup; +#endif + state.fRestore = (SkBool8) apply->restore; + state.fSave = apply->begin; + state.fStarted = false; + state.fSteps = apply->steps; + state.fTicks = 0; + state.fUnpostedEndEvent = (SkBool8) animate->fHasEndEvent; + calcDurations(index); + setInterpolator(index, from); + } + if (count == 0 && (apply->mode == SkApply::kMode_immediate || apply->mode == SkApply::kMode_create)) + fMaxTime = apply->begin + apply->steps * SK_MSec1; +} + +void SkActive::pickUp(SkActive* existing) { + SkTDOperandArray existingValues; + for (int index = 0; index < fAnimators.count(); index++) { + SkAnimateBase* animate = fAnimators[index]; + SkASSERT(animate->getValuesType() == SkType_Float); + int components = animate->components(); + SkOperand* from = animate->getValues(); + SkOperand* to = &from[animate->components()]; + existingValues.setCount(components); + existing->fInterpolators[index]->timeToValues( + existing->fState[index].fTicks - existing->fState[index].fStartTime, existingValues.begin()); + SkScalar originalSum = 0; + SkScalar workingSum = 0; + for (int cIndex = 0; cIndex < components; cIndex++) { + SkScalar delta = to[cIndex].fScalar - from[cIndex].fScalar; + originalSum += SkScalarMul(delta, delta); + delta = to[cIndex].fScalar - existingValues[cIndex].fScalar; + workingSum += SkScalarMul(delta, delta); + } + if (workingSum < originalSum) { + SkScalar originalDistance = SkScalarSqrt(originalSum); + SkScalar workingDistance = SkScalarSqrt(workingSum); + existing->fState[index].fDuration = (SkMSec) SkScalarMulDiv(fState[index].fDuration, + workingDistance, originalDistance); + } + fInterpolators[index]->reset(components, 2, SkType_Float); + fInterpolators[index]->setKeyFrame(0, 0, existingValues.begin(), animate->blend[0]); + fInterpolators[index]->setKeyFrame(1, fState[index].fDuration, to, animate->blend[0]); + } +} + +void SkActive::resetInterpolators() { + int animators = fAnimators.count(); + for (int index = 0; index < animators; index++) { + SkAnimateBase* animate = fAnimators[index]; + SkOperand* values = animate->getValues(); + setInterpolator(index, values); + } +} + +void SkActive::resetState() { + fDrawIndex = 0; + int count = fState.count(); + for (int index = 0; index < count; index++) { + SkState& state = fState[index]; + SkAnimateBase* animate = fAnimators[index]; +#if 0 // def SK_DEBUG + if (animate->fHasEndEvent) + SkDebugf("%8x %8x active resetState: has end event\n", this, animate); +#endif + state.fStartTime = state.fBegin = fApply.begin + animate->begin; + state.fStarted = false; + state.fTicks = 0; + } +} + +void SkActive::restoreInterpolatorValues(int index) { + SkOperandInterpolator& interpolator = *fInterpolators[index]; + index += fDrawIndex ; + int count = interpolator.getValuesCount(); + memcpy(interpolator.getValues(), fSaveInterpolators[index], count * sizeof(SkOperand)); +} + +void SkActive::saveInterpolatorValues(int index) { + SkOperandInterpolator& interpolator = *fInterpolators[index]; + index += fDrawIndex ; + int count = interpolator.getValuesCount(); + SkOperand* cache = new SkOperand[count]; // this should use sk_malloc/sk_free since SkOperand does not have a constructor/destructor + fSaveInterpolators[index] = cache; + memcpy(cache, interpolator.getValues(), count * sizeof(SkOperand)); +} + +void SkActive::setInterpolator(int index, SkOperand* from) { + if (from == NULL) // legitimate for set string + return; + SkAnimateBase* animate = fAnimators[index]; + int entries = animate->entries(); + SkASSERT(entries > 0); + SkMSec duration = fState[index].fDuration; + int components = animate->components(); + SkOperandInterpolator& interpolator = *fInterpolators[index]; + interpolator.reset(components, entries == 1 ? 2 : entries, animate->getValuesType()); + interpolator.setMirror(SkToBool(animate->fMirror)); + interpolator.setReset(SkToBool(animate->fReset)); + interpolator.setRepeatCount(animate->repeat); + if (entries == 1) { + interpolator.setKeyFrame(0, 0, from, animate->blend[0]); + interpolator.setKeyFrame(1, duration, from, animate->blend[0]); + return; + } + for (int entry = 0; entry < entries; entry++) { + int blendIndex = SkMin32(animate->blend.count() - 1, entry); + interpolator.setKeyFrame(entry, entry * duration / (entries - 1), from, + animate->blend[blendIndex]); + from += components; + } +} + +void SkActive::setSteps(int steps) { + int count = fState.count(); + fMaxTime = 0; + for (int index = 0; index < count; index++) { + SkState& state = fState[index]; + state.fSteps = steps; + calcDurations(index); + } +} + +void SkActive::start() { + int count = fState.count(); + SkASSERT(count == fAnimators.count()); + SkASSERT(count == fInterpolators.count()); + for (int index = 0; index < count; index++) { + SkState& state = fState[index]; + if (state.fStarted) + continue; + state.fStarted = true; +#if defined SK_DEBUG && defined SK_DEBUG_ANIMATION_TIMING + SkString debugOut; + SkMSec time = fMaker.getAppTime(); + debugOut.appendS32(time - fMaker.fDebugTimeBase); + debugOut.append(" active start adjust delay id="); + debugOut.append(fApply._id); + debugOut.append("; "); + debugOut.append(fAnimators[index]->_id); + debugOut.append("="); + debugOut.appendS32(fAnimators[index]->fStart - fMaker.fDebugTimeBase); + debugOut.append(":"); + debugOut.appendS32(state.fStartTime); +#endif + if (state.fStartTime > 0) { + SkMSec future = fAnimators[index]->fStart + state.fStartTime; + if (future > fMaker.fEnableTime) + fMaker.notifyInvalTime(future); + else + fMaker.notifyInval(); +#if defined SK_DEBUG && defined SK_DEBUG_ANIMATION_TIMING + debugOut.append(":"); + debugOut.appendS32(future - fMaker.fDebugTimeBase); +#endif + } + if (state.fStartTime >= fMaker.fAdjustedStart) { + state.fStartTime -= fMaker.fAdjustedStart; +#if defined SK_DEBUG && defined SK_DEBUG_ANIMATION_TIMING + debugOut.append(" (less adjust = "); + debugOut.appendS32(fMaker.fAdjustedStart); +#endif + } + state.fStartTime += fAnimators[index]->fStart; +#if defined SK_DEBUG && defined SK_DEBUG_ANIMATION_TIMING + debugOut.append(") new start = "); + debugOut.appendS32(state.fStartTime - fMaker.fDebugTimeBase); + SkDebugf("%s\n", debugOut.c_str()); +// SkASSERT((int) (state.fStartTime - fMaker.fDebugTimeBase) >= 0); +#endif + } + SkASSERT(fAnimators.count() == fInterpolators.count()); +} + +#ifdef SK_DEBUG +void SkActive::validate() { + int count = fState.count(); + SkASSERT(count == fAnimators.count()); + SkASSERT(count == fInterpolators.count()); + for (int index = 0; index < count; index++) { + SkASSERT(fAnimators[index]); + SkASSERT(fInterpolators[index]); +// SkAnimateBase* test = fAnimators[index]; +// SkASSERT(fApply.scope == test->fTarget || fApply.scope->contains(test->fTarget)); + } +} +#endif + +// think about this +// there should only be one animate object, not two, to go up and down +// when the apply with reverse came into play, it needs to pick up the value +// of the existing animate object then remove it from the list +// the code below should only be bumping fSave, and there shouldn't be anything +// it needs to be synchronized with + +// however, if there are two animates both operating on the same field, then +// when one replaces the other, it may make sense to pick up the old value as a starting +// value for the new one somehow. + +//void SkActive::SkState::bumpSave() { +// if (fMode != SkApply::kMode_hold) +// return; +// if (fTransition == SkApply::kTransition_reverse) { +// if (fSave > 0) +// fSave -= SK_MSec1; +// } else if (fSave < fDuration) +// fSave += SK_MSec1; +//} + +SkMSec SkActive::SkState::getRelativeTime(SkMSec time) { + SkMSec result = time; +// if (fMode == SkApply::kMode_hold) +// result = fSave; +// else + if (fTransition == SkApply::kTransition_reverse) { + if (SkMSec_LT(fDuration, time)) + result = 0; + else + result = fDuration - time; + } + return result; +} + + diff --git a/skia/animator/SkAnimateActive.h b/skia/animator/SkAnimateActive.h new file mode 100644 index 0000000..9f6756a --- /dev/null +++ b/skia/animator/SkAnimateActive.h @@ -0,0 +1,87 @@ +/* libs/graphics/animator/SkAnimateActive.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkAnimateActive_DEFINED +#define SkAnimateActive_DEFINED + +#include "SkDisplayApply.h" +#include "SkOperandInterpolator.h" +#include "SkIntArray.h" + +class SkAnimateMaker; + +class SkActive { +public: + SkActive(SkApply& , SkAnimateMaker& ); + ~SkActive(); + void advance(); + void append(SkApply* ); + void calcDurations(int index); + void create(SkDrawable* scope, SkMSec time); + bool draw() { return immediate(false); } + bool enable() { return immediate(true); } + void init( ); + SkMSec getTime(SkMSec inTime, int animatorIndex); + void pickUp(SkActive* existing); + void reset() { fDrawIndex = 0; } + void setInterpolator(int index, SkOperand* from); + void start(); +#ifdef SK_DEBUG + void validate(); +#endif +private: + void appendSave(int oldCount); + void fixInterpolator(SkBool save); + bool immediate(bool enable); + bool initializeSave(); + void initState(SkApply* , int offset); + void resetInterpolators(); + void resetState(); + void restoreInterpolatorValues(int index); + void saveInterpolatorValues(int index); + void setSteps(int steps); + struct SkState { +// void bumpSave(); + SkMSec getRelativeTime(SkMSec time); + SkApply::Mode fMode; + SkApply::Transition fTransition; + SkBool8 fPickup; + SkBool8 fRestore; + SkBool8 fStarted; + SkBool8 fUnpostedEndEvent; + int32_t fSteps; + SkMSec fBegin; + SkMSec fStartTime; + SkMSec fDuration; + SkMSec fSave; + SkMSec fTicks; + }; + SkActive& operator= (const SkActive& ); + SkTDArray<SkOperandInterpolator*> fInterpolators; + SkApply& fApply; + SkTDArray<SkState> fState; // one per animator + SkTDOperandPtrArray fSaveRestore; // if apply has restore="true" + SkTDOperandPtrArray fSaveInterpolators; + SkTDAnimateArray fAnimators; + SkMSec fMaxTime; // greatest of all animation durations; only used by immediate mode + SkAnimateMaker& fMaker; + int fDrawIndex; + int fDrawMax; + friend class SkApply; +}; + +#endif // SkAnimateActive_DEFINED diff --git a/skia/animator/SkAnimateBase.cpp b/skia/animator/SkAnimateBase.cpp new file mode 100644 index 0000000..cd11442 --- /dev/null +++ b/skia/animator/SkAnimateBase.cpp @@ -0,0 +1,247 @@ +/* libs/graphics/animator/SkAnimateBase.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkAnimateBase.h" +#include "SkAnimateMaker.h" +#include "SkAnimateProperties.h" +#include "SkAnimatorScript.h" +#include "SkDisplayApply.h" +#include "SkDrawable.h" + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkAnimateBase::fInfo[] = { + SK_MEMBER(begin, MSec), + SK_MEMBER_ARRAY(blend, Float), + SK_MEMBER(dur, MSec), + SK_MEMBER_PROPERTY(dynamic, Boolean), + SK_MEMBER(field, String), // name of member info in target + SK_MEMBER(formula, DynamicString), + SK_MEMBER(from, DynamicString), + SK_MEMBER(lval, DynamicString), + SK_MEMBER_PROPERTY(mirror, Boolean), + SK_MEMBER(repeat, Float), + SK_MEMBER_PROPERTY(reset, Boolean), + SK_MEMBER_PROPERTY(step, Int), + SK_MEMBER(target, DynamicString), + SK_MEMBER(to, DynamicString), + SK_MEMBER_PROPERTY(values, DynamicString) +}; + +#endif + +DEFINE_GET_MEMBER(SkAnimateBase); + +SkAnimateBase::SkAnimateBase() : begin(0), dur(1), repeat(SK_Scalar1), + fApply(NULL), fFieldInfo(NULL), fFieldOffset(0), fStart((SkMSec) -1), fTarget(NULL), + fChanged(0), fDelayed(0), fDynamic(0), fHasEndEvent(0), fHasValues(0), + fMirror(0), fReset(0), fResetPending(0), fTargetIsScope(0) { + blend.setCount(1); + blend[0] = SK_Scalar1; +} + +SkAnimateBase::~SkAnimateBase() { + SkDisplayTypes type = fValues.getType(); + if (type == SkType_String || type == SkType_DynamicString) { + SkASSERT(fValues.count() == 1); + delete fValues[0].fString; + } +} + +int SkAnimateBase::components() { + return 1; +} + +SkDisplayable* SkAnimateBase::deepCopy(SkAnimateMaker* maker) { + SkAnimateBase* result = (SkAnimateBase*) INHERITED::deepCopy(maker); + result->fApply = fApply; + result->fFieldInfo =fFieldInfo; + result->fHasValues = false; + return result; +} + +void SkAnimateBase::dirty() { + fChanged = true; +} + +#ifdef SK_DUMP_ENABLED +void SkAnimateBase::dump(SkAnimateMaker* maker) { + dumpBase(maker); + if (target.size() > 0) + SkDebugf("target=\"%s\" ", target.c_str()); + else if (fTarget && strcmp(fTarget->id, "")) + SkDebugf("target=\"%s\" ", fTarget->id); + if (lval.size() > 0) + SkDebugf("lval=\"%s\" ", lval.c_str()); + if (field.size() > 0) + SkDebugf("field=\"%s\" ", field.c_str()); + else if (fFieldInfo) + SkDebugf("field=\"%s\" ", fFieldInfo->fName); + if (formula.size() > 0) + SkDebugf("formula=\"%s\" ", formula.c_str()); + else { + if (from.size() > 0) + SkDebugf("from=\"%s\" ", from.c_str()); + SkDebugf("to=\"%s\" ", to.c_str()); + } + if (begin != 0) { +#ifdef SK_CAN_USE_FLOAT + SkDebugf("begin=\"%g\" ", SkScalarToFloat(SkScalarDiv(begin,1000))); +#else + SkDebugf("begin=\"%x\" ", SkScalarDiv(begin,1000)); +#endif + } +} +#endif + +SkDisplayable* SkAnimateBase::getParent() const { + return (SkDisplayable*) fApply; +} + +bool SkAnimateBase::getProperty(int index, SkScriptValue* value) const { + int boolResult; + switch (index) { + case SK_PROPERTY(dynamic): + boolResult = fDynamic; + goto returnBool; + case SK_PROPERTY(mirror): + boolResult = fMirror; + goto returnBool; + case SK_PROPERTY(reset): + boolResult = fReset; +returnBool: + value->fOperand.fS32 = SkToBool(boolResult); + value->fType = SkType_Boolean; + break; + case SK_PROPERTY(step): + if (fApply == NULL) + return false; // !!! notify there's an error? + fApply->getStep(value); + break; + case SK_PROPERTY(values): + value->fOperand.fString = (SkString*) &to; + value->fType = SkType_String; + break; + default: + SkASSERT(0); + return false; + } + return true; +} + +bool SkAnimateBase::hasExecute() const +{ + return false; +} + +void SkAnimateBase::onEndElement(SkAnimateMaker& maker) { + fChanged = false; + setTarget(maker); + if (field.size()) { + SkASSERT(fTarget); + fFieldInfo = fTarget->getMember(field.c_str()); + field.reset(); + } + if (lval.size()) { + // lval must be of the form x[y] + const char* lvalStr = lval.c_str(); + const char* arrayEnd = strchr(lvalStr, '['); + if (arrayEnd == NULL) + return; //should this return an error? + size_t arrayNameLen = arrayEnd - lvalStr; + SkString arrayStr(lvalStr, arrayNameLen); + SkASSERT(fTarget); //this return an error? + fFieldInfo = fTarget->getMember(arrayStr.c_str()); + SkString scriptStr(arrayEnd + 1, lval.size() - arrayNameLen - 2); + SkAnimatorScript::EvaluateInt(maker, this, scriptStr.c_str(), &fFieldOffset); + } +} + +void SkAnimateBase::packARGB(SkScalar array[], int count, SkTDOperandArray* converted) +{ + SkASSERT(count == 4); + converted->setCount(1); + SkColor color = SkColorSetARGB(SkScalarRound(array[0]), SkScalarRound(array[1]), + SkScalarRound(array[2]), SkScalarRound(array[3])); + (*converted)[0].fS32 = color; +} + + + +void SkAnimateBase::refresh(SkAnimateMaker& ) { +} + +bool SkAnimateBase::setParent(SkDisplayable* apply) { + SkASSERT(apply->isApply()); + fApply = (SkApply*) apply; + return false; +} + +bool SkAnimateBase::setProperty(int index, SkScriptValue& value) { + bool boolValue = SkToBool(value.fOperand.fS32); + switch (index) { + case SK_PROPERTY(dynamic): + fDynamic = boolValue; + goto checkForBool; + case SK_PROPERTY(values): + fHasValues = true; + SkASSERT(value.fType == SkType_String); + to = *value.fOperand.fString; + break; + case SK_PROPERTY(mirror): + fMirror = boolValue; + goto checkForBool; + case SK_PROPERTY(reset): + fReset = boolValue; +checkForBool: + SkASSERT(value.fType == SkType_Boolean); + break; + default: + return false; + } + return true; +} + +void SkAnimateBase::setTarget(SkAnimateMaker& maker) { + if (target.size()) { + SkAnimatorScript engine(maker, this, SkType_Displayable); + const char* script = target.c_str(); + SkScriptValue scriptValue; + bool success = engine.evaluateScript(&script, &scriptValue); + if (success && scriptValue.fType == SkType_Displayable) + fTarget = scriptValue.fOperand.fDrawable; + else if (maker.find(target.c_str(), (SkDisplayable**) &fTarget) == false) { + if (fApply->getMode() == SkApply::kMode_create) + return; // may not be an error + if (engine.getError() != SkScriptEngine::kNoError) + maker.setScriptError(engine); + else { + maker.setErrorNoun(target); + maker.setErrorCode(SkDisplayXMLParserError::kTargetIDNotFound); + } + return; + } + if (fApply && fApply->getMode() != SkApply::kMode_create) + target.reset(); + } +} + +bool SkAnimateBase::targetNeedsInitialization() const { + return false; +} + + diff --git a/skia/animator/SkAnimateBase.h b/skia/animator/SkAnimateBase.h new file mode 100644 index 0000000..5f7b1ec --- /dev/null +++ b/skia/animator/SkAnimateBase.h @@ -0,0 +1,91 @@ +/* libs/graphics/animator/SkAnimateBase.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkAnimateBase_DEFINED +#define SkAnimateBase_DEFINED + +#include "SkDisplayable.h" +#include "SkMath.h" +#include "SkMemberInfo.h" +#include "SkTypedArray.h" + +class SkApply; +class SkDrawable; + +class SkAnimateBase : public SkDisplayable { +public: + DECLARE_MEMBER_INFO(AnimateBase); + SkAnimateBase(); + virtual ~SkAnimateBase(); + virtual int components(); + virtual SkDisplayable* deepCopy(SkAnimateMaker* ); + virtual void dirty(); +#ifdef SK_DUMP_ENABLED + virtual void dump(SkAnimateMaker* ); +#endif + int entries() { return fValues.count() / components(); } + virtual bool hasExecute() const; + bool isDynamic() const { return SkToBool(fDynamic); } + virtual SkDisplayable* getParent() const; + virtual bool getProperty(int index, SkScriptValue* value) const; + SkMSec getStart() const { return fStart; } + SkOperand* getValues() { return fValues.begin(); } + SkDisplayTypes getValuesType() { return fValues.getType(); } + virtual void onEndElement(SkAnimateMaker& ); + void packARGB(SkScalar [], int count, SkTDOperandArray* ); + virtual void refresh(SkAnimateMaker& ); + void setChanged(bool changed) { fChanged = changed; } + void setHasEndEvent() { fHasEndEvent = true; } + virtual bool setParent(SkDisplayable* ); + virtual bool setProperty(int index, SkScriptValue& value); + void setTarget(SkAnimateMaker& ); + virtual bool targetNeedsInitialization() const; +protected: + SkMSec begin; + SkTDScalarArray blend; + SkMSec dur; + // !!! make field part of a union with fFieldInfo, or fValues, something known later? + SkString field; // temporary; once target is known, this is reset + SkString formula; + SkString from; + SkString lval; + SkScalar repeat; + SkString target; // temporary; once target is known, this is reset + SkString to; + SkApply* fApply; + const SkMemberInfo* fFieldInfo; + int fFieldOffset; + SkMSec fStart; // corrected time when this apply was enabled + SkDrawable* fTarget; + SkTypedArray fValues; + unsigned fChanged : 1; // true when value referenced by script has changed + unsigned fDelayed : 1; // enabled, but undrawn pending delay + unsigned fDynamic : 1; + unsigned fHasEndEvent : 1; + unsigned fHasValues : 1; // set if 'values' passed instead of 'to' + unsigned fMirror : 1; + unsigned fReset : 1; + unsigned fResetPending : 1; + unsigned fTargetIsScope : 1; +private: + typedef SkDisplayable INHERITED; + friend class SkActive; + friend class SkApply; + friend class SkDisplayList; +}; + +#endif // SkAnimateBase_DEFINED diff --git a/skia/animator/SkAnimateField.cpp b/skia/animator/SkAnimateField.cpp new file mode 100644 index 0000000..67381ea --- /dev/null +++ b/skia/animator/SkAnimateField.cpp @@ -0,0 +1,130 @@ +/* libs/graphics/animator/SkAnimateField.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkAnimate.h" +#include "SkAnimateMaker.h" +#include "SkDrawable.h" +#include "SkParse.h" + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkAnimate::fInfo[] = { + SK_MEMBER_INHERITED +}; + +#endif + +DEFINE_GET_MEMBER(SkAnimate); + +SkAnimate::SkAnimate() : fComponents(0) { +} + +SkAnimate::~SkAnimate() { +} + +int SkAnimate::components() { + return fComponents; +} + +#ifdef SK_DUMP_ENABLED +void SkAnimate::dump(SkAnimateMaker* maker) { + INHERITED::dump(maker); //from animateBase + //SkSet inherits from this class + if (getType() != SkType_Set) { + if (fMirror) + SkDebugf("mirror=\"true\" "); + if (fReset) + SkDebugf("reset=\"true\" "); +#ifdef SK_CAN_USE_FLOAT + SkDebugf("dur=\"%g\" ", SkScalarToFloat(SkScalarDiv(dur,1000))); + if (repeat != SK_Scalar1) + SkDebugf("repeat=\"%g\" ", SkScalarToFloat(repeat)); +#else + SkDebugf("dur=\"%x\" ", SkScalarDiv(dur,1000)); + if (repeat != SK_Scalar1) + SkDebugf("repeat=\"%x\" ", repeat); +#endif + //if (fHasValues) + // SkDebugf("values=\"%s\" ", values); + if (blend.count() != 1 || blend[0] != SK_Scalar1) { + SkDebugf("blend=\"["); + bool firstElem = true; + for (int i = 0; i < blend.count(); i++) { + if (!firstElem) + SkDebugf(","); + firstElem = false; +#ifdef SK_CAN_USE_FLOAT + SkDebugf("%g", SkScalarToFloat(blend[i])); +#else + SkDebugf("%x", blend[i]); +#endif + } + SkDebugf("]\" "); + } + SkDebugf("/>\n");//i assume that if it IS, we will do it separately + } +} +#endif + +bool SkAnimate::resolveCommon(SkAnimateMaker& maker) { + if (fTarget == NULL) // if NULL, recall onEndElement after apply closes and sets target to scope + return false; + INHERITED::onEndElement(maker); + return maker.hasError() == false; +} + +void SkAnimate::onEndElement(SkAnimateMaker& maker) { + bool resolved = resolveCommon(maker); + if (resolved && fFieldInfo == NULL) { + maker.setErrorNoun(field); + maker.setErrorCode(SkDisplayXMLParserError::kFieldNotInTarget); + } + if (resolved == false || fFieldInfo == NULL) + return; + SkDisplayTypes outType = fFieldInfo->getType(); + if (fHasValues) { + SkASSERT(to.size() > 0); + fFieldInfo->setValue(maker, &fValues, 0, 0, NULL, outType, to); + SkASSERT(0); + // !!! this needs to set fComponents + return; + } + fComponents = fFieldInfo->getCount(); + if (fFieldInfo->fType == SkType_Array) { + SkTypedArray* array = (SkTypedArray*) fFieldInfo->memberData(fTarget); + int count = array->count(); + if (count > 0) + fComponents = count; + } + if (outType == SkType_ARGB) { + fComponents <<= 2; // four color components + outType = SkType_Float; + } + fValues.setType(outType); + if (formula.size() > 0){ + fComponents = 1; + from.set("0"); + to.set("dur"); + outType = SkType_MSec; + } + int max = fComponents * 2; + fValues.setCount(max); + memset(fValues.begin(), 0, max * sizeof(fValues.begin()[0])); + fFieldInfo->setValue(maker, &fValues, fFieldOffset, max, this, outType, from); + fFieldInfo->setValue(maker, &fValues, fComponents + fFieldOffset, max, this, outType, to); +} + diff --git a/skia/animator/SkAnimateMaker.cpp b/skia/animator/SkAnimateMaker.cpp new file mode 100644 index 0000000..82c3f9a --- /dev/null +++ b/skia/animator/SkAnimateMaker.cpp @@ -0,0 +1,376 @@ +/* libs/graphics/animator/SkAnimateMaker.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkAnimateMaker.h" +#include "SkAnimator.h" +#include "SkAnimatorScript.h" +#include "SkDisplayable.h" +#include "SkDisplayApply.h" +#include "SkDisplayList.h" +#include "SkDisplayMovie.h" +#include "SkDisplayType.h" +#include "SkExtras.h" +#include "SkMemberInfo.h" +#include "SkStream.h" +#include "SkSystemEventTypes.h" +#include "SkTime.h" + +class DefaultTimeline : public SkAnimator::Timeline { + virtual SkMSec getMSecs() const { + return SkTime::GetMSecs(); + } +} gDefaultTimeline; + +SkAnimateMaker::SkAnimateMaker(SkAnimator* animator, SkCanvas* canvas, SkPaint* paint) + : fActiveEvent(NULL), fAdjustedStart(0), fCanvas(canvas), fEnableTime(0), + fHostEventSinkID(0), fMinimumInterval((SkMSec) -1), fPaint(paint), fParentMaker(NULL), + fTimeline(&gDefaultTimeline), fInInclude(false), fInMovie(false), + fFirstScriptError(false), fLoaded(false), fIDs(256), fAnimator(animator) +{ + fScreenplay.time = 0; +#if defined SK_DEBUG && defined SK_DEBUG_ANIMATION_TIMING + fDebugTimeBase = (SkMSec) -1; +#endif +#ifdef SK_DUMP_ENABLED + fDumpEvents = fDumpGConditions = fDumpPosts = false; +#endif +} + +SkAnimateMaker::~SkAnimateMaker() { + deleteMembers(); +} + +#if 0 +SkMSec SkAnimateMaker::adjustDelay(SkMSec expectedBase, SkMSec delay) { + SkMSec appTime = (*fTimeCallBack)(); + if (appTime) + delay -= appTime - expectedBase; + if (delay < 0) + delay = 0; + return delay; +} +#endif + +void SkAnimateMaker::appendActive(SkActive* active) { + fDisplayList.append(active); +} + +void SkAnimateMaker::clearExtraPropertyCallBack(SkDisplayTypes type) { + SkExtras** end = fExtras.end(); + for (SkExtras** extraPtr = fExtras.begin(); extraPtr < end; extraPtr++) { + SkExtras* extra = *extraPtr; + if (extra->definesType(type)) { + extra->fExtraCallBack = NULL; + extra->fExtraStorage = NULL; + break; + } + } +} + +bool SkAnimateMaker::computeID(SkDisplayable* displayable, SkDisplayable* parent, SkString* newID) { + const char* script; + if (findKey(displayable, &script) == false) + return true; + return SkAnimatorScript::EvaluateString(*this, displayable, parent, script, newID); +} + +SkDisplayable* SkAnimateMaker::createInstance(const char name[], size_t len) { + SkDisplayTypes type = SkDisplayType::GetType(this, name, len ); + if ((int)type >= 0) + return SkDisplayType::CreateInstance(this, type); + return NULL; +} + +// differs from SkAnimator::decodeStream in that it does not reset error state +bool SkAnimateMaker::decodeStream(SkStream* stream) +{ + SkDisplayXMLParser parser(*this); + return parser.parse(*stream); +} + +// differs from SkAnimator::decodeURI in that it does not set URI base +bool SkAnimateMaker::decodeURI(const char uri[]) { +// SkDebugf("animator decode %s\n", uri); + +// SkStream* stream = SkStream::GetURIStream(fPrefix.c_str(), uri); + SkStream* stream = new SkFILEStream(uri); + + SkAutoTDelete<SkStream> autoDel(stream); + bool success = decodeStream(stream); + if (hasError() && fError.hasNoun() == false) + fError.setNoun(uri); + return success; +} + +#if defined SK_DEBUG && 0 +//used for the if'd out section of deleteMembers +#include "SkTSearch.h" + +extern "C" { + int compare_disp(const void* a, const void* b) { + return *(const SkDisplayable**)a - *(const SkDisplayable**)b; + } +} +#endif + +void SkAnimateMaker::delayEnable(SkApply* apply, SkMSec time) { + int index = fDelayed.find(apply); + if (index < 0) + *fDelayed.append() = apply; + (new SkEvent(SK_EventType_Delay))->postTime(fAnimator->getSinkID(), time); +} + +void SkAnimateMaker::deleteMembers() { + int index; +#if defined SK_DEBUG && 0 + //this code checks to see if helpers are among the children, but it is not complete - + //it should check the children of the children + int result; + SkTDArray<SkDisplayable*> children(fChildren.begin(), fChildren.count()); + SkQSort(children.begin(), children.count(), sizeof(SkDisplayable*),compare_disp); + for (index = 0; index < fHelpers.count(); index++) { + SkDisplayable* helper = fHelpers[index]; + result = SkTSearch(children.begin(), children.count(), helper, sizeof(SkDisplayable*)); + SkASSERT(result < 0); + } +#endif + for (index = 0; index < fChildren.count(); index++) { + SkDisplayable* child = fChildren[index]; + delete child; + } + for (index = 0; index < fHelpers.count(); index++) { + SkDisplayable* helper = fHelpers[index]; + delete helper; + } + for (index = 0; index < fExtras.count(); index++) { + SkExtras* extras = fExtras[index]; + delete extras; + } +} + +void SkAnimateMaker::doDelayedEvent() { + fEnableTime = getAppTime(); + for (int index = 0; index < fDelayed.count(); ) { + SkDisplayable* child = fDelayed[index]; + SkASSERT(child->isApply()); + SkApply* apply = (SkApply*) child; + apply->interpolate(*this, fEnableTime); + if (apply->hasDelayedAnimator()) + index++; + else + fDelayed.remove(index); + } +} + +bool SkAnimateMaker::doEvent(const SkEvent& event) { + return (!fInMovie || fLoaded) && fAnimator->doEvent(event); +} + +#ifdef SK_DUMP_ENABLED +void SkAnimateMaker::dump(const char* match) { + SkTDict<SkDisplayable*>::Iter iter(fIDs); + const char* name; + SkDisplayable* result; + while ((name = iter.next(&result)) != NULL) { + if (strcmp(match,name) == 0) + result->dump(this); + } +} +#endif + +int SkAnimateMaker::dynamicProperty(SkString& nameStr, SkDisplayable** displayablePtr ) { + const char* name = nameStr.c_str(); + const char* dot = strchr(name, '.'); + SkASSERT(dot); + SkDisplayable* displayable; + if (find(name, dot - name, &displayable) == false) { + SkASSERT(0); + return 0; + } + const char* fieldName = dot + 1; + const SkMemberInfo* memberInfo = displayable->getMember(fieldName); + *displayablePtr = displayable; + return (int) memberInfo->fOffset; +} + +SkMSec SkAnimateMaker::getAppTime() const { + return fTimeline->getMSecs(); +} + +#ifdef SK_DEBUG +SkAnimator* SkAnimateMaker::getRoot() +{ + SkAnimateMaker* maker = this; + while (maker->fParentMaker) + maker = maker->fParentMaker; + return maker == this ? NULL : maker->fAnimator; +} +#endif + +void SkAnimateMaker::helperAdd(SkDisplayable* trackMe) { + SkASSERT(fHelpers.find(trackMe) < 0); + *fHelpers.append() = trackMe; +} + +void SkAnimateMaker::helperRemove(SkDisplayable* alreadyTracked) { + int helperIndex = fHelpers.find(alreadyTracked); + if (helperIndex >= 0) + fHelpers.remove(helperIndex); +} + +#if 0 +void SkAnimateMaker::loadMovies() { + for (SkDisplayable** dispPtr = fMovies.begin(); dispPtr < fMovies.end(); dispPtr++) { + SkDisplayable* displayable = *dispPtr; + SkASSERT(displayable->getType() == SkType_Movie); + SkDisplayMovie* movie = (SkDisplayMovie*) displayable; + SkAnimateMaker* movieMaker = movie->fMovie.fMaker; + movieMaker->fEvents.doEvent(*movieMaker, SkDisplayEvent::kOnload, NULL); + movieMaker->fEvents.removeEvent(SkDisplayEvent::kOnload, NULL); + movieMaker->loadMovies(); + } +} +#endif + +void SkAnimateMaker::notifyInval() { + if (fHostEventSinkID) + fAnimator->onEventPost(new SkEvent(SK_EventType_Inval), fHostEventSinkID); +} + +void SkAnimateMaker::notifyInvalTime(SkMSec time) { + if (fHostEventSinkID) + fAnimator->onEventPostTime(new SkEvent(SK_EventType_Inval), fHostEventSinkID, time); +} + +void SkAnimateMaker::postOnEnd(SkAnimateBase* animate, SkMSec end) { + SkEvent evt; + evt.setS32("time", animate->getStart() + end); + evt.setPtr("anim", animate); + evt.setType(SK_EventType_OnEnd); + SkEventSinkID sinkID = fAnimator->getSinkID(); + fAnimator->onEventPost(new SkEvent(evt), sinkID); +} + +void SkAnimateMaker::reset() { + deleteMembers(); + fChildren.reset(); + fHelpers.reset(); + fIDs.reset(); + fEvents.reset(); + fDisplayList.hardReset(); +} + +void SkAnimateMaker::removeActive(SkActive* active) { + if (active == NULL) + return; + fDisplayList.remove(active); +} + +bool SkAnimateMaker::resolveID(SkDisplayable* displayable, SkDisplayable* original) { + SkString newID; + bool success = computeID(original, NULL, &newID); + if (success) + setID(displayable, newID); + return success; +} + +void SkAnimateMaker::setErrorString() { + fErrorString.reset(); + if (fError.hasError()) { + SkString err; + if (fFileName.size() > 0) + fErrorString.set(fFileName.c_str()); + else + fErrorString.set("screenplay error"); + int line = fError.getLineNumber(); + if (line >= 0) { + fErrorString.append(", "); + fErrorString.append("line "); + fErrorString.appendS32(line); + } + fErrorString.append(": "); + fError.getErrorString(&err); + fErrorString.append(err); +#if defined SK_DEBUG + SkDebugf("%s\n", fErrorString.c_str()); +#endif + } +} + +void SkAnimateMaker::setEnableTime(SkMSec appTime, SkMSec expectedTime) { +#if defined SK_DEBUG && defined SK_DEBUG_ANIMATION_TIMING + SkString debugOut; + SkMSec time = getAppTime(); + debugOut.appendS32(time - fDebugTimeBase); + debugOut.append(" set enable old enable="); + debugOut.appendS32(fEnableTime - fDebugTimeBase); + debugOut.append(" old adjust="); + debugOut.appendS32(fAdjustedStart); + debugOut.append(" new enable="); + debugOut.appendS32(expectedTime - fDebugTimeBase); + debugOut.append(" new adjust="); + debugOut.appendS32(appTime - expectedTime); + SkDebugf("%s\n", debugOut.c_str()); +#endif + fAdjustedStart = appTime - expectedTime; + fEnableTime = expectedTime; + SkDisplayable** firstMovie = fMovies.begin(); + SkDisplayable** endMovie = fMovies.end(); + for (SkDisplayable** ptr = firstMovie; ptr < endMovie; ptr++) { + SkDisplayMovie* movie = (SkDisplayMovie*) *ptr; + movie->fMovie.fMaker->setEnableTime(appTime, expectedTime); + } +} + +void SkAnimateMaker::setExtraPropertyCallBack(SkDisplayTypes type, + SkScriptEngine::_propertyCallBack callBack, void* userStorage) { + SkExtras** end = fExtras.end(); + for (SkExtras** extraPtr = fExtras.begin(); extraPtr < end; extraPtr++) { + SkExtras* extra = *extraPtr; + if (extra->definesType(type)) { + extra->fExtraCallBack = callBack; + extra->fExtraStorage = userStorage; + break; + } + } +} + +void SkAnimateMaker::setID(SkDisplayable* displayable, const SkString& newID) { + fIDs.set(newID.c_str(), displayable); +#ifdef SK_DEBUG + displayable->_id.set(newID); + displayable->id = displayable->_id.c_str(); +#endif +} + +void SkAnimateMaker::setScriptError(const SkScriptEngine& engine) { + SkString errorString; +#ifdef SK_DEBUG + engine.getErrorString(&errorString); +#endif + setErrorNoun(errorString); + setErrorCode(SkDisplayXMLParserError::kErrorInScript); +} + +bool SkAnimateMaker::GetStep(const char* token, size_t len, void* stepPtr, SkScriptValue* value) { + if (SK_LITERAL_STR_EQUAL("step", token, len)) { + value->fOperand.fS32 = *(int32_t*) stepPtr; + value->fType = SkType_Int; + return true; + } + return false; +} diff --git a/skia/animator/SkAnimateMaker.h b/skia/animator/SkAnimateMaker.h new file mode 100644 index 0000000..3c22e5d --- /dev/null +++ b/skia/animator/SkAnimateMaker.h @@ -0,0 +1,169 @@ +/* libs/graphics/animator/SkAnimateMaker.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkAnimateMaker_DEFINED +#define SkAnimateMaker_DEFINED + +// #define SK_DEBUG_ANIMATION_TIMING + +#include "SkAnimator.h" +#include "SkBitmap.h" +#include "SkIntArray.h" +#include "SkDisplayEvents.h" +#include "SkDisplayList.h" +#include "SkDisplayScreenplay.h" +#include "SkDisplayXMLParser.h" +#include "SkScript.h" +#include "SkString.h" +#include "SkTDict.h" + +// not sure where this little helper macro should go + + +class SkActive; +class SkAnimate; +class SkCanvas; +class SkDisplayable; +class SkDrawable; +class SkDump; +class SkEvent; +class SkEventSink; +class SkExtras; +class SkGroup; +class SkPaint; +class SkStream; + +class SkAnimateMaker { +public: + SkAnimateMaker(SkAnimator* animator, SkCanvas* canvas, SkPaint* paint); + ~SkAnimateMaker(); + void appendActive(SkActive* ); + void childrenAdd(SkDisplayable* child) { *fChildren.append() = child; } + void clearExtraPropertyCallBack(SkDisplayTypes type); + bool computeID(SkDisplayable* displayable, SkDisplayable* parent, SkString* newID); + SkDisplayable* createInstance(const char name[], size_t len); + bool decodeStream(SkStream* stream); + bool decodeURI(const char uri[]); + void delayEnable(SkApply* apply, SkMSec time); + void doDelayedEvent(); + bool doEvent(const SkEvent& event); +#ifdef SK_DUMP_ENABLED + void dump(const char* match); +#endif + int dynamicProperty(SkString& nameStr, SkDisplayable** ); + bool find(const char* str, SkDisplayable** displayablePtr) const { + return fIDs.find(str, displayablePtr); + } + bool find(const char* str, size_t len, SkDisplayable** displayablePtr) const { + return fIDs.find(str, len, displayablePtr); + } + bool findKey(SkDisplayable* displayable, const char** string) const { + return fIDs.findKey(displayable, string); + } +// bool find(SkString& string, SkDisplayable** displayablePtr) { +// return fIDs.find(string.c_str(), displayablePtr); +// } + SkAnimator* getAnimator() { return fAnimator; } + SkMSec getAppTime() const; // call caller to get current time +#ifdef SK_DEBUG + SkAnimator* getRoot(); +#endif + SkXMLParserError::ErrorCode getErrorCode() const { return fError.getErrorCode(); } + SkMSec getInTime() { return fDisplayList.getTime(); } + int getNativeCode() const { return fError.getNativeCode(); } + bool hasError() { return fError.hasError(); } + void helperAdd(SkDisplayable* trackMe); + void helperRemove(SkDisplayable* alreadyTracked); + void idsSet(const char* attrValue, size_t len, SkDisplayable* displayable) { + fIDs.set(attrValue, len, displayable); } +// void loadMovies(); + void notifyInval(); + void notifyInvalTime(SkMSec time); + void postOnEnd(SkAnimateBase* animate, SkMSec end); + void removeActive(SkActive* ); + void reset(); + bool resolveID(SkDisplayable* displayable, SkDisplayable* original); + void setEnableTime(SkMSec appTime, SkMSec expectedTime); + void setErrorCode(SkXMLParserError::ErrorCode err) { if (fError.hasError() == false) fError.INHERITED::setCode(err); } + void setErrorCode(SkDisplayXMLParserError::ErrorCode err) { if (fError.hasError() == false) fError.setCode(err); } + void setErrorNoun(const SkString& str) { if (fError.hasError() == false) fError.setNoun(str); } + void setErrorString(); + void setExtraPropertyCallBack(SkDisplayTypes type, SkScriptEngine::_propertyCallBack , void* userStorage); + void setID(SkDisplayable* displayable, const SkString& newID); + void setInnerError(SkAnimateMaker* maker, const SkString& str) { fError.setInnerError(maker, str); } + void setScriptError(const SkScriptEngine& ); +#ifdef SK_DEBUG + void validate() { fDisplayList.validate(); } +#else + void validate() {} +#endif + SkDisplayEvent* fActiveEvent; + SkMSec fAdjustedStart; + SkCanvas* fCanvas; + SkMSec fEnableTime; + int fEndDepth; // passed parameter to onEndElement + SkEvents fEvents; + SkDisplayList fDisplayList; + SkEventSinkID fHostEventSinkID; + SkMSec fMinimumInterval; + SkPaint* fPaint; + SkAnimateMaker* fParentMaker; + SkString fPrefix; + SkDisplayScreenplay fScreenplay; + const SkAnimator::Timeline* fTimeline; + SkBool8 fInInclude; + SkBool8 fInMovie; + SkBool8 fFirstScriptError; +#if defined SK_DEBUG && defined SK_DEBUG_ANIMATION_TIMING + SkMSec fDebugTimeBase; +#endif +#ifdef SK_DUMP_ENABLED + SkString fDumpAnimated; + SkBool8 fDumpEvents; + SkBool8 fDumpGConditions; + SkBool8 fDumpPosts; +#endif +private: + void deleteMembers(); + static bool GetStep(const char* token, size_t len, void* stepPtr, SkScriptValue* ); + SkAnimateMaker& operator=(SkAnimateMaker& ); + SkTDDisplayableArray fChildren; + SkTDDisplayableArray fDelayed; // SkApply that contain delayed enable events + SkDisplayXMLParserError fError; + SkString fErrorString; + SkTDArray<SkExtras*> fExtras; + SkString fFileName; + SkTDDisplayableArray fHelpers; // helper displayables + SkBool8 fLoaded; + SkTDDisplayableArray fMovies; + SkTDict<SkDisplayable*> fIDs; + SkAnimator* fAnimator; + friend class SkAdd; + friend class SkAnimateBase; + friend class SkDisplayXMLParser; + friend class SkAnimator; + friend class SkAnimatorScript; + friend class SkApply; + friend class SkDisplayMovie; + friend class SkDisplayType; + friend class SkEvents; + friend class SkGroup; + friend struct SkMemberInfo; +}; + +#endif // SkAnimateMaker_DEFINED + diff --git a/skia/animator/SkAnimateProperties.h b/skia/animator/SkAnimateProperties.h new file mode 100644 index 0000000..a49d2b3 --- /dev/null +++ b/skia/animator/SkAnimateProperties.h @@ -0,0 +1,29 @@ +/* libs/graphics/animator/SkAnimateProperties.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkAnimateProperties_DEFINED +#define SkAnimateProperties_DEFINED + +enum SkAnimateBase_Properties { + SK_PROPERTY(dynamic), + SK_PROPERTY(mirror), + SK_PROPERTY(reset), + SK_PROPERTY(step), + SK_PROPERTY(values) +}; + +#endif // SkAnimateProperties_DEFINED diff --git a/skia/animator/SkAnimateSchema.xsd b/skia/animator/SkAnimateSchema.xsd new file mode 100644 index 0000000..754d6b4 --- /dev/null +++ b/skia/animator/SkAnimateSchema.xsd @@ -0,0 +1,2787 @@ +<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
+xmlns:Sk="urn:screenplay" targetNamespace="urn:screenplay">
+
+ <!-- /** Animate
+ An ID of an element of type <animate> or <set>
+ */ -->
+ <xs:simpleType name="Animate">
+ <xs:restriction base="xs:string"/>
+ </xs:simpleType>
+
+ <!-- /** 3D_Point
+ An array of three floats in ECMAScript notation: [x, y, z].
+ */ -->
+ <xs:simpleType name="3D_Point">
+ <xs:restriction base="xs:string">
+ <xs:pattern value="[+-]?([0-9]*\.[0-9]+|[0-9]+\.?)( *, *[+-]?([0-9]*\.[0-9]+|[0-9]+\.?)){2}" />
+ </xs:restriction>
+ </xs:simpleType>
+
+ <!-- /** ARGB
+ The red, green, blue, and optional alpha color components.
+ */ -->
+ <xs:simpleType name="ARGB">
+ <xs:restriction base="xs:string">
+ <!-- @pattern #[0-9a-fA-F]{3} #rgb contains three hexadecimal digits. #rgb is equivalent to 0xFFrrggbb. -->
+ <xs:pattern value="#[0-9a-fA-F]{3}"/>
+ <!-- @pattern #[0-9a-fA-F]{4} #argb contains four hexadecimal digits. #argb is equivalent to 0xaarrggbb. -->
+ <xs:pattern value="#[0-9a-fA-F]{4}"/>
+ <!-- @pattern #[0-9a-fA-F]{6} #rrggbb contains six hexadecimal digits. #rrggbb is equivalent to 0xFFrrggbb. -->
+ <xs:pattern value="#[0-9a-fA-F]{6}"/>
+ <!-- @pattern #[0-9a-fA-F]{8} #aarrggbb contains eight hexadecimal digits. #aarrggbb is equivalent to 0xaarrggbb. -->
+ <xs:pattern value="#[0-9a-fA-F]{8}"/>
+ <!-- @pattern 0[xX][0-9a-fA-F]{8} 0xaarrggbb describes the color as a packed hexadecimal; each pair of digits
+ corresponds to alpha, red, green, and blue respectively. -->
+ <xs:pattern value="0[xX][0-9a-fA-F]{8}"/>
+ <!-- @pattern rgb\(\d+{1,3},\d+{1,3},\d+{1,3}\) rgb(r, g, b) describes color with three integers ranging from 0 to 255,
+ corresponding to red, green, and blue respectively. -->
+ <xs:pattern value="rgb\(\d+{1,3},\d+{1,3},\d+{1,3}\)"/>
+ <!-- @patternList Color can be described by the following standard CSS color names. -->
+ <xs:pattern value="aliceblue"/>
+ <xs:pattern value="antiquewhite"/>
+ <xs:pattern value="aqua"/>
+ <xs:pattern value="aquamarine"/>
+ <xs:pattern value="azure"/>
+ <xs:pattern value="beige"/>
+ <xs:pattern value="bisque"/>
+ <xs:pattern value="black"/>
+ <xs:pattern value="blanchedalmond"/>
+ <xs:pattern value="blue"/>
+ <xs:pattern value="blueviolet"/>
+ <xs:pattern value="brown"/>
+ <xs:pattern value="burlywood"/>
+ <xs:pattern value="cadetblue"/>
+ <xs:pattern value="chartreuse"/>
+ <xs:pattern value="chocolate"/>
+ <xs:pattern value="coral"/>
+ <xs:pattern value="cornflowerblue"/>
+ <xs:pattern value="cornsilk"/>
+ <xs:pattern value="crimson"/>
+ <xs:pattern value="cyan"/>
+ <xs:pattern value="darkblue"/>
+ <xs:pattern value="darkcyan"/>
+ <xs:pattern value="darkgoldenrod"/>
+ <xs:pattern value="darkgray"/>
+ <xs:pattern value="darkgreen"/>
+ <xs:pattern value="darkkhaki"/>
+ <xs:pattern value="darkmagenta"/>
+ <xs:pattern value="darkolivegreen"/>
+ <xs:pattern value="darkorange"/>
+ <xs:pattern value="darkorchid"/>
+ <xs:pattern value="darkred"/>
+ <xs:pattern value="darksalmon"/>
+ <xs:pattern value="darkseagreen"/>
+ <xs:pattern value="darkslateblue"/>
+ <xs:pattern value="darkslategray"/>
+ <xs:pattern value="darkturquoise"/>
+ <xs:pattern value="darkviolet"/>
+ <xs:pattern value="deeppink"/>
+ <xs:pattern value="deepskyblue"/>
+ <xs:pattern value="dimgray"/>
+ <xs:pattern value="dodgerblue"/>
+ <xs:pattern value="firebrick"/>
+ <xs:pattern value="floralwhite"/>
+ <xs:pattern value="forestgreen"/>
+ <xs:pattern value="fuchsia"/>
+ <xs:pattern value="gainsboro"/>
+ <xs:pattern value="ghostwhite"/>
+ <xs:pattern value="gold"/>
+ <xs:pattern value="goldenrod"/>
+ <xs:pattern value="gray"/>
+ <xs:pattern value="green"/>
+ <xs:pattern value="greenyellow"/>
+ <xs:pattern value="honeydew"/>
+ <xs:pattern value="hotpink"/>
+ <xs:pattern value="indianred"/>
+ <xs:pattern value="indigo"/>
+ <xs:pattern value="ivory"/>
+ <xs:pattern value="khaki"/>
+ <xs:pattern value="lavender"/>
+ <xs:pattern value="lavenderblush"/>
+ <xs:pattern value="lawngreen"/>
+ <xs:pattern value="lemonchiffon"/>
+ <xs:pattern value="lightblue"/>
+ <xs:pattern value="lightcoral"/>
+ <xs:pattern value="lightcyan"/>
+ <xs:pattern value="lightgoldenrodyellow"/>
+ <xs:pattern value="lightgreen"/>
+ <xs:pattern value="lightgrey"/>
+ <xs:pattern value="lightpink"/>
+ <xs:pattern value="lightsalmon"/>
+ <xs:pattern value="lightseagreen"/>
+ <xs:pattern value="lightskyblue"/>
+ <xs:pattern value="lightslategray"/>
+ <xs:pattern value="lightsteelblue"/>
+ <xs:pattern value="lightyellow"/>
+ <xs:pattern value="lime"/>
+ <xs:pattern value="limegreen"/>
+ <xs:pattern value="linen"/>
+ <xs:pattern value="magenta"/>
+ <xs:pattern value="maroon"/>
+ <xs:pattern value="mediumaquamarine"/>
+ <xs:pattern value="mediumblue"/>
+ <xs:pattern value="mediumorchid"/>
+ <xs:pattern value="mediumpurple"/>
+ <xs:pattern value="mediumseagreen"/>
+ <xs:pattern value="mediumslateblue"/>
+ <xs:pattern value="mediumspringgreen"/>
+ <xs:pattern value="mediumturquoise"/>
+ <xs:pattern value="mediumvioletred"/>
+ <xs:pattern value="midnightblue"/>
+ <xs:pattern value="mintcream"/>
+ <xs:pattern value="mistyrose"/>
+ <xs:pattern value="moccasin"/>
+ <xs:pattern value="navajowhite"/>
+ <xs:pattern value="navy"/>
+ <xs:pattern value="oldlace"/>
+ <xs:pattern value="olive"/>
+ <xs:pattern value="olivedrab"/>
+ <xs:pattern value="orange"/>
+ <xs:pattern value="orangered"/>
+ <xs:pattern value="orchid"/>
+ <xs:pattern value="palegoldenrod"/>
+ <xs:pattern value="palegreen"/>
+ <xs:pattern value="paleturquoise"/>
+ <xs:pattern value="palevioletred"/>
+ <xs:pattern value="papayawhip"/>
+ <xs:pattern value="peachpuff"/>
+ <xs:pattern value="peru"/>
+ <xs:pattern value="pink"/>
+ <xs:pattern value="plum"/>
+ <xs:pattern value="powderblue"/>
+ <xs:pattern value="purple"/>
+ <xs:pattern value="red"/>
+ <xs:pattern value="rosybrown"/>
+ <xs:pattern value="royalblue"/>
+ <xs:pattern value="saddlebrown"/>
+ <xs:pattern value="salmon"/>
+ <xs:pattern value="sandybrown"/>
+ <xs:pattern value="seagreen"/>
+ <xs:pattern value="seashell"/>
+ <xs:pattern value="sienna"/>
+ <xs:pattern value="silver"/>
+ <xs:pattern value="skyblue"/>
+ <xs:pattern value="slateblue"/>
+ <xs:pattern value="slategray"/>
+ <xs:pattern value="snow"/>
+ <xs:pattern value="springgreen"/>
+ <xs:pattern value="steelblue"/>
+ <xs:pattern value="tan"/>
+ <xs:pattern value="teal"/>
+ <xs:pattern value="thistle"/>
+ <xs:pattern value="tomato"/>
+ <xs:pattern value="turquoise"/>
+ <xs:pattern value="violet"/>
+ <xs:pattern value="wheat"/>
+ <xs:pattern value="white"/>
+ <xs:pattern value="whitesmoke"/>
+ <xs:pattern value="yellow"/>
+ <!--@patternListLast -->
+ <xs:pattern value="yellowgreen"/>
+ </xs:restriction>
+ </xs:simpleType>
+
+ <!-- /** AddMode
+ AddMode controls how the add element adds its referenced element to the
+ display list. By default, the referenced element remains in the add element
+ so that the add element's use attribute may be animated to change the
+ element it refers to. Setting the mode attribute to "immediate" causes the
+ add element to put the referenced element in the display list directly.
+ The move and replace elements are not affected by the mode attribute;
+ they always move or replace the referenced element directly.
+ */ -->
+ <xs:simpleType name="AddMode">
+ <xs:restriction base="xs:string">
+ <!-- @pattern immediate Puts the referenced element in the display list. -->
+ <xs:pattern value="immediate"/>
+ <!-- @pattern indirect Puts the containing element in the display list. -->
+ <xs:pattern value="indirect"/>
+ </xs:restriction>
+ </xs:simpleType>
+
+ <!-- /** Align
+ Align places text to the left, center, or right of the text position.
+ */ -->
+ <xs:simpleType name="Align">
+ <xs:restriction base="xs:string">
+ <!-- @pattern left The first character in the text string is drawn at the text position. -->
+ <xs:pattern value="left"/>
+ <!-- @pattern center The text string is measured and centered on the text position. -->
+ <xs:pattern value="center"/>
+ <!-- @pattern right The last character in the text string is drawn to the left of the text position. -->
+ <xs:pattern value="right"/>
+ </xs:restriction>
+ </xs:simpleType>
+
+ <!-- /** ApplyMode
+ ApplyMode affects how the apply element animates values.
+ */ -->
+ <xs:simpleType name="ApplyMode">
+ <xs:restriction base="xs:string">
+ <!-- @pattern immediate Iterates through all animation values immediately. -->
+ <xs:pattern value="immediate"/>
+ <!-- @pattern once Performs the animation at once without adding the scope to
+ the display list. -->
+ <xs:pattern value="once"/>
+ </xs:restriction>
+ </xs:simpleType>
+
+ <!-- /** ApplyTransition
+ ApplyTransition affects how the apply element sets the time of the animators.
+ */ -->
+ <xs:simpleType name="ApplyTransition">
+ <xs:restriction base="xs:string">
+ <!-- @pattern reverse Performs the animation in reverse. -->
+ <xs:pattern value="reverse"/>
+ </xs:restriction>
+ </xs:simpleType>
+
+ <!-- /** Base64
+ Base64 describes 8 bit binary using 64 character values.
+ See http://rfc.net/rfc2045.html for the base64 format.
+ */ -->
+ <xs:simpleType name="Base64">
+ <xs:restriction base="xs:string">
+ <xs:pattern value="[A-Za-z0-9+/ ]+"/>
+ </xs:restriction>
+ </xs:simpleType>
+
+ <!-- /** BaseBitmap
+ A reference to an image like a JPEG, GIF, or PNG; or a reference to a bitmap element
+ that has been drawn into with a drawTo element.
+ */ -->
+ <xs:simpleType name="BaseBitmap">
+ <xs:restriction base="xs:string"/>
+ </xs:simpleType>
+
+ <!-- /** BitmapEncoding
+ Used to specify the compression format for writing an image file with the snapshot element.
+ */ -->
+ <xs:simpleType name="BitmapEncoding">
+ <xs:restriction base="xs:string">
+ <!-- @pattern jpeg See http://www.jpeg.org/jpeg/ for more information about JPEG. -->
+ <xs:pattern value="jpeg"/>
+ <!-- @pattern png See http://www.libpng.org/pub/png/ for more information about PNG. -->
+ <xs:pattern value="png"/>
+ </xs:restriction>
+ </xs:simpleType>
+
+ <!-- /** BitmapFormat
+ Determines the number of bits per pixel in a bitmap.
+ */ -->
+ <xs:simpleType name="BitmapFormat">
+ <xs:restriction base="xs:string">
+ <xs:pattern value="none"/>
+ <!-- @pattern A1 1-bit per pixel, (0 is transparent, 1 is opaque). -->
+ <xs:pattern value="A1"/>
+ <!-- @pattern A8 8-bits per pixel, with only alpha specified (0 is transparent, 0xFF is opaque). -->
+ <xs:pattern value="A8"/>
+ <!-- @pattern Index8 8-bits per pixel, using a ColorTable element to specify the colors. -->
+ <xs:pattern value="Index8"/>
+ <!-- @pattern RGB16 16-bits per pixel, compile-time configured to be either 555 or 565. -->
+ <xs:pattern value="RGB16"/>
+ <!-- @pattern RGB32 32-bits per pixel, plus alpha. -->
+ <xs:pattern value="RGB32"/>
+ </xs:restriction>
+ </xs:simpleType>
+
+ <!-- /** Boolean
+ Either "true" (non-zero) or "false" (zero).
+ */ -->
+ <xs:simpleType name="Boolean">
+ <xs:restriction base="xs:string">
+ <xs:pattern value="false"/>
+ <xs:pattern value="true"/>
+ </xs:restriction>
+ </xs:simpleType>
+
+ <!-- /** Cap
+ The values for the strokeCap attribute.
+ */ -->
+ <xs:simpleType name="Cap">
+ <xs:restriction base="xs:string">
+ <!-- @pattern butt begin and end a contour with no extension -->
+ <xs:pattern value="butt"/>
+ <!-- @pattern round begin and end a contour with a semi-circle extension -->
+ <xs:pattern value="round"/>
+ <!-- @pattern square begin and end a contour with a half square extension -->
+ <xs:pattern value="square"/>
+ </xs:restriction>
+ </xs:simpleType>
+
+ <!-- /** Color
+ A reference to a color element.
+ */ -->
+ <xs:simpleType name="Color">
+ <xs:restriction base="xs:string"/>
+ </xs:simpleType>
+
+ <!-- /** Displayable
+ A reference to any element: @list(Displayable)
+ */ -->
+ <xs:simpleType name="Displayable">
+ <xs:restriction base="xs:string"/>
+ </xs:simpleType>
+
+ <!-- /** DisplayableArray
+ An array of one or more element IDs.
+ */ -->
+ <xs:simpleType name="DisplayableArray">
+ <xs:restriction base="xs:string"/>
+ </xs:simpleType>
+
+ <!-- /** Drawable
+ A reference to an element that can be drawn: @list(Drawable)
+ */ -->
+ <xs:simpleType name="Drawable">
+ <xs:restriction base="xs:string"/>
+ </xs:simpleType>
+
+ <!-- /** DynamicString
+ Dynamic strings contain scripts that are re-evaluated each time the script is enabled.
+ */ -->
+ <xs:simpleType name="DynamicString">
+ <xs:restriction base="xs:string"/>
+ </xs:simpleType>
+
+ <!-- /** EventCode
+ Key codes that can trigger events, usually corresponding to physical buttons on the device.
+ */ -->
+ <xs:simpleType name="EventCode">
+ <xs:restriction base="xs:string">
+ <xs:pattern value="none"/>
+ <!-- @pattern up The up arrow. -->
+ <xs:pattern value="up"/>
+ <!-- @pattern down The down arrow. -->
+ <xs:pattern value="down"/>
+ <!-- @pattern left The left arrow. -->
+ <xs:pattern value="left"/>
+ <!-- @pattern right The right arrow. -->
+ <xs:pattern value="right"/>
+ <!-- @pattern back The back button (may not be present; the Backspace key on a PC). -->
+ <xs:pattern value="back"/>
+ <!-- @pattern end The end button (may not be present; the Esc key on a PC). -->
+ <xs:pattern value="end"/>
+ <!-- @pattern OK The OK button (the Enter key on a PC). -->
+ <xs:pattern value="OK"/>
+ </xs:restriction>
+ </xs:simpleType>
+
+ <!-- /** EventKind
+ Specifies how an event is triggered; by a key, when an animation ends, when the
+ document is loaded, or when this event is triggered by the user's C++ or XML.
+ */ -->
+ <xs:simpleType name="EventKind">
+ <xs:restriction base="xs:string">
+ <xs:pattern value="none"/>
+ <!-- @pattern keyChar A key corresponding to a Unichar value. -->
+ <xs:pattern value="keyChar"/>
+ <!-- @pattern keyPress A key with a particular function, such as an arrow key or the OK button. -->
+ <xs:pattern value="keyPress"/>
+ <!-- @pattern mouseDown Triggered when the primary mouse button is pressed. -->
+ <xs:pattern value="mouseDown"/>
+ <!-- @pattern mouseDrag Triggered when the primary mouse is moved while the button is pressed. -->
+ <xs:pattern value="mouseDrag"/>
+ <!-- @pattern mouseMove Triggered when the primary mouse is moved. -->
+ <xs:pattern value="mouseMove"/>
+ <!-- @pattern mouseUp Triggered when the primary mouse button is released. -->
+ <xs:pattern value="mouseUp"/>
+ <!-- @pattern onEnd Triggered when an event ends. -->
+ <xs:pattern value="onEnd"/>
+ <!-- @pattern onLoad Triggered when the document loads. -->
+ <xs:pattern value="onLoad"/>
+ <!-- @pattern user Triggered when a post element or C++ event is activated. -->
+ <xs:pattern value="user"/>
+ </xs:restriction>
+ </xs:simpleType>
+
+ <!-- /** EventMode
+ Specifies whether the event is delivered immediately to matching event element or deferred to
+ the application-wide event handler.
+ */ -->
+ <xs:simpleType name="EventMode">
+ <xs:restriction base="xs:string">
+ <!-- @pattern deferred Process the event using the host's event queue. -->
+ <xs:pattern value="deferred"/>
+ <!-- @pattern immediate Activate the event element immediately. -->
+ <xs:pattern value="immediate"/>
+ </xs:restriction>
+ </xs:simpleType>
+
+ <!-- /** FillType
+ Filled paths that self-intersect use the winding or evenOdd rule to determine whether the
+ overlaps are filled or are holes.
+ */ -->
+ <xs:simpleType name="FillType">
+ <xs:restriction base="xs:string">
+ <!-- @pattern winding Fill if the sum of edge directions is non-zero. -->
+ <xs:pattern value="winding"/>
+ <!-- @pattern evenOdd Fill if the sum of edges is an odd number. -->
+ <xs:pattern value="evenOdd"/>
+ </xs:restriction>
+ </xs:simpleType>
+
+ <!-- /** FilterType
+ Scaled bitmaps without a filter type set point-sample the source bitmap to determine the
+ destination pixels' colors. Bilinear and bicubic compute the values of intermediate pixels
+ by sampling the pixels around them.
+ */ -->
+ <xs:simpleType name="FilterType">
+ <xs:restriction base="xs:string">
+ <xs:pattern value="none"/>
+ <!-- @pattern bilinear Compute the pixel value as the linear interpolation of adjacent pixels. -->
+ <xs:pattern value="bilinear"/>
+ </xs:restriction>
+ </xs:simpleType>
+
+ <!-- /** Float
+ A signed fractional value.
+ */ -->
+ <xs:simpleType name="Float">
+ <xs:restriction base="xs:float">
+ <xs:pattern value="[+-]?([0-9]*\.[0-9]+|[0-9]+\.?)"/>
+ </xs:restriction>
+ </xs:simpleType>
+
+ <!-- /** FloatArray
+ An array of one or more signed fractional values.
+ */ -->
+ <xs:simpleType name="FloatArray">
+ <xs:restriction base="xs:float">
+ <xs:pattern value="\[[+-]?([0-9]*\.[0-9]+|[0-9]+\.?)( *, *[+-]?([0-9]*\.[0-9]+|[0-9]+\.?))*\]"/>
+ </xs:restriction>
+ </xs:simpleType>
+
+ <!-- /** FromPathMode
+ A matrix computed from an offset along a path may include the point's position, the angle
+ tangent, or both.
+ .
+ */ -->
+ <xs:simpleType name="FromPathMode">
+ <xs:restriction base="xs:string">
+ <!-- @pattern normal Compute the matrix using the path's angle and position. -->
+ <xs:pattern value="normal"/>
+ <!-- @pattern angle Compute the matrix using only the path's angle. -->
+ <xs:pattern value="angle"/>
+ <!-- @pattern position Compute the matrix using only the path's position. -->
+ <xs:pattern value="position"/>
+ </xs:restriction>
+ </xs:simpleType>
+
+ <!-- /** Int
+ A signed integer.
+ */ -->
+ <xs:simpleType name="Int">
+ <xs:restriction base="xs:integer"/>
+ </xs:simpleType>
+
+ <!-- /** IntArray
+ An array of one or more signed integer values.
+ */ -->
+ <xs:simpleType name="IntArray">
+ <xs:restriction base="xs:integer">
+ <xs:pattern value="\[[+-]?[0-9]+( *, *[+-]?[0-9]+)*\]"/>
+ </xs:restriction>
+ </xs:simpleType>
+
+ <!-- /** Join
+ The edges of thick lines in a path are joined by extending the outer edges to form a miter,
+ or by adding a round circle at the intersection point, or by connecting the outer edges with a line
+ to form a blunt joint.
+ */ -->
+ <xs:simpleType name="Join">
+ <xs:restriction base="xs:string">
+ <!-- @pattern miter Extend the outer edges to form a miter. -->
+ <xs:pattern value="miter"/>
+ <!-- @pattern round Join the outer edges with a circular arc. -->
+ <xs:pattern value="round"/>
+ <!-- @pattern blunt Connect the outer edges with a line. -->
+ <xs:pattern value="blunt"/>
+ </xs:restriction>
+ </xs:simpleType>
+
+ <!-- /** MaskFilterBlurStyle
+ A blur can affect the inside or outside part of the shape, or it can affect both. The shape
+ itself can be drawn solid, or can be invisible.
+ */ -->
+ <xs:simpleType name="MaskFilterBlurStyle">
+ <xs:restriction base="xs:string">
+ <!-- @pattern normal Blur inside and outside. -->
+ <xs:pattern value="normal"/>
+ <!-- @pattern solid Solid inside, blur outside. -->
+ <xs:pattern value="solid"/>
+ <!-- @pattern outer Invisible inside, blur outside. -->
+ <xs:pattern value="outer"/>
+ <!-- @pattern inner Blur inside only.. -->
+ <xs:pattern value="inner"/>
+ </xs:restriction>
+ </xs:simpleType>
+
+ <!-- /** MaskFilter
+ The ID of a blur or emboss element.
+ */ -->
+ <xs:simpleType name="MaskFilter">
+ <xs:restriction base="xs:string"/>
+ </xs:simpleType>
+
+ <!-- /** Matrix
+ The ID of a matrix element.
+ */ -->
+ <xs:simpleType name="Matrix">
+ <xs:restriction base="xs:string"/>
+ </xs:simpleType>
+
+ <!-- /** MSec
+ A fractional second with millisecond resolution.
+ */ -->
+ <xs:simpleType name="MSec">
+ <xs:restriction base="xs:float"/>
+ </xs:simpleType>
+
+ <!-- /** Paint
+ The ID of a paint element.
+ */ -->
+ <xs:simpleType name="Paint">
+ <xs:restriction base="xs:string"/>
+ </xs:simpleType>
+
+ <!-- /** Path
+ The ID of a path element.
+ */ -->
+ <xs:simpleType name="Path">
+ <xs:restriction base="xs:string"/>
+ </xs:simpleType>
+
+ <!-- /** PathDirection
+ PathDirection determines if the path is traveled clockwise or counterclockwise.
+ */ -->
+ <xs:simpleType name="PathDirection">
+ <xs:restriction base="xs:string">
+ <!-- @pattern cw The path is traveled clockwise. -->
+ <xs:pattern value="cw"/>
+ <!-- @pattern ccw The path is traveled counterclockwise. -->
+ <xs:pattern value="ccw"/>
+ </xs:restriction>
+ </xs:simpleType>
+
+ <!-- /** PathEffect
+ The ID of a dash or discrete element.
+ */ -->
+ <xs:simpleType name="PathEffect">
+ <xs:restriction base="xs:string"/>
+ </xs:simpleType>
+
+ <!-- /** Point
+ A pair of signed values representing the x and y coordinates of a point.
+ */ -->
+ <xs:simpleType name="Point">
+ <xs:restriction base="xs:string">
+ <xs:pattern value="\[ *[+-]?([0-9]*\.[0-9]+|[0-9]+\.?) *[ ,] *[+-]?([0-9]*\.[0-9]+|[0-9]+\.?)\]"/>
+ </xs:restriction>
+ </xs:simpleType>
+
+ <!-- /** Rect
+ The ID of a rectangle element.
+ */ -->
+ <xs:simpleType name="Rect">
+ <xs:restriction base="xs:string"/>
+ </xs:simpleType>
+
+ <!-- /** Shader
+ The ID of a linear or radial gradient.
+ */ -->
+ <xs:simpleType name="Shader">
+ <xs:restriction base="xs:string"/>
+ </xs:simpleType>
+
+ <!-- /** String
+ A sequence of characters.
+ */ -->
+ <xs:simpleType name="String">
+ <xs:restriction base="xs:string"/>
+ </xs:simpleType>
+
+ <!-- /** Style
+ Geometry can be filled, stroked or both.
+ */ -->
+ <xs:simpleType name="Style">
+ <xs:restriction base="xs:string">
+ <!-- @pattern fill The interior of the geometry is filled with the paint's color. -->
+ <xs:pattern value="fill"/>
+ <!-- @pattern stroke The outline of the geometry is stroked with the paint's color. -->
+ <xs:pattern value="stroke"/>
+ <!-- @pattern strokeAndFill The interior is filled and outline is stroked with the paint's color. -->
+ <xs:pattern value="strokeAndFill"/>
+ </xs:restriction>
+ </xs:simpleType>
+
+ <!-- /** Text
+ The ID of a text element.
+ */ -->
+ <xs:simpleType name="Text">
+ <xs:restriction base="xs:string"/>
+ </xs:simpleType>
+
+ <!-- /** TextBoxAlign
+ Multiple lines of text may be aligned to the start of the box, the center, or the end.
+ */ -->
+ <xs:simpleType name="TextBoxAlign">
+ <xs:restriction base="xs:string">
+ <!-- @pattern start The text begins within the upper left of the box. -->
+ <xs:pattern value="start"/>
+ <!-- @pattern center The text is positioned in the center of the box. -->
+ <xs:pattern value="center"/>
+ <!-- @pattern end The text ends within the lower right of the box. -->
+ <xs:pattern value="end"/>
+ </xs:restriction>
+ </xs:simpleType>
+
+ <!-- /** TextBoxMode
+ Fitting the text may optionally introduce line breaks.
+ */ -->
+ <xs:simpleType name="TextBoxMode">
+ <xs:restriction base="xs:string">
+ <!-- @pattern oneLine No additional linebreaks are added. -->
+ <xs:pattern value="oneLine"/>
+ <!-- @pattern lineBreak Line breaks may be added to fit the text to the box. -->
+ <xs:pattern value="lineBreak"/>
+ </xs:restriction>
+ </xs:simpleType>
+
+ <!-- /** TileMode
+ A shader describes how to draw within a rectangle.
+ Outside of the rectangle, the shader may be ignored, clamped on the edges, or repeated.
+ The repetitions may be mirrored from the original shader.
+ */ -->
+ <xs:simpleType name="TileMode">
+ <xs:restriction base="xs:string">
+ <!-- @pattern clamp The edge shader color is extended. -->
+ <xs:pattern value="clamp"/>
+ <!-- @pattern repeat The shader is repeated horizontally and vertically. -->
+ <xs:pattern value="repeat"/>
+ <!-- @pattern mirror The shader is mirrored horizontally and vertically. -->
+ <xs:pattern value="mirror"/>
+ </xs:restriction>
+ </xs:simpleType>
+
+ <!-- /** Typeface
+ The ID of a typeface element.
+ */ -->
+ <xs:simpleType name="Typeface">
+ <xs:restriction base="xs:string"/>
+ </xs:simpleType>
+
+ <!-- /** UnknownArray
+ An array of values of any type.
+ */ -->
+ <xs:simpleType name="UnknownArray">
+ <xs:restriction base="xs:string"/>
+ </xs:simpleType>
+
+ <!-- /** Xfermode
+ The operation applied when drawing a color to the destination background.
+ */ -->
+ <xs:simpleType name="Xfermode">
+ <xs:restriction base="xs:string">
+ <!-- @pattern clear Set the destination alpha to zero and the destination color to black. -->
+ <xs:pattern value="clear"/>
+ <!-- @pattern src Set the destination to the source alpha and color. -->
+ <xs:pattern value="src"/>
+ <!-- @pattern dst Set the destination to the destination alpha and color. -->
+ <xs:pattern value="dst"/>
+ <!-- @pattern srcOver The default. Set the destination to the source color blended
+ with the destination by the source alpha. -->
+ <xs:pattern value="srcOver"/>
+ <!-- @pattern dstOver Set the destination to the destination color blended
+ with the source by the destination alpha. -->
+ <xs:pattern value="dstOver"/>
+ <!-- @pattern srcIn Set the destination to the source color scaled by the destination
+ alpha. -->
+ <xs:pattern value="srcIn"/>
+ <!-- @pattern dstIn Set the destination to the destination color scaled by the source
+ alpha. -->
+ <xs:pattern value="dstIn"/>
+ <!-- @pattern srcOut Set the destination to the source color scaled by the
+ inverse of the destination alpha. -->
+ <xs:pattern value="srcOut"/>
+ <!-- @pattern dstOut Set the destination to the destination color scaled by the
+ inverse of the source alpha. -->
+ <xs:pattern value="dstOut"/>
+ <!-- @pattern srcATop Set the destination to the source color times the destination alpha,
+ blended with the destination times the inverse of the source alpha. -->
+ <xs:pattern value="srcATop"/>
+ <!-- @pattern dstATop Set the destination to the destination color times the source alpha,
+ blended with the source times the inverse of the destination alpha. -->
+ <xs:pattern value="dstATop"/>
+ <!-- @pattern xor Set the destination to the destination color times the
+ inverse of the source alpha,
+ blended with the source times the inverse of the destination alpha. -->
+ <xs:pattern value="xor"/>
+ </xs:restriction>
+ </xs:simpleType>
+
+ <!-- /** Math
+ Math provides functions and properties in the ECMAScript library to screenplay script expressions.
+ The Math element is always implicitly added at the top of every screenplay description, so
+ its functions and properties are always available.
+ */ -->
+ <xs:element name="Math">
+ <xs:complexType>
+ <!-- @attribute E The value 2.718281828. -->
+ <xs:attribute name="E" type="Sk:Float"/>
+ <!-- @attribute LN10 The value 2.302585093. -->
+ <xs:attribute name="LN10" type="Sk:Float"/>
+ <!-- @attribute LN2 The value 0.693147181. -->
+ <xs:attribute name="LN2" type="Sk:Float"/>
+ <!-- @attribute LOG10E The value 0.434294482. -->
+ <xs:attribute name="LOG10E" type="Sk:Float"/>
+ <!-- @attribute LOG2E The value 1.442695041. -->
+ <xs:attribute name="LOG2E" type="Sk:Float"/>
+ <!-- @attribute PI The value 3.141592654. -->
+ <xs:attribute name="PI" type="Sk:Float"/>
+ <!-- @attribute SQRT1_2 The value 0.707106781. -->
+ <xs:attribute name="SQRT1_2" type="Sk:Float"/>
+ <!-- @attribute SQRT2 The value 1.414213562. -->
+ <xs:attribute name="SQRT2" type="Sk:Float"/>
+ <!-- @attribute abs A function that returns the absolute value of its argument. -->
+ <xs:attribute name="abs" type="Sk:Float"/>
+ <!-- @attribute acos A function that returns the arc cosine of its argument. -->
+ <xs:attribute name="acos" type="Sk:Float"/>
+ <!-- @attribute asin A function that returns the arc sine of its argument. -->
+ <xs:attribute name="asin" type="Sk:Float"/>
+ <!-- @attribute atan A function that returns the arc tan of its argument. -->
+ <xs:attribute name="atan" type="Sk:Float"/>
+ <!-- @attribute atan2 A function that returns the arc tan of the ratio of its two arguments. -->
+ <xs:attribute name="atan2" type="Sk:Float"/>
+ <!-- @attribute ceil A function that returns the rounded up value of its argument. -->
+ <xs:attribute name="ceil" type="Sk:Float"/>
+ <!-- @attribute cos A function that returns the cosine of its argument. -->
+ <xs:attribute name="cos" type="Sk:Float"/>
+ <!-- @attribute exp A function that returns E raised to a power (the argument). -->
+ <xs:attribute name="exp" type="Sk:Float"/>
+ <!-- @attribute floor A function that returns the rounded down value of its argument. -->
+ <xs:attribute name="floor" type="Sk:Float"/>
+ <!-- @attribute log A function that returns the natural logarithm its argument. -->
+ <xs:attribute name="log" type="Sk:Float"/>
+ <!-- @attribute max A function that returns the largest of any number of arguments. -->
+ <xs:attribute name="max" type="Sk:Float"/>
+ <!-- @attribute min A function that returns the smallest of any number of arguments. -->
+ <xs:attribute name="min" type="Sk:Float"/>
+ <!-- @attribute pow A function that returns the first argument raised to the power of the second argument. -->
+ <xs:attribute name="pow" type="Sk:Float"/>
+ <!-- @attribute random A function that returns a random value from zero to one.
+ (See also the <random> element.) -->
+ <xs:attribute name="random" type="Sk:Float"/>
+ <!-- @attribute round A function that returns the rounded value of its argument. -->
+ <xs:attribute name="round" type="Sk:Float"/>
+ <!-- @attribute sin A function that returns the sine of its argument. -->
+ <xs:attribute name="sin" type="Sk:Float"/>
+ <!-- @attribute sqrt A function that returns the square root of its argument. -->
+ <xs:attribute name="sqrt" type="Sk:Float"/>
+ <!-- @attribute tan A function that returns the tangent of its argument. -->
+ <xs:attribute name="tan" type="Sk:Float"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** Number
+ Number provides properties in the ECMAScript library to screenplay script expressions.
+ The Number element is always implicitly added at the top of every screenplay description, so
+ its properties are always available.
+ */ -->
+ <xs:element name="Number">
+ <xs:complexType>
+ <!-- @attribute MAX_VALUE The maximum number value; approximately 32767.999985 fixed point,
+ 3.4028235e+38 floating point. -->
+ <xs:attribute name="MAX_VALUE" type="Sk:Float"/>
+ <!-- @attribute MIN_VALUE The minimum number value; approximately 0.000015 fixed point,
+ 1.1754944e-38 floating point. -->
+ <xs:attribute name="MIN_VALUE" type="Sk:Float"/>
+ <!-- @attribute NEGATIVE_INFINITY The most negative number value. Fixed point does not
+ have a value for negative infinity, and approximates it with -32767.999985. -->
+ <xs:attribute name="NEGATIVE_INFINITY" type="Sk:Float"/>
+ <!-- @attribute NaN A bit pattern representing "Not a Number". Fixed point does not
+ have a value for NaN, and approximates it with -32768. -->
+ <xs:attribute name="NaN" type="Sk:Float"/>
+ <!-- @attribute POSITIVE_INFINITY The greatest positive number value. Fixed point does not
+ have a value for positive infinity, and approximates it with 32767.999985. -->
+ <xs:attribute name="POSITIVE_INFINITY" type="Sk:Float"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** add
+ Add references a drawable element, and adds it to the display list.
+ If where and offset are omitted, the element is appended to the end of the display list.
+ If where is specified, the element is inserted at the first occurance of where in the display list.
+ If offset and where are specified, the element is inserted at where plus offset.
+ A positive offset without where inserts the element at the start of the list plus offset.
+ A negative offset without where inserts the element at the end of the list minus offset.
+ */ -->
+ <xs:element name="add">
+ <xs:complexType>
+ <!-- @attribute mode If indirect (the default), keep the add element in the display list,
+ and draw the add's use element. If immediate, put the add's use element in the display list. -->
+ <xs:attribute name="mode" type="Sk:AddMode"/>
+ <!-- @attribute offset The offset added to the insert index. -->
+ <xs:attribute name="offset" type="Sk:Int"/>
+ <!-- @attribute use The drawable element to add to the display list. -->
+ <xs:attribute name="use" type="Sk:Drawable"/>
+ <!-- @attribute where The drawable element marking where to insert. -->
+ <xs:attribute name="where" type="Sk:Drawable"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** addCircle
+ AddCircle adds a closed circle to the parent path element.
+ */ -->
+ <xs:element name="addCircle">
+ <xs:complexType>
+ <!-- @attribute direction One of @pattern. @patternDescription -->
+ <xs:attribute name="direction" type="Sk:PathDirection"/>
+ <!-- @attribute radius The distance from the center to the edge of the circle. -->
+ <xs:attribute name="radius" type="Sk:Float"/>
+ <!-- @attribute x The x coordinate of the circle's center. -->
+ <xs:attribute name="x" type="Sk:Float"/>
+ <!-- @attribute y The y coordinate of the circle's center.-->
+ <xs:attribute name="y" type="Sk:Float"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** addOval
+ AddOval adds a closed oval described by its bounding box to the parent path element.
+ */ -->
+ <xs:element name="addOval">
+ <xs:complexType>
+ <!-- @attribute direction One of @pattern. @patternDescription -->
+ <xs:attribute name="direction" type="Sk:PathDirection"/>
+ <!-- @attribute bottom The bottom edge of the oval's bounding box. -->
+ <xs:attribute name="bottom" type="Sk:Float"/>
+ <!-- @attribute left The left edge of the oval's bounding box. -->
+ <xs:attribute name="left" type="Sk:Float"/>
+ <!-- @attribute right The right edge of the oval's bounding box. -->
+ <xs:attribute name="right" type="Sk:Float"/>
+ <!-- @attribute top The top edge of the oval's bounding box. -->
+ <xs:attribute name="top" type="Sk:Float"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** addPath
+ AddPath adds a path to the parent path element.
+ An optional matrix may transform the path as it is added.
+ */ -->
+ <xs:element name="addPath">
+ <xs:complexType>
+ <!-- @attribute matrix The matrix applied to the path as it is added. -->
+ <xs:attribute name="matrix" type="Sk:Matrix"/>
+ <!-- @attribute path The path to add. -->
+ <xs:attribute name="path" type="Sk:Path"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** addRect
+ AddRect adds a closed rectangle to the parent path element.
+ */ -->
+ <xs:element name="addRect">
+ <xs:complexType>
+ <!-- @attribute direction One of @pattern. @patternDescription -->
+ <xs:attribute name="direction" type="Sk:PathDirection"/>
+ <!-- @attribute bottom The bottom edge of the rectangle. -->
+ <xs:attribute name="bottom" type="Sk:Float"/>
+ <!-- @attribute left The left edge of the rectangle. -->
+ <xs:attribute name="left" type="Sk:Float"/>
+ <!-- @attribute right The right edge of the rectangle. -->
+ <xs:attribute name="right" type="Sk:Float"/>
+ <!-- @attribute top" The top" edge of the rectangle. -->
+ <xs:attribute name="top" type="Sk:Float"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** addRoundRect
+ AddRoundRect adds a closed rounded rectangle to the parent path element.
+ */ -->
+ <xs:element name="addRoundRect">
+ <xs:complexType>
+ <!-- @attribute direction One of @pattern. @patternDescription -->
+ <xs:attribute name="direction" type="Sk:PathDirection"/>
+ <!-- @attribute bottom The bottom edge of the rounded rectangle's bounding box. -->
+ <xs:attribute name="bottom" type="Sk:Float"/>
+ <!-- @attribute left The left edge of the rounded rectangle's bounding box. -->
+ <xs:attribute name="left" type="Sk:Float"/>
+ <!-- @attribute right The right edge of the rounded rectangle's bounding box. -->
+ <xs:attribute name="right" type="Sk:Float"/>
+ <!-- @attribute top The top edge of the rounded rectangle's bounding box. -->
+ <xs:attribute name="top" type="Sk:Float"/>
+ <!-- @attribute rx The X-radius of the oval used to round the corners. -->
+ <xs:attribute name="rx" type="Sk:Float"/>
+ <!-- @attribute ry The Y-radius of the oval used to round the corners. -->
+ <xs:attribute name="ry" type="Sk:Float"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** animate
+ Animate varies the value of an element's attribute over time.
+ The animation may vary starting at the 'from' attribute, and ending at the 'to' attribute,
+ or may compute the value using the 'formula' attribute.
+ */ -->
+ <xs:element name="animate">
+ <xs:complexType>
+ <!-- @attribute begin An optional offset that must elapse before the animation begins. The apply
+ begin attribute is added to any animator's begin attribute. -->
+ <xs:attribute name="begin" type="Sk:MSec"/>
+ <!-- @attribute blend Specifies how the from and to values are blended. A value from 0.0 to
+ 1.0 specifies a cubic lag/log/lag blend (slow to change at the beginning and end); the closer
+ blend is to 1.0, the more linear the blend. If omitted, the blend is linear. -->
+ <xs:attribute name="blend" type="Sk:FloatArray"/>
+ <!-- @attribute dur The duration of the animation in milliseconds. -->
+ <xs:attribute name="dur" type="Sk:MSec"/>
+ <!-- @attribute dynamic If true, restart the animation if any of the simple values the 'from', 'formula',
+ 'lval', or 'to' attributes reference are changed. Simple values are contained by the array, boolean, float, int,
+ and string elements. -->
+ <xs:attribute name="dynamic" type="Sk:Boolean" />
+ <!-- @attribute field The attribute to animate. -->
+ <xs:attribute name="field" type="Sk:String"/>
+ <!-- @attribute formula A script to execute over time to compute the field's value. Typically,
+ the fomula is a script expression which includes a reference to the time attribute of the
+ containing apply element. Requires a dur. For animations that do not stop, set dur="Number.POSITIVE_INFINITY" -->
+ <xs:attribute name="formula" type="Sk:DynamicString"/>
+ <!-- @attribute from The starting value (requires a 'to' attribute) -->
+ <xs:attribute name="from" type="Sk:DynamicString"/>
+ <!-- @attribute lval An expression evaluating to the attribute to animate.
+ If present, lval overrides 'field'. The expression is typically an array element,
+ e.g. lval="x[y]" . -->
+ <xs:attribute name="lval" type="Sk:DynamicString"/>
+ <!-- @attribute mirror If true, reverses the interpolated value during even repeat cycles. -->
+ <xs:attribute name="mirror" type="Sk:Boolean"/>
+ <!-- @attribute repeat Specifies the number of times to repeat the animation.
+ (May be fractional.) -->
+ <xs:attribute name="repeat" type="Sk:Float"/>
+ <!-- @attribute reset If true, the computed value is the initial value after the
+ animation is complete. If false, or by default, the computed value is the final value
+ after the animation is complete. -->
+ <xs:attribute name="reset" type="Sk:Boolean"/>
+ <!-- @attribute step When the apply's attribute mode="immediate" or "create", the step attribute can be read by
+ script to determine the current animation iteration. -->
+ <xs:attribute name="step" type="Sk:Int" />
+ <!-- @attribute target The element to animate. By default, the element contained by the apply
+ or referenced by the apply's scope attribute is the animate target. -->
+ <xs:attribute name="target" type="Sk:DynamicString"/>
+ <!-- @attribute to The ending value (requires a 'from' attribute) -->
+ <xs:attribute name="to" type="Sk:DynamicString"/>
+ <!-- @attribute values [Depreciated] -->
+ <xs:attribute name="values" type="Sk:DynamicString"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** apply
+ Apply changes one or more attributes of an element.
+ Apply either contains one displayable element or references the element scoping the change
+ with the 'scope' attribute. Apply either contains one animator element or references it with
+ the 'animator' attribute.
+ In the display list, apply draws the element it scopes after evaluating the animation.
+ */ -->
+ <xs:element name="apply">
+ <xs:complexType>
+ <xs:choice minOccurs="0" maxOccurs="1">
+ <xs:element ref="Sk:animate"/>
+ <xs:element ref="Sk:set" />
+ <!-- not quite right; want to say 'one of the above, one of the below'
+ </xs:choice>
+ <xs:choice minOccurs="0" maxOccurs="1">
+ -->
+ <xs:element ref="Sk:add"/>
+ <xs:element ref="Sk:array"/>
+ <xs:element ref="Sk:apply"/>
+ <xs:element ref="Sk:bitmap"/>
+ <xs:element ref="Sk:boolean"/>
+ <xs:element ref="Sk:bounds"/>
+ <!-- <xs:element ref="Sk3D:camera"/> -->
+ <xs:element ref="Sk:clear"/>
+ <xs:element ref="Sk:clip"/>
+ <xs:element ref="Sk:color"/>
+ <xs:element ref="Sk:drawTo"/>
+ <xs:element ref="Sk:float"/>
+ <xs:element ref="Sk:full"/>
+ <xs:element ref="Sk:group"/>
+ <xs:element ref="Sk:image"/>
+ <xs:element ref="Sk:int"/>
+ <xs:element ref="Sk:line"/>
+ <xs:element ref="Sk:matrix"/>
+ <xs:element ref="Sk:move"/>
+ <xs:element ref="Sk:oval"/>
+ <xs:element ref="Sk:paint"/>
+ <!-- <xs:element ref="Sk:patch"/> -->
+ <xs:element ref="Sk:path"/>
+ <xs:element ref="Sk:point"/>
+ <xs:element ref="Sk:polygon"/>
+ <xs:element ref="Sk:polyline"/>
+ <xs:element ref="Sk:post"/>
+ <xs:element ref="Sk:random"/>
+ <xs:element ref="Sk:rect"/>
+ <xs:element ref="Sk:remove"/>
+ <xs:element ref="Sk:replace"/>
+ <xs:element ref="Sk:roundRect"/>
+ <xs:element ref="Sk:save"/>
+ <xs:element ref="Sk:snapshot"/>
+ <xs:element ref="Sk:string"/>
+ <xs:element ref="Sk:text"/>
+ <xs:element ref="Sk:textBox"/>
+ <xs:element ref="Sk:textOnPath"/>
+ <xs:element ref="Sk:textToPath"/>
+ </xs:choice>
+ <!-- @attribute animator The description of how the element is changed over time. -->
+ <xs:attribute name="animator" type="Sk:Animate"/>
+ <!-- @attribute begin An optional offset that must elapse before the animation begins. The apply
+ begin attribute is added to any animator's begin attribute. -->
+ <xs:attribute name="begin" type="Sk:MSec" />
+ <!-- @attribute dontDraw Edits an element's attribute without drawing it; for instance,
+ to edit a clip's rectangle without drawing the rectangle, set dontDraw="true". -->
+ <xs:attribute name="dontDraw" type="Sk:Boolean"/>
+ <!-- @attribute dynamicScope The location in the display list where animations are stored. Use
+ dynamicScope instead of scope if a script expression with potentially different values is desired to
+ describe the scope. -->
+ <xs:attribute name="dynamicScope" type="Sk:String"/>
+ <!-- @attribute interval The optional time interval from one animation frame to the next. -->
+ <xs:attribute name="interval" type="Sk:MSec" />
+ <!-- @attribute mode One of @pattern. @patternDescription -->
+ <xs:attribute name="mode" type="Sk:ApplyMode"/>
+ <!-- @attribute pickup Starts the animation at the current target's attribute value. Enabling
+ 'pickup' permits omitting the 'from' attribute of the animator. -->
+ <xs:attribute name="pickup" type="Sk:Boolean"/>
+ <!-- @attribute restore If true, multiple references to the same apply statement save and
+ restore the interpolated target values. -->
+ <xs:attribute name="restore" type="Sk:Boolean"/>
+ <!-- @attribute scope The location in the display list where animations are stored. -->
+ <xs:attribute name="scope" type="Sk:Drawable"/>
+ <!-- @attribute step When mode="immediate" or "create", the step attribute can be read by
+ script to determine the current animation iteration. -->
+ <xs:attribute name="step" type="Sk:Int" />
+ <!-- @attribute steps When mode="immediate", the number of times the animation
+ is stepped. The animation iterates 'steps' times plus one. -->
+ <xs:attribute name="steps" type="Sk:Int" />
+ <!-- @attribute time When read from script, returns the animation time. Typically used by
+ an animate element's formula attribute. -->
+ <xs:attribute name="time" type="Sk:MSec" />
+ <!-- @attribute transition One of @pattern. @patternDescription -->
+ <xs:attribute name="transition" type="Sk:ApplyTransition"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** array
+ Array contains an array of values of the same type. The values may be
+ numbers or strings.
+ */ -->
+ <xs:element name="array">
+ <xs:complexType>
+ <!-- @attribute length The number of elements in the array (read only). -->
+ <xs:attribute name="length" type="Sk:Int"/>
+ <!-- @attribute values The elements in the array. -->
+ <xs:attribute name="values" type="Sk:UnknownArray"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** bitmap
+ Bitmap describes a rectangle of pixels.
+ Use the <drawTo> element to draw to a bitmap.
+ Add the bitmap to the display list to draw from a bitmap.
+ */ -->
+ <xs:element name="bitmap">
+ <xs:complexType>
+ <!-- @attribute erase The color, including the alpha, the bitmap is intially set to. -->
+ <xs:attribute name="erase" type="Sk:ARGB"/>
+ <!-- @attribute format One of @pattern. @patternDescription -->
+ <xs:attribute name="format" type="Sk:BitmapFormat"/>
+ <!-- @attribute height The height of the bitmap in pixels. -->
+ <xs:attribute name="height" type="Sk:Int"/>
+ <!-- @attribute rowBytes The number of byte describing each row of pixels (optional). -->
+ <xs:attribute name="rowBytes" type="Sk:Int"/>
+ <!-- @attribute width The height of the width in pixels. -->
+ <xs:attribute name="width" type="Sk:Int"/>
+ <!-- @attribute x The left edge of the bitmap in unit space. -->
+ <xs:attribute name="x" type="Sk:Float"/>
+ <!-- @attribute y The top edge of teh bitmap in unit space. -->
+ <xs:attribute name="y" type="Sk:Float"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** bitmapShader
+ BitmapShader sets the paint shader to draw the bitmap as a texture.
+ */ -->
+ <xs:element name="bitmapShader">
+ <xs:complexType>
+ <xs:choice >
+ <xs:element ref="Sk:image" minOccurs="0" />
+ <xs:element ref="Sk:matrix" minOccurs="0" />
+ </xs:choice>
+ <!-- @attribute matrix Matrix applies a 3x3 transform to the gradient. -->
+ <xs:attribute name="matrix" type="Sk:Matrix"/>
+ <!-- @attribute tileMode One of @pattern. @patternDescription -->
+ <xs:attribute name="tileMode" type="Sk:TileMode"/>
+ <!-- @attribute filterType The bitmap filter to employ, one of @pattern. -->
+ <xs:attribute name="filterType" type="Sk:FilterType"/>
+ <!-- @attribute image The bitmap to draw. -->
+ <xs:attribute name="image" type="Sk:BaseBitmap"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+
+ <!-- /** blur
+ Blur describes an image filter in the paint that blurs the drawn geometry.
+ */ -->
+ <xs:element name="blur">
+ <xs:complexType>
+ <!-- @attribute blurStyle One of @pattern. @patternDescription -->
+ <xs:attribute name="blurStyle" type="Sk:MaskFilterBlurStyle"/>
+ <!-- @attribute radius The extent of the filter effect in unit space. If the radius is less
+ than zero, the blur has no effect. -->
+ <xs:attribute name="radius" type="Sk:Float"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** boolean
+ Boolean contains an boolean. The boolean element cannot be added to a display list, but can
+ by set by animations and read by any attribute definition. An boolean element may be referenced,
+ for instance, by a group's condition attribute to make an animation conditionally execute.
+ */ -->
+ <xs:element name="boolean">
+ <xs:complexType>
+ <!-- @attribute value The contained boolean. -->
+ <xs:attribute name="value" type="Sk:Boolean"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** bounds
+ Bounds describes a bounding box that is not drawn. Bounds is used to specify a rectangle to
+ invalidate or record whether the specified area was drawn.
+ The width and height attribute compute the rectangle's right and bottom edges when the rectangle
+ description is first seen. Animating the rectangle's left or top will not recompute the right or bottom
+ if the width or height have been specified.
+ */ -->
+ <xs:element name="bounds">
+ <xs:complexType>
+ <!-- @attribute bottom The bottom edge of the rectangle. -->
+ <xs:attribute name="bottom" type="Sk:Float"/>
+ <!-- @attribute height The height of the rectangle. Setting height computes the
+ bottom attribute from the top attribute. -->
+ <xs:attribute name="height" type="Sk:Float"/>
+ <!-- @attribute inval If set to true, union the drawn bounds to compute an inval area. -->
+ <xs:attribute name="inval" type="Sk:Boolean"/>
+ <!-- @attribute left The left edge of the rectangle. -->
+ <xs:attribute name="left" type="Sk:Float"/>
+ <!-- @attribute needsRedraw Set to true if last draw was visible. -->
+ <xs:attribute name="needsRedraw" type="Sk:Boolean"/>
+ <!-- @attribute right The right edge of the rectangle. -->
+ <xs:attribute name="right" type="Sk:Float"/>
+ <!-- @attribute top The top edge of the rectangle. -->
+ <xs:attribute name="top" type="Sk:Float"/>
+ <!-- @attribute width The width of the rectangle. -->
+ <xs:attribute name="width" type="Sk:Float"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** clear
+ Clear removes all entries in the display list.
+ */ -->
+ <xs:element name="clear">
+ <xs:complexType>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** clip
+ Clip sets the canvas to clip drawing to an element's geometry.
+ A clip element may contain an element or reference an element with the path or
+ rectangle attributes. To make the clip unrestricted, enclose a 'full' element.
+ */ -->
+ <xs:element name="clip">
+ <xs:complexType>
+ <xs:choice minOccurs="0" maxOccurs="1">
+ <xs:element ref="Sk:full"/>
+ <xs:element ref="Sk:rect"/>
+ <xs:element ref="Sk:path"/>
+ <xs:element ref="Sk:polygon"/>
+ <xs:element ref="Sk:polyline"/>
+ </xs:choice>
+ <!-- @attribute path A path-derived element to clip to: either an oval,
+ a path, a polygon, a polyline, or a roundRect. -->
+ <xs:attribute name="path" type="Sk:Path"/>
+ <!-- @attribute rect A rectangle element to clip to. -->
+ <xs:attribute name="rect" type="Sk:Rect"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** close
+ Close connects the last point in the path's contour to the first if the contour is not already closed.
+ */ -->
+ <xs:element name="close">
+ <xs:complexType>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** color
+ Color describes a color in RGB space or HSV space, and its alpha (transparency).
+ */ -->
+ <xs:element name="color">
+ <xs:complexType>
+ <!-- @attribute alpha The alpha component, which describes transparency.
+ Alpha ranges from 0.0 (transparent) to 1.0 (completely opaque). -->
+ <xs:attribute name="alpha" type="Sk:Float"/>
+ <!-- @attribute blue The blue component of an RGB color. Blue ranges from 0 to 255. -->
+ <xs:attribute name="blue" type="Sk:Float"/>
+ <!-- @attribute color The complete color. The color can be specified by name,
+ by hexadecimal value, or with the rgb function. -->
+ <xs:attribute name="color" type="Sk:ARGB"/>
+ <!-- @attribute green The green component of an RGB color. Green ranges from 0 to 255. -->
+ <xs:attribute name="green" type="Sk:Float"/>
+ <!-- @attribute hue The hue component of an HSV color. Hue ranges from 0 to 360. -->
+ <xs:attribute name="hue" type="Sk:Float"/>
+ <!-- @attribute red The red component of an RGB color. Red ranges from 0 to 255. -->
+ <xs:attribute name="red" type="Sk:Float"/>
+ <!-- @attribute saturation The saturation component of an HSV color. Saturation ranges from 0 to 1. -->
+ <xs:attribute name="saturation" type="Sk:Float"/>
+ <!-- @attribute value The value component of an HSV color. Value ranges from 0 to 1. -->
+ <xs:attribute name="value" type="Sk:Float"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** cubicTo
+ CubicTo adds a cubic to the path, using the last point in the path as the first point of the cubic.
+ */ -->
+ <xs:element name="cubicTo">
+ <xs:complexType>
+ <!-- @attribute x1 The x position of the first off-curve point. -->
+ <xs:attribute name="x1" type="Sk:Float"/>
+ <!-- @attribute x2 The x position of the second off-curve point. -->
+ <xs:attribute name="x2" type="Sk:Float"/>
+ <!-- @attribute x3 The x position of the final on-curve point. -->
+ <xs:attribute name="x3" type="Sk:Float"/>
+ <!-- @attribute y1 The y position of the first off-curve point. -->
+ <xs:attribute name="y1" type="Sk:Float"/>
+ <!-- @attribute y2 The y position of the second off-curve point. -->
+ <xs:attribute name="y2" type="Sk:Float"/>
+ <!-- @attribute y3 The y position of the final on-curve point. -->
+ <xs:attribute name="y3" type="Sk:Float"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** dash
+ Dash describes an array of dashes and gaps that describe how the paint strokes lines,
+ rectangles, and paths. The intervals, phase, and dashed path are all measured in the same
+ unit space. The phase and distance between dashes is unaffected by the paint's stroke width.
+ */ -->
+ <xs:element name="dash">
+ <xs:complexType>
+ <!-- @attribute intervals An array of floats that alternately describe the lengths of
+ dashes and gaps. Intervals must contain an even number of entries. -->
+ <xs:attribute name="intervals" type="Sk:FloatArray"/>
+ <!-- @attribute phase Phase advances the placement of the first dash. A positive phase
+ preceeds the first dash with a gap. A negative phase shortens the length of the first dash. -->
+ <xs:attribute name="phase" type="Sk:Float"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** data
+ Data provides metadata to an event. The metadata may be an integer, a float,
+ or a string.
+ */ -->
+ <xs:element name="data">
+ <xs:complexType>
+ <!-- @attribute float The float value associated with the metadata. -->
+ <xs:attribute name="float" type="Sk:Float"/>
+ <!-- @attribute initialized A read-only value set to false (unused by data). -->
+ <xs:attribute name="initialized" type="Sk:Boolean"/>
+ <!-- @attribute int The integer value associated with the metadata. -->
+ <xs:attribute name="int" type="Sk:Int"/>
+ <!-- @attribute name The name of the metadata. This is the name of the data. -->
+ <xs:attribute name="name" type="Sk:String"/>
+ <!-- @attribute string The string value associated with the metadata. -->
+ <xs:attribute name="string" type="Sk:String"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** discrete
+ Discrete alters the edge of the stroke randomly. Discrete is a path effect, and only has an
+ effect when referenced from a paint.. A <pathEffect/>
+ element with no attributes will dissable discrete.
+ */ -->
+ <xs:element name="discrete">
+ <xs:complexType>
+ <!-- @attribute deviation The amount of wobble in the stroke. -->
+ <xs:attribute name="deviation" type="Sk:Float"/>
+ <!-- @attribute segLength The length of wobble in the stroke. -->
+ <xs:attribute name="segLength" type="Sk:Float"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** drawTo
+ DrawTo images to a bitmap. The bitmap can be added to the display list
+ to draw the composite image.
+ DrawTo can be used as an offscreen to speed complicated animations, and
+ for bitmap effects such as pixelated zooming.
+ DrawTo can only reference a single drawable element. Use <add>,
+ <group>, or <save> to draw multiple elements with <drawTo>.
+ */ -->
+ <xs:element name="drawTo">
+ <xs:complexType>
+ <xs:choice maxOccurs="unbounded" >
+ <xs:element ref="Sk:add"/>
+ <xs:element ref="Sk:apply"/>
+ <xs:element ref="Sk:bitmap"/>
+ <xs:element ref="Sk:bounds"/>
+ <!-- <xs:element ref="Sk3D:camera"/> -->
+ <xs:element ref="Sk:clear"/>
+ <xs:element ref="Sk:clip"/>
+ <xs:element ref="Sk:color"/>
+ <xs:element ref="Sk:full"/>
+ <xs:element ref="Sk:group"/>
+ <xs:element ref="Sk:image"/>
+ <xs:element ref="Sk:line"/>
+ <xs:element ref="Sk:matrix"/>
+ <xs:element ref="Sk:move"/>
+ <xs:element ref="Sk:oval"/>
+ <xs:element ref="Sk:paint"/>
+ <!-- <xs:element ref="Sk:patch"/> -->
+ <xs:element ref="Sk:path"/>
+ <xs:element ref="Sk:point"/>
+ <xs:element ref="Sk:polygon"/>
+ <xs:element ref="Sk:polyline"/>
+ <xs:element ref="Sk:rect"/>
+ <xs:element ref="Sk:remove"/>
+ <xs:element ref="Sk:replace"/>
+ <xs:element ref="Sk:roundRect"/>
+ <xs:element ref="Sk:save"/>
+ <xs:element ref="Sk:text"/>
+ <xs:element ref="Sk:textBox"/>
+ <xs:element ref="Sk:textOnPath"/>
+ <xs:element ref="Sk:textToPath"/>
+ </xs:choice>
+ <!-- @attribute drawOnce If set, the drawTo will only draw a single time. -->
+ <xs:attribute name="drawOnce" type="Sk:Boolean"/>
+ <!-- @attribute use The bitmap to draw into. -->
+ <xs:attribute name="use" type="Sk:bitmap"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** dump
+ Dump prints a list of the items in the display list and all items'
+ children to the debug console. Dump is only available in Debug
+ builds. */ -->
+ <xs:element name="dump">
+ <xs:complexType>
+ <!-- @attribute displayList Dumps the current display list if true. The display list is also
+ dumped if dump has no attributes. -->
+ <xs:attribute name="displayList" type="Sk:Boolean"/>
+ <!-- @attribute eventList Dumps the list of events, both enabled and disabled. -->
+ <xs:attribute name="eventList" type="Sk:Boolean"/>
+ <!-- @attribute events Outputs each event element as it is enabled. -->
+ <xs:attribute name="events" type="Sk:Boolean"/>
+ <!-- @attribute groups Outputs each group element as its condition is evaluated. -->
+ <xs:attribute name="groups" type="Sk:Boolean"/>
+ <!-- @attribute name Outputs the values associated with a single named element. -->
+ <xs:attribute name="name" type="Sk:String"/>
+ <!-- @attribute posts Outputs each post element as it is enabled. -->
+ <xs:attribute name="posts" type="Sk:Boolean"/>
+ <!-- @attribute script Evaluates the provided script -->
+ <xs:attribute name="script" type=Sk:String"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** emboss
+ PRELIMINARY [to be replaced with SkEmbossMaskFilter.h doxyfomation
+ at some point]
+ Emboss applies a mask filter to the paint that makes bias the object's color
+ towards white or black depending on the normals of the path contour, giving
+ the shape a 3D raised or depressed effect.
+ Embossing is replaced by subsequent mask filter elements, or
+ disabled a negative radius, or by an empty <mask filter> element.
+ */ -->
+ <xs:element name="emboss">
+ <xs:complexType>
+ <!-- @attribute ambient The amount of ambient light, from 0 to 1. -->
+ <xs:attribute name="ambient" type="Sk:Float"/>
+ <!-- @attribute direction The direction of the light source, as descibed by a 3D vector.
+ (The vector is normalized to a unit length of 1.0.) -->
+ <xs:attribute name="direction" type="Sk:FloatArray"/>
+ <!-- @attribute radius The extent of the filter effect in unit space. If the radius is less
+ than zero, the emboss has no effect. -->
+ <xs:attribute name="radius" type="Sk:Float"/>
+ <!-- @attribute specular The expotential intensity of the light, from 0 to 1.
+ Each increase of 0.0625 doubles the intensity. -->
+ <xs:attribute name="specular" type="Sk:Float"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** event
+ Event contains a series of actions performed each time the event's criteria are satisfied.
+ These actions may modify the display list, may enable animations which in turn modify
+ elements' attributes, and may post other events.
+ */ -->
+ <xs:element name="event">
+ <xs:complexType>
+ <xs:choice maxOccurs="unbounded" >
+ <xs:element ref="Sk:add"/>
+ <xs:element ref="Sk:apply"/>
+ <xs:element ref="Sk:array"/>
+ <xs:element ref="Sk:bitmap"/>
+ <xs:element ref="Sk:boolean"/>
+ <xs:element ref="Sk:bounds"/>
+ <!-- <xs:element ref="Sk3D:camera"/> -->
+ <xs:element ref="Sk:clear"/>
+ <xs:element ref="Sk:clip"/>
+ <xs:element ref="Sk:color"/>
+ <xs:element ref="Sk:drawTo"/>
+ <xs:element ref="Sk:dump"/>
+ <xs:element ref="Sk:float"/>
+ <xs:element ref="Sk:full"/>
+ <xs:element ref="Sk:group"/>
+ <xs:element ref="Sk:hitClear"/>
+ <xs:element ref="Sk:hitTest"/>
+ <xs:element ref="Sk:image"/>
+ <xs:element ref="Sk:input"/>
+ <xs:element ref="Sk:int"/>
+ <xs:element ref="Sk:line"/>
+ <xs:element ref="Sk:matrix"/>
+ <xs:element ref="Sk:move"/>
+ <xs:element ref="Sk:movie"/>
+ <xs:element ref="Sk:oval"/>
+ <xs:element ref="Sk:paint"/>
+ <!-- <xs:element ref="Sk:patch"/> -->
+ <xs:element ref="Sk:path"/>
+ <xs:element ref="Sk:point"/>
+ <xs:element ref="Sk:polygon"/>
+ <xs:element ref="Sk:polyline"/>
+ <xs:element ref="Sk:post"/>
+ <xs:element ref="Sk:random"/>
+ <xs:element ref="Sk:rect"/>
+ <xs:element ref="Sk:remove"/>
+ <xs:element ref="Sk:replace"/>
+ <xs:element ref="Sk:roundRect"/>
+ <xs:element ref="Sk:save"/>
+ <xs:element ref="Sk:snapshot"/>
+ <xs:element ref="Sk:string"/>
+ <xs:element ref="Sk:text"/>
+ <xs:element ref="Sk:textBox"/>
+ <xs:element ref="Sk:textOnPath"/>
+ <xs:element ref="Sk:textToPath"/>
+ </xs:choice>
+ <!-- @attribute code The key code to match to a key press event, one of @pattern.
+ If the code is set to @pattern[0], the event is never activated. -->
+ <xs:attribute name="code" type="Sk:EventCode"/>
+ <!-- @attribute disable If true, the event cannot be activated. By default false.. -->
+ <xs:attribute name="disable" type="Sk:Boolean"/>
+ <!-- @attribute key The character code to match to a key down event.
+ When read, the key that activated this event. -->
+ <xs:attribute name="key" type="Sk:String"/>
+ <!-- @attribute keys A dash-separated continuous range of character codes to match
+ to a key down event. Read the key attribute to determine the key that activated this event. -->
+ <xs:attribute name="keys" type="Sk:String"/> <!-- single or range of keys -->
+ <!-- @attribute kind The event kind that activates this event, one of @pattern.
+ If kind equals keyChar, either attribute key or keys is expected.
+ If kind equals keyPress, attribute code is expected.
+ If kind equals onEnd, attribute target is expected.
+ If kind equals onLoad, the event is activated when the document containing the event
+ is loaded. The onLoad attribute cannot be activated through a post event.
+ If kind equals user, the event is activated when the posted event targets this event's ID. -->
+ <xs:attribute name="kind" type="Sk:EventKind"/>
+ <!-- @attribute target The element to listen to which activates this event. -->
+ <xs:attribute name="target" type="Sk:String" />
+ <!-- @attribute x For click events, the x-coordinate of the click. -->
+ <xs:attribute name="x" type="Sk:Float" />
+ <!-- @attribute y For click events, the y-coordinate of the click. -->
+ <xs:attribute name="y" type="Sk:Float" />
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** float
+ Float contains a signed fractional value. The float element cannot be added to a display list,
+ but can be set by animations and read by any attribute definition.
+ */ -->
+ <xs:element name="float">
+ <xs:complexType>
+ <!-- @attribute value The contained float. -->
+ <xs:attribute name="value" type="Sk:Float"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** fromPath
+ FromPath concatenates the parent matrix with a new matrix
+ that maps a unit vector to a point on the given path.
+ A fromPath element may contain a path element, or may refer to a previously
+ defined path element with the path attribute.
+ */ -->
+ <xs:element name="fromPath">
+ <xs:complexType>
+ <xs:choice >
+ <!-- @element path The path to evaluate. -->
+ <xs:element ref="Sk:path" minOccurs="0" />
+ </xs:choice>
+ <!-- @attribute mode One of @pattern.
+ If mode is set to normal, the matrix maps the unit vector's angle and position.
+ If mode is set to angle, the matrix maps only the unit vector's angle.
+ If mode is set to position, the matrix maps only the unit vector's position. -->
+ <xs:attribute name="mode" type="Sk:FromPathMode"/>
+ <!-- @attribute offset The distance along the path to evaluate. -->
+ <xs:attribute name="offset" type="Sk:Float"/>
+ <!-- @attribute path The path to evaluate. -->
+ <xs:attribute name="path" type="Sk:Path"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** full
+ Full paints the entire canvas to the limit of the canvas' clip.
+ */ -->
+ <xs:element name="full">
+ <xs:complexType>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** group
+ The group element collects a series of elements into a group. The group can be referenced
+ or defined within elements, like apply, which operate on any kind of element. Groups
+ may contain groups. An element in a group draws identically to an element outside a group.
+ */ -->
+ <xs:element name="group">
+ <xs:complexType>
+ <xs:choice maxOccurs="unbounded">
+ <xs:element ref="Sk:add"/>
+ <xs:element ref="Sk:apply"/>
+ <xs:element ref="Sk:array"/>
+ <xs:element ref="Sk:bitmap"/>
+ <xs:element ref="Sk:boolean"/>
+ <xs:element ref="Sk:bounds"/>
+ <!-- <xs:element ref="Sk3D:camera"/> -->
+ <xs:element ref="Sk:clear"/>
+ <xs:element ref="Sk:clip"/>
+ <xs:element ref="Sk:drawTo"/>
+ <xs:element ref="Sk:float"/>
+ <xs:element ref="Sk:full"/>
+ <xs:element ref="Sk:group"/>
+ <xs:element ref="Sk:hitClear"/>
+ <xs:element ref="Sk:hitTest"/>
+ <xs:element ref="Sk:image"/>
+ <xs:element ref="Sk:int"/>
+ <xs:element ref="Sk:line"/>
+ <xs:element ref="Sk:matrix"/>
+ <xs:element ref="Sk:move"/>
+ <xs:element ref="Sk:oval"/>
+ <xs:element ref="Sk:paint"/>
+ <!-- <xs:element ref="Sk:patch"/> -->
+ <xs:element ref="Sk:path"/>
+ <xs:element ref="Sk:point"/>
+ <xs:element ref="Sk:polygon"/>
+ <xs:element ref="Sk:polyline"/>
+ <xs:element ref="Sk:post"/>
+ <xs:element ref="Sk:random"/>
+ <xs:element ref="Sk:rect"/>
+ <xs:element ref="Sk:remove"/>
+ <xs:element ref="Sk:replace"/>
+ <xs:element ref="Sk:roundRect"/>
+ <xs:element ref="Sk:save"/>
+ <xs:element ref="Sk:snapshot"/>
+ <xs:element ref="Sk:string"/>
+ <xs:element ref="Sk:text"/>
+ <xs:element ref="Sk:textBox"/>
+ <xs:element ref="Sk:textOnPath"/>
+ <xs:element ref="Sk:textToPath"/>
+ </xs:choice>
+ <!-- @attribute condition If present and zero, the contained elements are ignored
+ when drawn. -->
+ <xs:attribute name="condition" type="Sk:DynamicString"/>
+ <!-- @attribute enableCondition If present and zero, the contained elements are ignored
+ when enabled. -->
+ <xs:attribute name="enableCondition" type="Sk:DynamicString"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <xs:element name="hitClear" >
+ <xs:complexType>
+ <xs:choice maxOccurs="1">
+ <xs:element ref="Sk:array"/>
+ </xs:choice>
+ <!-- @attribute targets An array of element IDs to clear their hit-tested state. -->
+ <xs:attribute name="targets" type="Sk:DisplayableArray"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <xs:element name="hitTest" >
+ <xs:complexType>
+ <xs:choice maxOccurs="2">
+ <xs:element ref="Sk:array"/>
+ </xs:choice>
+ <!-- @attribute bullets An array of element IDs to test for intersection with targets. -->
+ <xs:attribute name="bullets" type="Sk:DisplayableArray"/>
+ <!-- @attribute hits The targets the bullets hit. A read-only array of indices, one index
+ per bullet. The value of the array element is the index of the target hit, or -1 if no
+ target was hit. -->
+ <xs:attribute name="hits" type="Sk:IntArray"/>
+ <!-- @attribute targets An array of element IDs to test for intersection with bullets. -->
+ <xs:attribute name="targets" type="Sk:DisplayableArray"/>
+ <!-- @attribute value Read only; set to true if some bullet hit some target. -->
+ <xs:attribute name="value" type="Sk:Boolean"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** image
+ Image creates a reference to a JPEG, PNG or GIF. The image may be referenced
+ through the local file system, the internet, or embedded in the document in Base64
+ format. The specific image type is determined by examining the byte stream.
+ */ -->
+ <xs:element name="image">
+ <xs:complexType>
+ <!-- @attribute base64 The image in Base64 notation. See http://rfc.net/rfc2045.html
+ for the base64 format. -->
+ <xs:attribute name="base64" type="Sk:Base64"/>
+ <!-- @attribute height The height of the image (read-only). -->
+ <xs:attribute name="height" type="Sk:Int"/>
+ <!-- @attribute src The URI reference, local to the contaiing document. -->
+ <xs:attribute name="src" type="Sk:String"/>
+ <!-- @attribute width The width of the image (read-only). -->
+ <xs:attribute name="width" type="Sk:Int"/>
+ <!-- @attribute x The position of the left edge of the image in local coordinates. -->
+ <xs:attribute name="x" type="Sk:Float"/>
+ <!-- @attribute y The position of the top edge of the image in local coordinates. -->
+ <xs:attribute name="y" type="Sk:Float"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** include
+ Include adds the referenced XML to the containing document. Unlike movie, the XML
+ directives can reference the document's IDs and can define new IDs that are referenced
+ by the remainder of the document or subsequent includes.
+ */ -->
+ <xs:element name="include">
+ <xs:complexType>
+ <!-- @attribute src The URI reference, local to the containing document,
+ containing the include's XML. -->
+ <xs:attribute name="src" type="Sk:String"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** input
+ Input captures the metadata passed from an event. When the metadata's name or id
+ matches the metadata's name, the metadata's payload is copied to the corresponding
+ input attribute.
+ */ -->
+ <xs:element name="input">
+ <xs:complexType>
+ <!-- @attribute float The floating point payload carried by the metadata. -->
+ <xs:attribute name="float" type="Sk:Float"/>
+ <!-- @attribute initialized A read-only value set to true if the input received a value
+ from the event. -->
+ <xs:attribute name="initialized" type="Sk:Boolean"/>
+ <!-- @attribute int The signed integer payload carried by the metadata. -->
+ <xs:attribute name="int" type="Sk:Int"/>
+ <!-- @attribute name The name of the metadata containing the payload. Note that
+ the name or id may match the payload, but that XML requires the id to be
+ uniquely defined in the document, while multiple input elements may reuse
+ the name. -->
+ <xs:attribute name="name" type="Sk:String"/>
+ <!-- @attribute string The text payload carried by the metadata. -->
+ <xs:attribute name="string" type="Sk:String"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** int
+ Int contains an integer. The int element cannot be added to a display list, but can
+ by set by animations and read by any attribute definition. An int element may be used,
+ for instance, to index through an array element.
+ */ -->
+ <xs:element name="int">
+ <xs:complexType>
+ <!-- @attribute value The contained integer. -->
+ <xs:attribute name="value" type="Sk:Int"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** line
+ Line describes a line between two points. As noted below, the paint's stroke and
+ strokeAndFill attributes are ignored.
+ */ -->
+ <xs:element name="line">
+ <xs:complexType>
+ <!-- @attribute x1 The start point's x value. -->
+ <xs:attribute name="x1" type="Sk:Float"/>
+ <!-- @attribute x2 The stop point's x value. -->
+ <xs:attribute name="x2" type="Sk:Float"/>
+ <!-- @attribute y1 The start point's y value. -->
+ <xs:attribute name="y1" type="Sk:Float"/>
+ <!-- @attribute y2 The stop point's y value. -->
+ <xs:attribute name="y2" type="Sk:Float"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** lineTo
+ LineTo adds a line from the last point in a path to the specified point.
+ */ -->
+ <xs:element name="lineTo">
+ <xs:complexType>
+ <!-- @attribute x The final path x coordinate. -->
+ <xs:attribute name="x" type="Sk:Float"/>
+ <!-- @attribute y The final path y coordinate. -->
+ <xs:attribute name="y" type="Sk:Float"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** linearGradient
+ LinearGradient sets the paint shader to ramp between two or more colors.
+ */ -->
+ <xs:element name="linearGradient">
+ <xs:complexType>
+ <xs:choice maxOccurs="unbounded">
+ <xs:element ref="Sk:color"/>
+ <xs:element ref="Sk:matrix"/>
+ </xs:choice>
+ <!-- @attribute matrix Matrix applies a 3x3 transform to the gradient. -->
+ <xs:attribute name="matrix" type="Sk:Matrix"/>
+ <!-- @attribute tileMode One of @pattern. @patternDescription -->
+ <xs:attribute name="tileMode" type="Sk:TileMode"/>
+ <!-- @attribute offsets An optional array of values used to bias the colors. The first entry
+ in the array must be 0.0, the last must be 1.0, and intermediate values must ascend. -->
+ <xs:attribute name="offsets" type="Sk:FloatArray"/>
+ <!-- @attribute points Two points describing the start and end of the gradient. -->
+ <xs:attribute name="points" type="Sk:Point"/> <!-- not right; should be array of 2 points -->
+ <!-- @attribute unitMapper A script that returns the mapping for [0,1] for the gradient.
+ The script can use the predefined variable 'unit' to compute the mapping. For instance,
+ "unit*unit" squares the value (while still keeping it in the range of [0,1].) The computed number
+ is pinned to from 0 to 1 after the script is executed. -->
+ <xs:attribute name="unitMapper" type="Sk:String"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** maskFilter
+ MaskFilter disables any mask filter referenced by the paint.
+ */ -->
+ <xs:element name="maskFilter">
+ <xs:complexType>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** matrix
+ Matrix transforms all points drawn to the canvas. The matrix may translate, scale, skew, rotate,
+ or apply perspective, or apply any combination.
+ */ -->
+ <xs:element name="matrix">
+ <xs:complexType>
+ <xs:choice maxOccurs="unbounded">
+ <!-- @element fromPath FromPath maps a unit vector to a position and direction on a path. -->
+ <xs:element ref="Sk:fromPath"/>
+ <!-- @element polyToPoly PolyToPoly maps a points between two polygons. -->
+ <xs:element ref="Sk:polyToPoly"/>
+ <!-- @element rectToRect RectToRect maps a points between two rectangles. -->
+ <xs:element ref="Sk:rectToRect"/>
+ <!-- @element rotate Rotate computes the matrix rotation in degrees. -->
+ <xs:element ref="Sk:rotate"/>
+ <!-- @element scale Scale stretches or shrinks horizontally, vertically, or both. -->
+ <xs:element ref="Sk:scale"/>
+ <!-- @element skew Skew slants horizontally, vertically, or both. -->
+ <xs:element ref="Sk:skew"/>
+ <!-- @element translate Translate moves horizontally, vertically, or both. -->
+ <xs:element ref="Sk:translate"/>
+ </xs:choice>
+ <!-- @attribute matrix Nine floats describing a 3x3 matrix. -->
+ <xs:attribute name="matrix" type="Sk:FloatArray"/>
+ <!-- @attribute perspectX The [0][2] element of the 3x3 matrix. -->
+ <xs:attribute name="perspectX" type="Sk:Float"/>
+ <!-- @attribute perspectY The [1][2] element of the 3x3 matrix. -->
+ <xs:attribute name="perspectY" type="Sk:Float"/>
+ <!-- @attribute rotate The angle to rotate in degrees. -->
+ <xs:attribute name="rotate" type="Sk:Float"/>
+ <!-- @attribute scale The scale to apply in both X and Y.. -->
+ <xs:attribute name="scale" type="Sk:Float"/>
+ <!-- @attribute scaleX The [0][0] element of the 3x3 matrix. -->
+ <xs:attribute name="scaleX" type="Sk:Float"/>
+ <!-- @attribute scaleY The [1][1] element of the 3x3 matrix. -->
+ <xs:attribute name="scaleY" type="Sk:Float"/>
+ <!-- @attribute skewX The [0][1] element of the 3x3 matrix. -->
+ <xs:attribute name="skewX" type="Sk:Float"/>
+ <!-- @attribute skewY The [1][0] element of the 3x3 matrix. -->
+ <xs:attribute name="skewY" type="Sk:Float"/>
+ <!-- @attribute translate A point specifying the translation in X and Y. -->
+ <xs:attribute name="translate" type="Sk:Point"/>
+ <!-- @attribute translateX The [2][0] element of the 3x3 matrix. -->
+ <xs:attribute name="translateX" type="Sk:Float"/>
+ <!-- @attribute translateY The [2][1] element of the 3x3 matrix. -->
+ <xs:attribute name="translateY" type="Sk:Float"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** move
+ Move an element in the display list in front or behind other elements.
+ If where and offset are omitted, the element is moved to the end of the display list.
+ If where is specified, the element is moved before the first occurance of where in the display list.
+ If offset and where are specified, the element is moved before where plus offset.
+ A positive offset without where moves the element to the start of the list plus offset.
+ A negative offset without where moves the element to the end of the list minus offset.
+ */ -->
+ <xs:element name="move">
+ <xs:complexType>
+ <!-- @attribute mode Has no effect. -->
+ <xs:attribute name="mode" type="Sk:AddMode"/>
+ <!-- @attribute offset The destination position using the rules listed above. -->
+ <xs:attribute name="offset" type="Sk:Int"/>
+ <!-- @attribute use The element to move. -->
+ <xs:attribute name="use" type="Sk:Drawable"/>
+ <!-- @attribute where The ID of the first display list entry to move to. -->
+ <xs:attribute name="where" type="Sk:Drawable"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** moveTo
+ MoveTo specifies the first point in a path contour.
+ */ -->
+ <xs:element name="moveTo">
+ <xs:complexType>
+ <!-- @attribute x The point's x coordinate. -->
+ <xs:attribute name="x" type="Sk:Float"/>
+ <!-- @attribute y The point's y coordinate. -->
+ <xs:attribute name="y" type="Sk:Float"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** movie
+ Movie describes a display list within the current canvas and paint. Movies can contain
+ movies. One movie cannot affect how another movie draws, but movies can communicate
+ with each other by posting events.
+ */ -->
+ <xs:element name="movie">
+ <xs:complexType>
+ <!-- @attribute src The URI reference, local to the containing document, containing the movie's XML. -->
+ <xs:attribute name="src" type="Sk:String"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** oval
+ Oval describes a circle stretched to fit in a rectangle.
+ The width and height attribute compute the oval's right and bottom edges when the oval
+ description is first seen. Animating the oval's left or top will not recompute the right or bottom
+ if the width or height have been specified.
+ */ -->
+ <xs:element name="oval">
+ <xs:complexType>
+ <!-- @attribute bottom The bottom edge of the oval. -->
+ <xs:attribute name="bottom" type="Sk:Float"/>
+ <!-- @attribute height The height of the oval. -->
+ <xs:attribute name="height" type="Sk:Float"/>
+ <!-- @attribute left The left edge of the oval. -->
+ <xs:attribute name="left" type="Sk:Float"/>
+ <!-- @attribute needsRedraw Set to true if last draw was visible. -->
+ <xs:attribute name="needsRedraw" type="Sk:Boolean"/>
+ <!-- @attribute right The right edge of the oval. -->
+ <xs:attribute name="right" type="Sk:Float"/>
+ <!-- @attribute top The top edge of the oval. -->
+ <xs:attribute name="top" type="Sk:Float"/>
+ <!-- @attribute width The width of the oval. -->
+ <xs:attribute name="width" type="Sk:Float"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** paint
+ Paint uses color, flags, path effects, mask filters, shaders, and stroke effects when drawing
+ geometries, images, and text.
+ */ -->
+ <xs:element name="paint">
+ <xs:complexType>
+ <xs:choice maxOccurs="unbounded">
+ <!-- @element bitmapShader Sets or cancels an image to draw as the color. -->
+ <xs:element ref="Sk:bitmapShader"/>
+ <!-- @element blur Blur radially draws the shape with varying transparency. -->
+ <xs:element ref="Sk:blur"/>
+ <!-- @element color Color specifies a solid color in RGB or HSV. -->
+ <xs:element ref="Sk:color"/>
+ <!-- @element dash Dashes alternates stroking with dashes and gaps. -->
+ <xs:element ref="Sk:dash"/>
+ <!-- @element discrete Discrete wobbles the geometry randomly. -->
+ <xs:element ref="Sk:discrete"/>
+ <!-- @element emboss Emboss simulates a 3D light to show highlights and relief. -->
+ <xs:element ref="Sk:emboss"/>
+ <!-- @element linearGradient LinearGradient linearly ramps between two or more colors. -->
+ <xs:element ref="Sk:linearGradient"/>
+ <!-- @element maskFilter MaskFilter cancels a blur or emboss. -->
+ <xs:element ref="Sk:maskFilter"/>
+ <!-- @element pathEffect PathEffect cancels a discrete or dash. -->
+ <xs:element ref="Sk:pathEffect"/>
+ <!-- @element radialGradient RadialGradient radially ramps between two or more colors. -->
+ <xs:element ref="Sk:radialGradient"/>
+ <!-- @element shader Shader cancels a linear or radial gradient. -->
+ <xs:element ref="Sk:shader"/>
+ <!-- @element typeface Typeface chooses a font out of a font family. -->
+ <xs:element ref="Sk:typeface"/>
+ <!-- @element transparentShader TransparentShader ? [not sure what this is for] -->
+ <xs:element ref="Sk:transparentShader"/>
+ </xs:choice>
+ <!-- @attribute antiAlias AntiAlias uses gray shades to increase the definition of paths. -->
+ <xs:attribute name="antiAlias" type="Sk:Boolean"/>
+ <!-- @attribute ascent Ascent returns the height above the baseline defined by the font. -->
+ <xs:attribute name="ascent" type="Sk:Float"/>
+ <!-- @attribute color Color sets the paint to the color element with this ID. -->
+ <xs:attribute name="color" type="Sk:Color"/>
+ <!-- @attribute descent Descent returns the height below the baseline defined by thte font -->
+ <xs:attribute name="descent" type="Sk:Float"/>
+ <!-- @attribute fakeBold FakeBold enables a faked bold for text. -->
+ <xs:attribute name="fakeBold" type="Sk:Boolean"/>
+ <!-- @attribute filterType FilterType -->
+ <xs:attribute name="filterType" type="Sk:FilterType"/>
+ <!-- @attribute linearText LinearText uses the ideal path metrics at all sizes to describe text. -->
+ <xs:attribute name="linearText" type="Sk:Boolean"/>
+ <!-- @attribute maskFilter MaskFilter specifies a blur or emboss with this ID. -->
+ <xs:attribute name="maskFilter" type="Sk:MaskFilter"/>
+ <!-- @attribute measureText MeasureText(String) returns the width of the string in this paint. -->
+ <xs:attribute name="measureText" type="Sk:Float"/>
+ <!-- @attribute pathEffect PathEffect specifies a discrete or dash with this ID. -->
+ <xs:attribute name="pathEffect" type="Sk:PathEffect"/>
+ <!-- @attribute shader Shader specifies a gradient with this ID. -->
+ <xs:attribute name="shader" type="Sk:Shader"/>
+ <!-- @attribute strikeThru StrikeThru adds a line through the middle of drawn text. -->
+ <xs:attribute name="strikeThru" type="Sk:Boolean"/>
+ <!-- @attribute stroke Stroke draws the outline of geometry according to the pen attributes.
+ If style is also present, its setting overrides stroke. -->
+ <xs:attribute name="stroke" type="Sk:Boolean"/>
+ <!-- @attribute strokeCap StrokeCap is one of @pattern. -->
+ <xs:attribute name="strokeCap" type="Sk:Cap"/>
+ <!-- @attribute strokeJoin StrokeJoin is one of @pattern. -->
+ <xs:attribute name="strokeJoin" type="Sk:Join"/>
+ <!-- @attribute strokeMiter StrokeMiter limits the pen's joins on narrow angles. -->
+ <xs:attribute name="strokeMiter" type="Sk:Float"/>
+ <!-- @attribute strokeWidth StrokeWidth specifies the width of the pen. -->
+ <xs:attribute name="strokeWidth" type="Sk:Float"/>
+ <!-- @attribute style Style fills, strokes, or strokes and fills the geometry with the paint's color. -->
+ <xs:attribute name="style" type="Sk:Style"/>
+ <!-- @attribute textAlign TextAlign is one of @pattern. -->
+ <xs:attribute name="textAlign" type="Sk:Align"/>
+ <!-- @attribute textScaleX TextScaleX condenses or exapnds the text. -->
+ <xs:attribute name="textScaleX" type="Sk:Float"/>
+ <!-- @attribute textSize TextSize specifies the point size of the text. -->
+ <xs:attribute name="textSize" type="Sk:Float"/>
+ <!-- @attribute textSkewX TextSkewX draws the text obliquely. -->
+ <xs:attribute name="textSkewX" type="Sk:Float"/>
+ <!-- @attribute textTracking TextTracking specifies the space between letters. -->
+ <xs:attribute name="textTracking" type="Sk:Float"/>
+ <!-- @attribute typeface Typeface specifies a typeface element with this ID. -->
+ <xs:attribute name="typeface" type="Sk:Typeface"/>
+ <!-- @attribute underline Underline draws a line under the baseline of the text. -->
+ <xs:attribute name="underline" type="Sk:Boolean"/>
+ <!-- @attribute xfermode Xfermode specifies a transfer mode, one of @pattern. -->
+ <xs:attribute name="xfermode" type="Sk:Xfermode"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** path
+ Path creates a geometry out of lines and curves.
+ */ -->
+ <xs:element name="path">
+ <xs:complexType>
+ <xs:choice maxOccurs="unbounded">
+ <!-- @element addCircle Adds a circle to the path. -->
+ <xs:element ref="Sk:addCircle"/>
+ <!-- @element addOval Adds an oval to the path. -->
+ <xs:element ref="Sk:addOval"/>
+ <!-- @element addPath Adds another path to the path. -->
+ <xs:element ref="Sk:addPath"/>
+ <!-- @element addRoundRect Adds a rounded-corner rectangle to the path. -->
+ <xs:element ref="Sk:addRoundRect"/>
+ <!-- @element close Connects the last point on the path to the first. -->
+ <xs:element ref="Sk:close"/>
+ <!-- @element cubicTo Extends the path with a cubic curve. -->
+ <xs:element ref="Sk:cubicTo"/>
+ <!-- @element lineTo Extends the path with a line. -->
+ <xs:element ref="Sk:lineTo"/>
+ <!-- @element moveTo Starts a new path contour. -->
+ <xs:element ref="Sk:moveTo"/>
+ <!-- @element quadTo Extends the path with a quadratic curve. -->
+ <xs:element ref="Sk:quadTo"/>
+ <!-- @element rCubicTo Extends the path with a cubic curve expressed with relative offsets. -->
+ <xs:element ref="Sk:rCubicTo"/>
+ <!-- @element rLineTo Extends the path with a line expressed with relative offsets. -->
+ <xs:element ref="Sk:rLineTo"/>
+ <!-- @element rMoveTo Starts a new path contour relative to the path's last point. -->
+ <xs:element ref="Sk:rMoveTo"/>
+ <!-- @element rQuadTo Extends the path with a quadratic curve expressed with relative offsets. -->
+ <xs:element ref="Sk:rQuadTo"/>
+ </xs:choice>
+ <!-- @attribute d Creates a path using SVG path notation. -->
+ <xs:attribute name="d" type="Sk:String"/>
+ <!-- @attribute fillType One of @pattern. -->
+ <xs:attribute name="fillType" type="Sk:FillType"/>
+ <!-- @attribute length Returns the length of the path. -->
+ <xs:attribute name="length" type="Sk:Float"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** pathEffect
+ PathEffect cancels any current path effect within the paint, such as dashing or discrete.
+ */ -->
+ <xs:element name="pathEffect">
+ <xs:complexType>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** point
+ Point describes a two dimensional point in space. The point element can be added
+ to the display list and drawn.
+ */ -->
+ <xs:element name="point">
+ <xs:complexType>
+ <!-- @attribute x The x coordinate of the point. -->
+ <xs:attribute name="x" type="Sk:Float"/>
+ <!-- @attribute y The y coordinate of the point. -->
+ <xs:attribute name="y" type="Sk:Float"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** polygon
+ Polygon creates a geometry out of lines. Polygon is a specialization of path; element that
+ refers to a path can refer to a polygon also. A polygon specified through elements behaves identically
+ to a path. A polygon specified by the points attribute contains a single contour, and the contour is
+ automatically closed.
+ */ -->
+ <xs:element name="polygon">
+ <xs:complexType>
+ <xs:choice maxOccurs="unbounded">
+ <!-- @element close Connects the last point on the path to the first. -->
+ <xs:element ref="Sk:close"/>
+ <!-- @element addPath Adds another path to the path. -->
+ <xs:element ref="Sk:addPath"/>
+ <!-- @element lineTo Extends the path with a line. -->
+ <xs:element ref="Sk:lineTo"/>
+ <!-- @element moveTo Starts a new path contour. -->
+ <xs:element ref="Sk:moveTo"/>
+ <!-- @element rLineTo Extends the path with a line expressed with relative offsets. -->
+ <xs:element ref="Sk:rLineTo"/>
+ <!-- @element rMoveTo Starts a new path contour relative to the path's last point. -->
+ <xs:element ref="Sk:rMoveTo"/>
+ </xs:choice>
+ <!-- @attribute points An array of values that describe a sequence of points, compatible with SVG. -->
+ <xs:attribute name="points" type="Sk:FloatArray"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** polyline
+ Polyline creates a geometry out of lines. Polygon is a specialization of path; element that
+ refers to a path can refer to a polygon also. A polygon specified through elements behaves identically
+ to a path. A polygon specified by the points attribute contains a single contour, and the contour is
+ not automatically closed.
+ */ -->
+ <xs:element name="polyline">
+ <xs:complexType>
+ <xs:choice maxOccurs="unbounded">
+ <!-- @element close Connects the last point on the path to the first. -->
+ <xs:element ref="Sk:close"/>
+ <!-- @element addPath Adds another path to the path. -->
+ <xs:element ref="Sk:addPath"/>
+ <!-- @element lineTo Extends the path with a line. -->
+ <xs:element ref="Sk:lineTo"/>
+ <!-- @element moveTo Starts a new path contour. -->
+ <xs:element ref="Sk:moveTo"/>
+ <!-- @element rLineTo Extends the path with a line expressed with relative offsets. -->
+ <xs:element ref="Sk:rLineTo"/>
+ <!-- @element rMoveTo Starts a new path contour relative to the path's last point. -->
+ <xs:element ref="Sk:rMoveTo"/>
+ </xs:choice>
+ <!-- @attribute points An array of values that describe a sequence of points, compatible with SVG. -->
+ <xs:attribute name="points" type="Sk:FloatArray"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** polyToPoly
+ PolyToPoly creates a matrix which maps points proportionally from one polygon to the other.
+ */ -->
+ <xs:element name="polyToPoly">
+ <xs:complexType>
+ <xs:choice maxOccurs="2">
+ <xs:element ref="Sk:polygon"/>
+ </xs:choice>
+ <!-- @attribute source The polygon to map from.. -->
+ <xs:attribute name="source" type="Sk:polygon"/>
+ <!-- @attribute destination The polygon to map to.. -->
+ <xs:attribute name="destination" type="Sk:polygon"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** post
+ Post activates an event. The event can trigger one or more actions, and can carry a data payload.
+ */ -->
+ <xs:element name="post">
+ <xs:complexType>
+ <xs:choice maxOccurs="unbounded">
+ <xs:element ref="Sk:data"/>
+ </xs:choice>
+ <!-- @attribute delay Time in seconds that must elapse before the target event is activated. -->
+ <xs:attribute name="delay" type="Sk:MSec"/>
+ <!-- @attribute mode One of @pattern. @patternDescription -->
+ <xs:attribute name="mode" type="Sk:EventMode"/>
+ <!-- @attribute sink The optional named EventSink to direct the event to. -->
+ <xs:attribute name="sink" type="Sk:String"/>
+ <!-- @attribute target The ID of the user event to trigger. -->
+ <xs:attribute name="target" type="Sk:String"/>
+ <!-- @attribute type The name of the external event to post. -->
+ <xs:attribute name="type" type="Sk:String"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** quadTo
+ QuadTo adds a quadratic curve to a path.
+ */ -->
+ <xs:element name="quadTo">
+ <xs:complexType>
+ <!-- @attribute x1 The x position of the off-curve point. -->
+ <xs:attribute name="x1" type="Sk:Float"/>
+ <!-- @attribute x2 The x position of the final point. -->
+ <xs:attribute name="x2" type="Sk:Float"/>
+ <!-- @attribute y1 The y position of the off-curve point. -->
+ <xs:attribute name="y1" type="Sk:Float"/>
+ <!-- @attribute y2 The y position of the final point. -->
+ <xs:attribute name="y2" type="Sk:Float"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** rCubicTo
+ RCubicTo adds a cubic to the path, using the last point in the path as the first point of the cubic. THe
+ added points are offsets from the last point in the path.
+ */ -->
+ <xs:element name="rCubicTo">
+ <xs:complexType>
+ <!-- @attribute x1 The x offset of the first off-curve point. -->
+ <xs:attribute name="x1" type="Sk:Float"/>
+ <!-- @attribute x2 The x offset of the second off-curve point. -->
+ <xs:attribute name="x2" type="Sk:Float"/>
+ <!-- @attribute x3 The x offset of the final on-curve point. -->
+ <xs:attribute name="x3" type="Sk:Float"/>
+ <!-- @attribute y1 The y offset of the first off-curve point. -->
+ <xs:attribute name="y1" type="Sk:Float"/>
+ <!-- @attribute y2 The y offset of the second off-curve point. -->
+ <xs:attribute name="y2" type="Sk:Float"/>
+ <!-- @attribute y3 The y offset of the final on-curve point. -->
+ <xs:attribute name="y3" type="Sk:Float"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** rLineTo
+ RLineTo adds a line from the last point in a path to the specified point. The specified
+ point is relative to the last point in the path.
+ */ -->
+ <xs:element name="rLineTo">
+ <xs:complexType>
+ <!-- @attribute x The final path x coordinate. -->
+ <xs:attribute name="x" type="Sk:Float"/>
+ <!-- @attribute y The final path y coordinate. -->
+ <xs:attribute name="y" type="Sk:Float"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** rMoveTo
+ RMoveTo specifies the first point in a path contour. The specified
+ point is relative to the last point in the path.
+ */ -->
+ <xs:element name="rMoveTo">
+ <xs:complexType>
+ <!-- @attribute x The point's x coordinate. -->
+ <xs:attribute name="x" type="Sk:Float"/>
+ <!-- @attribute y The point's y coordinate. -->
+ <xs:attribute name="y" type="Sk:Float"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** rQuadTo
+ RQuadTo adds a quadratic curve to a path. The quadratic
+ points are relative to the last point in the path.
+ */ -->
+ <xs:element name="rQuadTo">
+ <xs:complexType>
+ <!-- @attribute x1 The x position of the off-curve point. -->
+ <xs:attribute name="x1" type="Sk:Float"/>
+ <!-- @attribute x2 The x position of the final point. -->
+ <xs:attribute name="x2" type="Sk:Float"/>
+ <!-- @attribute y1 The y position of the off-curve point. -->
+ <xs:attribute name="y1" type="Sk:Float"/>
+ <!-- @attribute y2 The y position of the final point. -->
+ <xs:attribute name="y2" type="Sk:Float"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** radialGradient
+ RadialGradient sets the paint shader to ramp between two or more colors in concentric circles.
+ */ -->
+ <xs:element name="radialGradient">
+ <xs:complexType>
+ <xs:choice maxOccurs="unbounded">
+ <xs:element ref="Sk:color"/>
+ <xs:element ref="Sk:matrix"/>
+ </xs:choice>
+ <!-- @attribute matrix Matrix applies a 3x3 transform to the gradient. -->
+ <xs:attribute name="matrix" type="Sk:Matrix"/>
+ <!-- @attribute tileMode One of @pattern. @patternDescription -->
+ <xs:attribute name="tileMode" type="Sk:TileMode"/>
+ <!-- @attribute center The center point of the radial gradient. -->
+ <xs:attribute name="center" type="Sk:Point"/>
+ <!-- @attribute offsets An optional array of values used to bias the colors. The first entry
+ in the array must be 0.0, the last must be 1.0, and intermediate values must ascend. -->
+ <xs:attribute name="offsets" type="Sk:FloatArray"/>
+ <!-- @attribute radius The distance from the first color to the last color. -->
+ <xs:attribute name="radius" type="Sk:Float"/>
+ <!-- @attribute unitMapper A script that returns the mapping for [0,1] for the gradient.
+ The script can use the predefined variable 'unit' to compute the mapping. For instance,
+ "unit*unit" squares the value (while still keeping it in the range of [0,1].) The computed number
+ is pinned to from 0 to 1 after the script is executed. -->
+ <xs:attribute name="unitMapper" type="Sk:String"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** random
+ Random generates a random number, from min to max. Each time the random attribute is
+ read, a new random number is generated.
+ */ -->
+ <xs:element name="random">
+ <xs:complexType>
+ <!-- @attribute blend The random bias from 0.0 to 1.0.
+ 0.0 biias the number towards the start and end of the range.
+ 1.0 (the default) generates a linear distribution.-->
+ <xs:attribute name="blend" type="Sk:Float"/>
+ <!-- @attribute max The largest value to generate. -->
+ <xs:attribute name="max" type="Sk:Float"/>
+ <!-- @attribute min The smallest value to generate. -->
+ <xs:attribute name="min" type="Sk:Float"/>
+ <!-- @attribute random The generated value. -->
+ <xs:attribute name="random" type="Sk:Float"/>
+ <!-- @attribute seed The random seed. Identical seeds generate the same series of
+ numbers. -->
+ <xs:attribute name="seed" type="Sk:Int"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** rect
+ Rect describes a bounding box.
+ The width and height attribute compute the rectangle's right and bottom edges when the rectangle
+ description is first seen. Animating the rectangle's left or top will not recompute the right or bottom
+ if the width or height have been specified.
+ */ -->
+ <xs:element name="rect">
+ <xs:complexType>
+ <!-- @attribute bottom The bottom edge of the rectangle. -->
+ <xs:attribute name="bottom" type="Sk:Float"/>
+ <!-- @attribute height The height of the rectangle. Setting height computes the
+ bottom attribute from the top attribute. -->
+ <xs:attribute name="height" type="Sk:Float"/>
+ <!-- @attribute left The left edge of the rectangle. -->
+ <xs:attribute name="left" type="Sk:Float"/>
+ <!-- @attribute needsRedraw Set to true if last draw was visible. -->
+ <xs:attribute name="needsRedraw" type="Sk:Boolean"/>
+ <!-- @attribute right The right edge of the rectangle. -->
+ <xs:attribute name="right" type="Sk:Float"/>
+ <!-- @attribute top The top edge of the rectangle. -->
+ <xs:attribute name="top" type="Sk:Float"/>
+ <!-- @attribute width The width of the rectangle. -->
+ <xs:attribute name="width" type="Sk:Float"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** rectToRect
+ RectToRect adds a matrix to map one rectangle's coordinates to another.
+ */ -->
+ <xs:element name="rectToRect">
+ <xs:complexType>
+ <xs:choice maxOccurs="2">
+ <xs:element ref="Sk:rect"/>
+ </xs:choice>
+ <!-- @attribute source The rectangle to map from. -->
+ <xs:attribute name="source" type="Sk:rect"/>
+ <!-- @attribute destination The rectangle to map to. -->
+ <xs:attribute name="destination" type="Sk:rect"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** remove
+ Remove an item from the display list.
+ If where is specified, the first occurance of where in the display list is removed.
+ If offset and where are specified, the element at where plus offset is removed.
+ A positive offset without where removes the element at the start of the list plus offset.
+ A negative offset without where removes the element at the end of the list minus offset.
+ */ -->
+ <xs:element name="remove">
+ <xs:complexType>
+ <!-- @attribute delete If true, reverse the action of apply's attribute mode="create".
+ (Experimental.) -->
+ <xs:attribute name="delete" type="Sk:Boolean"/>
+ <!-- @attribute offset The destination position using the rules listed above. -->
+ <xs:attribute name="offset" type="Sk:Int"/>
+ <!-- @attribute where The ID of the first display list entry to remove. -->
+ <xs:attribute name="where" type="Sk:Drawable"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** replace
+ Replace an item in the display list.
+ If where is specified, the first occurance of where in the display list is replaced by use.
+ If offset and where are specified, the element at where plus offset is replaced by use.
+ A positive offset without where replaces the element at the start of the list plus offset.
+ A negative offset without where replaces the element at the end of the list minus offset.
+ */ -->
+ <xs:element name="replace">
+ <xs:complexType>
+ <!-- @attribute mode Has no effect. -->
+ <xs:attribute name="mode" type="Sk:AddMode"/>
+ <!-- @attribute offset The destination position using the rules listed above. -->
+ <xs:attribute name="offset" type="Sk:Int"/>
+ <!-- @attribute use The element to be added to the display list.. -->
+ <xs:attribute name="use" type="Sk:Drawable"/>
+ <!-- @attribute where The ID of the first display list entry to remove. -->
+ <xs:attribute name="where" type="Sk:Drawable"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** rotate
+ Rotate creates a matrix that rotates a unit vector about a center point, and concatenated
+ with the containing matrix.
+ */ -->
+ <xs:element name="rotate">
+ <xs:complexType>
+ <!-- @attribute center A point the rotation is centered about; by default, [0.0, 0.0]. -->
+ <xs:attribute name="center" type="Sk:Point"/>
+ <!-- @attribute degrees The rotation in degrees. -->
+ <xs:attribute name="degrees" type="Sk:Float"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** roundRect
+ RoundRect creates a rectangle with rounded corners. The rounded corners are specified by
+ two axes, which describe an quarter-section of the oval which is used in each corner.
+ The width and height attribute compute the rectangle's right and bottom edges when the rectangle
+ description is first seen. Animating the rectangle's left or top will not recompute the right or bottom
+ if the width or height have been specified.
+ */ -->
+ <xs:element name="roundRect">
+ <xs:complexType>
+ <!-- @attribute bottom The bottom edge of the rectangle. -->
+ <xs:attribute name="bottom" type="Sk:Float"/>
+ <!-- @attribute height The height of the rectangle. Setting height computes the
+ bottom attribute from the top attribute. -->
+ <xs:attribute name="height" type="Sk:Float"/>
+ <!-- @attribute left The left edge of the rectangle. -->
+ <xs:attribute name="left" type="Sk:Float"/>
+ <!-- @attribute needsRedraw Set to true if last draw was visible. -->
+ <xs:attribute name="needsRedraw" type="Sk:Boolean"/>
+ <!-- @attribute right The right edge of the rectangle. -->
+ <xs:attribute name="right" type="Sk:Float"/>
+ <!-- @attribute top The top edge of the rectangle. -->
+ <xs:attribute name="top" type="Sk:Float"/>
+ <!-- @attribute rx The radius of the corners on the x axis. -->
+ <xs:attribute name="rx" type="Sk:Float"/>
+ <!-- @attribute ry The radius of the corners on the y axis. -->
+ <xs:attribute name="ry" type="Sk:Float"/>
+ <!-- @attribute width The width of the rectangle. Setting width computes the
+ right attribute from the left attribute. -->
+ <xs:attribute name="width" type="Sk:Float"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** save
+ The save element collects a series of elements into a group. The state of the paint and
+ canvas are saved, so that edits to the paint and canvas within the group are restored
+ to their original value at the end of the group.
+ The save element can be referenced
+ or defined within elements, like apply, which operate on any kind of element. Groups
+ may contain groups.
+ */ -->
+ <xs:element name="save">
+ <xs:complexType>
+ <xs:choice maxOccurs="unbounded">
+ <xs:element ref="Sk:add"/>
+ <xs:element ref="Sk:apply"/>
+ <xs:element ref="Sk:array"/>
+ <xs:element ref="Sk:bitmap"/>
+ <xs:element ref="Sk:boolean"/>
+ <xs:element ref="Sk:bounds"/>
+ <!-- <xs:element ref="Sk3D:camera"/> -->
+ <xs:element ref="Sk:clear"/>
+ <xs:element ref="Sk:clip"/>
+ <xs:element ref="Sk:color"/>
+ <xs:element ref="Sk:drawTo"/>
+ <xs:element ref="Sk:float"/>
+ <xs:element ref="Sk:full"/>
+ <xs:element ref="Sk:group"/>
+ <xs:element ref="Sk:hitClear"/>
+ <xs:element ref="Sk:hitTest"/>
+ <xs:element ref="Sk:image"/>
+ <xs:element ref="Sk:int"/>
+ <xs:element ref="Sk:line"/>
+ <xs:element ref="Sk:matrix"/>
+ <xs:element ref="Sk:move"/>
+ <xs:element ref="Sk:oval"/>
+ <xs:element ref="Sk:paint"/>
+ <!-- <xs:element ref="Sk:patch"/> -->
+ <xs:element ref="Sk:path"/>
+ <xs:element ref="Sk:point"/>
+ <xs:element ref="Sk:polygon"/>
+ <xs:element ref="Sk:polyline"/>
+ <xs:element ref="Sk:post"/>
+ <xs:element ref="Sk:random"/>
+ <xs:element ref="Sk:rect"/>
+ <xs:element ref="Sk:remove"/>
+ <xs:element ref="Sk:replace"/>
+ <xs:element ref="Sk:roundRect"/>
+ <xs:element ref="Sk:save"/>
+ <xs:element ref="Sk:set"/>
+ <xs:element ref="Sk:snapshot"/>
+ <xs:element ref="Sk:string"/>
+ <xs:element ref="Sk:text"/>
+ <xs:element ref="Sk:textBox"/>
+ <xs:element ref="Sk:textOnPath"/>
+ <xs:element ref="Sk:textToPath"/>
+ </xs:choice>
+ <!-- @attribute condition If present and zero, the contained elements are ignored. -->
+ <xs:attribute name="condition" type="Sk:DynamicString"/>
+ <!-- @attribute enableCondition If present and zero, the contained elements are ignored
+ when enabled. -->
+ <xs:attribute name="enableCondition" type="Sk:DynamicString"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** scale
+ Scale creates a matrix that scales a unit vector about a center point, and concatenated
+ with the containing matrix.
+ */ -->
+ <xs:element name="scale">
+ <xs:complexType>
+ <!-- @attribute center A point the scale is centered about; by default, [0.0, 0.0]. -->
+ <xs:attribute name="center" type="Sk:Point"/>
+ <!-- @attribute x The factor all x values are scaled by; by default, 1.0. -->
+ <xs:attribute name="x" type="Sk:Float"/>
+ <!-- @attribute y The factor all y values are scaled by; by default, 1.0. -->
+ <xs:attribute name="y" type="Sk:Float"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** screenplay
+ Screenplay contains all events and elements referenced by the events.
+ A document may only contain a single screenplay element.
+ */ -->
+ <xs:element name="screenplay">
+ <xs:complexType>
+ <xs:choice maxOccurs="unbounded" >
+ <xs:element ref="Sk:add"/>
+ <xs:element ref="Sk:apply"/>
+ <xs:element ref="Sk:array"/>
+ <xs:element ref="Sk:bitmap"/>
+ <xs:element ref="Sk:boolean"/>
+ <xs:element ref="Sk:bounds"/>
+ <!-- <xs:element ref="Sk3D:camera"/> -->
+ <xs:element ref="Sk:clear"/>
+ <xs:element ref="Sk:clip"/>
+ <xs:element ref="Sk:color"/>
+ <xs:element ref="Sk:drawTo"/>
+ <xs:element ref="Sk:event"/>
+ <xs:element ref="Sk:float"/>
+ <xs:element ref="Sk:full"/>
+ <xs:element ref="Sk:group"/>
+ <xs:element ref="Sk:hitClear"/>
+ <xs:element ref="Sk:hitTest"/>
+ <xs:element ref="Sk:image"/>
+ <xs:element ref="Sk:include"/>
+ <xs:element ref="Sk:int"/>
+ <xs:element ref="Sk:line"/>
+ <xs:element ref="Sk:matrix"/>
+ <xs:element ref="Sk:move"/>
+ <xs:element ref="Sk:movie"/>
+ <xs:element ref="Sk:oval"/>
+ <xs:element ref="Sk:paint"/>
+ <!-- <xs:element ref="Sk:patch"/> -->
+ <xs:element ref="Sk:path"/>
+ <xs:element ref="Sk:point"/>
+ <xs:element ref="Sk:polygon"/>
+ <xs:element ref="Sk:polyline"/>
+ <xs:element ref="Sk:post"/>
+ <xs:element ref="Sk:random"/>
+ <xs:element ref="Sk:rect"/>
+ <xs:element ref="Sk:remove"/>
+ <xs:element ref="Sk:replace"/>
+ <xs:element ref="Sk:roundRect"/>
+ <xs:element ref="Sk:save"/>
+ <xs:element ref="Sk:set"/>
+ <xs:element ref="Sk:snapshot"/>
+ <xs:element ref="Sk:string"/>
+ <xs:element ref="Sk:text"/>
+ <xs:element ref="Sk:textBox"/>
+ <xs:element ref="Sk:textOnPath"/>
+ <xs:element ref="Sk:textToPath"/>
+ </xs:choice>
+ <!-- @attribute time The time of the draw (readable from script; not part of the document XML) -->
+ <xs:attribute name="time" type="Sk:MSec"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** set
+ Set animates the target element's attribute directly to the specified value.
+ */ -->
+ <xs:element name="set">
+ <xs:complexType>
+ <!-- @attribute begin An optional offset that must elapse before the animation begins. The apply
+ begin attribute is added to any animator's begin attribute. -->
+ <xs:attribute name="begin" type="Sk:MSec"/>
+ <!-- @attribute dur The duration of the animation in milliseconds. -->
+ <xs:attribute name="dur" type="Sk:MSec"/>
+ <!-- @attribute dynamic If true, restart the animation if any of the simple values the
+ 'lval' or 'to' attributes reference are changed. Simple values are contained by the array, boolean, float, int,
+ and string elements. -->
+ <!-- @attribute dynamic [Depreciated.] -->
+ <xs:attribute name="dynamic" type="Sk:Boolean" />
+ <!-- @attribute field The attribute to animate. -->
+ <xs:attribute name="field" type="Sk:String"/>
+ <!-- @attribute formula A script to execute over time to compute the field's value. Typically,
+ the fomula is a script expression which includes a reference to the time attribute of the
+ containing apply element. -->
+ <xs:attribute name="formula" type="Sk:DynamicString"/>
+ <!-- @attribute lval An expression evaluating to the attribute to animate.
+ If present, lval overrides 'field'. The expression is typically an array element,
+ e.g. lval="x[y]" . -->
+ <xs:attribute name="lval" type="Sk:DynamicString"/>
+ <!-- @attribute reset If true, the computed value is the initial value after the
+ animation is complete. If false, or by default, the computed value is the final value
+ after the animation is complete. -->
+ <xs:attribute name="reset" type="Sk:Boolean"/>
+ <!-- @attribute step When apply's attribute mode="immediate" or "create", the step attribute can be read by
+ script to determine the current animation iteration. -->
+ <xs:attribute name="step" type="Sk:Int" />
+ <!-- @attribute target The element to animate. By default, the element contained by the apply
+ or referenced by the apply's scope attribute is the animate target. -->
+ <xs:attribute name="target" type="Sk:DynamicString"/>
+ <!-- @attribute to The ending value (requires a 'from' attribute) -->
+ <xs:attribute name="to" type="Sk:DynamicString"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** skew
+ Skew creates a matrix that skews a unit vector about a center point, and concatenated
+ with the containing matrix.
+ */ -->
+ <xs:element name="skew">
+ <xs:complexType>
+ <!-- @attribute center A point the skew is centered about; by default, [0.0, 0.0]. -->
+ <xs:attribute name="center" type="Sk:Point"/>
+ <!-- @attribute x The factor all x values are skewed by; by default, 0.0. -->
+ <xs:attribute name="x" type="Sk:Float"/>
+ <!-- @attribute y The factor all y values are skewed by; by default, 0.0. -->
+ <xs:attribute name="y" type="Sk:Float"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** snapshot
+ Snapshot creates an image file containing the display list.
+ */ -->
+ <xs:element name="snapshot">
+ <xs:complexType>
+ <!-- @attribute filename The name of the file to generate. -->
+ <xs:attribute name="filename" type="Sk:String"/>
+ <!-- @attribute quality The quality of the image, from 0 to 100. -->
+ <xs:attribute name="quality" type="Sk:Float"/>
+ <!-- @attribute sequence Set to true to number the filenames sequentially. -->
+ <xs:attribute name="sequence" type="Sk:Boolean"/>
+ <!-- @attribute type One of @pattern. The type of encoding to use. -->
+ <xs:attribute name="type" type="Sk:BitmapEncoding"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** string
+ String contains an array of characters.
+ */ -->
+ <xs:element name="string" >
+ <xs:complexType>
+ <!-- @attribute length The number of characters in the string (read only). -->
+ <xs:attribute name="length" type="Sk:Int"/>
+ <!-- @attribute slice An ECMAScript compatible function that returns part of the string. -->
+ <xs:attribute name="slice" type="Sk:String"/>
+ <!-- @attribute value The string itself. -->
+ <xs:attribute name="value" type="Sk:String"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** text
+ A drawable string with a position.
+ */ -->
+ <xs:element name="text">
+ <xs:complexType>
+ <!-- @attribute length The number of characters in the string (read only). -->
+ <xs:attribute name="length" type="Sk:Int"/>
+ <!-- @attribute text The string itself. -->
+ <xs:attribute name="text" type="Sk:String"/>
+ <!-- @attribute x The x coordinate of the string. -->
+ <xs:attribute name="x" type="Sk:Float"/>
+ <!-- @attribute y The y coordinate of the string. -->
+ <xs:attribute name="y" type="Sk:Float"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** textBox
+ A drawable string fit into a box.
+ */ -->
+ <xs:element name="textBox" >
+ <xs:complexType>
+ <!-- @attribute bottom The bottom of the box. -->
+ <xs:attribute name="bottom" type="Sk:Float"/>
+ <!-- @attribute height The height of the box, computed from top and bottom. -->
+ <xs:attribute name="height" type="Sk:Float"/>
+ <!-- @attribute left The left side of the box. -->
+ <xs:attribute name="left" type="Sk:Float"/>
+ <!-- @attribute mode One of @pattern. -->
+ <xs:attribute name="mode" type="Sk:TextBoxMode"/>
+ <!-- @attribute needsRedraw Set to true if last draw was visible. -->
+ <xs:attribute name="needsRedraw" type="Sk:Boolean"/>
+ <!-- @attribute right The right side of the box. -->
+ <xs:attribute name="right" type="Sk:Float"/>
+ <!-- @attribute spacingAdd The extra spacing between lines. -->
+ <xs:attribute name="spacingAdd" type="Sk:Float"/>
+ <!-- @attribute spacingAlign One of @pattern. -->
+ <xs:attribute name="spacingAlign" type="Sk:TextBoxAlign"/>
+ <!-- @attribute spacingMul The line spacing scaled by the text height. -->
+ <xs:attribute name="spacingMul" type="Sk:Float"/>
+ <!-- @attribute text The text to fit to the box. -->
+ <xs:attribute name="text" type="Sk:String"/>
+ <!-- @attribute top The top of the box. -->
+ <xs:attribute name="top" type="Sk:Float"/>
+ <!-- @attribute width The width of the box, computed from left and right. -->
+ <xs:attribute name="width" type="Sk:Float"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** textOnPath
+ TextOnPath specifies the baseline for a string of text with a path.
+ */ -->
+ <xs:element name="textOnPath">
+ <xs:complexType>
+ <xs:choice >
+ <xs:element ref="Sk:text" minOccurs="0" />
+ <xs:element ref="Sk:path" minOccurs="0" />
+ </xs:choice>
+ <!-- @attribute offset The distance along the path to place the first text character. -->
+ <xs:attribute name="offset" type="Sk:Float"/>
+ <!-- @attribute path The baseline of the text. -->
+ <xs:attribute name="path" type="Sk:Path"/>
+ <!-- @attribute text The text to place along the path. -->
+ <xs:attribute name="text" type="Sk:Text"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** textToPath
+ TextToPath sets the path to the contours described by the text's glyphs, using the current paint.
+ */ -->
+ <xs:element name="textToPath">
+ <xs:complexType>
+ <xs:choice >
+ <xs:element ref="Sk:text" minOccurs="0" />
+ <xs:element ref="Sk:paint" minOccurs="0" />
+ <xs:element ref="Sk:path" minOccurs="0" />
+ </xs:choice>
+ <!-- @attribute paint The paint selects the text font, size and other text properties. -->
+ <xs:attribute name="paint" type="Sk:Paint"/>
+ <!-- @attribute path The reference to the path element where the text as path is stored. -->
+ <xs:attribute name="path" type="Sk:Path"/>
+ <!-- @attribute text The reference to the text element to turn into a path. -->
+ <xs:attribute name="text" type="Sk:Text"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** translate
+ Translate concatenates a translation-only matrix onto the current matrix.
+ */ -->
+ <xs:element name="translate">
+ <xs:complexType>
+ <!-- @attribute x The translation in x. -->
+ <xs:attribute name="x" type="Sk:Float"/>
+ <!-- @attribute y The translation in y. -->
+ <xs:attribute name="y" type="Sk:Float"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** transparentShader
+ TransparentShader uses the background for its paint. Works well with emboss.
+ */ -->
+ <xs:element name="transparentShader">
+ <xs:complexType>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- /** typeface
+ Typeface describes the text font.
+ */ -->
+ <xs:element name="typeface">
+ <xs:complexType>
+ <!-- @attribute fontName The name of the font. -->
+ <xs:attribute name="fontName" type="Sk:String"/>
+ </xs:complexType>
+ </xs:element>
+
+</xs:schema>
+
diff --git a/skia/animator/SkAnimateSchema.xsx b/skia/animator/SkAnimateSchema.xsx new file mode 100644 index 0000000..5be2933 --- /dev/null +++ b/skia/animator/SkAnimateSchema.xsx @@ -0,0 +1,3 @@ +<?xml version="1.0" encoding="utf-8"?>
+<!--This file is auto-generated by the XML Schema Designer. It holds layout information for components on the designer surface.-->
+<XSDDesignerLayout />
diff --git a/skia/animator/SkAnimateSet.cpp b/skia/animator/SkAnimateSet.cpp new file mode 100644 index 0000000..c20496d --- /dev/null +++ b/skia/animator/SkAnimateSet.cpp @@ -0,0 +1,98 @@ +/* libs/graphics/animator/SkAnimateSet.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkAnimateSet.h" +#include "SkAnimateMaker.h" +#include "SkAnimateProperties.h" +#include "SkParse.h" + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkSet::fInfo[] = { + SK_MEMBER(begin, MSec), + SK_MEMBER(dur, MSec), + SK_MEMBER_PROPERTY(dynamic, Boolean), + SK_MEMBER(field, String), +// SK_MEMBER(formula, DynamicString), + SK_MEMBER(lval, DynamicString), +// SK_MEMBER_PROPERTY(reset, Boolean), + SK_MEMBER_PROPERTY(step, Int), + SK_MEMBER(target, DynamicString), + SK_MEMBER(to, DynamicString) +}; + +#endif + +DEFINE_GET_MEMBER(SkSet); + +SkSet::SkSet() { + dur = 1; +} + +#ifdef SK_DUMP_ENABLED +void SkSet::dump(SkAnimateMaker* maker) { + INHERITED::dump(maker); + if (dur != 1) { +#ifdef SK_CAN_USE_FLOAT + SkDebugf("dur=\"%g\" ", SkScalarToFloat(SkScalarDiv(dur,1000))); +#else + SkDebugf("dur=\"%x\" ", SkScalarDiv(dur,1000)); +#endif + } + //don't want double />\n's + SkDebugf("/>\n"); + +} +#endif + +void SkSet::refresh(SkAnimateMaker& maker) { + fFieldInfo->setValue(maker, &fValues, 0, fFieldInfo->fCount, NULL, + fFieldInfo->getType(), to); +} + +void SkSet::onEndElement(SkAnimateMaker& maker) { + if (resolveCommon(maker) == false) + return; + if (fFieldInfo == NULL) { + maker.setErrorCode(SkDisplayXMLParserError::kFieldNotInTarget); + return; + } + fReset = dur != 1; + SkDisplayTypes outType = fFieldInfo->getType(); + int comps = outType == SkType_String || outType == SkType_DynamicString ? 1 : + fFieldInfo->getSize((const SkDisplayable*) fTarget) / sizeof(int); + if (fValues.getType() == SkType_Unknown) { + fValues.setType(outType); + fValues.setCount(comps); + if (outType == SkType_String || outType == SkType_DynamicString) + fValues[0].fString = SkNEW(SkString); + else + memset(fValues.begin(), 0, fValues.count() * sizeof(fValues.begin()[0])); + } else { + SkASSERT(fValues.getType() == outType); + if (fFieldInfo->fType == SkType_Array) + comps = fValues.count(); + else + SkASSERT(fValues.count() == comps); + } + if (formula.size() > 0) { + comps = 1; + outType = SkType_MSec; + } + fFieldInfo->setValue(maker, &fValues, fFieldOffset, comps, this, outType, formula.size() > 0 ? formula : to); + fComponents = fValues.count(); +} diff --git a/skia/animator/SkAnimateSet.h b/skia/animator/SkAnimateSet.h new file mode 100644 index 0000000..8e88b41 --- /dev/null +++ b/skia/animator/SkAnimateSet.h @@ -0,0 +1,36 @@ +/* libs/graphics/animator/SkAnimateSet.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkAnimateSet_DEFINED +#define SkAnimateSet_DEFINED + +#include "SkAnimate.h" + +class SkSet : public SkAnimate { + DECLARE_MEMBER_INFO(Set); + SkSet(); +#ifdef SK_DUMP_ENABLED + virtual void dump(SkAnimateMaker* ); +#endif + virtual void onEndElement(SkAnimateMaker& ); + virtual void refresh(SkAnimateMaker& ); +private: + typedef SkAnimate INHERITED; +}; + +#endif // SkAnimateSet_DEFINED + diff --git a/skia/animator/SkAnimator.cpp b/skia/animator/SkAnimator.cpp new file mode 100644 index 0000000..19c0214 --- /dev/null +++ b/skia/animator/SkAnimator.cpp @@ -0,0 +1,724 @@ +/* libs/graphics/animator/SkAnimator.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkAnimator.h" +#include "SkAnimateMaker.h" +#include "SkCanvas.h" +#include "SkDisplayApply.h" +#include "SkDisplayMovie.h" +#include "SkDisplayTypes.h" +#include "SkDisplayXMLParser.h" +#include "SkStream.h" +#include "SkScript.h" +#include "SkScript2.h" // compiled script experiment +#include "SkSystemEventTypes.h" +#include "SkTypedArray.h" +#ifdef ANDROID +#include "SkDrawExtraPathEffect.h" +#endif +#ifdef SK_DEBUG +#include "SkTime.h" +#endif + +#if defined SK_BUILD_FOR_WIN32 && defined SK_DEBUG + #define _static + extern const char gMathPrimerText[]; + extern const char gMathPrimerBinary[]; +#else + #define _static static +#endif + +#if !defined SK_BUILD_FOR_BREW || defined SK_DEBUG + _static const char gMathPrimerText[] = + "<screenplay>" + "<Math id=\"Math\"/>" + "<Number id=\"Number\"/>" + "</screenplay>"; +#endif + +#if defined SK_BUILD_FOR_BREW || defined SK_DEBUG + _static const char gMathPrimerBinary[] = + "\x0Ascreenplay\x04Mathbid\x04Math@@"; // !!! now out of date -- does not include Number +#endif + +#if defined SK_BUILD_FOR_BREW + #define gMathPrimer gMathPrimerBinary +#else + #define gMathPrimer gMathPrimerText +#endif + +SkAnimator::SkAnimator() : fMaker(NULL) { + initialize(); +} + +SkAnimator::~SkAnimator() { + SkDELETE(fMaker); +} + +void SkAnimator::addExtras(SkExtras* extras) { + *fMaker->fExtras.append() = extras; +} + +bool SkAnimator::appendStream(SkStream* stream) { + return decodeStream(stream); +} + +bool SkAnimator::decodeMemory(const void* buffer, size_t size) +{ + fMaker->fFileName.reset(); + SkDisplayXMLParser parser(*fMaker); + return parser.parse((const char*)buffer, size); +} + +bool SkAnimator::decodeStream(SkStream* stream) +{ + SkDisplayXMLParser parser(*fMaker); + bool result = parser.parse(*stream); + fMaker->setErrorString(); + return result; +} + +bool SkAnimator::decodeDOM(const SkDOM& dom, const SkDOMNode* node) +{ + fMaker->fFileName.reset(); + SkDisplayXMLParser parser(*fMaker); + return parser.parse(dom, node); +} + +bool SkAnimator::decodeURI(const char uri[]) { +// SkDebugf("animator decode %s\n", uri); + +// SkStream* stream = SkStream::GetURIStream(fMaker->fPrefix.c_str(), uri); + SkStream* stream = new SkFILEStream(uri); + + SkAutoTDelete<SkStream> autoDel(stream); + setURIBase(uri); + return decodeStream(stream); +} + +bool SkAnimator::doCharEvent(SkUnichar code) { + if (code == 0) + return false; + struct SkEventState state; + state.fCode = code; + fMaker->fEnableTime = fMaker->getAppTime(); + bool result = fMaker->fEvents.doEvent(*fMaker, SkDisplayEvent::kKeyChar, &state); + fMaker->notifyInval(); + return result; +} + +bool SkAnimator::doClickEvent(int clickState, SkScalar x, SkScalar y) { + SkASSERT(clickState >= 0 && clickState <= 2); + struct SkEventState state; + state.fX = x; + state.fY = y; + fMaker->fEnableTime = fMaker->getAppTime(); + bool result = fMaker->fEvents.doEvent(*fMaker, + clickState == 0 ? SkDisplayEvent::kMouseDown : + clickState == 1 ? SkDisplayEvent::kMouseDrag : + SkDisplayEvent::kMouseUp, &state); + fMaker->notifyInval(); + return result; +} + +bool SkAnimator::doKeyEvent(SkKey code) { + if (code == 0) + return false; + struct SkEventState state; + state.fCode = code; + fMaker->fEnableTime = fMaker->getAppTime(); + bool result = fMaker->fEvents.doEvent(*fMaker, SkDisplayEvent::kKeyPress, &state); + fMaker->notifyInval(); + return result; +} + +bool SkAnimator::doKeyUpEvent(SkKey code) { + if (code == 0) + return false; + struct SkEventState state; + state.fCode = code; + fMaker->fEnableTime = fMaker->getAppTime(); + bool result = fMaker->fEvents.doEvent(*fMaker, SkDisplayEvent::kKeyPressUp, &state); + fMaker->notifyInval(); + return result; +} + +bool SkAnimator::doUserEvent(const SkEvent& evt) { + fMaker->fEnableTime = fMaker->getAppTime(); + return onEvent(evt); +} + +SkAnimator::DifferenceType SkAnimator::draw(SkCanvas* canvas, SkPaint* paint, SkMSec time) { + if (paint == NULL) + return draw(canvas, time); + fMaker->fScreenplay.time = time; + fMaker->fCanvas = canvas; + fMaker->fPaint = paint; + fMaker->fDisplayList.fHasUnion = false; + int result = fMaker->fDisplayList.draw(*fMaker, time); + if (result) + result += fMaker->fDisplayList.fHasUnion; + return (DifferenceType) result; +} + +SkAnimator::DifferenceType SkAnimator::draw(SkCanvas* canvas, SkMSec time) { + SkPaint paint; + return draw(canvas, &paint, time); +} + +#ifdef SK_DEBUG +void SkAnimator::eventDone(const SkEvent& ) { +} +#endif + +bool SkAnimator::findClickEvent(SkScalar x, SkScalar y) { + struct SkEventState state; + state.fDisable = true; + state.fX = x; + state.fY = y; + fMaker->fEnableTime = fMaker->getAppTime(); + bool result = fMaker->fEvents.doEvent(*fMaker, SkDisplayEvent::kMouseDown, &state); + fMaker->notifyInval(); + return result; +} + +const SkAnimator* SkAnimator::getAnimator(const SkDisplayable* displayable) const { + if (displayable->getType() != SkType_Movie) + return NULL; + const SkDisplayMovie* movie = (const SkDisplayMovie*) displayable; + return movie->getAnimator(); +} + +const SkDisplayable* SkAnimator::getElement(const char* id) { + SkDisplayable* element; + if (fMaker->find(id, &element) == false) + return NULL; + return (const SkDisplayable*) element; +} + +SkElementType SkAnimator::getElementType(const SkDisplayable* ae) { + SkDisplayable* element = (SkDisplayable*) ae; + const SkMemberInfo* info = SkDisplayType::GetMembers(fMaker, element->getType(), NULL); + return (SkElementType) SkDisplayType::Find(fMaker, info); +} + +SkElementType SkAnimator::getElementType(const char* id) { + const SkDisplayable* element = getElement(id); + return getElementType(element); +} + +const SkMemberInfo* SkAnimator::getField(const SkDisplayable* ae, const char* field) { + SkDisplayable* element = (SkDisplayable*) ae; + const SkMemberInfo* info = element->getMember(field); + return (const SkMemberInfo*) info; +} + +const SkMemberInfo* SkAnimator::getField(const char* elementID, const char* field) { + const SkDisplayable* element = getElement(elementID); + return getField(element, field); +} + +SkFieldType SkAnimator::getFieldType(const SkMemberInfo* ai) { + const SkMemberInfo* info = (const SkMemberInfo*) ai; + return (SkFieldType) info->getType(); +} + +SkFieldType SkAnimator::getFieldType(const char* id, const char* fieldID) { + const SkMemberInfo* field = getField(id, fieldID); + return getFieldType(field); +} + + static bool getArrayCommon(const SkDisplayable* ae, const SkMemberInfo* ai, + int index, SkOperand* operand, SkDisplayTypes type) { + const SkDisplayable* element = (const SkDisplayable*) ae; + const SkMemberInfo* info = (const SkMemberInfo*) ai; + SkASSERT(info->fType == SkType_Array); + return info->getArrayValue(element, index, operand); +} + +int32_t SkAnimator::getArrayInt(const SkDisplayable* ae, + const SkMemberInfo* ai, int index) { + SkOperand operand; + bool result = getArrayCommon(ae, ai, index, &operand, SkType_Int); + return result ? operand.fS32 : SK_NaN32; +} + +int32_t SkAnimator::getArrayInt(const char* id, const char* fieldID, int index) { + const SkDisplayable* element = getElement(id); + if (element == NULL) + return SK_NaN32; + const SkMemberInfo* field = getField(element, fieldID); + if (field == NULL) + return SK_NaN32; + return getArrayInt(element, field, index); +} + +SkScalar SkAnimator::getArrayScalar(const SkDisplayable* ae, + const SkMemberInfo* ai, int index) { + SkOperand operand; + bool result = getArrayCommon(ae, ai, index, &operand, SkType_Float); + return result ? operand.fScalar : SK_ScalarNaN; +} + +SkScalar SkAnimator::getArrayScalar(const char* id, const char* fieldID, int index) { + const SkDisplayable* element = getElement(id); + if (element == NULL) + return SK_ScalarNaN; + const SkMemberInfo* field = getField(element, fieldID); + if (field == NULL) + return SK_ScalarNaN; + return getArrayScalar(element, field, index); +} + +const char* SkAnimator::getArrayString(const SkDisplayable* ae, + const SkMemberInfo* ai, int index) { + SkOperand operand; + bool result = getArrayCommon(ae, ai, index, &operand, SkType_String); + return result ? operand.fString->c_str() : NULL; +} + +const char* SkAnimator::getArrayString(const char* id, const char* fieldID, int index) { + const SkDisplayable* element = getElement(id); + if (element == NULL) + return NULL; + const SkMemberInfo* field = getField(element, fieldID); + if (field == NULL) + return NULL; + return getArrayString(element, field, index); +} + +SkMSec SkAnimator::getInterval() { + return fMaker->fMinimumInterval == (SkMSec) -1 ? 0 : fMaker->fMinimumInterval; +} + +void SkAnimator::getInvalBounds(SkRect* inval) { + if (fMaker->fDisplayList.fHasUnion) { + inval->fLeft = SkIntToScalar(fMaker->fDisplayList.fInvalBounds.fLeft); + inval->fTop = SkIntToScalar(fMaker->fDisplayList.fInvalBounds.fTop); + inval->fRight = SkIntToScalar(fMaker->fDisplayList.fInvalBounds.fRight); + inval->fBottom = SkIntToScalar(fMaker->fDisplayList.fInvalBounds.fBottom); + } else { + inval->fLeft = inval->fTop = -SK_ScalarMax; + inval->fRight = inval->fBottom = SK_ScalarMax; + } +} + +const SkXMLParserError* SkAnimator::getParserError() { + return &fMaker->fError; +} + +const char* SkAnimator::getParserErrorString() { + if (fMaker->fErrorString.size() == 0 && fMaker->fError.hasError()) + fMaker->setErrorString(); + return fMaker->fErrorString.c_str(); +} + +int32_t SkAnimator::getInt(const SkDisplayable* element, const SkMemberInfo* info) { + if (info->fType != SkType_MemberProperty) { + SkOperand operand; + if (info->getType() == SkType_Int) { + info->getValue(element, &operand, 1); + return operand.fS32; + } + return SK_NaN32; + } + SkScriptValue scriptValue; + bool success = element->getProperty(info->propertyIndex(), &scriptValue); + if (success && scriptValue.fType == SkType_Int) + return scriptValue.fOperand.fS32; + return SK_NaN32; +} + +int32_t SkAnimator::getInt(const char* id, const char* fieldID) { + const SkDisplayable* element = getElement(id); + if (element == NULL) + return SK_NaN32; + const SkMemberInfo* field = getField(element, fieldID); + if (field == NULL) + return SK_NaN32; + return getInt(element, field); +} + +SkScalar SkAnimator::getScalar(const SkDisplayable* element, const SkMemberInfo* info) { + if (info->fType != SkType_MemberProperty) { + SkOperand operand; + if (info->getType() == SkType_Float) { + info->getValue(element, &operand, 1); + return operand.fScalar; + } + return SK_ScalarNaN; + } + SkScriptValue scriptValue; + bool success = element->getProperty(info->propertyIndex(), &scriptValue); + if (success && scriptValue.fType == SkType_Float) + return scriptValue.fOperand.fScalar; + return SK_ScalarNaN; +} + +SkScalar SkAnimator::getScalar(const char* id, const char* fieldID) { + const SkDisplayable* element = getElement(id); + if (element == NULL) + return SK_ScalarNaN; + const SkMemberInfo* field = getField(element, fieldID); + if (field == NULL) + return SK_ScalarNaN; + return getScalar(element, field); +} + +const char* SkAnimator::getString(const SkDisplayable* ae, + const SkMemberInfo* ai) { + const SkDisplayable* element = (const SkDisplayable*) ae; + const SkMemberInfo* info = (const SkMemberInfo*) ai; + SkString* temp; + info->getString(element, &temp); + return temp->c_str(); +} + +const char* SkAnimator::getString(const char* id, const char* fieldID) { + const SkDisplayable* element = getElement(id); + if (element == NULL) + return NULL; + const SkMemberInfo* field = getField(element, fieldID); + if (field == NULL) + return NULL; + return getString(element, field); +} + +const char* SkAnimator::getURIBase() { + return fMaker->fPrefix.c_str(); +} + +void SkAnimator::initialize() { + SkDELETE(fMaker); + fMaker = SkNEW_ARGS(SkAnimateMaker, (this, NULL, NULL)); + decodeMemory(gMathPrimer, sizeof(gMathPrimer)-1); +#ifdef ANDROID + InitializeSkExtraPathEffects(this); +#endif +} + + +#ifdef SK_DEBUG +bool SkAnimator::isTrackingEvents() { + return false; +} +#endif + +bool SkAnimator::onEvent(const SkEvent& evt) { +#ifdef SK_DEBUG + SkAnimator* root = fMaker->getRoot(); + if (root == NULL) + root = this; + if (root->isTrackingEvents()) + root->eventDone(evt); +#endif + if (evt.isType(SK_EventType_OnEnd)) { + SkEventState eventState; + bool success = evt.findPtr("anim", (void**) &eventState.fDisplayable); + SkASSERT(success); + success = evt.findS32("time", (int32_t*) &fMaker->fEnableTime); + SkASSERT(success); + fMaker->fAdjustedStart = fMaker->getAppTime() - fMaker->fEnableTime; + fMaker->fEvents.doEvent(*fMaker, SkDisplayEvent::kOnEnd, &eventState); + fMaker->fAdjustedStart = 0; + goto inval; + } + if (evt.isType(SK_EventType_Delay)) { + fMaker->doDelayedEvent(); + goto inval; + } + { + const char* id = evt.findString("id"); + if (id == NULL) + return false; + SkDisplayable** firstMovie = fMaker->fMovies.begin(); + SkDisplayable** endMovie = fMaker->fMovies.end(); + for (SkDisplayable** ptr = firstMovie; ptr < endMovie; ptr++) { + SkDisplayMovie* movie = (SkDisplayMovie*) *ptr; + movie->doEvent(evt); + } + { + SkDisplayable* event; + if (fMaker->find(id, &event) == false) + return false; + #if defined SK_DEBUG && defined SK_DEBUG_ANIMATION_TIMING + SkString debugOut; + SkMSec realTime = fMaker->getAppTime(); + debugOut.appendS32(realTime - fMaker->fDebugTimeBase); + debugOut.append(" onEvent id="); + debugOut.append(id); + #endif + SkMSec time = evt.getFast32(); + if (time != 0) { + SkMSec app = fMaker->getAppTime(); + fMaker->setEnableTime(app, time); + #if defined SK_DEBUG && defined SK_DEBUG_ANIMATION_TIMING + debugOut.append(" time="); + debugOut.appendS32(time - fMaker->fDebugTimeBase); + debugOut.append(" adjust="); + debugOut.appendS32(fMaker->fAdjustedStart); + #endif + } + #if defined SK_DEBUG && defined SK_DEBUG_ANIMATION_TIMING + SkDebugf("%s\n", debugOut.c_str()); + #endif + SkASSERT(event->isEvent()); + SkDisplayEvent* displayEvent = (SkDisplayEvent*) event; + displayEvent->populateInput(*fMaker, evt); + displayEvent->enableEvent(*fMaker); + } + } +inval: + fMaker->notifyInval(); + return true; +} + +void SkAnimator::onEventPost(SkEvent* evt, SkEventSinkID sinkID) +{ +#ifdef SK_DEBUG + SkAnimator* root = fMaker->getRoot(); + if (root) { + root->onEventPost(evt, sinkID); + return; + } +#else + SkASSERT(sinkID == this->getSinkID() || this->getHostEventSinkID() == sinkID); +#endif + SkEvent::Post(evt, sinkID); +} + +void SkAnimator::onEventPostTime(SkEvent* evt, SkEventSinkID sinkID, SkMSec time) +{ +#ifdef SK_DEBUG + SkAnimator* root = fMaker->getRoot(); + if (root) { + root->onEventPostTime(evt, sinkID, time); + return; + } +#else + SkASSERT(sinkID == this->getSinkID() || this->getHostEventSinkID() == sinkID); +#endif + SkEvent::PostTime(evt, sinkID, time); +} + +void SkAnimator::reset() { + fMaker->fDisplayList.reset(); +} + +SkEventSinkID SkAnimator::getHostEventSinkID() const { + return fMaker->fHostEventSinkID; +} + +void SkAnimator::setHostEventSinkID(SkEventSinkID target) { + fMaker->fHostEventSinkID = target; +} + +void SkAnimator::onSetHostHandler(Handler ) { +} + +void SkAnimator::setJavaOwner(Handler ) { +} + +bool SkAnimator::setArrayString(const char* id, const char* fieldID, const char** array, int num) +{ + SkTypedArray tArray(SkType_String); + tArray.setCount(num); + for (int i = 0; i < num; i++) { + SkOperand op; + op.fString = new SkString(array[i]); + tArray[i] = op; + } + return setArray(id, fieldID, tArray); +} +bool SkAnimator::setArrayInt(const char* id, const char* fieldID, const int* array, int num) +{ + SkTypedArray tArray(SkType_Int); + tArray.setCount(num); + for (int i = 0; i < num; i++) { + SkOperand op; + op.fS32 = array[i]; + tArray[i] = op; + } + return setArray(id, fieldID, tArray); +} + +bool SkAnimator::setArray(SkDisplayable* element, const SkMemberInfo* info, SkTypedArray array) { + if (info->fType != SkType_Array) + return false; //the field is not an array + //i think we can handle the case where the displayable itself is an array differently from the + //case where it has an array - for one thing, if it is an array, i think we can change its type + //if it's not, we cannot + SkDisplayTypes type = element->getType(); + if (type == SkType_Array) { + SkDisplayArray* dispArray = (SkDisplayArray*) element; + dispArray->values = array; + return true; + } + else + return false; //currently i don't care about this case +} + +bool SkAnimator::setArray(const char* id, const char* fieldID, SkTypedArray array) { + SkDisplayable* element = (SkDisplayable*) getElement(id); + //should I go ahead and change all 'NULL's to 'NULL'? + if (element == NULL) + return false; + const SkMemberInfo* field = getField(element, fieldID); + if (field == NULL) + return false; + return setArray(element, field, array); +} + +bool SkAnimator::setInt(SkDisplayable* element, const SkMemberInfo* info, int32_t s32) { + if (info->fType != SkType_MemberProperty) { + SkOperand operand; + operand.fS32 = s32; + SkASSERT(info->getType() == SkType_Int); + info->setValue(element, &operand, 1); + } else { + SkScriptValue scriptValue; + scriptValue.fType = SkType_Int; + scriptValue.fOperand.fS32 = s32; + element->setProperty(info->propertyIndex(), scriptValue); + } + return true; +} + +bool SkAnimator::setInt(const char* id, const char* fieldID, int32_t s32) { + SkDisplayable* element = (SkDisplayable*) getElement(id); + if (element == NULL) + return false; + const SkMemberInfo* field = getField(element, fieldID); + if (field == NULL) + return false; + return setInt(element, field, s32); +} + +bool SkAnimator::setScalar(SkDisplayable* element, const SkMemberInfo* info, SkScalar scalar) { + if (info->fType != SkType_MemberProperty) { + SkOperand operand; + operand.fScalar = scalar; + SkASSERT(info->getType() == SkType_Float); + info->setValue(element, &operand, 1); + } else { + SkScriptValue scriptValue; + scriptValue.fType = SkType_Float; + scriptValue.fOperand.fScalar = scalar; + element->setProperty(info->propertyIndex(), scriptValue); + } + return true; +} + +bool SkAnimator::setScalar(const char* id, const char* fieldID, SkScalar scalar) { + SkDisplayable* element = (SkDisplayable*) getElement(id); + if (element == NULL) + return false; + const SkMemberInfo* field = getField(element, fieldID); + if (field == NULL) + return false; + return setScalar(element, field, scalar); +} + +bool SkAnimator::setString(SkDisplayable* element, + const SkMemberInfo* info, const char* str) { + // !!! until this is fixed, can't call script with global references from here + info->setValue(*fMaker, NULL, 0, info->fCount, element, info->getType(), str, strlen(str)); + return true; +} + +bool SkAnimator::setString(const char* id, const char* fieldID, const char* str) { + SkDisplayable* element = (SkDisplayable*) getElement(id); + if (element == NULL) + return false; + const SkMemberInfo* field = getField(element, fieldID); + if (field == NULL) + return false; + return setString(element, field, str); +} + +void SkAnimator::setTimeline(const Timeline& timeline) { + fMaker->fTimeline = &timeline; +} + +void SkAnimator::setURIBase(const char* uri) { + if (uri) + { + const char* tail = strrchr(uri, '/'); + if (tail) { + SkString prefix(uri, tail - uri + 1); + if (uri[0] != '.' /*SkStream::IsAbsoluteURI(uri)*/) + fMaker->fPrefix.reset(); + fMaker->fPrefix.append(prefix); + fMaker->fFileName.set(tail + 1); + } else + fMaker->fFileName.set(uri); + } +} + +#ifdef SK_DEBUG +bool SkAnimator::NoLeaks() { +#ifdef SK_BUILD_FOR_MAC + if (SkDisplayable::fAllocations.count() == 0) + return true; +// return SkDisplayable::fAllocationCount == 0; + SkDebugf("!!! leaked %d displayables:\n", SkDisplayable::fAllocations.count()); + for (SkDisplayable** leak = SkDisplayable::fAllocations.begin(); leak < SkDisplayable::fAllocations.end(); leak++) + SkDebugf("%08x %s\n", *leak, (*leak)->id); +#endif + return false; +} +#endif + +#ifdef SK_SUPPORT_UNITTEST +#include "SkAnimatorScript.h" +#include "SkBase64.h" +#include "SkParse.h" +#include "SkMemberInfo.h" + +#define unittestline(type) { #type , type::UnitTest } +#endif + + +void SkAnimator::Init(bool runUnitTests) { +#ifdef SK_SUPPORT_UNITTEST + if (runUnitTests == false) + return; + static const struct { + const char* fTypeName; + void (*fUnitTest)( ); + } gUnitTests[] = { + unittestline(SkBase64), + unittestline(SkDisplayType), + unittestline(SkParse), + unittestline(SkScriptEngine), +// unittestline(SkScriptEngine2), // compiled script experiment + unittestline(SkAnimatorScript) + }; + for (int i = 0; i < (int)SK_ARRAY_COUNT(gUnitTests); i++) + { + SkDebugf("SkAnimator: Running UnitTest for %s\n", gUnitTests[i].fTypeName); + gUnitTests[i].fUnitTest(); + SkDebugf("SkAnimator: End UnitTest for %s\n", gUnitTests[i].fTypeName); + } +#endif +} + +void SkAnimator::Term() { +} + + + diff --git a/skia/animator/SkAnimatorScript.cpp b/skia/animator/SkAnimatorScript.cpp new file mode 100644 index 0000000..808a596 --- /dev/null +++ b/skia/animator/SkAnimatorScript.cpp @@ -0,0 +1,607 @@ +/* libs/graphics/animator/SkAnimatorScript.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkAnimatorScript.h" +#include "SkAnimateBase.h" +#include "SkAnimateMaker.h" +#include "SkDisplayTypes.h" +#include "SkExtras.h" +#include "SkMemberInfo.h" +#include "SkParse.h" + +static const SkDisplayEnumMap gEnumMaps[] = { + { SkType_AddMode, "indirect|immediate" }, + { SkType_Align, "left|center|right" }, + { SkType_ApplyMode, "create|immediate|once" }, + { SkType_ApplyTransition, "normal|reverse" }, + { SkType_BitmapEncoding, "jpeg|png" }, + { SkType_BitmapFormat, "none|A1|A8|Index8|RGB16|RGB32" }, + { SkType_Boolean, "false|true" }, + { SkType_Cap, "butt|round|square" }, + { SkType_EventCode, "none|leftSoftKey|rightSoftKey|home|back|send|end|key0|key1|key2|key3|key4|key5|key6|key7|key8|key9|star|hash|up|down|left|right|OK|volUp|volDown|camera" }, + { SkType_EventKind, "none|keyChar|keyPress|keyPressUp|mouseDown|mouseDrag|mouseMove|mouseUp|onEnd|onLoad|user" }, + { SkType_EventMode, "deferred|immediate" }, + { SkType_FillType, "winding|evenOdd" }, + { SkType_FilterType, "none|bilinear" }, + { SkType_FontStyle, "normal|bold|italic|boldItalic" }, + { SkType_FromPathMode, "normal|angle|position" }, + { SkType_Join, "miter|round|blunt" }, + { SkType_MaskFilterBlurStyle, "normal|solid|outer|inner" }, + { SkType_PathDirection, "cw|ccw" }, + { SkType_Style, "fill|stroke|strokeAndFill" }, + { SkType_TextBoxAlign, "start|center|end" }, + { SkType_TextBoxMode, "oneLine|lineBreak" }, + { SkType_TileMode, "clamp|repeat|mirror" }, + { SkType_Xfermode, "clear|src|dst|srcOver|dstOver|srcIn|dstIn|srcOut|dstOut|" + "srcATop|dstATop|xor|darken|lighten" }, +}; + +static int gEnumMapCount = SK_ARRAY_COUNT(gEnumMaps); + +SkAnimatorScript::SkAnimatorScript(SkAnimateMaker& maker, SkDisplayable* working, SkDisplayTypes type) + : SkScriptEngine(SkScriptEngine::ToOpType(type)), fMaker(maker), fParent(NULL), fWorking(working) +{ + memberCallBack(EvalMember, (void*) this); + memberFunctionCallBack(EvalMemberFunction, (void*) this); + boxCallBack(Box, (void*) this); + unboxCallBack(Unbox, (void*) &maker); + propertyCallBack(EvalID, (void*) this); // must be first (entries are prepended, will be last), since it never fails + propertyCallBack(Infinity, (void*) this); + propertyCallBack(NaN, (void*) this); + functionCallBack(Eval, (void*) this); + functionCallBack(IsFinite, (void*) this); + functionCallBack(IsNaN, (void*) this); + if (type == SkType_ARGB) { + functionCallBack(EvalRGB, (void*) this); + propertyCallBack(EvalNamedColor, (void*) &maker.fIDs); + } + if (SkDisplayType::IsEnum(&maker, type)) { + // !!! for SpiderMonkey, iterate through the enum values, and map them to globals + const SkDisplayEnumMap& map = GetEnumValues(type); + propertyCallBack(EvalEnum, (void*) map.fValues); + } + for (SkExtras** extraPtr = maker.fExtras.begin(); extraPtr < maker.fExtras.end(); extraPtr++) { + SkExtras* extra = *extraPtr; + if (extra->fExtraCallBack) + propertyCallBack(extra->fExtraCallBack, extra->fExtraStorage); + } +} + +SkAnimatorScript::~SkAnimatorScript() { + for (SkDisplayable** dispPtr = fTrackDisplayable.begin(); dispPtr < fTrackDisplayable.end(); dispPtr++) + delete *dispPtr; +} + +bool SkAnimatorScript::evaluate(const char* original, SkScriptValue* result, SkDisplayTypes type) { + const char* script = original; + bool success = evaluateScript(&script, result); + if (success == false || result->fType != type) { + fMaker.setScriptError(*this); + return false; + } + return true; +} + +bool SkAnimatorScript::Box(void* user, SkScriptValue* scriptValue) { + SkAnimatorScript* engine = (SkAnimatorScript*) user; + SkDisplayTypes type = scriptValue->fType; + SkDisplayable* displayable; + switch (type) { + case SkType_Array: { + SkDisplayArray* boxedValue = new SkDisplayArray(*scriptValue->fOperand.fArray); + displayable = boxedValue; + } break; + case SkType_Boolean: { + SkDisplayBoolean* boxedValue = new SkDisplayBoolean; + displayable = boxedValue; + boxedValue->value = !! scriptValue->fOperand.fS32; + } break; + case SkType_Int: { + SkDisplayInt* boxedValue = new SkDisplayInt; + displayable = boxedValue; + boxedValue->value = scriptValue->fOperand.fS32; + } break; + case SkType_Float: { + SkDisplayFloat* boxedValue = new SkDisplayFloat; + displayable = boxedValue; + boxedValue->value = scriptValue->fOperand.fScalar; + } break; + case SkType_String: { + SkDisplayString* boxedValue = new SkDisplayString(*scriptValue->fOperand.fString); + displayable = boxedValue; + } break; + case SkType_Displayable: + scriptValue->fOperand.fObject = scriptValue->fOperand.fDisplayable; + scriptValue->fType = SkType_Displayable; + return true; + default: + SkASSERT(0); + return false; + } + engine->track(displayable); + scriptValue->fOperand.fObject = displayable; + scriptValue->fType = SkType_Displayable; + return true; +} + +bool SkAnimatorScript::Eval(const char* function, size_t len, SkTDArray<SkScriptValue>& params, + void* eng, SkScriptValue* value) { + if (SK_LITERAL_STR_EQUAL("eval", function, len) == false) + return false; + if (params.count() != 1) + return false; + SkAnimatorScript* host = (SkAnimatorScript*) eng; + SkAnimatorScript engine(host->fMaker, host->fWorking, SkScriptEngine::ToDisplayType(host->fReturnType)); + SkScriptValue* scriptValue = params.begin(); + bool success = true; + if (scriptValue->fType == SkType_String) { + const char* script = scriptValue->fOperand.fString->c_str(); + success = engine.evaluateScript(&script, value); + } else + *value = *scriptValue; + return success; +} + +bool SkAnimatorScript::EvalEnum(const char* token, size_t len, void* callBack, SkScriptValue* value) { + const char* tokens = (const char*) callBack; + value->fType = SkType_Int; + if (MapEnums(tokens, token, len, (int*)&value->fOperand.fS32)) + return true; + return false; +} + +bool SkAnimatorScript::EvalID(const char* token, size_t len, void* user, SkScriptValue* value) { + SkAnimatorScript* engine = (SkAnimatorScript*) user; + SkTDict<SkDisplayable*>* ids = &engine->fMaker.fIDs; + SkDisplayable* displayable; + bool success = ids->find(token, len, &displayable); + if (success == false) { + displayable = engine->fWorking; + if (SK_LITERAL_STR_EQUAL("parent", token, len)) { + SkDisplayable* parent = displayable->getParent(); + if (parent == false) + parent = engine->fParent; + if (parent) { + value->fOperand.fDisplayable = parent; + value->fType = SkType_Displayable; + return true; + } + } + if (displayable && EvalMember(token, len, displayable, engine, value)) + return true; + value->fOperand.fString = NULL; + value->fType = SkType_String; + } else { + SkDisplayable* working = engine->fWorking; + value->fOperand.fDisplayable = displayable; + value->fType = SkType_Displayable; + if (displayable->canContainDependents() && working && working->isAnimate()) { + SkAnimateBase* animator = (SkAnimateBase*) working; + if (animator->isDynamic()) { + SkDisplayDepend* depend = (SkDisplayDepend* ) displayable; + depend->addDependent(working); + } + } + } + return true; +} + +bool SkAnimatorScript::EvalNamedColor(const char* token, size_t len, void* callback, SkScriptValue* value) { + value->fType = SkType_Int; + if (SkParse::FindNamedColor(token, len, (SkColor*) &value->fOperand.fS32) != NULL) + return true; + return false; +} + +bool SkAnimatorScript::EvalRGB(const char* function, size_t len, SkTDArray<SkScriptValue>& params, + void* eng, SkScriptValue* value) { + if (SK_LITERAL_STR_EQUAL("rgb", function, len) == false) + return false; + if (params.count() != 3) + return false; + SkScriptEngine* engine = (SkScriptEngine*) eng; + unsigned result = 0xFF000000; + int shift = 16; + for (SkScriptValue* valuePtr = params.begin(); valuePtr < params.end(); valuePtr++) { + engine->convertTo(SkType_Int, valuePtr); + result |= SkClampMax(valuePtr->fOperand.fS32, 255) << shift; + shift -= 8; + } + value->fOperand.fS32 = result; + value->fType = SkType_Int; + return true; +} + +bool SkAnimatorScript::EvalMemberCommon(SkScriptEngine* engine, const SkMemberInfo* info, + SkDisplayable* displayable, SkScriptValue* value) { + SkDisplayTypes original; + SkDisplayTypes type = original = (SkDisplayTypes) info->getType(); + if (info->fType == SkType_Array) + type = SkType_Array; + switch (type) { + case SkType_ARGB: + type = SkType_Int; + case SkType_Boolean: + case SkType_Int: + case SkType_MSec: + case SkType_Float: + SkASSERT(info->getCount() == 1); + if (info->fType != SkType_MemberProperty && info->fType != SkType_MemberFunction) + value->fOperand.fS32 = *(int32_t*) info->memberData(displayable); // OK for SkScalar too + if (type == SkType_MSec) { + value->fOperand.fScalar = SkScalarDiv((SkScalar) value->fOperand.fS32, 1000); // dividing two ints is the same as dividing two scalars + type = SkType_Float; + } + break; + case SkType_String: { + SkString* displayableString; + if (info->fType != SkType_MemberProperty && info->fType != SkType_MemberFunction) { + info->getString(displayable, &displayableString); + value->fOperand.fString = new SkString(*displayableString); + } + } break; + case SkType_Array: { + SkASSERT(info->fType != SkType_MemberProperty); // !!! incomplete + SkTDOperandArray* displayableArray = (SkTDOperandArray*) info->memberData(displayable); + if (displayable->getType() == SkType_Array) { + SkDisplayArray* typedArray = (SkDisplayArray*) displayable; + original = typedArray->values.getType(); + } + SkASSERT(original != SkType_Unknown); + SkTypedArray* array = value->fOperand.fArray = new SkTypedArray(original); + engine->track(array); + int count = displayableArray->count(); + if (count > 0) { + array->setCount(count); + memcpy(array->begin(), displayableArray->begin(), count * sizeof(SkOperand)); + } + } break; + default: + SkASSERT(0); // unimplemented + } + value->fType = type; + return true; +} + +bool SkAnimatorScript::EvalMember(const char* member, size_t len, void* object, void* eng, + SkScriptValue* value) { + SkScriptEngine* engine = (SkScriptEngine*) eng; + SkDisplayable* displayable = (SkDisplayable*) object; + SkString name(member, len); + SkDisplayable* named = displayable->contains(name); + if (named) { + value->fOperand.fDisplayable = named; + value->fType = SkType_Displayable; + return true; + } + const SkMemberInfo* info = displayable->getMember(name.c_str()); + if (info == NULL) + return false; + if (info->fType == SkType_MemberProperty) { + if (displayable->getProperty(info->propertyIndex(), value) == false) { + SkASSERT(0); + return false; + } + } + return EvalMemberCommon(engine, info, displayable, value); +} + +bool SkAnimatorScript::EvalMemberFunction(const char* member, size_t len, void* object, + SkTDArray<SkScriptValue>& params, void* eng, SkScriptValue* value) { + SkScriptEngine* engine = (SkScriptEngine*) eng; + SkDisplayable* displayable = (SkDisplayable*) object; + SkString name(member, len); + const SkMemberInfo* info = displayable->getMember(name.c_str()); + SkASSERT(info != NULL); /* !!! error handling unimplemented */ + if (info->fType != SkType_MemberFunction) { + SkASSERT(0); + return false; + } + displayable->executeFunction(displayable, info->functionIndex(), params, info->getType(), + value); + return EvalMemberCommon(engine, info, displayable, value); +} + +bool SkAnimatorScript::EvaluateDisplayable(SkAnimateMaker& maker, SkDisplayable* displayable, const char* script, SkDisplayable** result) { + SkAnimatorScript engine(maker, displayable, SkType_Displayable); + SkScriptValue value; + bool success = engine.evaluate(script, &value, SkType_Displayable); + if (success) + *result = value.fOperand.fDisplayable; + return success; +} + +bool SkAnimatorScript::EvaluateInt(SkAnimateMaker& maker, SkDisplayable* displayable, const char* script, int32_t* result) { + SkAnimatorScript engine(maker, displayable, SkType_Int); + SkScriptValue value; + bool success = engine.evaluate(script, &value, SkType_Int); + if (success) + *result = value.fOperand.fS32; + return success; +} + +bool SkAnimatorScript::EvaluateFloat(SkAnimateMaker& maker, SkDisplayable* displayable, const char* script, SkScalar* result) { + SkAnimatorScript engine(maker, displayable, SkType_Float); + SkScriptValue value; + bool success = engine.evaluate(script, &value, SkType_Float); + if (success) + *result = value.fOperand.fScalar; + return success; +} + +bool SkAnimatorScript::EvaluateString(SkAnimateMaker& maker, SkDisplayable* displayable, const char* script, SkString* result) { + SkAnimatorScript engine(maker, displayable, SkType_String); + SkScriptValue value; + bool success = engine.evaluate(script, &value, SkType_String); + if (success) + result->set(*(value.fOperand.fString)); + return success; +} + +bool SkAnimatorScript::EvaluateString(SkAnimateMaker& maker, SkDisplayable* displayable, SkDisplayable* parent, const char* script, SkString* result) { + SkAnimatorScript engine(maker, displayable, SkType_String); + engine.fParent = parent; + SkScriptValue value; + bool success = engine.evaluate(script, &value, SkType_String); + if (success) + result->set(*(value.fOperand.fString)); + return success; +} + +const SkDisplayEnumMap& SkAnimatorScript::GetEnumValues(SkDisplayTypes type) { + int index = SkTSearch<SkDisplayTypes>(&gEnumMaps[0].fType, gEnumMapCount, type, + sizeof(SkDisplayEnumMap)); + SkASSERT(index >= 0); + return gEnumMaps[index]; +} + +bool SkAnimatorScript::Infinity(const char* token, size_t len, void* user, SkScriptValue* value) { + if (SK_LITERAL_STR_EQUAL("Infinity", token, len) == false) + return false; + value->fType = SkType_Float; + value->fOperand.fScalar = SK_ScalarInfinity; + return true; +} + +bool SkAnimatorScript::IsFinite(const char* function, size_t len, SkTDArray<SkScriptValue>& params, + void* eng, SkScriptValue* value) { + if (SK_LITERAL_STR_EQUAL(function, "isFinite", len) == false) + return false; + if (params.count() != 1) + return false; + SkScriptValue* scriptValue = params.begin(); + SkDisplayTypes type = scriptValue->fType; + SkScalar scalar = scriptValue->fOperand.fScalar; + value->fType = SkType_Int; + value->fOperand.fS32 = type == SkType_Float ? SkScalarIsNaN(scalar) == false && + SkScalarAbs(scalar) != SK_ScalarInfinity : type == SkType_Int; + return true; +} + +bool SkAnimatorScript::IsNaN(const char* function, size_t len, SkTDArray<SkScriptValue>& params, + void* eng, SkScriptValue* value) { + if (SK_LITERAL_STR_EQUAL("isNaN", function, len) == false) + return false; + if (params.count() != 1) + return false; + SkScriptValue* scriptValue = params.begin(); + value->fType = SkType_Int; + value->fOperand.fS32 = scriptValue->fType == SkType_Float ? SkScalarIsNaN(scriptValue->fOperand.fScalar) : 0; + return true; +} + +bool SkAnimatorScript::MapEnums(const char* ptr, const char* match, size_t len, int* value) { + int index = 0; + bool more = true; + do { + const char* last = strchr(ptr, '|'); + if (last == NULL) { + last = &ptr[strlen(ptr)]; + more = false; + } + size_t length = last - ptr; + if (len == length && strncmp(ptr, match, length) == 0) { + *value = index; + return true; + } + index++; + ptr = last + 1; + } while (more); + return false; +} + +bool SkAnimatorScript::NaN(const char* token, size_t len, void* user, SkScriptValue* value) { + if (SK_LITERAL_STR_EQUAL("NaN", token, len) == false) + return false; + value->fType = SkType_Float; + value->fOperand.fScalar = SK_ScalarNaN; + return true; +} + +#if 0 +bool SkAnimatorScript::ObjectToString(void* object, void* user, SkScriptValue* value) { + SkTDict<SkDisplayable*>* ids = (SkTDict<SkDisplayable*>*) user; + SkDisplayable* displayable = (SkDisplayable*) object; + const char* key; + bool success = ids->findKey(displayable, &key); + if (success == false) + return false; + value->fOperand.fString = new SkString(key); + value->fType = SkType_String; + return true; +} +#endif + +bool SkAnimatorScript::Unbox(void* m, SkScriptValue* scriptValue) { + SkAnimateMaker* maker = (SkAnimateMaker*) m; + SkASSERT((unsigned) scriptValue->fType == (unsigned) SkType_Displayable); + SkDisplayable* displayable = (SkDisplayable*) scriptValue->fOperand.fObject; + SkDisplayTypes type = displayable->getType(); + switch (displayable->getType()) { + case SkType_Array: { + SkDisplayArray* boxedValue = (SkDisplayArray*) displayable; + scriptValue->fOperand.fArray = &boxedValue->values; + } break; + case SkType_Boolean: { + SkDisplayBoolean* boxedValue = (SkDisplayBoolean*) displayable; + scriptValue->fOperand.fS32 = boxedValue->value; + } break; + case SkType_Int: { + SkDisplayInt* boxedValue = (SkDisplayInt*) displayable; + scriptValue->fOperand.fS32 = boxedValue->value; + } break; + case SkType_Float: { + SkDisplayFloat* boxedValue = (SkDisplayFloat*) displayable; + scriptValue->fOperand.fScalar = boxedValue->value; + } break; + case SkType_String: { + SkDisplayString* boxedValue = (SkDisplayString*) displayable; + scriptValue->fOperand.fString = SkNEW_ARGS(SkString, (boxedValue->value)); + } break; + default: { + const char* id; + bool success = maker->findKey(displayable, &id); + SkASSERT(success); + scriptValue->fOperand.fString = SkNEW_ARGS(SkString, (id)); + type = SkType_String; + } + } + scriptValue->fType = type; + return true; +} + +#if defined SK_SUPPORT_UNITTEST + +#include "SkAnimator.h" + +static const char scriptTestSetup[] = +"<screenplay>\n" + "<text id='label' text='defg'/>\n" + "<add id='addLabel' use='label'/>\n" + "<text id='text1' text='test'/>\n" + "<apply scope='addLabel'>\n" + "<set target='label' field='text' to='#script:text1.text'/>\n" + "</apply>\n" + "<apply>\n" + "<paint id='labelPaint'>\n" + "<emboss id='emboss' direction='[1,1,1]' />\n" + "</paint>\n" + "<animate id='animation' field='direction' target='emboss' from='[1,1,1]' to='[-1,1,1]' dur='1'/>\n" + "<set lval='direction[0]' target='emboss' to='-1' />\n" + "</apply>\n" + "<color id='testColor' color='0 ? rgb(0,0,0) : rgb(255,255,255)' />\n" + "<color id='xColor' color='rgb(12,34,56)' />\n" + "<array id='emptyArray' />\n" + "<array id='intArray' values='[1, 4, 6]' />\n" + "<int id='idx' value='2' />\n" + "<int id='idy' value='2' />\n" + "<string id='alpha' value='abc' />\n" + "<rect id='testRect' left='Math.cos(0)' top='2' right='12' bottom='5' />\n" + "<event id='evt'>\n" + "<input name='x' />\n" + "<apply scope='idy'>\n" + "<set field='value' to='evt.x.int' />\n" + "</apply>\n" + "</event>\n" +"</screenplay>"; + +#if !defined(SK_BUILD_FOR_BREW) + +#define DEFAULT_ANSWER , 0 + +static const SkScriptNAnswer scriptTests[] = { + { "label.text.length == 4", SkType_Int, 1 DEFAULT_ANSWER DEFAULT_ANSWER }, +// { "labelPaint.measureText(label.text) > 0 ? labelPaint.measureText(label.text)+10 : 40", SkType_Float, 0, SkIntToScalar(0x23) }, + { "Number.POSITIVE_INFINITY >= Number.MAX_VALUE ? 1 : 0", SkType_Int, 1 DEFAULT_ANSWER DEFAULT_ANSWER }, + { "Infinity >= Number.MAX_VALUE ? 1 : 0", SkType_Int, 1 DEFAULT_ANSWER DEFAULT_ANSWER }, + { "Number.NEGATIVE_INFINITY <= -Number.MAX_VALUE ? 1 : 0", SkType_Int, 1 DEFAULT_ANSWER DEFAULT_ANSWER }, + { "Number.MIN_VALUE > 0 ? 1 : 0", SkType_Int, 1 DEFAULT_ANSWER DEFAULT_ANSWER }, + { "isNaN(Number.NaN)", SkType_Int, 1 DEFAULT_ANSWER DEFAULT_ANSWER }, + { "isNaN(NaN)", SkType_Int, 1 DEFAULT_ANSWER DEFAULT_ANSWER }, + { "Math.sin(0)", SkType_Float, 0, SkIntToScalar(0) DEFAULT_ANSWER }, + { "alpha+alpha", SkType_String, 0, 0, "abcabc" }, + { "intArray[4]", SkType_Unknown DEFAULT_ANSWER DEFAULT_ANSWER DEFAULT_ANSWER }, + { "emptyArray[4]", SkType_Unknown DEFAULT_ANSWER DEFAULT_ANSWER DEFAULT_ANSWER }, + { "idx", SkType_Int, 2 DEFAULT_ANSWER DEFAULT_ANSWER }, + { "intArray.length", SkType_Int, 3 DEFAULT_ANSWER DEFAULT_ANSWER }, + { "intArray.values[0]", SkType_Int, 1 DEFAULT_ANSWER DEFAULT_ANSWER }, + { "intArray[0]", SkType_Int, 1 DEFAULT_ANSWER DEFAULT_ANSWER }, + { "idx.value", SkType_Int, 2 DEFAULT_ANSWER DEFAULT_ANSWER }, + { "alpha.value", SkType_String, 0, 0, "abc" }, + { "alpha", SkType_String, 0, 0, "abc" }, + { "alpha.value+alpha.value", SkType_String, 0, 0, "abcabc" }, + { "alpha+idx", SkType_String, 0, 0, "abc2" }, + { "idx+alpha", SkType_String, 0, 0, "2abc" }, + { "intArray[idx]", SkType_Int, 6 DEFAULT_ANSWER DEFAULT_ANSWER }, + { "alpha.slice(1,2)", SkType_String, 0, 0, "b" }, + { "alpha.value.slice(1,2)", SkType_String, 0, 0, "b" }, + { "testRect.left+2", SkType_Float, 0, SkIntToScalar(3) DEFAULT_ANSWER }, + { "0 ? Math.sin(0) : 1", SkType_Int, 1 DEFAULT_ANSWER DEFAULT_ANSWER }, + { "0 ? intArray[0] : 1", SkType_Int, 1 DEFAULT_ANSWER DEFAULT_ANSWER }, + { "0 ? intArray.values[0] : 1", SkType_Int, 1 DEFAULT_ANSWER DEFAULT_ANSWER }, + { "0 ? idx : 1", SkType_Int, 1 DEFAULT_ANSWER DEFAULT_ANSWER }, + { "0 ? idx.value : 1", SkType_Int, 1 DEFAULT_ANSWER DEFAULT_ANSWER }, + { "0 ? alpha.slice(1,2) : 1", SkType_Int, 1 DEFAULT_ANSWER DEFAULT_ANSWER }, + { "0 ? alpha.value.slice(1,2) : 1", SkType_Int, 1 DEFAULT_ANSWER DEFAULT_ANSWER }, + { "idy", SkType_Int, 3 DEFAULT_ANSWER DEFAULT_ANSWER } +}; +#endif + +#define SkScriptNAnswer_testCount SK_ARRAY_COUNT(scriptTests) + +void SkAnimatorScript::UnitTest() { +#if !defined(SK_BUILD_FOR_BREW) && defined(SK_SUPPORT_UNITTEST) + SkAnimator animator; + SkASSERT(animator.decodeMemory(scriptTestSetup, sizeof(scriptTestSetup)-1)); + SkEvent evt; + evt.setString("id", "evt"); + evt.setS32("x", 3); + animator.doUserEvent(evt); + // set up animator with memory script above, then run value tests + for (unsigned index = 0; index < SkScriptNAnswer_testCount; index++) { + SkAnimatorScript engine(*animator.fMaker, NULL, scriptTests[index].fType); + SkScriptValue value; + const char* script = scriptTests[index].fScript; + bool success = engine.evaluateScript(&script, &value); + if (success == false) { + SkDebugf("script failed: %s\n", scriptTests[index].fScript); + SkASSERT(scriptTests[index].fType == SkType_Unknown); + continue; + } + SkASSERT(value.fType == scriptTests[index].fType); + SkScalar error; + switch (value.fType) { + case SkType_Int: + SkASSERT(value.fOperand.fS32 == scriptTests[index].fIntAnswer); + break; + case SkType_Float: + error = SkScalarAbs(value.fOperand.fScalar - scriptTests[index].fScalarAnswer); + SkASSERT(error < SK_Scalar1 / 10000); + break; + case SkType_String: + SkASSERT(strcmp(value.fOperand.fString->c_str(), scriptTests[index].fStringAnswer) == 0); + break; + default: + SkASSERT(0); + } + } +#endif +} + +#endif + + diff --git a/skia/animator/SkAnimatorScript.h b/skia/animator/SkAnimatorScript.h new file mode 100644 index 0000000..b3e946b --- /dev/null +++ b/skia/animator/SkAnimatorScript.h @@ -0,0 +1,84 @@ +/* libs/graphics/animator/SkAnimatorScript.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkAnimatorScript_DEFINED +#define SkAnimatorScript_DEFINED + +#include "SkDisplayable.h" +#include "SkScript.h" +#include "SkTypedArray.h" + +class SkAnimateMaker; +struct SkMemberInfo; + +struct SkDisplayEnumMap { + SkDisplayTypes fType; + const char* fValues; +}; + +class SkAnimatorScript : public SkScriptEngine { +public: + SkAnimatorScript(SkAnimateMaker& , SkDisplayable* , SkDisplayTypes type); + ~SkAnimatorScript(); + bool evaluate(const char* script, SkScriptValue* , SkDisplayTypes type); + void track(SkDisplayable* displayable) { + SkASSERT(fTrackDisplayable.find(displayable) < 0); + *fTrackDisplayable.append() = displayable; } + static bool EvaluateDisplayable(SkAnimateMaker& , SkDisplayable* , const char* script, SkDisplayable** ); + static bool EvaluateFloat(SkAnimateMaker& , SkDisplayable* , const char* script, SkScalar* ); + static bool EvaluateInt(SkAnimateMaker& , SkDisplayable* , const char* script, int32_t* ); + static bool EvaluateString(SkAnimateMaker& , SkDisplayable* , const char* script, SkString* ); + static bool EvaluateString(SkAnimateMaker& , SkDisplayable* , SkDisplayable* parent, const char* script, SkString* ); + static bool MapEnums(const char* ptr, const char* match, size_t len, int* value); +protected: + static bool Box(void* user, SkScriptValue* ); + static bool Eval(const char* function, size_t len, SkTDArray<SkScriptValue>& params, + void* callBack, SkScriptValue* ); + static bool EvalEnum(const char* token, size_t len, void* callBack, SkScriptValue* ); + static bool EvalID(const char* token, size_t len, void* callBack, SkScriptValue* ); + static bool EvalMember(const char* member, size_t len, void* object, void* eng, + SkScriptValue* value); + static bool EvalMemberCommon(SkScriptEngine* , const SkMemberInfo* info, + SkDisplayable* displayable, SkScriptValue* value); + static bool EvalMemberFunction(const char* member, size_t len, void* object, + SkTDArray<SkScriptValue>& params, void* user, SkScriptValue* value); + static bool EvalNamedColor(const char* token, size_t len, void* callBack, SkScriptValue* ); + static bool EvalRGB(const char* function, size_t len, SkTDArray<SkScriptValue>& params, + void* callBack, SkScriptValue* ); + static const SkDisplayEnumMap& GetEnumValues(SkDisplayTypes type); + static bool Infinity(const char* token, size_t len, void* callBack, SkScriptValue* ); + static bool IsFinite(const char* function, size_t len, SkTDArray<SkScriptValue>& params, + void* callBack, SkScriptValue* ); + static bool IsNaN(const char* function, size_t len, SkTDArray<SkScriptValue>& params, + void* callBack, SkScriptValue* ); + static bool NaN(const char* token, size_t len, void* callBack, SkScriptValue* ); + static bool Unbox(void* , SkScriptValue* scriptValue); + SkTDDisplayableArray fTrackDisplayable; + SkAnimateMaker& fMaker; + SkDisplayable* fParent; + SkDisplayable* fWorking; +private: + friend class SkDump; + friend struct SkScriptNAnswer; +#ifdef SK_SUPPORT_UNITTEST +public: + static void UnitTest(); +#endif +}; + +#endif // SkAnimatorScript_DEFINED + diff --git a/skia/animator/SkAnimatorScript2.cpp b/skia/animator/SkAnimatorScript2.cpp new file mode 100644 index 0000000..08dbf16 --- /dev/null +++ b/skia/animator/SkAnimatorScript2.cpp @@ -0,0 +1,618 @@ +#include "SkAnimatorScript2.h" +#include "SkAnimateBase.h" +#include "SkAnimateMaker.h" +#include "SkDisplayTypes.h" +#include "SkExtras.h" +#include "SkMemberInfo.h" +#include "SkOpArray.h" +#include "SkParse.h" +#include "SkScript2.h" +#include "SkScriptCallBack.h" + +static const SkDisplayEnumMap gEnumMaps[] = { + { SkType_AddMode, "indirect|immediate" }, + { SkType_Align, "left|center|right" }, + { SkType_ApplyMode, "immediate|once" }, + { SkType_ApplyTransition, "reverse" }, + { SkType_BitmapEncoding, "jpeg|png" }, + { SkType_BitmapFormat, "none|A1|A8|Index8|RGB16|RGB32" }, + { SkType_Boolean, "false|true" }, + { SkType_Cap, "butt|round|square" }, + { SkType_EventCode, "none|up|down|left|right|back|end|OK|send|leftSoftKey|rightSoftKey|key0|key1|key2|key3|key4|key5|key6|key7|key8|key9|star|hash" }, + { SkType_EventKind, "none|keyChar|keyPress|mouseDown|mouseDrag|mouseMove|mouseUp|onEnd|onLoad|user" }, + { SkType_EventMode, "deferred|immediate" }, + { SkType_FillType, "winding|evenOdd" }, + { SkType_FilterType, "none|bilinear" }, + { SkType_FromPathMode, "normal|angle|position" }, + { SkType_Join, "miter|round|blunt" }, + { SkType_MaskFilterBlurStyle, "normal|solid|outer|inner" }, + { SkType_PathDirection, "cw|ccw" }, + { SkType_Style, "fill|stroke|strokeAndFill" }, + { SkType_TextBoxAlign, "start|center|end" }, + { SkType_TextBoxMode, "oneLine|lineBreak" }, + { SkType_TileMode, "clamp|repeat|mirror" }, + { SkType_Xfermode, "clear|src|dst|srcOver|dstOver|srcIn|dstIn|srcOut|dstOut|" + "srcATop|dstATop|xor|darken|lighten" }, +}; + +static int gEnumMapCount = SK_ARRAY_COUNT(gEnumMaps); + + +class SkAnimatorScript_Box : public SkScriptCallBackConvert { +public: + SkAnimatorScript_Box() {} + + ~SkAnimatorScript_Box() { + for (SkDisplayable** dispPtr = fTrackDisplayable.begin(); dispPtr < fTrackDisplayable.end(); dispPtr++) + delete *dispPtr; + } + + virtual bool convert(SkOperand2::OpType type, SkOperand2* operand) { + SkDisplayable* displayable; + switch (type) { + case SkOperand2::kArray: { + SkDisplayArray* boxedValue = new SkDisplayArray(*operand->fArray); + displayable = boxedValue; + } break; + case SkOperand2::kS32: { + SkDisplayInt* boxedValue = new SkDisplayInt; + displayable = boxedValue; + boxedValue->value = operand->fS32; + } break; + case SkOperand2::kScalar: { + SkDisplayFloat* boxedValue = new SkDisplayFloat; + displayable = boxedValue; + boxedValue->value = operand->fScalar; + } break; + case SkOperand2::kString: { + SkDisplayString* boxedValue = new SkDisplayString(*operand->fString); + displayable = boxedValue; + } break; + case SkOperand2::kObject: + return true; + default: + SkASSERT(0); + return false; + } + track(displayable); + operand->fObject = (void*) displayable; + return true; + } + + virtual SkOperand2::OpType getReturnType(int index) { + return SkOperand2::kObject; + } + + virtual Type getType() const { + return kBox; + } + + void track(SkDisplayable* displayable) { + SkASSERT(fTrackDisplayable.find(displayable) < 0); + *fTrackDisplayable.append() = displayable; + } + + SkTDDisplayableArray fTrackDisplayable; +}; + + +class SkAnimatorScript_Enum : public SkScriptCallBackProperty { +public: + SkAnimatorScript_Enum(const char* tokens) : fTokens(tokens) {} + + virtual bool getConstValue(const char* name, int len, SkOperand2* value) { + return SkAnimatorScript2::MapEnums(fTokens, name, len, &value->fS32); + } + +private: + const char* fTokens; +}; + + // !!! if type is string, call invoke + // if any other type, return original value + // distinction is undone: could do this by returning index == 0 only if param is string + // still, caller of getParamTypes will attempt to convert param to string (I guess) +class SkAnimatorScript_Eval : public SkScriptCallBackFunction { +public: + SkAnimatorScript_Eval(SkAnimatorScript2* engine) : fEngine(engine) {} + + virtual bool getIndex(const char* name, int len, size_t* result) { + if (SK_LITERAL_STR_EQUAL("eval", name, len) != 0) + return false; + *result = 0; + return true; + } + + virtual void getParamTypes(SkIntArray(SkOperand2::OpType)* types) { + types->setCount(1); + SkOperand2::OpType* type = types->begin(); + type[0] = SkOperand2::kString; + } + + virtual bool invoke(size_t index, SkOpArray* params, SkOperand2* answer) { + SkAnimatorScript2 engine(fEngine->getMaker(), fEngine->getWorking(), + SkAnimatorScript2::ToDisplayType(fEngine->getReturnType())); + SkOperand2* op = params->begin(); + const char* script = op->fString->c_str(); + SkScriptValue2 value; + return engine.evaluateScript(&script, &value); + SkASSERT(value.fType == fEngine->getReturnType()); + *answer = value.fOperand; + // !!! incomplete ? + return true; + } + +private: + SkAnimatorScript2* fEngine; +}; + +class SkAnimatorScript_ID : public SkScriptCallBackProperty { +public: + SkAnimatorScript_ID(SkAnimatorScript2* engine) : fEngine(engine) {} + + virtual bool getIndex(const char* token, int len, size_t* result) { + SkDisplayable* displayable; + bool success = fEngine->getMaker().find(token, len, &displayable); + if (success == false) { + *result = 0; + } else { + *result = (size_t) displayable; + SkDisplayable* working = fEngine->getWorking(); + if (displayable->canContainDependents() && working && working->isAnimate()) { + SkAnimateBase* animator = (SkAnimateBase*) working; + if (animator->isDynamic()) { + SkDisplayDepend* depend = (SkDisplayDepend* ) displayable; + depend->addDependent(working); + } + } + } + return true; + } + + virtual bool getResult(size_t ref, SkOperand2* answer) { + answer->fObject = (void*) ref; + return true; + } + + virtual SkOperand2::OpType getReturnType(size_t index) { + return index == 0 ? SkOperand2::kString : SkOperand2::kObject; + } + +private: + SkAnimatorScript2* fEngine; +}; + + +class SkAnimatorScript_Member : public SkScriptCallBackMember { +public: + + SkAnimatorScript_Member(SkAnimatorScript2* engine) : fEngine(engine) {} + + bool getMemberReference(const char* member, size_t len, void* object, SkScriptValue2* ref) { + SkDisplayable* displayable = (SkDisplayable*) object; + SkString name(member, len); + SkDisplayable* named = displayable->contains(name); + if (named) { + ref->fType = SkOperand2::kObject; + ref->fOperand.fObject = named; + return true; + } + const SkMemberInfo* info = displayable->getMember(name.c_str()); + if (info == NULL) + return false; // !!! add additional error info? + ref->fType = SkAnimatorScript2::ToOpType(info->getType()); + ref->fOperand.fObject = (void*) info; + return true; + } + + bool invoke(size_t ref, void* object, SkOperand2* value) { + const SkMemberInfo* info = (const SkMemberInfo* ) ref; + SkDisplayable* displayable = (SkDisplayable*) object; + if (info->fType == SkType_MemberProperty) { + if (displayable->getProperty2(info->propertyIndex(), value) == false) { + return false; + } + } + return fEngine->evalMemberCommon(info, displayable, value); + } + + SkAnimatorScript2* fEngine; +}; + + +class SkAnimatorScript_MemberFunction : public SkScriptCallBackMemberFunction { +public: + SkAnimatorScript_MemberFunction(SkAnimatorScript2* engine) : fEngine(engine) {} + + bool getMemberReference(const char* member, size_t len, void* object, SkScriptValue2* ref) { + SkDisplayable* displayable = (SkDisplayable*) object; + SkString name(member, len); + const SkMemberInfo* info = displayable->getMember(name.c_str()); + if (info == NULL || info->fType != SkType_MemberFunction) + return false; // !!! add additional error info? + ref->fType = SkAnimatorScript2::ToOpType(info->getType()); + ref->fOperand.fObject = (void*) info; + return true; + } + + virtual void getParamTypes(SkIntArray(SkOperand2::OpType)* types) { + types->setCount(3); + SkOperand2::OpType* type = types->begin(); + type[0] = type[1] = type[2] = SkOperand2::kS32; + } + + bool invoke(size_t ref, void* object, SkOpArray* params, SkOperand2* value) + { + const SkMemberInfo* info = (const SkMemberInfo* ) ref; + SkDisplayable* displayable = (SkDisplayable*) object; + displayable->executeFunction2(displayable, info->functionIndex(), params, info->getType(), + value); + return fEngine->evalMemberCommon(info, displayable, value); + } + + SkAnimatorScript2* fEngine; +}; + + +class SkAnimatorScript_NamedColor : public SkScriptCallBackProperty { +public: + virtual bool getConstValue(const char* name, int len, SkOperand2* value) { + return SkParse::FindNamedColor(name, len, (SkColor*) &value->fS32) != NULL; + } +}; + + +class SkAnimatorScript_RGB : public SkScriptCallBackFunction { +public: + virtual bool getIndex(const char* name, int len, size_t* result) { + if (SK_LITERAL_STR_EQUAL("rgb", name, len) != 0) + return false; + *result = 0; + return true; + } + + virtual void getParamTypes(SkIntArray(SkOperand2::OpType)* types) { + types->setCount(3); + SkOperand2::OpType* type = types->begin(); + type[0] = type[1] = type[2] = SkOperand2::kS32; + } + + virtual bool invoke(size_t index, SkOpArray* params, SkOperand2* answer) { + SkASSERT(index == 0); + unsigned result = 0xFF000000; + int shift = 16; + for (int index = 0; index < 3; index++) { + result |= SkClampMax(params->begin()[index].fS32, 255) << shift; + shift -= 8; + } + answer->fS32 = result; + return true; + } + +}; + + +class SkAnimatorScript_Unbox : public SkScriptCallBackConvert { +public: + SkAnimatorScript_Unbox(SkAnimatorScript2* engine) : fEngine(engine) {} + + virtual bool convert(SkOperand2::OpType type, SkOperand2* operand) { + SkASSERT(type == SkOperand2::kObject); + SkDisplayable* displayable = (SkDisplayable*) operand->fObject; + switch (displayable->getType()) { + case SkType_Array: { + SkDisplayArray* boxedValue = (SkDisplayArray*) displayable; + operand->fArray = new SkOpArray(SkAnimatorScript2::ToOpType(boxedValue->values.getType())); + int count = boxedValue->values.count(); + operand->fArray->setCount(count); + memcpy(operand->fArray->begin(), boxedValue->values.begin(), count * sizeof(SkOperand2)); + fEngine->track(operand->fArray); + } break; + case SkType_Boolean: { + SkDisplayBoolean* boxedValue = (SkDisplayBoolean*) displayable; + operand->fS32 = boxedValue->value; + } break; + case SkType_Int: { + SkDisplayInt* boxedValue = (SkDisplayInt*) displayable; + operand->fS32 = boxedValue->value; + } break; + case SkType_Float: { + SkDisplayFloat* boxedValue = (SkDisplayFloat*) displayable; + operand->fScalar = boxedValue->value; + } break; + case SkType_String: { + SkDisplayString* boxedValue = (SkDisplayString*) displayable; + operand->fString = SkNEW_ARGS(SkString, (boxedValue->value)); + } break; + default: { + const char* id; + bool success = fEngine->getMaker().findKey(displayable, &id); + SkASSERT(success); + operand->fString = SkNEW_ARGS(SkString, (id)); + } + } + return true; + } + + virtual SkOperand2::OpType getReturnType(int /*index*/, SkOperand2* operand) { + SkDisplayable* displayable = (SkDisplayable*) operand->fObject; + switch (displayable->getType()) { + case SkType_Array: + return SkOperand2::kArray; + case SkType_Int: + return SkOperand2::kS32; + case SkType_Float: + return SkOperand2::kScalar; + case SkType_String: + default: + return SkOperand2::kString; + } + } + + virtual Type getType() const { + return kUnbox; + } + + SkAnimatorScript2* fEngine; +}; + +SkAnimatorScript2::SkAnimatorScript2(SkAnimateMaker& maker, SkDisplayable* working, SkDisplayTypes type) : + SkScriptEngine2(ToOpType(type)), fMaker(maker), fWorking(working) { + *fCallBackArray.append() = new SkAnimatorScript_Member(this); + *fCallBackArray.append() = new SkAnimatorScript_MemberFunction(this); + *fCallBackArray.append() = new SkAnimatorScript_Box(); + *fCallBackArray.append() = new SkAnimatorScript_Unbox(this); + *fCallBackArray.append() = new SkAnimatorScript_ID(this); + if (type == SkType_ARGB) { + *fCallBackArray.append() = new SkAnimatorScript_RGB(); + *fCallBackArray.append() = new SkAnimatorScript_NamedColor(); + } + if (SkDisplayType::IsEnum(&maker, type)) { + // !!! for SpiderMonkey, iterate through the enum values, and map them to globals + const SkDisplayEnumMap& map = GetEnumValues(type); + *fCallBackArray.append() = new SkAnimatorScript_Enum(map.fValues); + } + *fCallBackArray.append() = new SkAnimatorScript_Eval(this); +#if 0 // !!! no extra support for now + for (SkExtras** extraPtr = maker.fExtras.begin(); extraPtr < maker.fExtras.end(); extraPtr++) { + SkExtras* extra = *extraPtr; + if (extra->fExtraCallBack) + *fCallBackArray.append() = new propertyCallBack(extra->fExtraCallBack, extra->fExtraStorage); + } +#endif +} + +SkAnimatorScript2::~SkAnimatorScript2() { + SkScriptCallBack** end = fCallBackArray.end(); + for (SkScriptCallBack** ptr = fCallBackArray.begin(); ptr < end; ptr++) + delete *ptr; +} + +bool SkAnimatorScript2::evalMemberCommon(const SkMemberInfo* info, + SkDisplayable* displayable, SkOperand2* value) { + SkDisplayTypes original; + SkDisplayTypes type = original = (SkDisplayTypes) info->getType(); + if (info->fType == SkType_Array) + type = SkType_Array; + switch (type) { + case SkType_ARGB: + type = SkType_Int; + case SkType_Boolean: + case SkType_Int: + case SkType_MSec: + case SkType_Float: + SkASSERT(info->getCount() == 1); + if (info->fType != SkType_MemberProperty && info->fType != SkType_MemberFunction) + value->fS32 = *(int32_t*) info->memberData(displayable); // OK for SkScalar too + if (type == SkType_MSec) { + value->fScalar = SkScalarDiv((SkScalar) value->fS32, 1000); // dividing two ints is the same as dividing two scalars + type = SkType_Float; + } + break; + case SkType_String: { + SkString* displayableString; + if (info->fType != SkType_MemberProperty && info->fType != SkType_MemberFunction) { + info->getString(displayable, &displayableString); + value->fString = new SkString(*displayableString); + } + } break; + case SkType_Array: { + SkASSERT(info->fType != SkType_MemberProperty); // !!! incomplete + SkTDOperandArray* displayableArray = (SkTDOperandArray*) info->memberData(displayable); + if (displayable->getType() == SkType_Array) { + SkDisplayArray* typedArray = (SkDisplayArray*) displayable; + original = typedArray->values.getType(); + } + SkASSERT(original != SkType_Unknown); + SkOpArray* array = value->fArray = new SkOpArray(ToOpType(original)); + track(array); + int count = displayableArray->count(); + if (count > 0) { + array->setCount(count); + memcpy(array->begin(), displayableArray->begin(), count * sizeof(SkOperand2)); + } + } break; + default: + SkASSERT(0); // unimplemented + } + return true; +} + +const SkDisplayEnumMap& SkAnimatorScript2::GetEnumValues(SkDisplayTypes type) { + int index = SkTSearch<SkDisplayTypes>(&gEnumMaps[0].fType, gEnumMapCount, type, + sizeof(SkDisplayEnumMap)); + SkASSERT(index >= 0); + return gEnumMaps[index]; +} + +SkDisplayTypes SkAnimatorScript2::ToDisplayType(SkOperand2::OpType type) { + int val = type; + switch (val) { + case SkOperand2::kNoType: + return SkType_Unknown; + case SkOperand2::kS32: + return SkType_Int; + case SkOperand2::kScalar: + return SkType_Float; + case SkOperand2::kString: + return SkType_String; + case SkOperand2::kArray: + return SkType_Array; + case SkOperand2::kObject: + return SkType_Displayable; + default: + SkASSERT(0); + return SkType_Unknown; + } +} + +SkOperand2::OpType SkAnimatorScript2::ToOpType(SkDisplayTypes type) { + if (SkDisplayType::IsDisplayable(NULL /* fMaker */, type)) + return SkOperand2::kObject; + if (SkDisplayType::IsEnum(NULL /* fMaker */, type)) + return SkOperand2::kS32; + switch (type) { + case SkType_ARGB: + case SkType_MSec: + case SkType_Int: + return SkOperand2::kS32; + case SkType_Float: + case SkType_Point: + case SkType_3D_Point: + return SkOperand2::kScalar; + case SkType_Base64: + case SkType_DynamicString: + case SkType_String: + return SkOperand2::kString; + case SkType_Array: + return SkOperand2::kArray; + case SkType_Unknown: + return SkOperand2::kNoType; + default: + SkASSERT(0); + return SkOperand2::kNoType; + } +} + +bool SkAnimatorScript2::MapEnums(const char* ptr, const char* match, size_t len, int* value) { + int index = 0; + bool more = true; + do { + const char* last = strchr(ptr, '|'); + if (last == NULL) { + last = &ptr[strlen(ptr)]; + more = false; + } + size_t length = last - ptr; + if (len == length && strncmp(ptr, match, length) == 0) { + *value = index; + return true; + } + index++; + ptr = last + 1; + } while (more); + return false; +} + +#if defined SK_DEBUG + +#include "SkAnimator.h" + +static const char scriptTestSetup[] = +"<screenplay>" + "<apply>" + "<paint>" + "<emboss id='emboss' direction='[1,1,1]' />" + "</paint>" + "<animateField id='animation' field='direction' target='emboss' from='[1,1,1]' to='[-1,1,1]' dur='1'/>" + "<set lval='direction[0]' target='emboss' to='-1' />" + "</apply>" + "<color id='testColor' color='0 ? rgb(0,0,0) : rgb(255,255,255)' />" + "<color id='xColor' color='rgb(12,34,56)' />" + "<typedArray id='emptyArray' />" + "<typedArray id='intArray' values='[1, 4, 6]' />" + "<s32 id='idx' value='2' />" + "<s32 id='idy' value='2' />" + "<string id='alpha' value='abc' />" + "<rectangle id='testRect' left='Math.cos(0)' top='2' right='12' bottom='5' />" + "<event id='evt'>" + "<input name='x' />" + "<apply scope='idy'>" + "<set field='value' to='evt.x.s32' />" + "</apply>" + "</event>" +"</screenplay>"; + +#if !defined(SK_BUILD_FOR_BREW) +static const SkScriptNAnswer scriptTests[] = { + { "alpha+alpha", SkType_String, 0, 0, "abcabc" }, + { "0 ? Math.sin(0) : 1", SkType_Int, 1 }, + { "intArray[4]", SkType_Unknown }, + { "emptyArray[4]", SkType_Unknown }, + { "idx", SkType_Int, 2 }, + { "intArray.length", SkType_Int, 3 }, + { "intArray.values[0]", SkType_Int, 1 }, + { "intArray[0]", SkType_Int, 1 }, + { "idx.value", SkType_Int, 2 }, + { "alpha.value", SkType_String, 0, 0, "abc" }, + { "alpha", SkType_String, 0, 0, "abc" }, + { "alpha.value+alpha.value", SkType_String, 0, 0, "abcabc" }, + { "alpha+idx", SkType_String, 0, 0, "abc2" }, + { "idx+alpha", SkType_String, 0, 0, "2abc" }, + { "intArray[idx]", SkType_Int, 6 }, + { "alpha.slice(1,2)", SkType_String, 0, 0, "b" }, + { "alpha.value.slice(1,2)", SkType_String, 0, 0, "b" }, + { "Math.sin(0)", SkType_Float, 0, SkIntToScalar(0) }, + { "testRect.left+2", SkType_Float, 0, SkIntToScalar(3) }, + { "0 ? intArray[0] : 1", SkType_Int, 1 }, + { "0 ? intArray.values[0] : 1", SkType_Int, 1 }, + { "0 ? idx : 1", SkType_Int, 1 }, + { "0 ? idx.value : 1", SkType_Int, 1 }, + { "0 ? alpha.slice(1,2) : 1", SkType_Int, 1 }, + { "0 ? alpha.value.slice(1,2) : 1", SkType_Int, 1 }, + { "idy", SkType_Int, 3 } +}; +#endif + +#define SkScriptNAnswer_testCount SK_ARRAY_COUNT(scriptTests) + +void SkAnimatorScript2::UnitTest() { +#if !defined(SK_BUILD_FOR_BREW) && defined(SK_SUPPORT_UNITTEST) + SkAnimator animator; + SkASSERT(animator.decodeMemory(scriptTestSetup, sizeof(scriptTestSetup)-1)); + SkEvent evt; + evt.setString("id", "evt"); + evt.setS32("x", 3); + animator.doUserEvent(evt); + // set up animator with memory script above, then run value tests + for (int index = 0; index < SkScriptNAnswer_testCount; index++) { + SkAnimatorScript2 engine(*animator.fMaker, NULL, scriptTests[index].fType); + SkScriptValue2 value; + const char* script = scriptTests[index].fScript; + bool success = engine.evaluateScript(&script, &value); + if (success == false) { + SkASSERT(scriptTests[index].fType == SkType_Unknown); + continue; + } + SkASSERT(value.fType == ToOpType(scriptTests[index].fType)); + SkScalar error; + switch (value.fType) { + case SkOperand2::kS32: + SkASSERT(value.fOperand.fS32 == scriptTests[index].fIntAnswer); + break; + case SkOperand2::kScalar: + error = SkScalarAbs(value.fOperand.fScalar - scriptTests[index].fScalarAnswer); + SkASSERT(error < SK_Scalar1 / 10000); + break; + case SkOperand2::kString: + SkASSERT(value.fOperand.fString->equals(scriptTests[index].fStringAnswer)); + break; + default: + SkASSERT(0); + } + } +#endif +} + +#endif + diff --git a/skia/animator/SkAnimatorScript2.h b/skia/animator/SkAnimatorScript2.h new file mode 100644 index 0000000..61261e9 --- /dev/null +++ b/skia/animator/SkAnimatorScript2.h @@ -0,0 +1,43 @@ +#ifndef SkAnimatorScript2_DEFINED +#define SkAnimatorScript2_DEFINED + +#include "SkDisplayable.h" +#include "SkScript2.h" +#include "SkTypedArray.h" + +class SkAnimateMaker; +struct SkMemberInfo; + +#ifndef SkAnimatorScript_DEFINED +struct SkDisplayEnumMap { + SkDisplayTypes fType; + const char* fValues; +}; +#endif + +class SkAnimatorScript2 : public SkScriptEngine2 { +public: + SkAnimatorScript2(SkAnimateMaker& , SkDisplayable* working, SkDisplayTypes type); + ~SkAnimatorScript2(); + bool evalMemberCommon(const SkMemberInfo* info, + SkDisplayable* displayable, SkOperand2* value); + SkAnimateMaker& getMaker() { return fMaker; } + SkDisplayable* getWorking() { return fWorking; } + static bool MapEnums(const char* ptr, const char* match, size_t len, int* value); + static const SkDisplayEnumMap& GetEnumValues(SkDisplayTypes type); + static SkDisplayTypes ToDisplayType(SkOperand2::OpType type); + static SkOperand2::OpType ToOpType(SkDisplayTypes type); +private: + SkAnimateMaker& fMaker; + SkDisplayable* fWorking; + friend class SkDump; + friend struct SkScriptNAnswer; + // illegal + SkAnimatorScript2& operator=(const SkAnimatorScript2&); +#ifdef SK_DEBUG +public: + static void UnitTest(); +#endif +}; + +#endif // SkAnimatorScript2_DEFINED diff --git a/skia/animator/SkBase64.cpp b/skia/animator/SkBase64.cpp new file mode 100644 index 0000000..5b48acd --- /dev/null +++ b/skia/animator/SkBase64.cpp @@ -0,0 +1,188 @@ +/* libs/graphics/animator/SkBase64.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkBase64.h" + +#define DecodePad -2 +#define EncodePad 64 + +static const char encode[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/="; + +static const signed char decodeData[] = { + 62, -1, -1, -1, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, DecodePad, -1, -1, + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, + -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 +}; + +SkBase64::SkBase64() : fLength((size_t) -1), fData(NULL) { +} + +#if defined _WIN32 && _MSC_VER >= 1300 // disable 'two', etc. may be used without having been initialized +#pragma warning ( push ) +#pragma warning ( disable : 4701 ) +#endif + +SkBase64::Error SkBase64::decode(const void* srcPtr, size_t size, bool writeDestination) { + unsigned char* dst = (unsigned char*) fData; + const unsigned char* dstStart = (const unsigned char*) fData; + const unsigned char* src = (const unsigned char*) srcPtr; + bool padTwo = false; + bool padThree = false; + const unsigned char* end = src + size; + while (src < end) { + unsigned char bytes[4]; + int byte = 0; + do { + unsigned char srcByte = *src++; + if (srcByte == 0) + goto goHome; + if (srcByte <= ' ') + continue; // treat as white space + if (srcByte < '+' || srcByte > 'z') + return kBadCharError; + signed char decoded = decodeData[srcByte - '+']; + bytes[byte] = decoded; + if (decoded < 0) { + if (decoded == DecodePad) + goto handlePad; + return kBadCharError; + } else + byte++; + if (*src) + continue; + if (byte == 0) + goto goHome; + if (byte == 4) + break; +handlePad: + if (byte < 2) + return kPadError; + padThree = true; + if (byte == 2) + padTwo = true; + break; + } while (byte < 4); + int two, three; + if (writeDestination) { + int one = (uint8_t) (bytes[0] << 2); + two = bytes[1]; + one |= two >> 4; + two = (uint8_t) (two << 4); + three = bytes[2]; + two |= three >> 2; + three = (uint8_t) (three << 6); + three |= bytes[3]; + SkASSERT(one < 256 && two < 256 && three < 256); + *dst = (unsigned char) one; + } + dst++; + if (padTwo) + break; + if (writeDestination) + *dst = (unsigned char) two; + dst++; + if (padThree) + break; + if (writeDestination) + *dst = (unsigned char) three; + dst++; + } +goHome: + fLength = dst - dstStart; + return kNoError; +} + +#if defined _WIN32 && _MSC_VER >= 1300 +#pragma warning ( pop ) +#endif + +size_t SkBase64::Encode(const void* srcPtr, size_t length, void* dstPtr) { + const unsigned char* src = (const unsigned char*) srcPtr; + unsigned char* dst = (unsigned char*) dstPtr; + if (dst) { + size_t remainder = length % 3; + const unsigned char* end = &src[length - remainder]; + while (src < end) { + unsigned a = *src++; + unsigned b = *src++; + unsigned c = *src++; + int d = c & 0x3F; + c = (c >> 6 | b << 2) & 0x3F; + b = (b >> 4 | a << 4) & 0x3F; + a = a >> 2; + *dst++ = encode[a]; + *dst++ = encode[b]; + *dst++ = encode[c]; + *dst++ = encode[d]; + } + if (remainder > 0) { + int k1 = 0; + int k2 = EncodePad; + int a = (uint8_t) *src++; + if (remainder == 2) + { + int b = *src++; + k1 = b >> 4; + k2 = (b << 2) & 0x3F; + } + *dst++ = encode[a >> 2]; + *dst++ = encode[(k1 | a << 4) & 0x3F]; + *dst++ = encode[k2]; + *dst++ = encode[EncodePad]; + } + } + return (length + 2) / 3 * 4; +} + +SkBase64::Error SkBase64::decode(const char* src, size_t len) { + Error err = decode(src, len, false); + SkASSERT(err == kNoError); + if (err != kNoError) + return err; + fData = new char[fLength]; // should use sk_malloc/sk_free + decode(src, len, true); + return kNoError; +} + +#ifdef SK_SUPPORT_UNITTEST +void SkBase64::UnitTest() { + signed char all[256]; + for (int index = 0; index < 256; index++) + all[index] = (signed char) (index + 1); + for (int offset = 0; offset < 6; offset++) { + size_t length = 256 - offset; + size_t encodeLength = Encode(all + offset, length, NULL); + char* src = (char*)sk_malloc_throw(encodeLength + 1); + Encode(all + offset, length, src); + src[encodeLength] = '\0'; + SkBase64 tryMe; + tryMe.decode(src, encodeLength); + SkASSERT(length == tryMe.fLength); + SkASSERT(strcmp((const char*) (all + offset), tryMe.fData) == 0); + sk_free(src); + delete[] tryMe.fData; + } +} +#endif + + diff --git a/skia/animator/SkBase64.h b/skia/animator/SkBase64.h new file mode 100644 index 0000000..89a179b --- /dev/null +++ b/skia/animator/SkBase64.h @@ -0,0 +1,47 @@ +/* libs/graphics/animator/SkBase64.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkBase64_DEFINED +#define SkBase64_DEFINED + +#include "SkTypes.h" + +struct SkBase64 { +public: + enum Error { + kNoError, + kPadError, + kBadCharError + }; + + SkBase64(); + Error decode(const char* src, size_t length); + char* getData() { return fData; } + static size_t Encode(const void* src, size_t length, void* dest); + +#ifdef SK_SUPPORT_UNITTEST + static void UnitTest(); +#endif +private: + Error decode(const void* srcPtr, size_t length, bool writeDestination); + + size_t fLength; + char* fData; + friend class SkImage; +}; + +#endif // SkBase64_DEFINED diff --git a/skia/animator/SkBoundable.cpp b/skia/animator/SkBoundable.cpp new file mode 100644 index 0000000..12fb201 --- /dev/null +++ b/skia/animator/SkBoundable.cpp @@ -0,0 +1,64 @@ +/* libs/graphics/animator/SkBoundable.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkBoundable.h" +#include "SkAnimateMaker.h" +#include "SkCanvas.h" + +SkBoundable::SkBoundable() { + clearBounds(); + fBounds.fTop = 0; + fBounds.fRight = 0; + fBounds.fBottom = 0; +} + +void SkBoundable::clearBounder() { + fBounds.fLeft = 0x7fff; +} + +void SkBoundable::getBounds(SkRect* rect) { + SkASSERT(rect); + if (fBounds.fLeft == (int16_t)0x8000U) { + INHERITED::getBounds(rect); + return; + } + rect->fLeft = SkIntToScalar(fBounds.fLeft); + rect->fTop = SkIntToScalar(fBounds.fTop); + rect->fRight = SkIntToScalar(fBounds.fRight); + rect->fBottom = SkIntToScalar(fBounds.fBottom); +} + +void SkBoundable::enableBounder() { + fBounds.fLeft = 0; +} + + +SkBoundableAuto::SkBoundableAuto(SkBoundable* boundable, + SkAnimateMaker& maker) : fBoundable(boundable), fMaker(maker) { + if (fBoundable->hasBounds()) { + fMaker.fCanvas->setBounder(&maker.fDisplayList); + fMaker.fDisplayList.fBounds.setEmpty(); + } +} + +SkBoundableAuto::~SkBoundableAuto() { + if (fBoundable->hasBounds() == false) + return; + fMaker.fCanvas->setBounder(NULL); + fBoundable->setBounds(fMaker.fDisplayList.fBounds); +} + diff --git a/skia/animator/SkBoundable.h b/skia/animator/SkBoundable.h new file mode 100644 index 0000000..1c21cd2 --- /dev/null +++ b/skia/animator/SkBoundable.h @@ -0,0 +1,50 @@ +/* libs/graphics/animator/SkBoundable.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkBoundable_DEFINED +#define SkBoundable_DEFINED + +#include "SkDrawable.h" +#include "SkRect.h" + +class SkBoundable : public SkDrawable { +public: + SkBoundable(); + virtual void clearBounder(); + virtual void enableBounder(); + virtual void getBounds(SkRect* ); + bool hasBounds() { return fBounds.fLeft != (int16_t)0x8000U; } + void setBounds(SkIRect& bounds) { fBounds = bounds; } +protected: + void clearBounds() { fBounds.fLeft = (int16_t) SkToU16(0x8000); }; // mark bounds as unset + SkIRect fBounds; +private: + typedef SkDrawable INHERITED; +}; + +class SkBoundableAuto { +public: + SkBoundableAuto(SkBoundable* boundable, SkAnimateMaker& maker); + ~SkBoundableAuto(); +private: + SkBoundable* fBoundable; + SkAnimateMaker& fMaker; + SkBoundableAuto& operator= (const SkBoundableAuto& ); +}; + +#endif // SkBoundable_DEFINED + diff --git a/skia/animator/SkBuildCondensedInfo.cpp b/skia/animator/SkBuildCondensedInfo.cpp new file mode 100644 index 0000000..361a8bd --- /dev/null +++ b/skia/animator/SkBuildCondensedInfo.cpp @@ -0,0 +1,292 @@ +/* libs/graphics/animator/SkBuildCondensedInfo.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkTypes.h" +#if defined SK_BUILD_CONDENSED +#include "SkMemberInfo.h" +#if SK_USE_CONDENSED_INFO == 1 +#error "SK_USE_CONDENSED_INFO must be zero to build condensed info" +#endif +#if !defined SK_BUILD_FOR_WIN32 +#error "SK_BUILD_FOR_WIN32 must be defined to build condensed info" +#endif +#include "SkDisplayType.h" +#include "SkIntArray.h" +#include <stdio.h> + +SkTDMemberInfoArray gInfos; +SkTDIntArray gInfosCounts; +SkTDDisplayTypesArray gInfosTypeIDs; +SkTDMemberInfoArray gUnknowns; +SkTDIntArray gUnknownsCounts; + +static void AddInfo(SkDisplayTypes type, const SkMemberInfo* info, int infoCount) { + SkASSERT(gInfos[type] == NULL); + gInfos[type] = info; + gInfosCounts[type] = infoCount; + *gInfosTypeIDs.append() = type; + size_t allStrs = 0; + for (int inner = 0; inner < infoCount; inner++) { + SkASSERT(info[inner].fCount < 256); + int offset = (int) info[inner].fOffset; + SkASSERT(offset < 128 && offset > -129); + SkASSERT(allStrs < 256); + if (info[inner].fType == SkType_BaseClassInfo) { + const SkMemberInfo* innerInfo = (const SkMemberInfo*) info[inner].fName; + if (gUnknowns.find(innerInfo) == -1) { + *gUnknowns.append() = innerInfo; + *gUnknownsCounts.append() = info[inner].fCount; + } + } + if (info[inner].fType != SkType_BaseClassInfo && info[inner].fName) + allStrs += strlen(info[inner].fName); + allStrs += 1; + SkASSERT(info[inner].fType < 256); + } +} + +static void WriteInfo(FILE* condensed, const SkMemberInfo* info, int infoCount, + const char* typeName, bool draw, bool display) { + fprintf(condensed, "static const char g%sStrings[] = \n", typeName); + int inner; + // write strings + for (inner = 0; inner < infoCount; inner++) { + const char* name = (info[inner].fType != SkType_BaseClassInfo && info[inner].fName) ? + info[inner].fName : ""; + const char* zero = inner < infoCount - 1 ? "\\0" : ""; + fprintf(condensed, "\t\"%s%s\"\n", name, zero); + } + fprintf(condensed, ";\n\nstatic const SkMemberInfo g%s", draw ? "Draw" : display ? "Display" : ""); + fprintf(condensed, "%sInfo[] = {", typeName); + size_t nameOffset = 0; + // write info tables + for (inner = 0; inner < infoCount; inner++) { + size_t offset = info[inner].fOffset; + if (info[inner].fType == SkType_BaseClassInfo) { + offset = (size_t) gInfos.find((const SkMemberInfo* ) info[inner].fName); + SkASSERT((int) offset >= 0); + offset = gInfosTypeIDs.find((SkDisplayTypes) offset); + SkASSERT((int) offset >= 0); + } + fprintf(condensed, "\n\t{%d, %d, %d, %d}", nameOffset, offset, + info[inner].fType, info[inner].fCount); + if (inner < infoCount - 1) + putc(',', condensed); + if (info[inner].fType != SkType_BaseClassInfo && info[inner].fName) + nameOffset += strlen(info[inner].fName); + nameOffset += 1; + } + fprintf(condensed, "\n};\n\n"); +} + +static void Get3DName(char* scratch, const char* name) { + if (strncmp("skia3d:", name, sizeof("skia3d:") - 1) == 0) { + strcpy(scratch, "3D_"); + scratch[3]= name[7] & ~0x20; + strcpy(&scratch[4], &name[8]); + } else { + scratch[0] = name[0] & ~0x20; + strcpy(&scratch[1], &name[1]); + } +} + +int type_compare(const void* a, const void* b) { + SkDisplayTypes first = *(SkDisplayTypes*) a; + SkDisplayTypes second = *(SkDisplayTypes*) b; + return first < second ? -1 : first == second ? 0 : 1; +} + +void SkDisplayType::BuildCondensedInfo(SkAnimateMaker* maker) { + gInfos.setCount(kNumberOfTypes); + memset(gInfos.begin(), 0, sizeof(gInfos[0]) * kNumberOfTypes); + gInfosCounts.setCount(kNumberOfTypes); + memset(gInfosCounts.begin(), -1, sizeof(gInfosCounts[0]) * kNumberOfTypes); + // check to see if it is condensable + int index, infoCount; + for (index = 0; index < kTypeNamesSize; index++) { + const SkMemberInfo* info = GetMembers(maker, gTypeNames[index].fType, &infoCount); + if (info == NULL) + continue; + AddInfo(gTypeNames[index].fType, info, infoCount); + } + const SkMemberInfo* extraInfo = + SkDisplayType::GetMembers(maker, SkType_3D_Point, &infoCount); + AddInfo(SkType_Point, extraInfo, infoCount); + AddInfo(SkType_3D_Point, extraInfo, infoCount); +// int baseInfos = gInfos.count(); + do { + SkTDMemberInfoArray oldRefs = gUnknowns; + SkTDIntArray oldRefCounts = gUnknownsCounts; + gUnknowns.reset(); + gUnknownsCounts.reset(); + for (index = 0; index < oldRefs.count(); index++) { + const SkMemberInfo* info = oldRefs[index]; + if (gInfos.find(info) == -1) { + int typeIndex = 0; + for (; typeIndex < kNumberOfTypes; typeIndex++) { + const SkMemberInfo* temp = SkDisplayType::GetMembers( + maker, (SkDisplayTypes) typeIndex, NULL); + if (temp == info) + break; + } + SkASSERT(typeIndex < kNumberOfTypes); + AddInfo((SkDisplayTypes) typeIndex, info, oldRefCounts[index]); + } + } + } while (gUnknowns.count() > 0); + qsort(gInfosTypeIDs.begin(), gInfosTypeIDs.count(), sizeof(gInfosTypeIDs[0]), &type_compare); +#ifdef SK_DEBUG + FILE* condensed = fopen("../../src/animator/SkCondensedDebug.cpp", "w+"); + fprintf(condensed, "#include \"SkTypes.h\"\n"); + fprintf(condensed, "#ifdef SK_DEBUG\n"); +#else + FILE* condensed = fopen("../../src/animator/SkCondensedRelease.cpp", "w+"); + fprintf(condensed, "#include \"SkTypes.h\"\n"); + fprintf(condensed, "#ifdef SK_RELEASE\n"); +#endif + // write header + fprintf(condensed, "// This file was automatically generated.\n"); + fprintf(condensed, "// To change it, edit the file with the matching debug info.\n"); + fprintf(condensed, "// Then execute SkDisplayType::BuildCondensedInfo() to " + "regenerate this file.\n\n"); + // write name of memberInfo + int typeNameIndex = 0; + int unknown = 1; + for (index = 0; index < gInfos.count(); index++) { + const SkMemberInfo* info = gInfos[index]; + if (info == NULL) + continue; + char scratch[64]; + bool drawPrefix, displayPrefix; + while (gTypeNames[typeNameIndex].fType < index) + typeNameIndex++; + if (gTypeNames[typeNameIndex].fType == index) { + Get3DName(scratch, gTypeNames[typeNameIndex].fName); + drawPrefix = gTypeNames[typeNameIndex].fDrawPrefix; + displayPrefix = gTypeNames[typeNameIndex].fDisplayPrefix; + } else { + sprintf(scratch, "Unknown%d", unknown++); + drawPrefix = displayPrefix = false; + } + WriteInfo(condensed, info, gInfosCounts[index], scratch, drawPrefix, displayPrefix); + } + // write array of table pointers +// start here; + fprintf(condensed, "static const SkMemberInfo* const gInfoTables[] = {"); + typeNameIndex = 0; + unknown = 1; + for (index = 0; index < gInfos.count(); index++) { + const SkMemberInfo* info = gInfos[index]; + if (info == NULL) + continue; + char scratch[64]; + bool drawPrefix, displayPrefix; + while (gTypeNames[typeNameIndex].fType < index) + typeNameIndex++; + if (gTypeNames[typeNameIndex].fType == index) { + Get3DName(scratch, gTypeNames[typeNameIndex].fName); + drawPrefix = gTypeNames[typeNameIndex].fDrawPrefix; + displayPrefix = gTypeNames[typeNameIndex].fDisplayPrefix; + } else { + sprintf(scratch, "Unknown%d", unknown++); + drawPrefix = displayPrefix = false; + } + fprintf(condensed, "\n\tg"); + if (drawPrefix) + fprintf(condensed, "Draw"); + if (displayPrefix) + fprintf(condensed, "Display"); + fprintf(condensed, "%sInfo", scratch); + if (index < gInfos.count() - 1) + putc(',', condensed); + } + fprintf(condensed, "\n};\n\n"); + // write the array of number of entries in the info table + fprintf(condensed, "static const unsigned char gInfoCounts[] = {\n\t"); + int written = 0; + for (index = 0; index < gInfosCounts.count(); index++) { + int count = gInfosCounts[index]; + if (count < 0) + continue; + if (written > 0) + putc(',', condensed); + if (written % 20 == 19) + fprintf(condensed, "\n\t"); + fprintf(condensed, "%d",count); + written++; + } + fprintf(condensed, "\n};\n\n"); + // write array of type ids table entries correspond to + fprintf(condensed, "static const unsigned char gTypeIDs[] = {\n\t"); + int typeIDCount = 0; + typeNameIndex = 0; + unknown = 1; + for (index = 0; index < gInfosCounts.count(); index++) { + const SkMemberInfo* info = gInfos[index]; + if (info == NULL) + continue; + typeIDCount++; + char scratch[64]; + while (gTypeNames[typeNameIndex].fType < index) + typeNameIndex++; + if (gTypeNames[typeNameIndex].fType == index) { + Get3DName(scratch, gTypeNames[typeNameIndex].fName); + } else + sprintf(scratch, "Unknown%d", unknown++); + fprintf(condensed, "%d%c // %s\n\t", index, + index < gInfosCounts.count() ? ',' : ' ', scratch); + } + fprintf(condensed, "\n};\n\n"); + fprintf(condensed, "static const int kTypeIDs = %d;\n\n", typeIDCount); + // write the array of string pointers + fprintf(condensed, "static const char* const gInfoNames[] = {"); + typeNameIndex = 0; + unknown = 1; + written = 0; + for (index = 0; index < gInfosCounts.count(); index++) { + const SkMemberInfo* info = gInfos[index]; + if (info == NULL) + continue; + if (written > 0) + putc(',', condensed); + written++; + fprintf(condensed, "\n\tg"); + char scratch[64]; + while (gTypeNames[typeNameIndex].fType < index) + typeNameIndex++; + if (gTypeNames[typeNameIndex].fType == index) { + Get3DName(scratch, gTypeNames[typeNameIndex].fName); + } else + sprintf(scratch, "Unknown%d", unknown++); + fprintf(condensed, "%sStrings", scratch); + } + fprintf(condensed, "\n};\n\n"); + fprintf(condensed, "#endif\n"); + fclose(condensed); + gInfos.reset(); + gInfosCounts.reset(); + gInfosTypeIDs.reset(); + gUnknowns.reset(); + gUnknownsCounts.reset(); +} + +#elif defined SK_DEBUG +#include "SkDisplayType.h" +void SkDisplayType::BuildCondensedInfo(SkAnimateMaker* ) {} +#endif + + diff --git a/skia/animator/SkCondensedDebug.cpp b/skia/animator/SkCondensedDebug.cpp new file mode 100644 index 0000000..1d20025a --- /dev/null +++ b/skia/animator/SkCondensedDebug.cpp @@ -0,0 +1,1397 @@ +/* libs/graphics/animator/SkCondensedDebug.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkTypes.h" +#ifndef SK_BUILD_FOR_UNIX +#ifdef SK_DEBUG +// This file was automatically generated. +// To change it, edit the file with the matching debug info. +// Then execute SkDisplayType::BuildCondensedInfo() to regenerate this file. + +static const char gMathStrings[] = + "E\0" + "LN10\0" + "LN2\0" + "LOG10E\0" + "LOG2E\0" + "PI\0" + "SQRT1_2\0" + "SQRT2\0" + "abs\0" + "acos\0" + "asin\0" + "atan\0" + "atan2\0" + "ceil\0" + "cos\0" + "exp\0" + "floor\0" + "log\0" + "max\0" + "min\0" + "pow\0" + "random\0" + "round\0" + "sin\0" + "sqrt\0" + "tan" +; + +static const SkMemberInfo gMathInfo[] = { + {0, -1, 67, 98}, + {2, -2, 67, 98}, + {7, -3, 67, 98}, + {11, -4, 67, 98}, + {18, -5, 67, 98}, + {24, -6, 67, 98}, + {27, -7, 67, 98}, + {35, -8, 67, 98}, + {41, -1, 66, 98}, + {45, -2, 66, 98}, + {50, -3, 66, 98}, + {55, -4, 66, 98}, + {60, -5, 66, 98}, + {66, -6, 66, 98}, + {71, -7, 66, 98}, + {75, -8, 66, 98}, + {79, -9, 66, 98}, + {85, -10, 66, 98}, + {89, -11, 66, 98}, + {93, -12, 66, 98}, + {97, -13, 66, 98}, + {101, -14, 66, 98}, + {108, -15, 66, 98}, + {114, -16, 66, 98}, + {118, -17, 66, 98}, + {123, -18, 66, 98} +}; + +static const char gAddStrings[] = + "inPlace\0" + "offset\0" + "use\0" + "where" +; + +static const SkMemberInfo gAddInfo[] = { + {0, 16, 26, 1}, + {8, 20, 96, 1}, + {15, 24, 37, 1}, + {19, 28, 37, 1} +}; + +static const char gAddCircleStrings[] = + "\0" + "radius\0" + "x\0" + "y" +; + +static const SkMemberInfo gAddCircleInfo[] = { + {0, 3, 18, 1}, + {1, 24, 98, 1}, + {8, 28, 98, 1}, + {10, 32, 98, 1} +}; + +static const char gUnknown1Strings[] = + "direction" +; + +static const SkMemberInfo gUnknown1Info[] = { + {0, 20, 75, 1} +}; + +static const char gAddOvalStrings[] = + "" +; + +static const SkMemberInfo gAddOvalInfo[] = { + {0, 6, 18, 5} +}; + +static const char gAddPathStrings[] = + "matrix\0" + "path" +; + +static const SkMemberInfo gAddPathInfo[] = { + {0, 20, 65, 1}, + {7, 24, 74, 1} +}; + +static const char gAddRectangleStrings[] = + "\0" + "bottom\0" + "left\0" + "right\0" + "top" +; + +static const SkMemberInfo gAddRectangleInfo[] = { + {0, 3, 18, 1}, + {1, 36, 98, 1}, + {8, 24, 98, 1}, + {13, 32, 98, 1}, + {19, 28, 98, 1} +}; + +static const char gAddRoundRectStrings[] = + "\0" + "rx\0" + "ry" +; + +static const SkMemberInfo gAddRoundRectInfo[] = { + {0, 6, 18, 5}, + {1, 40, 98, 1}, + {4, 44, 98, 1} +}; + +static const char gUnknown2Strings[] = + "begin\0" + "blend\0" + "dur\0" + "dynamic\0" + "field\0" + "formula\0" + "from\0" + "mirror\0" + "repeat\0" + "reset\0" + "target\0" + "to\0" + "values" +; + +static const SkMemberInfo gUnknown2Info[] = { + {0, 16, 71, 1}, + {6, 20, 119, 98}, + {12, 36, 71, 1}, + {16, -1, 67, 26}, + {24, 40, 108, 2}, + {30, 48, 40, 2}, + {38, 56, 40, 2}, + {43, -2, 67, 26}, + {50, 64, 98, 1}, + {57, -3, 67, 26}, + {63, 68, 40, 2}, + {70, 76, 40, 2}, + {73, -4, 67, 40} +}; + +static const char gAnimateFieldStrings[] = + "" +; + +static const SkMemberInfo gAnimateFieldInfo[] = { + {0, 8, 18, 13} +}; + +static const char gApplyStrings[] = + "animator\0" + "begin\0" + "dontDraw\0" + "dynamicScope\0" + "interval\0" + "mode\0" + "pickup\0" + "restore\0" + "scope\0" + "step\0" + "steps\0" + "time\0" + "transition" +; + +static const SkMemberInfo gApplyInfo[] = { + {0, -1, 67, 10}, + {9, 16, 71, 1}, + {15, 20, 26, 1}, + {24, 24, 108, 2}, + {37, 32, 71, 1}, + {46, 36, 13, 1}, + {51, 40, 26, 1}, + {58, 44, 26, 1}, + {66, 48, 37, 1}, + {72, -2, 67, 96}, + {77, 52, 96, 1}, + {83, -3, 67, 71}, + {88, 56, 14, 1} +}; + +static const char gUnknown3Strings[] = + "x\0" + "y" +; + +static const SkMemberInfo gUnknown3Info[] = { + {0, 48, 98, 1}, + {2, 52, 98, 1} +}; + +static const char gBitmapStrings[] = + "\0" + "erase\0" + "format\0" + "height\0" + "rowBytes\0" + "width" +; + +static const SkMemberInfo gDrawBitmapInfo[] = { + {0, 11, 18, 2}, + {1, -1, 67, 15}, + {7, 56, 21, 1}, + {14, 60, 96, 1}, + {21, 64, 96, 1}, + {30, 68, 96, 1} +}; + +static const char gBitmapShaderStrings[] = + "\0" + "filterType\0" + "image" +; + +static const SkMemberInfo gDrawBitmapShaderInfo[] = { + {0, 67, 18, 2}, + {1, 28, 47, 1}, + {12, 32, 17, 1} +}; + +static const char gBlurStrings[] = + "blurStyle\0" + "radius" +; + +static const SkMemberInfo gDrawBlurInfo[] = { + {0, 24, 63, 1}, + {10, 20, 98, 1} +}; + +static const char gBoundsStrings[] = + "\0" + "inval" +; + +static const SkMemberInfo gDisplayBoundsInfo[] = { + {0, 58, 18, 7}, + {1, 44, 26, 1} +}; + +static const char gClipStrings[] = + "path\0" + "rectangle" +; + +static const SkMemberInfo gDrawClipInfo[] = { + {0, 20, 74, 1}, + {5, 16, 91, 1} +}; + +static const char gColorStrings[] = + "alpha\0" + "blue\0" + "color\0" + "green\0" + "hue\0" + "red\0" + "saturation\0" + "value" +; + +static const SkMemberInfo gDrawColorInfo[] = { + {0, -1, 67, 98}, + {6, -2, 67, 98}, + {11, 20, 15, 1}, + {17, -3, 67, 98}, + {23, -4, 67, 98}, + {27, -5, 67, 98}, + {31, -6, 67, 98}, + {42, -7, 67, 98} +}; + +static const char gCubicToStrings[] = + "x1\0" + "x2\0" + "x3\0" + "y1\0" + "y2\0" + "y3" +; + +static const SkMemberInfo gCubicToInfo[] = { + {0, 20, 98, 1}, + {3, 28, 98, 1}, + {6, 36, 98, 1}, + {9, 24, 98, 1}, + {12, 32, 98, 1}, + {15, 40, 98, 1} +}; + +static const char gDashStrings[] = + "intervals\0" + "phase" +; + +static const SkMemberInfo gDashInfo[] = { + {0, 20, 119, 98}, + {10, 36, 98, 1} +}; + +static const char gDataStrings[] = + "\0" + "name" +; + +static const SkMemberInfo gDataInfo[] = { + {0, 33, 18, 3}, + {1, 32, 108, 2} +}; + +static const char gDiscreteStrings[] = + "deviation\0" + "segLength" +; + +static const SkMemberInfo gDiscreteInfo[] = { + {0, 20, 98, 1}, + {10, 24, 98, 1} +}; + +static const char gDrawToStrings[] = + "drawOnce\0" + "use" +; + +static const SkMemberInfo gDrawToInfo[] = { + {0, 72, 26, 1}, + {9, 76, 19, 1} +}; + +static const char gDumpStrings[] = + "displayList\0" + "eventList\0" + "events\0" + "groups\0" + "name\0" + "posts" +; + +static const SkMemberInfo gDumpInfo[] = { + {0, 16, 26, 1}, + {12, 20, 26, 1}, + {22, 24, 26, 1}, + {29, 36, 26, 1}, + {36, 28, 108, 2}, + {41, 40, 26, 1} +}; + +static const char gEmbossStrings[] = + "ambient\0" + "direction\0" + "radius\0" + "specular" +; + +static const SkMemberInfo gDrawEmbossInfo[] = { + {0, -1, 67, 98}, + {8, 20, 119, 98}, + {18, 36, 98, 1}, + {25, -2, 67, 98} +}; + +static const char gEventStrings[] = + "code\0" + "disable\0" + "key\0" + "keys\0" + "kind\0" + "target\0" + "x\0" + "y" +; + +static const SkMemberInfo gDisplayEventInfo[] = { + {0, 16, 43, 1}, + {5, 20, 26, 1}, + {13, -1, 67, 108}, + {17, -2, 67, 108}, + {22, 24, 44, 1}, + {27, 28, 108, 2}, + {34, 36, 98, 1}, + {36, 40, 98, 1} +}; + +static const char gFromPathStrings[] = + "mode\0" + "offset\0" + "path" +; + +static const SkMemberInfo gFromPathInfo[] = { + {0, 20, 49, 1}, + {5, 24, 98, 1}, + {12, 28, 74, 1} +}; + +static const char gUnknown4Strings[] = + "\0" + "offsets\0" + "unitMapper" +; + +static const SkMemberInfo gUnknown4Info[] = { + {0, 67, 18, 2}, + {1, 28, 119, 98}, + {9, 44, 108, 2} +}; + +static const char gGStrings[] = + "condition\0" + "enableCondition" +; + +static const SkMemberInfo gGInfo[] = { + {0, 16, 40, 2}, + {10, 24, 40, 2} +}; + +static const char gHitClearStrings[] = + "targets" +; + +static const SkMemberInfo gHitClearInfo[] = { + {0, 16, 119, 36} +}; + +static const char gHitTestStrings[] = + "bullets\0" + "hits\0" + "targets\0" + "value" +; + +static const SkMemberInfo gHitTestInfo[] = { + {0, 16, 119, 36}, + {8, 32, 119, 96}, + {13, 48, 119, 36}, + {21, 64, 26, 1} +}; + +static const char gImageStrings[] = + "\0" + "base64\0" + "src" +; + +static const SkMemberInfo gImageInfo[] = { + {0, 11, 18, 2}, + {1, 56, 16, 2}, + {8, 64, 108, 2} +}; + +static const char gIncludeStrings[] = + "src" +; + +static const SkMemberInfo gIncludeInfo[] = { + {0, 16, 108, 2} +}; + +static const char gInputStrings[] = + "s32\0" + "scalar\0" + "string" +; + +static const SkMemberInfo gInputInfo[] = { + {0, 16, 96, 1}, + {4, 20, 98, 1}, + {11, 24, 108, 2} +}; + +static const char gLineStrings[] = + "x1\0" + "x2\0" + "y1\0" + "y2" +; + +static const SkMemberInfo gLineInfo[] = { + {0, 24, 98, 1}, + {3, 28, 98, 1}, + {6, 32, 98, 1}, + {9, 36, 98, 1} +}; + +static const char gLineToStrings[] = + "x\0" + "y" +; + +static const SkMemberInfo gLineToInfo[] = { + {0, 20, 98, 1}, + {2, 24, 98, 1} +}; + +static const char gLinearGradientStrings[] = + "\0" + "points" +; + +static const SkMemberInfo gLinearGradientInfo[] = { + {0, 27, 18, 3}, + {1, 88, 77, 4} +}; + +static const char gMatrixStrings[] = + "matrix\0" + "perspectX\0" + "perspectY\0" + "rotate\0" + "scale\0" + "scaleX\0" + "scaleY\0" + "skewX\0" + "skewY\0" + "translate\0" + "translateX\0" + "translateY" +; + +static const SkMemberInfo gDrawMatrixInfo[] = { + {0, 16, 119, 98}, + {7, -1, 67, 98}, + {17, -2, 67, 98}, + {27, -3, 67, 98}, + {34, -4, 67, 98}, + {40, -5, 67, 98}, + {47, -6, 67, 98}, + {54, -7, 67, 98}, + {60, -8, 67, 98}, + {66, -9, 67, 77}, + {76, -10, 67, 98}, + {87, -11, 67, 98} +}; + +static const char gMoveStrings[] = + "" +; + +static const SkMemberInfo gMoveInfo[] = { + {0, 1, 18, 4} +}; + +static const char gMoveToStrings[] = + "x\0" + "y" +; + +static const SkMemberInfo gMoveToInfo[] = { + {0, 20, 98, 1}, + {2, 24, 98, 1} +}; + +static const char gMovieStrings[] = + "src" +; + +static const SkMemberInfo gMovieInfo[] = { + {0, 16, 108, 2} +}; + +static const char gOvalStrings[] = + "" +; + +static const SkMemberInfo gOvalInfo[] = { + {0, 58, 18, 7} +}; + +static const char gPaintStrings[] = + "antiAlias\0" + "ascent\0" + "color\0" + "descent\0" + "filterType\0" + "linearText\0" + "maskFilter\0" + "measureText\0" + "pathEffect\0" + "shader\0" + "strikeThru\0" + "stroke\0" + "strokeCap\0" + "strokeJoin\0" + "strokeMiter\0" + "strokeWidth\0" + "style\0" + "textAlign\0" + "textScaleX\0" + "textSize\0" + "textSkewX\0" + "textTracking\0" + "typeface\0" + "underline\0" + "xfermode" +; + +static const SkMemberInfo gDrawPaintInfo[] = { + {0, 16, 26, 1}, + {10, -1, 67, 98}, + {17, 20, 31, 1}, + {23, -2, 67, 98}, + {31, 24, 47, 1}, + {42, 28, 26, 1}, + {53, 32, 62, 1}, + {64, -1, 66, 98}, + {76, 36, 76, 1}, + {87, 40, 102, 1}, + {94, 44, 26, 1}, + {105, 48, 26, 1}, + {112, 52, 27, 1}, + {122, 56, 58, 1}, + {133, 60, 98, 1}, + {145, 64, 98, 1}, + {157, 68, 109, 1}, + {163, 72, 9, 1}, + {173, 76, 98, 1}, + {184, 80, 98, 1}, + {193, 84, 98, 1}, + {203, 88, 98, 1}, + {216, 92, 120, 1}, + {225, 96, 26, 1}, + {235, 100, 121, 1} +}; + +static const char gPathStrings[] = + "d\0" + "fillType\0" + "length" +; + +static const SkMemberInfo gDrawPathInfo[] = { + {0, 52, 108, 2}, + {2, -1, 67, 46}, + {11, -2, 67, 98} +}; + +static const char gUnknown5Strings[] = + "x\0" + "y\0" + "z" +; + +static const SkMemberInfo gUnknown5Info[] = { + {0, 0, 98, 1}, + {2, 4, 98, 1}, + {4, 8, 98, 1} +}; + +static const char gPointStrings[] = + "x\0" + "y" +; + +static const SkMemberInfo gDrawPointInfo[] = { + {0, 16, 98, 1}, + {2, 20, 98, 1} +}; + +static const char gPolyToPolyStrings[] = + "destination\0" + "source" +; + +static const SkMemberInfo gPolyToPolyInfo[] = { + {0, 24, 80, 1}, + {12, 20, 80, 1} +}; + +static const char gPolygonStrings[] = + "" +; + +static const SkMemberInfo gPolygonInfo[] = { + {0, 48, 18, 1} +}; + +static const char gPolylineStrings[] = + "points" +; + +static const SkMemberInfo gPolylineInfo[] = { + {0, 88, 119, 98} +}; + +static const char gPostStrings[] = + "delay\0" + "initialized\0" + "mode\0" + "sink\0" + "target\0" + "type" +; + +static const SkMemberInfo gPostInfo[] = { + {0, 16, 71, 1}, + {6, 20, 26, 1}, + {18, 24, 45, 1}, + {23, -1, 67, 108}, + {28, -2, 67, 108}, + {35, -3, 67, 108} +}; + +static const char gQuadToStrings[] = + "x1\0" + "x2\0" + "y1\0" + "y2" +; + +static const SkMemberInfo gQuadToInfo[] = { + {0, 20, 98, 1}, + {3, 28, 98, 1}, + {6, 24, 98, 1}, + {9, 32, 98, 1} +}; + +static const char gRCubicToStrings[] = + "" +; + +static const SkMemberInfo gRCubicToInfo[] = { + {0, 18, 18, 6} +}; + +static const char gRLineToStrings[] = + "" +; + +static const SkMemberInfo gRLineToInfo[] = { + {0, 35, 18, 2} +}; + +static const char gRMoveToStrings[] = + "" +; + +static const SkMemberInfo gRMoveToInfo[] = { + {0, 39, 18, 2} +}; + +static const char gRQuadToStrings[] = + "" +; + +static const SkMemberInfo gRQuadToInfo[] = { + {0, 50, 18, 4} +}; + +static const char gRadialGradientStrings[] = + "\0" + "center\0" + "radius" +; + +static const SkMemberInfo gRadialGradientInfo[] = { + {0, 27, 18, 3}, + {1, 88, 77, 2}, + {8, 96, 98, 1} +}; + +static const char gRandomStrings[] = + "blend\0" + "max\0" + "min\0" + "random\0" + "seed" +; + +static const SkMemberInfo gDisplayRandomInfo[] = { + {0, 16, 98, 1}, + {6, 24, 98, 1}, + {10, 20, 98, 1}, + {14, 1, 67, 98}, + {21, -2, 67, 96} +}; + +static const char gRectToRectStrings[] = + "destination\0" + "source" +; + +static const SkMemberInfo gRectToRectInfo[] = { + {0, 24, 91, 1}, + {12, 20, 91, 1} +}; + +static const char gRectangleStrings[] = + "bottom\0" + "height\0" + "left\0" + "needsRedraw\0" + "right\0" + "top\0" + "width" +; + +static const SkMemberInfo gRectangleInfo[] = { + {0, 36, 98, 1}, + {7, -1, 67, 98}, + {14, 24, 98, 1}, + {19, -2, 67, 26}, + {31, 32, 98, 1}, + {37, 28, 98, 1}, + {41, -3, 67, 98} +}; + +static const char gRemoveStrings[] = + "offset\0" + "where" +; + +static const SkMemberInfo gRemoveInfo[] = { + {0, 20, 96, 1}, + {7, 28, 37, 1} +}; + +static const char gReplaceStrings[] = + "" +; + +static const SkMemberInfo gReplaceInfo[] = { + {0, 1, 18, 4} +}; + +static const char gRotateStrings[] = + "center\0" + "degrees" +; + +static const SkMemberInfo gRotateInfo[] = { + {0, 24, 77, 2}, + {7, 20, 98, 1} +}; + +static const char gRoundRectStrings[] = + "\0" + "rx\0" + "ry" +; + +static const SkMemberInfo gRoundRectInfo[] = { + {0, 58, 18, 7}, + {1, 44, 98, 1}, + {4, 48, 98, 1} +}; + +static const char gS32Strings[] = + "value" +; + +static const SkMemberInfo gS32Info[] = { + {0, 16, 96, 1} +}; + +static const char gScalarStrings[] = + "value" +; + +static const SkMemberInfo gScalarInfo[] = { + {0, 16, 98, 1} +}; + +static const char gScaleStrings[] = + "center\0" + "x\0" + "y" +; + +static const SkMemberInfo gScaleInfo[] = { + {0, 28, 77, 2}, + {7, 20, 98, 1}, + {9, 24, 98, 1} +}; + +static const char gSetStrings[] = + "begin\0" + "dur\0" + "dynamic\0" + "field\0" + "formula\0" + "reset\0" + "target\0" + "to" +; + +static const SkMemberInfo gSetInfo[] = { + {0, 16, 71, 1}, + {6, 36, 71, 1}, + {10, -1, 67, 26}, + {18, 40, 108, 2}, + {24, 48, 40, 2}, + {32, -3, 67, 26}, + {38, 68, 40, 2}, + {45, 76, 40, 2} +}; + +static const char gShaderStrings[] = + "matrix\0" + "tileMode" +; + +static const SkMemberInfo gShaderInfo[] = { + {0, 20, 65, 1}, + {7, 24, 116, 1} +}; + +static const char gSkewStrings[] = + "center\0" + "x\0" + "y" +; + +static const SkMemberInfo gSkewInfo[] = { + {0, 28, 77, 2}, + {7, 20, 98, 1}, + {9, 24, 98, 1} +}; + +static const char g3D_CameraStrings[] = + "axis\0" + "hackHeight\0" + "hackWidth\0" + "location\0" + "observer\0" + "patch\0" + "zenith" +; + +static const SkMemberInfo g3D_CameraInfo[] = { + {0, 36, 106, 3}, + {5, 20, 98, 1}, + {16, 16, 98, 1}, + {26, 24, 106, 3}, + {35, 60, 106, 3}, + {44, 108, 105, 1}, + {50, 48, 106, 3} +}; + +static const char g3D_PatchStrings[] = + "origin\0" + "rotateDegrees\0" + "u\0" + "v" +; + +static const SkMemberInfo g3D_PatchInfo[] = { + {0, 40, 106, 3}, + {7, -1, 66, 98}, + {21, 16, 106, 3}, + {23, 28, 106, 3} +}; + +static const char gUnknown6Strings[] = + "x\0" + "y\0" + "z" +; + +static const SkMemberInfo gUnknown6Info[] = { + {0, 0, 98, 1}, + {2, 4, 98, 1}, + {4, 8, 98, 1} +}; + +static const char gSnapshotStrings[] = + "filename\0" + "quality\0" + "sequence\0" + "type" +; + +static const SkMemberInfo gSnapshotInfo[] = { + {0, 16, 108, 2}, + {9, 24, 98, 1}, + {17, 28, 26, 1}, + {26, 32, 20, 1} +}; + +static const char gStringStrings[] = + "length\0" + "slice\0" + "value" +; + +static const SkMemberInfo gStringInfo[] = { + {0, -1, 67, 96}, + {7, -1, 66, 108}, + {13, 16, 108, 2} +}; + +static const char gTextStrings[] = + "length\0" + "text\0" + "x\0" + "y" +; + +static const SkMemberInfo gTextInfo[] = { + {0, -1, 67, 96}, + {7, 24, 108, 2}, + {12, 32, 98, 1}, + {14, 36, 98, 1} +}; + +static const char gTextBoxStrings[] = + "\0" + "mode\0" + "spacingAdd\0" + "spacingAlign\0" + "spacingMul\0" + "text" +; + +static const SkMemberInfo gTextBoxInfo[] = { + {0, 58, 18, 7}, + {1, 60, 113, 1}, + {6, 56, 98, 1}, + {17, 64, 112, 1}, + {30, 52, 98, 1}, + {41, 44, 108, 2} +}; + +static const char gTextOnPathStrings[] = + "offset\0" + "path\0" + "text" +; + +static const SkMemberInfo gTextOnPathInfo[] = { + {0, 24, 98, 1}, + {7, 28, 74, 1}, + {12, 32, 110, 1} +}; + +static const char gTextToPathStrings[] = + "path\0" + "text" +; + +static const SkMemberInfo gTextToPathInfo[] = { + {0, 16, 74, 1}, + {5, 20, 110, 1} +}; + +static const char gTranslateStrings[] = + "x\0" + "y" +; + +static const SkMemberInfo gTranslateInfo[] = { + {0, 20, 98, 1}, + {2, 24, 98, 1} +}; + +static const char gTypedArrayStrings[] = + "length\0" + "values" +; + +static const SkMemberInfo gTypedArrayInfo[] = { + {0, -1, 67, 96}, + {7, 16, 119, 0} +}; + +static const char gTypefaceStrings[] = + "fontName" +; + +static const SkMemberInfo gTypefaceInfo[] = { + {0, 20, 108, 2} +}; + +static const SkMemberInfo* const gInfoTables[] = { + gMathInfo, + gAddInfo, + gAddCircleInfo, + gUnknown1Info, + gAddOvalInfo, + gAddPathInfo, + gAddRectangleInfo, + gAddRoundRectInfo, + gUnknown2Info, + gAnimateFieldInfo, + gApplyInfo, + gUnknown3Info, + gDrawBitmapInfo, + gDrawBitmapShaderInfo, + gDrawBlurInfo, + gDisplayBoundsInfo, + gDrawClipInfo, + gDrawColorInfo, + gCubicToInfo, + gDashInfo, + gDataInfo, + gDiscreteInfo, + gDrawToInfo, + gDumpInfo, + gDrawEmbossInfo, + gDisplayEventInfo, + gFromPathInfo, + gUnknown4Info, + gGInfo, + gHitClearInfo, + gHitTestInfo, + gImageInfo, + gIncludeInfo, + gInputInfo, + gLineInfo, + gLineToInfo, + gLinearGradientInfo, + gDrawMatrixInfo, + gMoveInfo, + gMoveToInfo, + gMovieInfo, + gOvalInfo, + gDrawPaintInfo, + gDrawPathInfo, + gUnknown5Info, + gDrawPointInfo, + gPolyToPolyInfo, + gPolygonInfo, + gPolylineInfo, + gPostInfo, + gQuadToInfo, + gRCubicToInfo, + gRLineToInfo, + gRMoveToInfo, + gRQuadToInfo, + gRadialGradientInfo, + gDisplayRandomInfo, + gRectToRectInfo, + gRectangleInfo, + gRemoveInfo, + gReplaceInfo, + gRotateInfo, + gRoundRectInfo, + gS32Info, + gScalarInfo, + gScaleInfo, + gSetInfo, + gShaderInfo, + gSkewInfo, + g3D_CameraInfo, + g3D_PatchInfo, + gUnknown6Info, + gSnapshotInfo, + gStringInfo, + gTextInfo, + gTextBoxInfo, + gTextOnPathInfo, + gTextToPathInfo, + gTranslateInfo, + gTypedArrayInfo, + gTypefaceInfo, +}; + +static const unsigned char gInfoCounts[] = { + 26,4,4,1,1,2,5,3,13,1,13,2,6,3,2,2,2,8,6, + 2,2,2,2,6,4,8,3,3,2,1,4,3,1,3,4,2,2,12,1, + 2,1,1,25,3,3,2,2,1,1,6,4,1,1,1,1,3,5,2,7, + 2,1,2,3,1,1,3,8,2,3,7,4,3,4,3,4,6,3,2,2, + 2,1 +}; + +static const unsigned char gTypeIDs[] = { + 1, // Math + 2, // Add + 3, // AddCircle + 4, // Unknown1 + 5, // AddOval + 6, // AddPath + 7, // AddRectangle + 8, // AddRoundRect + 10, // Unknown2 + 11, // AnimateField + 12, // Apply + 17, // Unknown3 + 19, // Bitmap + 22, // BitmapShader + 23, // Blur + 25, // Bounds + 29, // Clip + 31, // Color + 32, // CubicTo + 33, // Dash + 34, // Data + 35, // Discrete + 38, // DrawTo + 39, // Dump + 41, // Emboss + 42, // Event + 48, // FromPath + 51, // Unknown4 + 52, // G + 53, // HitClear + 54, // HitTest + 55, // Image + 56, // Include + 57, // Input + 59, // Line + 60, // LineTo + 61, // LinearGradient + 65, // Matrix + 68, // Move + 69, // MoveTo + 70, // Movie + 72, // Oval + 73, // Paint + 74, // Path + 77, // Unknown5 + 78, // Point + 79, // PolyToPoly + 80, // Polygon + 81, // Polyline + 82, // Post + 83, // QuadTo + 84, // RCubicTo + 85, // RLineTo + 86, // RMoveTo + 87, // RQuadTo + 88, // RadialGradient + 89, // Random + 90, // RectToRect + 91, // Rectangle + 92, // Remove + 93, // Replace + 94, // Rotate + 95, // RoundRect + 96, // S32 + 98, // Scalar + 99, // Scale + 101, // Set + 102, // Shader + 103, // Skew + 104, // 3D_Camera + 105, // 3D_Patch + 106, // Unknown6 + 107, // Snapshot + 108, // String + 110, // Text + 111, // TextBox + 114, // TextOnPath + 115, // TextToPath + 117, // Translate + 119, // TypedArray + 120, // Typeface + +}; + +static const int kTypeIDs = 81; + +static const char* const gInfoNames[] = { + gMathStrings, + gAddStrings, + gAddCircleStrings, + gUnknown1Strings, + gAddOvalStrings, + gAddPathStrings, + gAddRectangleStrings, + gAddRoundRectStrings, + gUnknown2Strings, + gAnimateFieldStrings, + gApplyStrings, + gUnknown3Strings, + gBitmapStrings, + gBitmapShaderStrings, + gBlurStrings, + gBoundsStrings, + gClipStrings, + gColorStrings, + gCubicToStrings, + gDashStrings, + gDataStrings, + gDiscreteStrings, + gDrawToStrings, + gDumpStrings, + gEmbossStrings, + gEventStrings, + gFromPathStrings, + gUnknown4Strings, + gGStrings, + gHitClearStrings, + gHitTestStrings, + gImageStrings, + gIncludeStrings, + gInputStrings, + gLineStrings, + gLineToStrings, + gLinearGradientStrings, + gMatrixStrings, + gMoveStrings, + gMoveToStrings, + gMovieStrings, + gOvalStrings, + gPaintStrings, + gPathStrings, + gUnknown5Strings, + gPointStrings, + gPolyToPolyStrings, + gPolygonStrings, + gPolylineStrings, + gPostStrings, + gQuadToStrings, + gRCubicToStrings, + gRLineToStrings, + gRMoveToStrings, + gRQuadToStrings, + gRadialGradientStrings, + gRandomStrings, + gRectToRectStrings, + gRectangleStrings, + gRemoveStrings, + gReplaceStrings, + gRotateStrings, + gRoundRectStrings, + gS32Strings, + gScalarStrings, + gScaleStrings, + gSetStrings, + gShaderStrings, + gSkewStrings, + g3D_CameraStrings, + g3D_PatchStrings, + gUnknown6Strings, + gSnapshotStrings, + gStringStrings, + gTextStrings, + gTextBoxStrings, + gTextOnPathStrings, + gTextToPathStrings, + gTranslateStrings, + gTypedArrayStrings, + gTypefaceStrings +}; + +#endif +#endif + + diff --git a/skia/animator/SkCondensedRelease.cpp b/skia/animator/SkCondensedRelease.cpp new file mode 100644 index 0000000..87d5d7c --- /dev/null +++ b/skia/animator/SkCondensedRelease.cpp @@ -0,0 +1,1374 @@ +/* libs/graphics/animator/SkCondensedRelease.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkTypes.h" +#ifndef SK_BUILD_FOR_UNIX +#ifdef SK_RELEASE +// This file was automatically generated. +// To change it, edit the file with the matching debug info. +// Then execute SkDisplayType::BuildCondensedInfo() to regenerate this file. + +static const char gMathStrings[] = + "E\0" + "LN10\0" + "LN2\0" + "LOG10E\0" + "LOG2E\0" + "PI\0" + "SQRT1_2\0" + "SQRT2\0" + "abs\0" + "acos\0" + "asin\0" + "atan\0" + "atan2\0" + "ceil\0" + "cos\0" + "exp\0" + "floor\0" + "log\0" + "max\0" + "min\0" + "pow\0" + "random\0" + "round\0" + "sin\0" + "sqrt\0" + "tan" +; + +static const SkMemberInfo gMathInfo[] = { + {0, -1, 67, 98}, + {2, -2, 67, 98}, + {7, -3, 67, 98}, + {11, -4, 67, 98}, + {18, -5, 67, 98}, + {24, -6, 67, 98}, + {27, -7, 67, 98}, + {35, -8, 67, 98}, + {41, -1, 66, 98}, + {45, -2, 66, 98}, + {50, -3, 66, 98}, + {55, -4, 66, 98}, + {60, -5, 66, 98}, + {66, -6, 66, 98}, + {71, -7, 66, 98}, + {75, -8, 66, 98}, + {79, -9, 66, 98}, + {85, -10, 66, 98}, + {89, -11, 66, 98}, + {93, -12, 66, 98}, + {97, -13, 66, 98}, + {101, -14, 66, 98}, + {108, -15, 66, 98}, + {114, -16, 66, 98}, + {118, -17, 66, 98}, + {123, -18, 66, 98} +}; + +static const char gAddStrings[] = + "inPlace\0" + "offset\0" + "use\0" + "where" +; + +static const SkMemberInfo gAddInfo[] = { + {0, 4, 26, 1}, + {8, 8, 96, 1}, + {15, 12, 37, 1}, + {19, 16, 37, 1} +}; + +static const char gAddCircleStrings[] = + "\0" + "radius\0" + "x\0" + "y" +; + +static const SkMemberInfo gAddCircleInfo[] = { + {0, 3, 18, 1}, + {1, 12, 98, 1}, + {8, 16, 98, 1}, + {10, 20, 98, 1} +}; + +static const char gUnknown1Strings[] = + "direction" +; + +static const SkMemberInfo gUnknown1Info[] = { + {0, 8, 75, 1} +}; + +static const char gAddOvalStrings[] = + "" +; + +static const SkMemberInfo gAddOvalInfo[] = { + {0, 6, 18, 5} +}; + +static const char gAddPathStrings[] = + "matrix\0" + "path" +; + +static const SkMemberInfo gAddPathInfo[] = { + {0, 8, 65, 1}, + {7, 12, 74, 1} +}; + +static const char gAddRectangleStrings[] = + "\0" + "bottom\0" + "left\0" + "right\0" + "top" +; + +static const SkMemberInfo gAddRectangleInfo[] = { + {0, 3, 18, 1}, + {1, 24, 98, 1}, + {8, 12, 98, 1}, + {13, 20, 98, 1}, + {19, 16, 98, 1} +}; + +static const char gAddRoundRectStrings[] = + "\0" + "rx\0" + "ry" +; + +static const SkMemberInfo gAddRoundRectInfo[] = { + {0, 6, 18, 5}, + {1, 28, 98, 1}, + {4, 32, 98, 1} +}; + +static const char gUnknown2Strings[] = + "begin\0" + "blend\0" + "dur\0" + "dynamic\0" + "field\0" + "formula\0" + "from\0" + "mirror\0" + "repeat\0" + "reset\0" + "target\0" + "to\0" + "values" +; + +static const SkMemberInfo gUnknown2Info[] = { + {0, 4, 71, 1}, + {6, 8, 119, 98}, + {12, 16, 71, 1}, + {16, -1, 67, 26}, + {24, 20, 108, 1}, + {30, 24, 40, 1}, + {38, 28, 40, 1}, + {43, -2, 67, 26}, + {50, 32, 98, 1}, + {57, -3, 67, 26}, + {63, 36, 40, 1}, + {70, 40, 40, 1}, + {73, -4, 67, 40} +}; + +static const char gAnimateFieldStrings[] = + "" +; + +static const SkMemberInfo gAnimateFieldInfo[] = { + {0, 8, 18, 13} +}; + +static const char gApplyStrings[] = + "animator\0" + "begin\0" + "dontDraw\0" + "dynamicScope\0" + "interval\0" + "mode\0" + "pickup\0" + "restore\0" + "scope\0" + "step\0" + "steps\0" + "time\0" + "transition" +; + +static const SkMemberInfo gApplyInfo[] = { + {0, -1, 67, 10}, + {9, 4, 71, 1}, + {15, 8, 26, 1}, + {24, 12, 108, 1}, + {37, 16, 71, 1}, + {46, 20, 13, 1}, + {51, 24, 26, 1}, + {58, 28, 26, 1}, + {66, 32, 37, 1}, + {72, -2, 67, 96}, + {77, 36, 96, 1}, + {83, -3, 67, 71}, + {88, 40, 14, 1} +}; + +static const char gUnknown3Strings[] = + "x\0" + "y" +; + +static const SkMemberInfo gUnknown3Info[] = { + {0, 36, 98, 1}, + {2, 40, 98, 1} +}; + +static const char gBitmapStrings[] = + "\0" + "erase\0" + "format\0" + "height\0" + "rowBytes\0" + "width" +; + +static const SkMemberInfo gDrawBitmapInfo[] = { + {0, 11, 18, 2}, + {1, -1, 67, 15}, + {7, 44, 21, 1}, + {14, 48, 96, 1}, + {21, 52, 96, 1}, + {30, 56, 96, 1} +}; + +static const char gBitmapShaderStrings[] = + "\0" + "filterType\0" + "image" +; + +static const SkMemberInfo gDrawBitmapShaderInfo[] = { + {0, 66, 18, 2}, + {1, 16, 47, 1}, + {12, 20, 17, 1} +}; + +static const char gBlurStrings[] = + "blurStyle\0" + "radius" +; + +static const SkMemberInfo gDrawBlurInfo[] = { + {0, 12, 63, 1}, + {10, 8, 98, 1} +}; + +static const char gBoundsStrings[] = + "\0" + "inval" +; + +static const SkMemberInfo gDisplayBoundsInfo[] = { + {0, 57, 18, 7}, + {1, 32, 26, 1} +}; + +static const char gClipStrings[] = + "path\0" + "rectangle" +; + +static const SkMemberInfo gDrawClipInfo[] = { + {0, 8, 74, 1}, + {5, 4, 91, 1} +}; + +static const char gColorStrings[] = + "alpha\0" + "blue\0" + "color\0" + "green\0" + "hue\0" + "red\0" + "saturation\0" + "value" +; + +static const SkMemberInfo gDrawColorInfo[] = { + {0, -1, 67, 98}, + {6, -2, 67, 98}, + {11, 8, 15, 1}, + {17, -3, 67, 98}, + {23, -4, 67, 98}, + {27, -5, 67, 98}, + {31, -6, 67, 98}, + {42, -7, 67, 98} +}; + +static const char gCubicToStrings[] = + "x1\0" + "x2\0" + "x3\0" + "y1\0" + "y2\0" + "y3" +; + +static const SkMemberInfo gCubicToInfo[] = { + {0, 8, 98, 1}, + {3, 16, 98, 1}, + {6, 24, 98, 1}, + {9, 12, 98, 1}, + {12, 20, 98, 1}, + {15, 28, 98, 1} +}; + +static const char gDashStrings[] = + "intervals\0" + "phase" +; + +static const SkMemberInfo gDashInfo[] = { + {0, 8, 119, 98}, + {10, 16, 98, 1} +}; + +static const char gDataStrings[] = + "\0" + "name" +; + +static const SkMemberInfo gDataInfo[] = { + {0, 32, 18, 3}, + {1, 16, 108, 1} +}; + +static const char gDiscreteStrings[] = + "deviation\0" + "segLength" +; + +static const SkMemberInfo gDiscreteInfo[] = { + {0, 8, 98, 1}, + {10, 12, 98, 1} +}; + +static const char gDrawToStrings[] = + "drawOnce\0" + "use" +; + +static const SkMemberInfo gDrawToInfo[] = { + {0, 36, 26, 1}, + {9, 40, 19, 1} +}; + +static const char gEmbossStrings[] = + "ambient\0" + "direction\0" + "radius\0" + "specular" +; + +static const SkMemberInfo gDrawEmbossInfo[] = { + {0, -1, 67, 98}, + {8, 8, 119, 98}, + {18, 16, 98, 1}, + {25, -2, 67, 98} +}; + +static const char gEventStrings[] = + "code\0" + "disable\0" + "key\0" + "keys\0" + "kind\0" + "target\0" + "x\0" + "y" +; + +static const SkMemberInfo gDisplayEventInfo[] = { + {0, 4, 43, 1}, + {5, 8, 26, 1}, + {13, -1, 67, 108}, + {17, -2, 67, 108}, + {22, 12, 44, 1}, + {27, 16, 108, 1}, + {34, 20, 98, 1}, + {36, 24, 98, 1} +}; + +static const char gFromPathStrings[] = + "mode\0" + "offset\0" + "path" +; + +static const SkMemberInfo gFromPathInfo[] = { + {0, 8, 49, 1}, + {5, 12, 98, 1}, + {12, 16, 74, 1} +}; + +static const char gUnknown4Strings[] = + "\0" + "offsets\0" + "unitMapper" +; + +static const SkMemberInfo gUnknown4Info[] = { + {0, 66, 18, 2}, + {1, 16, 119, 98}, + {9, 24, 108, 1} +}; + +static const char gGStrings[] = + "condition\0" + "enableCondition" +; + +static const SkMemberInfo gGInfo[] = { + {0, 4, 40, 1}, + {10, 8, 40, 1} +}; + +static const char gHitClearStrings[] = + "targets" +; + +static const SkMemberInfo gHitClearInfo[] = { + {0, 4, 119, 36} +}; + +static const char gHitTestStrings[] = + "bullets\0" + "hits\0" + "targets\0" + "value" +; + +static const SkMemberInfo gHitTestInfo[] = { + {0, 4, 119, 36}, + {8, 12, 119, 96}, + {13, 20, 119, 36}, + {21, 28, 26, 1} +}; + +static const char gImageStrings[] = + "\0" + "base64\0" + "src" +; + +static const SkMemberInfo gImageInfo[] = { + {0, 11, 18, 2}, + {1, 44, 16, 2}, + {8, 52, 108, 1} +}; + +static const char gIncludeStrings[] = + "src" +; + +static const SkMemberInfo gIncludeInfo[] = { + {0, 4, 108, 1} +}; + +static const char gInputStrings[] = + "s32\0" + "scalar\0" + "string" +; + +static const SkMemberInfo gInputInfo[] = { + {0, 4, 96, 1}, + {4, 8, 98, 1}, + {11, 12, 108, 1} +}; + +static const char gLineStrings[] = + "x1\0" + "x2\0" + "y1\0" + "y2" +; + +static const SkMemberInfo gLineInfo[] = { + {0, 12, 98, 1}, + {3, 16, 98, 1}, + {6, 20, 98, 1}, + {9, 24, 98, 1} +}; + +static const char gLineToStrings[] = + "x\0" + "y" +; + +static const SkMemberInfo gLineToInfo[] = { + {0, 8, 98, 1}, + {2, 12, 98, 1} +}; + +static const char gLinearGradientStrings[] = + "\0" + "points" +; + +static const SkMemberInfo gLinearGradientInfo[] = { + {0, 26, 18, 3}, + {1, 48, 77, 4} +}; + +static const char gMatrixStrings[] = + "matrix\0" + "perspectX\0" + "perspectY\0" + "rotate\0" + "scale\0" + "scaleX\0" + "scaleY\0" + "skewX\0" + "skewY\0" + "translate\0" + "translateX\0" + "translateY" +; + +static const SkMemberInfo gDrawMatrixInfo[] = { + {0, 4, 119, 98}, + {7, -1, 67, 98}, + {17, -2, 67, 98}, + {27, -3, 67, 98}, + {34, -4, 67, 98}, + {40, -5, 67, 98}, + {47, -6, 67, 98}, + {54, -7, 67, 98}, + {60, -8, 67, 98}, + {66, -9, 67, 77}, + {76, -10, 67, 98}, + {87, -11, 67, 98} +}; + +static const char gMoveStrings[] = + "" +; + +static const SkMemberInfo gMoveInfo[] = { + {0, 1, 18, 4} +}; + +static const char gMoveToStrings[] = + "x\0" + "y" +; + +static const SkMemberInfo gMoveToInfo[] = { + {0, 8, 98, 1}, + {2, 12, 98, 1} +}; + +static const char gMovieStrings[] = + "src" +; + +static const SkMemberInfo gMovieInfo[] = { + {0, 4, 108, 1} +}; + +static const char gOvalStrings[] = + "" +; + +static const SkMemberInfo gOvalInfo[] = { + {0, 57, 18, 7} +}; + +static const char gPaintStrings[] = + "antiAlias\0" + "ascent\0" + "color\0" + "descent\0" + "filterType\0" + "linearText\0" + "maskFilter\0" + "measureText\0" + "pathEffect\0" + "shader\0" + "strikeThru\0" + "stroke\0" + "strokeCap\0" + "strokeJoin\0" + "strokeMiter\0" + "strokeWidth\0" + "style\0" + "textAlign\0" + "textScaleX\0" + "textSize\0" + "textSkewX\0" + "textTracking\0" + "typeface\0" + "underline\0" + "xfermode" +; + +static const SkMemberInfo gDrawPaintInfo[] = { + {0, 4, 26, 1}, + {10, -1, 67, 98}, + {17, 8, 31, 1}, + {23, -2, 67, 98}, + {31, 12, 47, 1}, + {42, 16, 26, 1}, + {53, 20, 62, 1}, + {64, -1, 66, 98}, + {76, 24, 76, 1}, + {87, 28, 102, 1}, + {94, 32, 26, 1}, + {105, 36, 26, 1}, + {112, 40, 27, 1}, + {122, 44, 58, 1}, + {133, 48, 98, 1}, + {145, 52, 98, 1}, + {157, 56, 109, 1}, + {163, 60, 9, 1}, + {173, 64, 98, 1}, + {184, 68, 98, 1}, + {193, 72, 98, 1}, + {203, 76, 98, 1}, + {216, 80, 120, 1}, + {225, 84, 26, 1}, + {235, 88, 121, 1} +}; + +static const char gPathStrings[] = + "d\0" + "fillType\0" + "length" +; + +static const SkMemberInfo gDrawPathInfo[] = { + {0, 32, 108, 1}, + {2, -1, 67, 46}, + {11, -2, 67, 98} +}; + +static const char gUnknown5Strings[] = + "x\0" + "y\0" + "z" +; + +static const SkMemberInfo gUnknown5Info[] = { + {0, 0, 98, 1}, + {2, 4, 98, 1}, + {4, 8, 98, 1} +}; + +static const char gPointStrings[] = + "x\0" + "y" +; + +static const SkMemberInfo gDrawPointInfo[] = { + {0, 4, 98, 1}, + {2, 8, 98, 1} +}; + +static const char gPolyToPolyStrings[] = + "destination\0" + "source" +; + +static const SkMemberInfo gPolyToPolyInfo[] = { + {0, 12, 80, 1}, + {12, 8, 80, 1} +}; + +static const char gPolygonStrings[] = + "" +; + +static const SkMemberInfo gPolygonInfo[] = { + {0, 47, 18, 1} +}; + +static const char gPolylineStrings[] = + "points" +; + +static const SkMemberInfo gPolylineInfo[] = { + {0, 56, 119, 98} +}; + +static const char gPostStrings[] = + "delay\0" + "initialized\0" + "mode\0" + "sink\0" + "target\0" + "type" +; + +static const SkMemberInfo gPostInfo[] = { + {0, 4, 71, 1}, + {6, 8, 26, 1}, + {18, 12, 45, 1}, + {23, -1, 67, 108}, + {28, -2, 67, 108}, + {35, -3, 67, 108} +}; + +static const char gQuadToStrings[] = + "x1\0" + "x2\0" + "y1\0" + "y2" +; + +static const SkMemberInfo gQuadToInfo[] = { + {0, 8, 98, 1}, + {3, 16, 98, 1}, + {6, 12, 98, 1}, + {9, 20, 98, 1} +}; + +static const char gRCubicToStrings[] = + "" +; + +static const SkMemberInfo gRCubicToInfo[] = { + {0, 18, 18, 6} +}; + +static const char gRLineToStrings[] = + "" +; + +static const SkMemberInfo gRLineToInfo[] = { + {0, 34, 18, 2} +}; + +static const char gRMoveToStrings[] = + "" +; + +static const SkMemberInfo gRMoveToInfo[] = { + {0, 38, 18, 2} +}; + +static const char gRQuadToStrings[] = + "" +; + +static const SkMemberInfo gRQuadToInfo[] = { + {0, 49, 18, 4} +}; + +static const char gRadialGradientStrings[] = + "\0" + "center\0" + "radius" +; + +static const SkMemberInfo gRadialGradientInfo[] = { + {0, 26, 18, 3}, + {1, 48, 77, 2}, + {8, 56, 98, 1} +}; + +static const char gRandomStrings[] = + "blend\0" + "max\0" + "min\0" + "random\0" + "seed" +; + +static const SkMemberInfo gDisplayRandomInfo[] = { + {0, 4, 98, 1}, + {6, 12, 98, 1}, + {10, 8, 98, 1}, + {14, 1, 67, 98}, + {21, -2, 67, 96} +}; + +static const char gRectToRectStrings[] = + "destination\0" + "source" +; + +static const SkMemberInfo gRectToRectInfo[] = { + {0, 12, 91, 1}, + {12, 8, 91, 1} +}; + +static const char gRectangleStrings[] = + "bottom\0" + "height\0" + "left\0" + "needsRedraw\0" + "right\0" + "top\0" + "width" +; + +static const SkMemberInfo gRectangleInfo[] = { + {0, 24, 98, 1}, + {7, -1, 67, 98}, + {14, 12, 98, 1}, + {19, -2, 67, 26}, + {31, 20, 98, 1}, + {37, 16, 98, 1}, + {41, -3, 67, 98} +}; + +static const char gRemoveStrings[] = + "offset\0" + "where" +; + +static const SkMemberInfo gRemoveInfo[] = { + {0, 8, 96, 1}, + {7, 16, 37, 1} +}; + +static const char gReplaceStrings[] = + "" +; + +static const SkMemberInfo gReplaceInfo[] = { + {0, 1, 18, 4} +}; + +static const char gRotateStrings[] = + "center\0" + "degrees" +; + +static const SkMemberInfo gRotateInfo[] = { + {0, 12, 77, 2}, + {7, 8, 98, 1} +}; + +static const char gRoundRectStrings[] = + "\0" + "rx\0" + "ry" +; + +static const SkMemberInfo gRoundRectInfo[] = { + {0, 57, 18, 7}, + {1, 32, 98, 1}, + {4, 36, 98, 1} +}; + +static const char gS32Strings[] = + "value" +; + +static const SkMemberInfo gS32Info[] = { + {0, 4, 96, 1} +}; + +static const char gScalarStrings[] = + "value" +; + +static const SkMemberInfo gScalarInfo[] = { + {0, 4, 98, 1} +}; + +static const char gScaleStrings[] = + "center\0" + "x\0" + "y" +; + +static const SkMemberInfo gScaleInfo[] = { + {0, 16, 77, 2}, + {7, 8, 98, 1}, + {9, 12, 98, 1} +}; + +static const char gSetStrings[] = + "begin\0" + "dur\0" + "dynamic\0" + "field\0" + "formula\0" + "reset\0" + "target\0" + "to" +; + +static const SkMemberInfo gSetInfo[] = { + {0, 4, 71, 1}, + {6, 16, 71, 1}, + {10, -1, 67, 26}, + {18, 20, 108, 1}, + {24, 24, 40, 1}, + {32, -3, 67, 26}, + {38, 36, 40, 1}, + {45, 40, 40, 1} +}; + +static const char gShaderStrings[] = + "matrix\0" + "tileMode" +; + +static const SkMemberInfo gShaderInfo[] = { + {0, 8, 65, 1}, + {7, 12, 116, 1} +}; + +static const char gSkewStrings[] = + "center\0" + "x\0" + "y" +; + +static const SkMemberInfo gSkewInfo[] = { + {0, 16, 77, 2}, + {7, 8, 98, 1}, + {9, 12, 98, 1} +}; + +static const char g3D_CameraStrings[] = + "axis\0" + "hackHeight\0" + "hackWidth\0" + "location\0" + "observer\0" + "patch\0" + "zenith" +; + +static const SkMemberInfo g3D_CameraInfo[] = { + {0, 24, 106, 3}, + {5, 8, 98, 1}, + {16, 4, 98, 1}, + {26, 12, 106, 3}, + {35, 48, 106, 3}, + {44, 96, 105, 1}, + {50, 36, 106, 3} +}; + +static const char g3D_PatchStrings[] = + "origin\0" + "rotateDegrees\0" + "u\0" + "v" +; + +static const SkMemberInfo g3D_PatchInfo[] = { + {0, 28, 106, 3}, + {7, -1, 66, 98}, + {21, 4, 106, 3}, + {23, 16, 106, 3} +}; + +static const char gUnknown6Strings[] = + "x\0" + "y\0" + "z" +; + +static const SkMemberInfo gUnknown6Info[] = { + {0, 0, 98, 1}, + {2, 4, 98, 1}, + {4, 8, 98, 1} +}; + +static const char gSnapshotStrings[] = + "filename\0" + "quality\0" + "sequence\0" + "type" +; + +static const SkMemberInfo gSnapshotInfo[] = { + {0, 4, 108, 1}, + {9, 8, 98, 1}, + {17, 12, 26, 1}, + {26, 16, 20, 1} +}; + +static const char gStringStrings[] = + "length\0" + "slice\0" + "value" +; + +static const SkMemberInfo gStringInfo[] = { + {0, -1, 67, 96}, + {7, -1, 66, 108}, + {13, 4, 108, 1} +}; + +static const char gTextStrings[] = + "length\0" + "text\0" + "x\0" + "y" +; + +static const SkMemberInfo gTextInfo[] = { + {0, -1, 67, 96}, + {7, 12, 108, 1}, + {12, 16, 98, 1}, + {14, 20, 98, 1} +}; + +static const char gTextBoxStrings[] = + "\0" + "mode\0" + "spacingAdd\0" + "spacingAlign\0" + "spacingMul\0" + "text" +; + +static const SkMemberInfo gTextBoxInfo[] = { + {0, 57, 18, 7}, + {1, 44, 113, 1}, + {6, 40, 98, 1}, + {17, 48, 112, 1}, + {30, 36, 98, 1}, + {41, 32, 108, 1} +}; + +static const char gTextOnPathStrings[] = + "offset\0" + "path\0" + "text" +; + +static const SkMemberInfo gTextOnPathInfo[] = { + {0, 12, 98, 1}, + {7, 16, 74, 1}, + {12, 20, 110, 1} +}; + +static const char gTextToPathStrings[] = + "path\0" + "text" +; + +static const SkMemberInfo gTextToPathInfo[] = { + {0, 4, 74, 1}, + {5, 8, 110, 1} +}; + +static const char gTranslateStrings[] = + "x\0" + "y" +; + +static const SkMemberInfo gTranslateInfo[] = { + {0, 8, 98, 1}, + {2, 12, 98, 1} +}; + +static const char gTypedArrayStrings[] = + "length\0" + "values" +; + +static const SkMemberInfo gTypedArrayInfo[] = { + {0, -1, 67, 96}, + {7, 4, 119, 0} +}; + +static const char gTypefaceStrings[] = + "fontName" +; + +static const SkMemberInfo gTypefaceInfo[] = { + {0, 8, 108, 1} +}; + +static const SkMemberInfo* const gInfoTables[] = { + gMathInfo, + gAddInfo, + gAddCircleInfo, + gUnknown1Info, + gAddOvalInfo, + gAddPathInfo, + gAddRectangleInfo, + gAddRoundRectInfo, + gUnknown2Info, + gAnimateFieldInfo, + gApplyInfo, + gUnknown3Info, + gDrawBitmapInfo, + gDrawBitmapShaderInfo, + gDrawBlurInfo, + gDisplayBoundsInfo, + gDrawClipInfo, + gDrawColorInfo, + gCubicToInfo, + gDashInfo, + gDataInfo, + gDiscreteInfo, + gDrawToInfo, + gDrawEmbossInfo, + gDisplayEventInfo, + gFromPathInfo, + gUnknown4Info, + gGInfo, + gHitClearInfo, + gHitTestInfo, + gImageInfo, + gIncludeInfo, + gInputInfo, + gLineInfo, + gLineToInfo, + gLinearGradientInfo, + gDrawMatrixInfo, + gMoveInfo, + gMoveToInfo, + gMovieInfo, + gOvalInfo, + gDrawPaintInfo, + gDrawPathInfo, + gUnknown5Info, + gDrawPointInfo, + gPolyToPolyInfo, + gPolygonInfo, + gPolylineInfo, + gPostInfo, + gQuadToInfo, + gRCubicToInfo, + gRLineToInfo, + gRMoveToInfo, + gRQuadToInfo, + gRadialGradientInfo, + gDisplayRandomInfo, + gRectToRectInfo, + gRectangleInfo, + gRemoveInfo, + gReplaceInfo, + gRotateInfo, + gRoundRectInfo, + gS32Info, + gScalarInfo, + gScaleInfo, + gSetInfo, + gShaderInfo, + gSkewInfo, + g3D_CameraInfo, + g3D_PatchInfo, + gUnknown6Info, + gSnapshotInfo, + gStringInfo, + gTextInfo, + gTextBoxInfo, + gTextOnPathInfo, + gTextToPathInfo, + gTranslateInfo, + gTypedArrayInfo, + gTypefaceInfo, +}; + +static const unsigned char gInfoCounts[] = { + 26,4,4,1,1,2,5,3,13,1,13,2,6,3,2,2,2,8,6, + 2,2,2,2,4,8,3,3,2,1,4,3,1,3,4,2,2,12,1,2, + 1,1,25,3,3,2,2,1,1,6,4,1,1,1,1,3,5,2,7,2, + 1,2,3,1,1,3,8,2,3,7,4,3,4,3,4,6,3,2,2,2, + 1 +}; + +static const unsigned char gTypeIDs[] = { + 1, // Math + 2, // Add + 3, // AddCircle + 4, // Unknown1 + 5, // AddOval + 6, // AddPath + 7, // AddRectangle + 8, // AddRoundRect + 10, // Unknown2 + 11, // AnimateField + 12, // Apply + 17, // Unknown3 + 19, // Bitmap + 22, // BitmapShader + 23, // Blur + 25, // Bounds + 29, // Clip + 31, // Color + 32, // CubicTo + 33, // Dash + 34, // Data + 35, // Discrete + 38, // DrawTo + 41, // Emboss + 42, // Event + 48, // FromPath + 51, // Unknown4 + 52, // G + 53, // HitClear + 54, // HitTest + 55, // Image + 56, // Include + 57, // Input + 59, // Line + 60, // LineTo + 61, // LinearGradient + 65, // Matrix + 68, // Move + 69, // MoveTo + 70, // Movie + 72, // Oval + 73, // Paint + 74, // Path + 77, // Unknown5 + 78, // Point + 79, // PolyToPoly + 80, // Polygon + 81, // Polyline + 82, // Post + 83, // QuadTo + 84, // RCubicTo + 85, // RLineTo + 86, // RMoveTo + 87, // RQuadTo + 88, // RadialGradient + 89, // Random + 90, // RectToRect + 91, // Rectangle + 92, // Remove + 93, // Replace + 94, // Rotate + 95, // RoundRect + 96, // S32 + 98, // Scalar + 99, // Scale + 101, // Set + 102, // Shader + 103, // Skew + 104, // 3D_Camera + 105, // 3D_Patch + 106, // Unknown6 + 107, // Snapshot + 108, // String + 110, // Text + 111, // TextBox + 114, // TextOnPath + 115, // TextToPath + 117, // Translate + 119, // TypedArray + 120, // Typeface + +}; + +static const int kTypeIDs = 80; + +static const char* const gInfoNames[] = { + gMathStrings, + gAddStrings, + gAddCircleStrings, + gUnknown1Strings, + gAddOvalStrings, + gAddPathStrings, + gAddRectangleStrings, + gAddRoundRectStrings, + gUnknown2Strings, + gAnimateFieldStrings, + gApplyStrings, + gUnknown3Strings, + gBitmapStrings, + gBitmapShaderStrings, + gBlurStrings, + gBoundsStrings, + gClipStrings, + gColorStrings, + gCubicToStrings, + gDashStrings, + gDataStrings, + gDiscreteStrings, + gDrawToStrings, + gEmbossStrings, + gEventStrings, + gFromPathStrings, + gUnknown4Strings, + gGStrings, + gHitClearStrings, + gHitTestStrings, + gImageStrings, + gIncludeStrings, + gInputStrings, + gLineStrings, + gLineToStrings, + gLinearGradientStrings, + gMatrixStrings, + gMoveStrings, + gMoveToStrings, + gMovieStrings, + gOvalStrings, + gPaintStrings, + gPathStrings, + gUnknown5Strings, + gPointStrings, + gPolyToPolyStrings, + gPolygonStrings, + gPolylineStrings, + gPostStrings, + gQuadToStrings, + gRCubicToStrings, + gRLineToStrings, + gRMoveToStrings, + gRQuadToStrings, + gRadialGradientStrings, + gRandomStrings, + gRectToRectStrings, + gRectangleStrings, + gRemoveStrings, + gReplaceStrings, + gRotateStrings, + gRoundRectStrings, + gS32Strings, + gScalarStrings, + gScaleStrings, + gSetStrings, + gShaderStrings, + gSkewStrings, + g3D_CameraStrings, + g3D_PatchStrings, + gUnknown6Strings, + gSnapshotStrings, + gStringStrings, + gTextStrings, + gTextBoxStrings, + gTextOnPathStrings, + gTextToPathStrings, + gTranslateStrings, + gTypedArrayStrings, + gTypefaceStrings +}; +#endif +#endif + diff --git a/skia/animator/SkDisplayAdd.cpp b/skia/animator/SkDisplayAdd.cpp new file mode 100644 index 0000000..ac8d9c0 --- /dev/null +++ b/skia/animator/SkDisplayAdd.cpp @@ -0,0 +1,254 @@ +/* libs/graphics/animator/SkDisplayAdd.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkDisplayAdd.h" +#include "SkAnimateMaker.h" +#include "SkDisplayApply.h" +#include "SkDisplayList.h" +#include "SkDrawable.h" +#include "SkDrawGroup.h" + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkAdd::fInfo[] = { + SK_MEMBER(mode, AddMode), + SK_MEMBER(offset, Int), + SK_MEMBER(use, Drawable), + SK_MEMBER(where, Drawable) +}; + +#endif + +// start here; +// add onEndElement to turn where string into f_Where +// probably need new SkAnimateMaker::resolve flavor that takes +// where="id", where="event-target" or not-specified +// offset="#" (implements before, after, and index if no 'where') + +DEFINE_GET_MEMBER(SkAdd); + +SkAdd::SkAdd() : mode(kMode_indirect), + offset(SK_MaxS32), use(NULL), where(NULL) { +} + +SkDisplayable* SkAdd::deepCopy(SkAnimateMaker* maker) { + SkDrawable* saveUse = use; + SkDrawable* saveWhere = where; + use = NULL; + where = NULL; + SkAdd* copy = (SkAdd*) INHERITED::deepCopy(maker); + copy->use = use = saveUse; + copy->where = where = saveWhere; + return copy; +} + +bool SkAdd::draw(SkAnimateMaker& maker) { + SkASSERT(use); + SkASSERT(use->isDrawable()); + if (mode == kMode_indirect) + use->draw(maker); + return false; +} + +#ifdef SK_DUMP_ENABLED +void SkAdd::dump(SkAnimateMaker* maker) { + dumpBase(maker); + dumpAttrs(maker); + if (where) + SkDebugf("where=\"%s\" ", where->id); + if (mode == kMode_immediate) + SkDebugf("mode=\"immediate\" "); + SkDebugf(">\n"); + SkDisplayList::fIndent += 4; + int save = SkDisplayList::fDumpIndex; + if (use) //just in case + use->dump(maker); + SkDisplayList::fIndent -= 4; + SkDisplayList::fDumpIndex = save; + dumpEnd(maker); +} +#endif + +bool SkAdd::enable(SkAnimateMaker& maker ) { + SkDisplayTypes type = getType(); + SkDisplayList& displayList = maker.fDisplayList; + SkTDDrawableArray* parentList = displayList.getDrawList(); + if (type == SkType_Add) { + if (use == NULL) // not set in apply yet + return true; + } + bool skipAddToParent = true; + SkASSERT(type != SkType_Replace || where); + SkTDDrawableArray* grandList SK_INIT_TO_AVOID_WARNING; + SkGroup* parentGroup = NULL; + SkGroup* thisGroup = NULL; + int index = where ? displayList.findGroup(where, &parentList, &parentGroup, + &thisGroup, &grandList) : 0; + if (index < 0) + return true; + int max = parentList->count(); + if (where == NULL && type == SkType_Move) + index = max; + if (offset != SK_MaxS32) { + index += offset; + if (index > max) { + maker.setErrorCode(SkDisplayXMLParserError::kIndexOutOfRange); + return true; // caller should not add + } + } + if (offset < 0 && where == NULL) + index += max + 1; + switch (type) { + case SkType_Add: + if (offset == SK_MaxS32 && where == NULL) { + if (use->isDrawable()) { + skipAddToParent = mode == kMode_immediate; + if (skipAddToParent) { + if (where == NULL) { + SkTDDrawableArray* useParentList; + index = displayList.findGroup(this, &useParentList, &parentGroup, + &thisGroup, &grandList); + if (index >= 0) { + parentGroup->markCopySize(index); + parentGroup->markCopySet(index); + useParentList->begin()[index] = use; + break; + } + } + *parentList->append() = use; + } + } + break; + } else { + if (thisGroup) + thisGroup->markCopySize(index); + *parentList->insert(index) = use; + if (thisGroup) + thisGroup->markCopySet(index); + if (use->isApply()) + ((SkApply*) use)->setEmbedded(); + } + break; + case SkType_Move: { + int priorLocation = parentList->find(use); + if (priorLocation < 0) + break; + *parentList->insert(index) = use; + if (index < priorLocation) + priorLocation++; + parentList->remove(priorLocation); + } break; + case SkType_Remove: { + SkDisplayable* old = (*parentList)[index]; + if (((SkRemove*)(this))->fDelete) { + delete old; + goto noHelperNeeded; + } + for (int inner = 0; inner < maker.fChildren.count(); inner++) { + SkDisplayable* child = maker.fChildren[inner]; + if (child == old || child->contains(old)) + goto noHelperNeeded; + } + if (maker.fHelpers.find(old) < 0) + maker.helperAdd(old); +noHelperNeeded: + parentList->remove(index); + } break; + case SkType_Replace: + if (thisGroup) { + thisGroup->markCopySize(index); + if (thisGroup->markedForDelete(index)) { + SkDisplayable* old = (*parentList)[index]; + if (maker.fHelpers.find(old) < 0) + maker.helperAdd(old); + } + } + (*parentList)[index] = use; + if (thisGroup) + thisGroup->markCopySet(index); + break; + default: + SkASSERT(0); + } + if (type == SkType_Remove) + return true; + if (use->hasEnable()) + use->enable(maker); + return skipAddToParent; // append if indirect: *parentList->append() = this; +} + +bool SkAdd::hasEnable() const { + return true; +} + +void SkAdd::initialize() { + if (use) + use->initialize(); +} + +bool SkAdd::isDrawable() const { + return getType() == SkType_Add && mode == kMode_indirect && offset == SK_MaxS32 && + where == NULL && use != NULL && use->isDrawable(); +} + +//SkDisplayable* SkAdd::resolveTarget(SkAnimateMaker& maker) { +// return use; +//} + + +bool SkClear::enable(SkAnimateMaker& maker ) { + SkDisplayList& displayList = maker.fDisplayList; + displayList.clear(); + return true; +} + + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkMove::fInfo[] = { + SK_MEMBER_INHERITED +}; + +#endif + +DEFINE_GET_MEMBER(SkMove); + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkRemove::fInfo[] = { + SK_MEMBER_ALIAS(delete, fDelete, Boolean), // !!! experimental + SK_MEMBER(offset, Int), + SK_MEMBER(where, Drawable) +}; + +#endif + +DEFINE_GET_MEMBER(SkRemove); + +SkRemove::SkRemove() : fDelete(false) { +} + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkReplace::fInfo[] = { + SK_MEMBER_INHERITED +}; + +#endif + +DEFINE_GET_MEMBER(SkReplace); + diff --git a/skia/animator/SkDisplayAdd.h b/skia/animator/SkDisplayAdd.h new file mode 100644 index 0000000..8429fc8 --- /dev/null +++ b/skia/animator/SkDisplayAdd.h @@ -0,0 +1,81 @@ +/* libs/graphics/animator/SkDisplayAdd.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkDisplayAdd_DEFINED +#define SkDisplayAdd_DEFINED + +#include "SkDrawable.h" +#include "SkMemberInfo.h" + +class SkAdd : public SkDrawable { + DECLARE_MEMBER_INFO(Add); + SkAdd(); + + enum Mode { + kMode_indirect, + kMode_immediate + }; + + virtual SkDisplayable* deepCopy(SkAnimateMaker* ); + virtual bool draw(SkAnimateMaker& ); +#ifdef SK_DUMP_ENABLED + virtual void dump(SkAnimateMaker* ); +#endif + virtual bool enable(SkAnimateMaker& ); + virtual bool hasEnable() const; + virtual void initialize(); + virtual bool isDrawable() const; +protected: +// struct _A { + Mode mode; + int32_t offset; + SkDrawable* use; + SkDrawable* where; // if NULL, offset becomes index +// } A; +private: + typedef SkDrawable INHERITED; +}; + +class SkClear : public SkDisplayable { + virtual bool enable(SkAnimateMaker& ); +}; + +class SkMove : public SkAdd { + DECLARE_MEMBER_INFO(Move); +private: + typedef SkAdd INHERITED; +}; + +class SkRemove : public SkAdd { + DECLARE_MEMBER_INFO(Remove); + SkRemove(); +protected: + SkBool fDelete; +private: + friend class SkAdd; + typedef SkAdd INHERITED; +}; + +class SkReplace : public SkAdd { + DECLARE_MEMBER_INFO(Replace); +private: + typedef SkAdd INHERITED; +}; + +#endif // SkDisplayAdd_DEFINED + + diff --git a/skia/animator/SkDisplayApply.cpp b/skia/animator/SkDisplayApply.cpp new file mode 100644 index 0000000..8926299 --- /dev/null +++ b/skia/animator/SkDisplayApply.cpp @@ -0,0 +1,814 @@ +/* libs/graphics/animator/SkDisplayApply.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkDisplayApply.h" +#include "SkAnimateActive.h" +#include "SkAnimateMaker.h" +#include "SkAnimateSet.h" +#include "SkAnimatorScript.h" +#include "SkDisplayType.h" +#include "SkDrawGroup.h" +#include "SkParse.h" +#include "SkScript.h" +#include "SkSystemEventTypes.h" +#ifdef SK_DEBUG +#include "SkTime.h" +#endif +#include <ctype.h> + +enum SkApply_Properties { + SK_PROPERTY(animator), + SK_PROPERTY(step), + SK_PROPERTY(steps), + SK_PROPERTY(time) +}; + +#if SK_USE_CONDENSED_INFO == 0 + +// if no attibutes, enclosed displayable is both scope & target +// only if both scope & target are specified, or if target and enclosed displayable, are scope and target different +const SkMemberInfo SkApply::fInfo[] = { + SK_MEMBER_PROPERTY(animator, Animate), + SK_MEMBER(begin, MSec), + SK_MEMBER(dontDraw, Boolean), + SK_MEMBER(dynamicScope, String), + SK_MEMBER(interval, MSec), // recommended redraw interval + SK_MEMBER(mode, ApplyMode), +#if 0 + SK_MEMBER(pickup, Boolean), +#endif + SK_MEMBER(restore, Boolean), + SK_MEMBER(scope, Drawable), // thing that scopes animation (unnamed enclosed displayable goes here) + SK_MEMBER_PROPERTY(step, Int), + SK_MEMBER_PROPERTY(steps, Int), + SK_MEMBER_PROPERTY(time, MSec), + SK_MEMBER(transition, ApplyTransition) +}; + +#endif + +DEFINE_GET_MEMBER(SkApply); + +SkApply::SkApply() : begin(0), dontDraw(false), interval((SkMSec) -1), mode((Mode) -1), /*pickup(false), */ + restore(false), scope(NULL), steps(-1), transition((Transition) -1), fActive(NULL), /*fCurrentScope(NULL),*/ + fLastTime(0), fAppended(false), fContainsScope(false), fDeleteScope(false), fEmbedded(false), + fEnabled(false), fEnabling(false) { +} + +SkApply::~SkApply() { + for (SkDrawable** curPtr = fScopes.begin(); curPtr < fScopes.end(); curPtr++) + delete *curPtr; + if (fDeleteScope) + delete scope; + // !!! caller must call maker.removeActive(fActive) + delete fActive; +} + +void SkApply::activate(SkAnimateMaker& maker) { + if (fActive != NULL) { + if (fActive->fDrawIndex == 0 && fActive->fDrawMax == 0) + return; // if only one use, nothing more to do + if (restore == false) + return; // all share same state, regardless of instance number + bool save = fActive->initializeSave(); + fActive->fixInterpolator(save); + } else { + fActive = new SkActive(*this, maker); + fActive->init(); + maker.appendActive(fActive); + if (restore) { + fActive->initializeSave(); + int animators = fAnimators.count(); + for (int index = 0; index < animators; index++) + fActive->saveInterpolatorValues(index); + } + } +} + +void SkApply::append(SkApply* apply) { + if (fActive == NULL) + return; + int oldCount = fActive->fAnimators.count(); + fActive->append(apply); + if (restore) { + fActive->appendSave(oldCount); + int newCount = fActive->fAnimators.count(); + for (int index = oldCount; index < newCount; index++) + fActive->saveInterpolatorValues(index); + } +} + +void SkApply::applyValues(int animatorIndex, SkOperand* values, int count, + SkDisplayTypes valuesType, SkMSec time) +{ + SkAnimateBase* animator = fActive->fAnimators[animatorIndex]; + const SkMemberInfo * info = animator->fFieldInfo; + SkASSERT(animator); + SkASSERT(info != NULL); + SkDisplayTypes type = (SkDisplayTypes) info->fType; + SkDisplayable* target = getTarget(animator); + if (animator->hasExecute() || type == SkType_MemberFunction || type == SkType_MemberProperty) { + SkDisplayable* executor = animator->hasExecute() ? animator : target; + if (type != SkType_MemberProperty) { + SkTDArray<SkScriptValue> typedValues; + for (int index = 0; index < count; index++) { + SkScriptValue temp; + temp.fType = valuesType; + temp.fOperand = values[index]; + *typedValues.append() = temp; + } + executor->executeFunction(target, info->functionIndex(), typedValues, info->getType(), NULL); + } else { + SkScriptValue scriptValue; + scriptValue.fOperand = values[0]; + scriptValue.fType = info->getType(); + target->setProperty(info->propertyIndex(), scriptValue); + } + } else { + SkTypedArray converted; + if (type == SkType_ARGB) { + if (count == 4) { + // !!! assert that it is SkType_Float ? + animator->packARGB(&values->fScalar, count, &converted); + values = converted.begin(); + count = converted.count(); + } else + SkASSERT(count == 1); + } +// SkASSERT(type == SkType_ARGB || type == SkType_String ||info->isSettable()); + if (type == SkType_String || type == SkType_DynamicString) + info->setString(target, values->fString); + else if (type == SkType_Drawable || type == SkType_Displayable) + target->setReference(info, values->fDisplayable); + else + info->setValue(target, values, count); + } +} + +bool SkApply::contains(SkDisplayable* child) { + for (SkDrawable** curPtr = fScopes.begin(); curPtr < fScopes.end(); curPtr++) { + if (*curPtr == child || (*curPtr)->contains(child)) + return true; + } + return fDeleteScope && scope == child; +} + +SkDisplayable* SkApply::deepCopy(SkAnimateMaker* maker) { + SkDrawable* saveScope = scope; + scope = NULL; + SkApply* result = (SkApply*) INHERITED::deepCopy(maker); + result->scope = scope = saveScope; + SkAnimateBase** end = fAnimators.end(); + for (SkAnimateBase** animPtr = fAnimators.begin(); animPtr < end; animPtr++) { + SkAnimateBase* anim = (SkAnimateBase*) (*animPtr)->deepCopy(maker); + *result->fAnimators.append() = anim; + maker->helperAdd(anim); + } + return result; +} + +void SkApply::disable() { + //!!! this is the right thing to do, but has bad side effects because of other problems + // currently, if an apply is in a g and scopes a statement in another g, it ends up as members + // of both containers. The disabling here incorrectly disables both instances + // maybe the fEnabled flag needs to be moved to the fActive data so that both + // instances are not affected. +// fEnabled = false; +} + +bool SkApply::draw(SkAnimateMaker& maker) { + if (scope ==NULL) + return false; + if (scope->isApply() || scope->isDrawable() == false) + return false; + if (fEnabled == false) + enable(maker); + SkASSERT(scope); + activate(maker); + if (mode == kMode_immediate) + return fActive->draw(); + bool result = interpolate(maker, maker.getInTime()); + if (dontDraw == false) { +// if (scope->isDrawable()) + result |= scope->draw(maker); + } + if (restore) { + for (int index = 0; index < fActive->fAnimators.count(); index++) + endSave(index); + fActive->advance(); + } + return result; +} + +#ifdef SK_DUMP_ENABLED +void SkApply::dump(SkAnimateMaker* maker) { + dumpBase(maker); + if (dynamicScope.isEmpty() == false) + SkDebugf("dynamicScope=\"%s\" ", dynamicScope.c_str()); + if (dontDraw) + SkDebugf("dontDraw=\"true\" "); + if (begin != 0) //perhaps we want this no matter what? + SkDebugf("begin=\"%g\" ", (float) begin/1000.0f); //is this correct? + if (interval != (SkMSec) -1) + SkDebugf("interval=\"%g\" ", (float) interval/1000.0f); + if (steps != -1) + SkDebugf("steps=\"%d\" ", steps); + if (restore) + SkDebugf("restore=\"true\" "); + if (transition == kTransition_reverse) + SkDebugf("transition=\"reverse\" "); + if (mode == kMode_immediate) { + SkDebugf("mode=\"immediate\" "); + } + else if (mode == kMode_create) { + SkDebugf("mode=\"create\" "); + } + bool closedYet = false; + SkDisplayList::fIndent += 4; + int save = SkDisplayList::fDumpIndex; + if (scope) { + if (closedYet == false) { + SkDebugf(">\n"); + closedYet = true; + } + scope->dump(maker); + } + int index; +// if (fActive) { + for (index = 0; index < fAnimators.count(); index++) { + if (closedYet == false) { + SkDebugf(">\n"); + closedYet = true; + } + SkAnimateBase* animator = fAnimators[index]; + animator->dump(maker); +// } + } + SkDisplayList::fIndent -= 4; + SkDisplayList::fDumpIndex = save; + if (closedYet) + dumpEnd(maker); + else + SkDebugf("/>\n"); +} +#endif + +bool SkApply::enable(SkAnimateMaker& maker) { + fEnabled = true; + bool initialized = fActive != NULL; + if (dynamicScope.size() > 0) + enableDynamic(maker); + if (maker.fError.hasError()) + return false; + int animators = fAnimators.count(); + int index; + for (index = 0; index < animators; index++) { + SkAnimateBase* animator = fAnimators[index]; + animator->fStart = maker.fEnableTime; + animator->fResetPending = animator->fReset; + } + if (scope && scope->isApply()) + ((SkApply*) scope)->setEmbedded(); +/* if (mode == kMode_once) { + if (scope) { + activate(maker); + interpolate(maker, maker.fEnableTime); + inactivate(maker); + } + return true; + }*/ + if ((mode == kMode_immediate || mode == kMode_create) && scope == NULL) + return false; // !!! error? + bool enableMe = scope && (scope->hasEnable() || scope->isApply() || scope->isDrawable() == false); + if (mode == kMode_immediate && enableMe || mode == kMode_create) + activate(maker); // for non-drawables like post, prime them here + if (mode == kMode_immediate && enableMe) + fActive->enable(); + if (mode == kMode_create && scope != NULL) { + enableCreate(maker); + return true; + } + if (mode == kMode_immediate) { + return scope->isApply() || scope->isDrawable() == false; + } + refresh(maker); + SkDisplayList& displayList = maker.fDisplayList; + SkDrawable* drawable; +#if defined SK_DEBUG && defined SK_DEBUG_ANIMATION_TIMING + SkString debugOut; + SkMSec time = maker.getAppTime(); + debugOut.appendS32(time - maker.fDebugTimeBase); + debugOut.append(" apply enable id="); + debugOut.append(_id); + debugOut.append("; start="); + debugOut.appendS32(maker.fEnableTime - maker.fDebugTimeBase); + SkDebugf("%s\n", debugOut.c_str()); +#endif + if (scope == NULL || scope->isApply() || scope->getType() == SkType_Movie || scope->isDrawable() == false) { + activate(maker); // for non-drawables like post, prime them here + if (initialized) { + append(this); + } + fEnabling = true; + interpolate(maker, maker.fEnableTime); + fEnabling = false; + if (scope != NULL && dontDraw == false) + scope->enable(maker); + return true; + } else if (initialized && restore == false) + append(this); +#if 0 + bool wasActive = inactivate(maker); // start fresh + if (wasActive) { + activate(maker); + interpolate(maker, maker.fEnableTime); + return true; + } +#endif +// start here; + // now that one apply might embed another, only the parent apply should replace the scope + // or get appended to the display list + // similarly, an apply added by an add immediate has already been located in the display list + // and should not get moved or added again here + if (fEmbedded) { + return false; // already added to display list by embedder + } + drawable = (SkDrawable*) scope; + SkTDDrawableArray* parentList; + SkTDDrawableArray* grandList; + SkGroup* parentGroup; + SkGroup* thisGroup; + int old = displayList.findGroup(drawable, &parentList, &parentGroup, &thisGroup, &grandList); + if (old < 0) + goto append; + else if (fContainsScope) { + if ((*parentList)[old] != this || restore == true) { +append: + if (parentGroup) + parentGroup->markCopySize(old); + if (parentList->count() < 10000) { + fAppended = true; + *parentList->append() = this; + } else + maker.setErrorCode(SkDisplayXMLParserError::kDisplayTreeTooDeep); + old = -1; + } else + reset(); + } else { + SkASSERT(old < parentList->count()); + if ((*parentList)[old]->isApply()) { + SkApply* apply = (SkApply*) (*parentList)[old]; + if (apply != this && apply->fActive == NULL) + apply->activate(maker); + apply->append(this); + parentGroup = NULL; + } else { + if (parentGroup) + parentGroup->markCopySize(old); + SkDrawable** newApplyLocation = &(*parentList)[old]; + SkGroup* pGroup; + int oldApply = displayList.findGroup(this, &parentList, &pGroup, &thisGroup, &grandList); + if (oldApply >= 0) { + (*parentList)[oldApply] = (SkDrawable*) SkDisplayType::CreateInstance(&maker, SkType_Apply); + parentGroup = NULL; + fDeleteScope = true; + } + *newApplyLocation = this; + } + } + if (parentGroup) { + parentGroup->markCopySet(old); + fDeleteScope = dynamicScope.size() == 0; + } + return true; +} + +void SkApply::enableCreate(SkAnimateMaker& maker) { + SkString newID; + for (int step = 0; step <= steps; step++) { + fLastTime = step * SK_MSec1; + bool success = maker.computeID(scope, this, &newID); + if (success == false) + return; + if (maker.find(newID.c_str(), NULL)) + continue; + SkApply* copy = (SkApply*) deepCopy(&maker); // work on copy of animator state + if (mode == kMode_create) + copy->mode = (Mode) -1; + SkDrawable* copyScope = copy->scope = (SkDrawable*) scope->deepCopy(&maker); + *fScopes.append() = copyScope; + if (copyScope->resolveIDs(maker, scope, this)) { + step = steps; // quit + goto next; // resolveIDs failed + } + if (newID.size() > 0) + maker.setID(copyScope, newID); + if (copy->resolveIDs(maker, this, this)) { // fix up all fields, including target + step = steps; // quit + goto next; // resolveIDs failed + } + copy->activate(maker); + copy->interpolate(maker, step * SK_MSec1); + maker.removeActive(copy->fActive); + next: + delete copy; + } +} + +void SkApply::enableDynamic(SkAnimateMaker& maker) { + SkASSERT(mode != kMode_create); // create + dynamic are not currently compatible + SkDisplayable* newScope; + bool success = SkAnimatorScript::EvaluateDisplayable(maker, this, dynamicScope.c_str(), + &newScope); + if (success && scope != newScope) { + SkTDDrawableArray* pList, * gList; + SkGroup* pGroup = NULL, * found = NULL; + int old = maker.fDisplayList.findGroup(scope, &pList, &pGroup, &found, &gList); + if (pList && old >= 0 && (*pList)[old]->isApply() && (*pList)[old] != this) { + if (fAppended == false) { + if (found != NULL) { + SkDisplayable* oldChild = (*pList)[old]; + if (oldChild->isApply() && found->copySet(old)) { + found->markCopyClear(old); + // delete oldChild; + } + } + (*pList)[old] = scope; + } else + pList->remove(old); + } + scope = (SkDrawable*) newScope; + onEndElement(maker); + } + maker.removeActive(fActive); + delete fActive; + fActive = NULL; +} + +void SkApply::endSave(int index) { + SkAnimateBase* animate = fActive->fAnimators[index]; + const SkMemberInfo* info = animate->fFieldInfo; + SkDisplayTypes type = (SkDisplayTypes) info->fType; + if (type == SkType_MemberFunction) + return; + SkDisplayable* target = getTarget(animate); + size_t size = info->getSize(target); + int count = (int) (size / sizeof(SkScalar)); + int activeIndex = fActive->fDrawIndex + index; + SkOperand* last = new SkOperand[count]; + SkAutoTDelete<SkOperand> autoLast(last); + if (type != SkType_MemberProperty) { + info->getValue(target, last, count); + SkOperand* saveOperand = fActive->fSaveRestore[activeIndex]; + if (saveOperand) + info->setValue(target, fActive->fSaveRestore[activeIndex], count); + } else { + SkScriptValue scriptValue; + bool success = target->getProperty(info->propertyIndex(), &scriptValue); + SkASSERT(success = true); + last[0] = scriptValue.fOperand; + scriptValue.fOperand = fActive->fSaveRestore[activeIndex][0]; + target->setProperty(info->propertyIndex(), scriptValue); + } + SkOperand* save = fActive->fSaveRestore[activeIndex]; + if (save) + memcpy(save, last, count * sizeof(SkOperand)); +} + +bool SkApply::getProperty(int index, SkScriptValue* value) const { + switch (index) { + case SK_PROPERTY(step): + value->fType = SkType_Int; + value->fOperand.fS32 = fLastTime / SK_MSec1; + break; + case SK_PROPERTY(steps): + value->fType = SkType_Int; + value->fOperand.fS32 = steps; + break; + case SK_PROPERTY(time): + value->fType = SkType_MSec; + value->fOperand.fS32 = fLastTime; + break; + default: + // SkASSERT(0); + return false; + } + return true; +} + +void SkApply::getStep(SkScriptValue* value) { + getProperty(SK_PROPERTY(step), value); +} + +SkDrawable* SkApply::getTarget(SkAnimateBase* animate) { + if (animate->fTargetIsScope == false || mode != kMode_create) + return animate->fTarget; + return scope; +} + +bool SkApply::hasDelayedAnimator() const { + SkAnimateBase** animEnd = fAnimators.end(); + for (SkAnimateBase** animPtr = fAnimators.begin(); animPtr < animEnd; animPtr++) { + SkAnimateBase* animator = *animPtr; + if (animator->fDelayed) + return true; + } + return false; +} + +bool SkApply::hasEnable() const { + return true; +} + +bool SkApply::inactivate(SkAnimateMaker& maker) { + if (fActive == NULL) + return false; + maker.removeActive(fActive); + delete fActive; + fActive = NULL; + return true; +} + +#ifdef SK_DEBUG +SkMSec lastTime = (SkMSec) -1; +#endif + +bool SkApply::interpolate(SkAnimateMaker& maker, SkMSec rawTime) { + if (fActive == NULL) + return false; + bool result = false; +#if defined SK_DEBUG && defined SK_DEBUG_ANIMATION_TIMING + SkMSec time = maker.getAppTime(); + if (lastTime == (SkMSec) -1) + lastTime = rawTime - 1; + if (fActive != NULL && + strcmp(id, "a3") == 0 && rawTime > lastTime) { + lastTime += 1000; + SkString debugOut; + debugOut.appendS32(time - maker.fDebugTimeBase); + debugOut.append(" apply id="); + debugOut.append(_id); + debugOut.append("; "); + debugOut.append(fActive->fAnimators[0]->_id); + debugOut.append("="); + debugOut.appendS32(rawTime - fActive->fState[0].fStartTime); + debugOut.append(")"); + SkDebugf("%s\n", debugOut.c_str()); + } +#endif + fActive->start(); + if (restore) + fActive->initializeSave(); + int animators = fActive->fAnimators.count(); + for (int inner = 0; inner < animators; inner++) { + SkAnimateBase* animate = fActive->fAnimators[inner]; + if (animate->fChanged) { + animate->fChanged = false; + animate->fStart = rawTime; + // SkTypedArray values; + // int count = animate->fValues.count(); + // values.setCount(count); + // memcpy(values.begin(), animate->fValues.begin(), sizeof(SkOperand) * count); + animate->onEndElement(maker); + // if (memcmp(values.begin(), animate->fValues.begin(), sizeof(SkOperand) * count) != 0) { + fActive->append(this); + fActive->start(); + // } + } + SkMSec time = fActive->getTime(rawTime, inner); + SkActive::SkState& state = fActive->fState[inner]; + if (SkMSec_LT(rawTime, state.fStartTime)) { + if (fEnabling) { + animate->fDelayed = true; + maker.delayEnable(this, state.fStartTime); + } + continue; + } else + animate->fDelayed = false; + SkMSec innerTime = fLastTime = state.getRelativeTime(time); + if (restore) + fActive->restoreInterpolatorValues(inner); + if (animate->fReset) { + if (transition != SkApply::kTransition_reverse) { + if (SkMSec_LT(state.fBegin + state.fDuration, innerTime)) { + if (animate->fResetPending) { + innerTime = 0; + animate->fResetPending = false; + } else + continue; + } + } else if (innerTime == 0) { + if (animate->fResetPending) { + innerTime = state.fBegin + state.fDuration; + animate->fResetPending = false; + } else + continue; + } + } + int count = animate->components(); + SkAutoSTMalloc<16, SkOperand> values(count); + SkInterpolatorBase::Result interpResult = fActive->fInterpolators[inner]->timeToValues( + innerTime, values.get()); + result |= (interpResult != SkInterpolatorBase::kFreezeEnd_Result); + if ((transition != SkApply::kTransition_reverse && interpResult == SkInterpolatorBase::kFreezeEnd_Result || + transition == SkApply::kTransition_reverse && fLastTime == 0) && state.fUnpostedEndEvent) { +// SkDEBUGF(("interpolate: post on end\n")); + state.fUnpostedEndEvent = false; + maker.postOnEnd(animate, state.fBegin + state.fDuration); + maker.fAdjustedStart = 0; // !!! left over from synchronizing animation days, undoubtably out of date (and broken) + } + if (animate->formula.size() > 0) { + if (fLastTime > animate->dur) + fLastTime = animate->dur; + SkTypedArray formulaValues; + formulaValues.setCount(count); + bool success = animate->fFieldInfo->setValue(maker, &formulaValues, 0, 0, NULL, + animate->getValuesType(), animate->formula); + SkASSERT(success); + if (restore) + save(inner); // save existing value + applyValues(inner, formulaValues.begin(), count, animate->getValuesType(), innerTime); + } else { + if (restore) + save(inner); // save existing value + applyValues(inner, values.get(), count, animate->getValuesType(), innerTime); + } + } + return result; +} + +void SkApply::initialize() { + if (scope == NULL) + return; + if (scope->isApply() || scope->isDrawable() == false) + return; + scope->initialize(); +} + +void SkApply::onEndElement(SkAnimateMaker& maker) +{ + SkDrawable* scopePtr = scope; + while (scopePtr && scopePtr->isApply()) { + SkApply* scopedApply = (SkApply*) scopePtr; + if (scopedApply->scope == this) { + maker.setErrorCode(SkDisplayXMLParserError::kApplyScopesItself); + return; + } + scopePtr = scopedApply->scope; + } + if (mode == kMode_create) + return; + if (scope != NULL && steps >= 0 && scope->isApply() == false && scope->isDrawable()) + scope->setSteps(steps); + for (SkAnimateBase** animPtr = fAnimators.begin(); animPtr < fAnimators.end(); animPtr++) { + SkAnimateBase* anim = *animPtr; + //for reusing apply statements with dynamic scope + if (anim->fTarget == NULL || anim->fTargetIsScope) { + anim->fTargetIsScope = true; + if (scope) + anim->fTarget = scope; + else + anim->setTarget(maker); + anim->onEndElement(maker); // allows animate->fFieldInfo to be set + } + if (scope != NULL && steps >= 0 && anim->fTarget != scope && anim->fTarget->isDrawable()) + anim->fTarget->setSteps(steps); + } +} + +const SkMemberInfo* SkApply::preferredChild(SkDisplayTypes type) { + SkASSERT(SkDisplayType::IsAnimate(type) == false); + fContainsScope = true; + return getMember("scope"); // !!! cwap! need to refer to member through enum like kScope instead +} + +void SkApply::refresh(SkAnimateMaker& maker) { + for (SkAnimateBase** animPtr = fAnimators.begin(); animPtr < fAnimators.end(); animPtr++) { + SkAnimateBase* animate = *animPtr; + animate->onEndElement(maker); + } + if (fActive) + fActive->resetInterpolators(); +} + +void SkApply::reset() { + if (fActive) + fActive->resetState(); +} + +bool SkApply::resolveIDs(SkAnimateMaker& maker, SkDisplayable* original, SkApply* apply) { // replace to/formula strings in animators of the form xxx.step with the step value, if xxx.step is in scope + if (resolveField(maker, apply, &dynamicScope) == false) + return true; // failed + SkAnimateBase** endPtr = fAnimators.end(); + SkAnimateBase** origPtr = ((SkApply*) original)->fAnimators.begin(); + for (SkAnimateBase** animPtr = fAnimators.begin(); animPtr < endPtr; ) { + SkAnimateBase* animator = *animPtr++; + maker.resolveID(animator, *origPtr++); + if (resolveField(maker, this, &animator->target) == false) + return true; + if (resolveField(maker, this, &animator->from) == false) + return true; + if (resolveField(maker, this, &animator->to) == false) + return true; + if (resolveField(maker, this, &animator->formula) == false) + return true; + } +// setEmbedded(); + onEndElement(maker); + return false; // succeeded +} + +bool SkApply::resolveField(SkAnimateMaker& maker, SkDisplayable* parent, SkString* str) { + const char* script = str->c_str(); + if (str->startsWith("#string:") == false) + return true; + script += sizeof("#string:") - 1; + return SkAnimatorScript::EvaluateString(maker, this, parent, script, str); +} + +void SkApply::save(int index) { + SkAnimateBase* animate = fActive->fAnimators[index]; + const SkMemberInfo * info = animate->fFieldInfo; + SkDisplayable* target = getTarget(animate); +// if (animate->hasExecute()) +// info = animate->getResolvedInfo(); + SkDisplayTypes type = (SkDisplayTypes) info->fType; + if (type == SkType_MemberFunction) + return; // nothing to save + size_t size = info->getSize(target); + int count = (int) (size / sizeof(SkScalar)); + bool useLast = true; +// !!! this all may be unneeded, at least in the dynamic case ?? + int activeIndex = fActive->fDrawIndex + index; + SkTDOperandArray last; + if (fActive->fSaveRestore[activeIndex] == NULL) { + fActive->fSaveRestore[activeIndex] = new SkOperand[count]; + useLast = false; + } else { + last.setCount(count); + memcpy(last.begin(), fActive->fSaveRestore[activeIndex], count * sizeof(SkOperand)); + } + if (type != SkType_MemberProperty) { + info->getValue(target, fActive->fSaveRestore[activeIndex], count); + if (useLast) + info->setValue(target, last.begin(), count); + } else { + SkScriptValue scriptValue; + bool success = target->getProperty(info->propertyIndex(), &scriptValue); + SkASSERT(success == true); + SkASSERT(scriptValue.fType == SkType_Float); + fActive->fSaveRestore[activeIndex][0] = scriptValue.fOperand; + if (useLast) { + SkScriptValue scriptValue; + scriptValue.fType = type; + scriptValue.fOperand = last[0]; + target->setProperty(info->propertyIndex(), scriptValue); + } + } +// !!! end of unneeded +} + +bool SkApply::setProperty(int index, SkScriptValue& scriptValue) { + switch (index) { + case SK_PROPERTY(animator): { + SkAnimateBase* animate = (SkAnimateBase*) scriptValue.fOperand.fDisplayable; + SkASSERT(animate->isAnimate()); + *fAnimators.append() = animate; + return true; + } + case SK_PROPERTY(steps): + steps = scriptValue.fOperand.fS32; + if (fActive) + fActive->setSteps(steps); + return true; + } + return false; +} + +void SkApply::setSteps(int _steps) { + steps = _steps; +} + +#ifdef SK_DEBUG +void SkApply::validate() { + if (fActive) + fActive->validate(); +} +#endif + + + diff --git a/skia/animator/SkDisplayApply.h b/skia/animator/SkDisplayApply.h new file mode 100644 index 0000000..208caa2 --- /dev/null +++ b/skia/animator/SkDisplayApply.h @@ -0,0 +1,116 @@ +/* libs/graphics/animator/SkDisplayApply.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkDisplayApply_DEFINED +#define SkDisplayApply_DEFINED + +#include "SkAnimateBase.h" +#include "SkDrawable.h" +#include "SkIntArray.h" + +class SkActive; + +class SkApply : public SkDrawable { + DECLARE_MEMBER_INFO(Apply); +public: + + SkApply(); + virtual ~SkApply(); + + enum Transition { + kTransition_normal, + kTransition_reverse + }; + + enum Mode { + kMode_create, + kMode_immediate, + //kMode_once + }; + void activate(SkAnimateMaker& ); + void append(SkApply* apply); + void appendActive(SkActive* ); + void applyValues(int animatorIndex, SkOperand* values, int count, + SkDisplayTypes , SkMSec time); + virtual bool contains(SkDisplayable*); +// void createActive(SkAnimateMaker& ); + virtual SkDisplayable* deepCopy(SkAnimateMaker* ); + void disable(); + virtual bool draw(SkAnimateMaker& ); +#ifdef SK_DUMP_ENABLED + virtual void dump(SkAnimateMaker* ); +#endif + virtual bool enable(SkAnimateMaker& ); + void enableCreate(SkAnimateMaker& ); + void enableDynamic(SkAnimateMaker& ); + void endSave(int index); + Mode getMode() { return mode; } + virtual bool getProperty(int index, SkScriptValue* value) const; + SkDrawable* getScope() { return scope; } + void getStep(SkScriptValue* ); + SkDrawable* getTarget(SkAnimateBase* ); + bool hasDelayedAnimator() const; + virtual bool hasEnable() const; + bool inactivate(SkAnimateMaker& maker); + virtual void initialize(); + bool interpolate(SkAnimateMaker& , SkMSec time); + virtual void onEndElement(SkAnimateMaker& ); + virtual const SkMemberInfo* preferredChild(SkDisplayTypes type); + void refresh(SkAnimateMaker& ); + void reset(); + virtual bool resolveIDs(SkAnimateMaker& maker, SkDisplayable* original, SkApply* ); + bool resolveField(SkAnimateMaker& , SkDisplayable* parent, SkString* str); + void save(int index); + void setEmbedded() { fEmbedded = true; } + virtual bool setProperty(int index, SkScriptValue& ); + virtual void setSteps(int _steps); +// virtual void setTime(SkMSec time); +#ifdef SK_DEBUG + virtual void validate(); +#endif +private: + SkMSec begin; + SkBool dontDraw; + SkString dynamicScope; + SkMSec interval; + Mode mode; +#if 0 + SkBool pickup; +#endif + SkBool restore; + SkDrawable* scope; + int32_t steps; + Transition transition; + SkActive* fActive; + SkTDAnimateArray fAnimators; +// SkDrawable* fCurrentScope; + SkMSec fLastTime; // used only to return script property time + SkTDDrawableArray fScopes; + SkBool fAppended : 1; + SkBool fContainsScope : 1; + SkBool fDeleteScope : 1; + SkBool fEmbedded : 1; + SkBool fEnabled : 1; + SkBool fEnabling : 1; // set if calling interpolate from enable + friend class SkActive; + friend class SkDisplayList; + typedef SkDrawable INHERITED; +}; + +#endif // SkDisplayApply_DEFINED + + diff --git a/skia/animator/SkDisplayBounds.cpp b/skia/animator/SkDisplayBounds.cpp new file mode 100644 index 0000000..03d7366 --- /dev/null +++ b/skia/animator/SkDisplayBounds.cpp @@ -0,0 +1,54 @@ +/* libs/graphics/animator/SkDisplayBounds.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkDisplayBounds.h" +#include "SkAnimateMaker.h" + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkDisplayBounds::fInfo[] = { + SK_MEMBER_INHERITED, + SK_MEMBER(inval, Boolean) +}; + +#endif + +DEFINE_GET_MEMBER(SkDisplayBounds); + +SkDisplayBounds::SkDisplayBounds() : inval(false) { +} + +bool SkDisplayBounds::draw(SkAnimateMaker& maker) { + maker.fDisplayList.fUnionBounds = SkToBool(inval); + maker.fDisplayList.fDrawBounds = false; + fBounds.setEmpty(); + bool result = INHERITED::draw(maker); + maker.fDisplayList.fUnionBounds = false; + maker.fDisplayList.fDrawBounds = true; + if (inval && fBounds.isEmpty() == false) { + SkIRect& rect = maker.fDisplayList.fInvalBounds; + maker.fDisplayList.fHasUnion = true; + if (rect.isEmpty()) + rect = fBounds; + else + rect.join(fBounds); + } + return result; +} + + + diff --git a/skia/animator/SkDisplayBounds.h b/skia/animator/SkDisplayBounds.h new file mode 100644 index 0000000..fdfb78b --- /dev/null +++ b/skia/animator/SkDisplayBounds.h @@ -0,0 +1,33 @@ +/* libs/graphics/animator/SkDisplayBounds.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkDisplayBounds_DEFINED +#define SkDisplayBounds_DEFINED + +#include "SkDrawRectangle.h" + +class SkDisplayBounds : public SkDrawRect { + DECLARE_DISPLAY_MEMBER_INFO(Bounds); + SkDisplayBounds(); + virtual bool draw(SkAnimateMaker& ); +private: + SkBool inval; + typedef SkDrawRect INHERITED; +}; + +#endif // SkDisplayBounds_DEFINED + diff --git a/skia/animator/SkDisplayEvent.cpp b/skia/animator/SkDisplayEvent.cpp new file mode 100644 index 0000000..4d27158 --- /dev/null +++ b/skia/animator/SkDisplayEvent.cpp @@ -0,0 +1,339 @@ +/* libs/graphics/animator/SkDisplayEvent.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkDisplayEvent.h" +#include "SkAnimateMaker.h" +#include "SkDisplayApply.h" +#include "SkDisplayInput.h" +#include "SkDisplayList.h" +#ifdef SK_DEBUG +#include "SkDump.h" +#endif +#include "SkEvent.h" +#include "SkDisplayInput.h" +#include "SkKey.h" +#include "SkMetaData.h" +#include "SkScript.h" +#include "SkUtils.h" + +enum SkDisplayEvent_Properties { + SK_PROPERTY(key), + SK_PROPERTY(keys) +}; + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkDisplayEvent::fInfo[] = { + SK_MEMBER(code, EventCode), + SK_MEMBER(disable, Boolean), + SK_MEMBER_PROPERTY(key, String), // a single key (also last key pressed) + SK_MEMBER_PROPERTY(keys, String), // a single key or dash-delimited range of keys + SK_MEMBER(kind, EventKind), + SK_MEMBER(target, String), + SK_MEMBER(x, Float), + SK_MEMBER(y, Float) +}; + +#endif + +DEFINE_GET_MEMBER(SkDisplayEvent); + +SkDisplayEvent::SkDisplayEvent() : code((SkKey) -1), disable(false), + kind(kUser), x(0), y(0), fLastCode((SkKey) -1), fMax((SkKey) -1), fTarget(NULL) { +} + +SkDisplayEvent::~SkDisplayEvent() { + deleteMembers(); +} + +bool SkDisplayEvent::add(SkAnimateMaker& , SkDisplayable* child) { + *fChildren.append() = child; + return true; +} + +bool SkDisplayEvent::contains(SkDisplayable* match) { + for (int index = 0; index < fChildren.count(); index++) { + if (fChildren[index] == match || fChildren[index]->contains(match)) + return true; + } + return false; +} + +SkDisplayable* SkDisplayEvent::contains(const SkString& match) { + for (int index = 0; index < fChildren.count(); index++) { + SkDisplayable* child = fChildren[index]; + if (child->contains(match)) + return child; + } + return NULL; +} + +void SkDisplayEvent::deleteMembers() { + for (int index = 0; index < fChildren.count(); index++) { + SkDisplayable* evt = fChildren[index]; + delete evt; + } +} + +#ifdef SK_DUMP_ENABLED +void SkDisplayEvent::dumpEvent(SkAnimateMaker* maker) { + dumpBase(maker); + SkString str; + SkDump::GetEnumString(SkType_EventKind, kind, &str); + SkDebugf("kind=\"%s\" ", str.c_str()); + if (kind == SkDisplayEvent::kKeyPress || kind == SkDisplayEvent::kKeyPressUp) { + if (code >= 0) + SkDump::GetEnumString(SkType_EventCode, code, &str); + else + str.set("none"); + SkDebugf("code=\"%s\" ", str.c_str()); + } + if (kind == SkDisplayEvent::kKeyChar) { + if (fMax != (SkKey) -1 && fMax != code) + SkDebugf("keys=\"%c - %c\" ", code, fMax); + else + SkDebugf("key=\"%c\" ", code); + } + if (fTarget != NULL) { + SkDebugf("target=\"%s\" ", fTarget->id); + } + if (kind >= SkDisplayEvent::kMouseDown && kind <= SkDisplayEvent::kMouseUp) { +#ifdef SK_CAN_USE_FLOAT + SkDebugf("x=\"%g\" y=\"%g\" ", SkScalarToFloat(x), SkScalarToFloat(y)); +#else + SkDebugf("x=\"%x\" y=\"%x\" ", x, y); +#endif + } + if (disable) + SkDebugf("disable=\"true\" "); + SkDebugf("/>\n"); +} +#endif + +bool SkDisplayEvent::enableEvent(SkAnimateMaker& maker) +{ + maker.fActiveEvent = this; + if (fChildren.count() == 0) + return false; + if (disable) + return false; +#ifdef SK_DUMP_ENABLED + if (maker.fDumpEvents) { + SkDebugf("enable: "); + dumpEvent(&maker); + } +#endif + SkDisplayList& displayList = maker.fDisplayList; + for (int index = 0; index < fChildren.count(); index++) { + SkDisplayable* displayable = fChildren[index]; + if (displayable->isGroup()) { + SkTDDrawableArray* parentList = displayList.getDrawList(); + *parentList->append() = (SkDrawable*) displayable; // make it findable before children are enabled + } + if (displayable->enable(maker)) + continue; + if (maker.hasError()) + return true; + if (displayable->isDrawable() == false) + return true; // error + SkDrawable* drawable = (SkDrawable*) displayable; + SkTDDrawableArray* parentList = displayList.getDrawList(); + *parentList->append() = drawable; + } + return false; +} + +bool SkDisplayEvent::getProperty(int index, SkScriptValue* value) const { + switch (index) { + case SK_PROPERTY(key): + case SK_PROPERTY(keys): { + value->fType = SkType_String; + char scratch[8]; + SkKey convert = index == SK_PROPERTY(keys) ? code : fLastCode; + size_t size = convert > 0 ? SkUTF8_FromUnichar(convert, scratch) : 0; + fKeyString.set(scratch, size); + value->fOperand.fString = &fKeyString; + if (index != SK_PROPERTY(keys) || fMax == (SkKey) -1 || fMax == code) + break; + value->fOperand.fString->append("-"); + size = SkUTF8_FromUnichar(fMax, scratch); + value->fOperand.fString->append(scratch, size); + } break; + default: + SkASSERT(0); + return false; + } + return true; +} + +void SkDisplayEvent::onEndElement(SkAnimateMaker& maker) +{ + if (kind == kUser) + return; + maker.fEvents.addEvent(this); + if (kind == kOnEnd) { + bool found = maker.find(target.c_str(), &fTarget); + SkASSERT(found); + SkASSERT(fTarget && fTarget->isAnimate()); + SkAnimateBase* animate = (SkAnimateBase*) fTarget; + animate->setHasEndEvent(); + } +} + +void SkDisplayEvent::populateInput(SkAnimateMaker& maker, const SkEvent& fEvent) { + const SkMetaData& meta = fEvent.getMetaData(); + SkMetaData::Iter iter(meta); + SkMetaData::Type type; + int number; + const char* name; + while ((name = iter.next(&type, &number)) != NULL) { + if (name[0] == '\0') + continue; + SkDisplayable* displayable; + SkInput* input; + for (int index = 0; index < fChildren.count(); index++) { + displayable = fChildren[index]; + if (displayable->getType() != SkType_Input) + continue; + input = (SkInput*) displayable; + if (input->name.equals(name)) + goto found; + } + if (!maker.find(name, &displayable) || displayable->getType() != SkType_Input) + continue; + input = (SkInput*) displayable; + found: + switch (type) { + case SkMetaData::kS32_Type: + meta.findS32(name, &input->fInt); + break; + case SkMetaData::kScalar_Type: + meta.findScalar(name, &input->fFloat); + break; + case SkMetaData::kPtr_Type: + SkASSERT(0); + break; // !!! not handled for now + case SkMetaData::kString_Type: + input->string.set(meta.findString(name)); + break; + default: + SkASSERT(0); + } + } + // re-evaluate all animators that may have built their values from input strings + for (SkDisplayable** childPtr = fChildren.begin(); childPtr < fChildren.end(); childPtr++) { + SkDisplayable* displayable = *childPtr; + if (displayable->isApply() == false) + continue; + SkApply* apply = (SkApply*) displayable; + apply->refresh(maker); + } +} + +bool SkDisplayEvent::setProperty(int index, SkScriptValue& value) { + SkASSERT(index == SK_PROPERTY(key) || index == SK_PROPERTY(keys)); + SkASSERT(value.fType == SkType_String); + SkString* string = value.fOperand.fString; + const char* chars = string->c_str(); + int count = SkUTF8_CountUnichars(chars); + SkASSERT(count >= 1); + code = (SkKey) SkUTF8_NextUnichar(&chars); + fMax = code; + SkASSERT(count == 1 || index == SK_PROPERTY(keys)); + if (--count > 0) { + SkASSERT(*chars == '-'); + chars++; + fMax = (SkKey) SkUTF8_NextUnichar(&chars); + SkASSERT(fMax >= code); + } + return true; +} + +#ifdef ANDROID + +#include "SkMetaData.h" +#include "SkParse.h" +#include "SkTextBox.h" +#include "SkXMLWriter.h" + +void SkMetaData::setPtr(char const*, void* ) {} +void SkMetaData::setS32(char const*, int ) {} +bool SkEventSink::doEvent(SkEvent const& ) { return false; } +bool SkXMLParser::parse(SkStream& ) { return false; } +SkXMLParserError::SkXMLParserError( ) {} +void SkEvent::setType(char const*, unsigned long ) {} +bool SkEvent::PostTime(SkEvent*, unsigned int, unsigned int ) { return false; } +SkEvent::SkEvent(char const* ) {} +SkEvent::SkEvent(SkEvent const& ) {} +SkEvent::SkEvent( ) {} +SkEvent::~SkEvent( ) {} +bool SkEventSink::onQuery(SkEvent* ) { return false; } +SkEventSink::SkEventSink( ) {} +SkEventSink::~SkEventSink( ) {} +bool SkXMLParser::parse(char const*, unsigned long ) { return false; } +bool SkXMLParser::parse(SkDOM const&, SkDOMNode const* ) { return false; } +bool SkEvent::Post(SkEvent*, unsigned int, unsigned int ) { return false; } +void SkParse::UnitTest( ) {} +const char* SkMetaData::findString(char const*) const {return 0;} +bool SkMetaData::findPtr(char const*, void**) const {return false;} +bool SkMetaData::findS32(char const*, int*) const {return false;} +bool SkEvent::isType(char const*, unsigned long) const { return false; } +void SkMetaData::setString(char const*, char const* ) {} +const char* SkParse::FindNamedColor(char const*, unsigned long, unsigned int* ) {return false; } +const char* SkMetaData::Iter::next(SkMetaData::Type*, int* ) { return false; } +SkMetaData::Iter::Iter(SkMetaData const& ) {} +bool SkMetaData::findScalar(char const*, int*) const {return false;} +void SkMetaData::reset( ) {} +void SkEvent::setType(SkString const& ) {} +bool SkMetaData::findBool(char const*, bool*) const {return false;} +void SkEvent::getType(SkString*) const {} +bool SkXMLParser::endElement(char const* ) { return false; } +bool SkXMLParser::addAttribute(char const*, char const* ) { return false;} +bool SkXMLParser::startElement(char const* ) { return false;} +bool SkXMLParser::text(char const*, int ) { return false;} +bool SkXMLParser::onText(char const*, int ) { return false;} +SkXMLParser::SkXMLParser(SkXMLParserError* ) {} +SkXMLParser::~SkXMLParser( ) {} +SkXMLParserError::~SkXMLParserError( ) {} +void SkXMLParserError::getErrorString(SkString*) const {} +void SkTextBox::setSpacing(int, int ) {} +void SkTextBox::setSpacingAlign(SkTextBox::SpacingAlign ) {} +void SkTextBox::draw(SkCanvas*, char const*, unsigned long, SkPaint const& ) {} +void SkTextBox::setBox(SkRect const& ) {} +void SkTextBox::setMode(SkTextBox::Mode ) {} +SkTextBox::SkTextBox( ) {} +void SkMetaData::setScalar(char const*, int ) {} +const char* SkParse::FindScalar(char const*, int* ) {return 0; } +const char* SkParse::FindScalars(char const*, int*, int ) {return 0; } +const char* SkParse::FindHex(char const*, unsigned int* ) {return 0; } +const char* SkParse::FindS32(char const*, int* ) {return 0; } +void SkXMLWriter::addAttribute(char const*, char const* ) {} +void SkXMLWriter::startElement(char const* ) {} +void SkXMLWriter::doEnd(SkXMLWriter::Elem* ) {} +SkXMLWriter::Elem* SkXMLWriter::getEnd( ) { return 0; } +bool SkXMLWriter::doStart(char const*, unsigned long ) { return false; } +SkXMLWriter::SkXMLWriter(bool ) {} +SkXMLWriter::~SkXMLWriter( ) {} +SkMetaData::SkMetaData() {} +SkMetaData::~SkMetaData() {} +bool SkEventSink::onEvent(SkEvent const&) {return false;} +bool SkXMLParser::onEndElement(char const*) {return false;} +bool SkXMLParser::onAddAttribute(char const*, char const*) {return false;} +bool SkXMLParser::onStartElement(char const*) {return false;} +void SkXMLWriter::writeHeader() {} + +#endif diff --git a/skia/animator/SkDisplayEvent.h b/skia/animator/SkDisplayEvent.h new file mode 100644 index 0000000..e1e66df --- /dev/null +++ b/skia/animator/SkDisplayEvent.h @@ -0,0 +1,75 @@ +/* libs/graphics/animator/SkDisplayEvent.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkDisplayEvent_DEFINED +#define SkDisplayEvent_DEFINED + +#include "SkDisplayable.h" +#include "SkMemberInfo.h" +#include "SkIntArray.h" +#include "SkKey.h" + +class SkEvent; + +class SkDisplayEvent : public SkDisplayable { + DECLARE_DISPLAY_MEMBER_INFO(Event); + enum Kind { + kNo_kind, + kKeyChar, + kKeyPress, + kKeyPressUp, //i assume the order here is intended to match with skanimatorscript.cpp + kMouseDown, + kMouseDrag, + kMouseMove, + kMouseUp, + kOnEnd, + kOnload, + kUser + }; + SkDisplayEvent(); + virtual ~SkDisplayEvent(); + virtual bool add(SkAnimateMaker& , SkDisplayable* child); + virtual bool contains(SkDisplayable*); + virtual SkDisplayable* contains(const SkString& ); +#ifdef SK_DEBUG + void dumpEvent(SkAnimateMaker* ); +#endif + bool enableEvent(SkAnimateMaker& ); + virtual bool getProperty(int index, SkScriptValue* ) const; + virtual void onEndElement(SkAnimateMaker& maker); + void populateInput(SkAnimateMaker& , const SkEvent& fEvent); + virtual bool setProperty(int index, SkScriptValue& ); +protected: + SkKey code; + SkBool disable; + Kind kind; + SkString target; + SkScalar x; + SkScalar y; + SkTDDisplayableArray fChildren; + mutable SkString fKeyString; + SkKey fLastCode; // last key to trigger this event + SkKey fMax; // if the code expresses a range + SkDisplayable* fTarget; // used by onEnd +private: + void deleteMembers(); + friend class SkEvents; + typedef SkDisplayable INHERITED; +}; + +#endif // SkDisplayEvent_DEFINED + diff --git a/skia/animator/SkDisplayEvents.cpp b/skia/animator/SkDisplayEvents.cpp new file mode 100644 index 0000000..0f13b70 --- /dev/null +++ b/skia/animator/SkDisplayEvents.cpp @@ -0,0 +1,121 @@ +/* libs/graphics/animator/SkDisplayEvents.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkDisplayEvents.h" +#include "SkAnimateMaker.h" +#include "SkAnimator.h" +#include "SkDisplayEvent.h" +#include "SkDisplayMovie.h" +#include "SkDrawable.h" +#ifdef SK_DEBUG +#include "SkDump.h" +#endif + +SkEventState::SkEventState() : fCode(0), fDisable(false), fDisplayable(0), fX(0), fY(0) { +} + +SkEvents::SkEvents() { +} + +SkEvents::~SkEvents() { +} + +bool SkEvents::doEvent(SkAnimateMaker& maker, SkDisplayEvent::Kind kind, SkEventState* state) { +/*#ifdef SK_DUMP_ENABLED + if (maker.fDumpEvents) { + SkDebugf("doEvent: "); + SkString str; + SkDump::GetEnumString(SkType_EventKind, kind, &str); + SkDebugf("kind=%s ", str.c_str()); + if (state && state->fDisplayable) + state->fDisplayable->SkDisplayable::dump(&maker); + else + SkDebugf("\n"); + } +#endif*/ + bool handled = false; + SkDisplayable** firstMovie = maker.fMovies.begin(); + SkDisplayable** endMovie = maker.fMovies.end(); + for (SkDisplayable** ptr = firstMovie; ptr < endMovie; ptr++) { + SkDisplayMovie* movie = (SkDisplayMovie*) *ptr; + if (kind != SkDisplayEvent::kOnload) + movie->doEvent(kind, state); + } + SkDisplayable* displayable = state ? state->fDisplayable : NULL; + int keyCode = state ? state->fCode : 0; + int count = fEvents.count(); + for (int index = 0; index < count; index++) { + SkDisplayEvent* evt = fEvents[index]; + if (evt->disable) + continue; + if (evt->kind != kind) + continue; + if (evt->code != (SkKey) -1) { + if ((int) evt->code > keyCode || (int) (evt->fMax != (SkKey) -1 ? evt->fMax : evt->code) < keyCode) + continue; + evt->fLastCode = (SkKey) keyCode; + } + if (evt->fTarget != NULL && evt->fTarget != displayable) + continue; + if (state == NULL || state->fDisable == 0) { + if (kind >= SkDisplayEvent::kMouseDown && kind <= SkDisplayEvent::kMouseUp) { + evt->x = state->fX; + evt->y = state->fY; + } + if (evt->enableEvent(maker)) + fError = true; + } + handled = true; + } + return handled; +} + +#ifdef SK_DUMP_ENABLED +void SkEvents::dump(SkAnimateMaker& maker) { + int index; + SkTDDrawableArray& drawArray = maker.fDisplayList.fDrawList; + int count = drawArray.count(); + for (index = 0; index < count; index++) { + SkDrawable* drawable = drawArray[index]; + drawable->dumpEvents(); + } + count = fEvents.count(); + for (index = 0; index < count; index++) { + SkDisplayEvent* evt = fEvents[index]; + evt->dumpEvent(&maker); + } +} +#endif + +// currently this only removes onLoad events +void SkEvents::removeEvent(SkDisplayEvent::Kind kind, SkEventState* state) { + int keyCode = state ? state->fCode : 0; + SkDisplayable* displayable = state ? state->fDisplayable : NULL; + for (SkDisplayEvent** evtPtr = fEvents.begin(); evtPtr < fEvents.end(); evtPtr++) { + SkDisplayEvent* evt = *evtPtr; + if (evt->kind != kind) + continue; + if (evt->code != (SkKey) -1) { + if ((int) evt->code > keyCode || (int) (evt->fMax != (SkKey) -1 ? evt->fMax : evt->code) < keyCode) + continue; + } + if (evt->fTarget != NULL && evt->fTarget != displayable) + continue; + int index = fEvents.find(evt); + fEvents.remove(index); + } +} diff --git a/skia/animator/SkDisplayEvents.h b/skia/animator/SkDisplayEvents.h new file mode 100644 index 0000000..e22f5c8 --- /dev/null +++ b/skia/animator/SkDisplayEvents.h @@ -0,0 +1,51 @@ +/* libs/graphics/animator/SkDisplayEvents.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkDisplayEvents_DEFINED +#define SkDisplayEvents_DEFINED + +#include "SkEvent.h" +#include "SkDisplayEvent.h" + +struct SkEventState { + SkEventState(); + int fCode; + SkBool fDisable; + SkDisplayable* fDisplayable; + SkScalar fX; + SkScalar fY; +}; + +class SkEvents { +public: + SkEvents(); + ~SkEvents(); + void addEvent(SkDisplayEvent* evt) { *fEvents.append() = evt; } + bool doEvent(SkAnimateMaker& , SkDisplayEvent::Kind , SkEventState* ); +#ifdef SK_DUMP_ENABLED + void dump(SkAnimateMaker& ); +#endif + void reset() { fEvents.reset(); } + void removeEvent(SkDisplayEvent::Kind kind, SkEventState* ); +private: + SkTDDisplayEventArray fEvents; + SkBool fError; + friend class SkDisplayXMLParser; +}; + +#endif // SkDisplayEvents_DEFINED + diff --git a/skia/animator/SkDisplayInclude.cpp b/skia/animator/SkDisplayInclude.cpp new file mode 100644 index 0000000..52f2e7c --- /dev/null +++ b/skia/animator/SkDisplayInclude.cpp @@ -0,0 +1,67 @@ +/* libs/graphics/animator/SkDisplayInclude.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkDisplayInclude.h" +#include "SkAnimateMaker.h" +#include "SkAnimator.h" + +#if 0 +#undef SK_MEMBER +#define SK_MEMBER(_member, _type) \ + { #_member, SK_OFFSETOF(BASE_CLASS::_A, _member), SkType_##_type, \ + sizeof(((BASE_CLASS::_A*) 0)->_member) / sizeof(SkScalar) } +#endif + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkInclude::fInfo[] = { + SK_MEMBER(src, String) +}; + +#endif + +DEFINE_GET_MEMBER(SkInclude); + +//SkInclude::SkInclude() { +// src.init(); +//} + +//SkInclude::~SkInclude() { +// src.unref(); +//} + +bool SkInclude::enable(SkAnimateMaker & ) { + return true; +} + +bool SkInclude::hasEnable() const { + return true; +} + +void SkInclude::onEndElement(SkAnimateMaker& maker) { + maker.fInInclude = true; + if (src.size() == 0 || maker.decodeURI(src.c_str()) == false) { + if (maker.getErrorCode() != SkXMLParserError::kNoError || maker.getNativeCode() != -1) { + maker.setInnerError(&maker, src); + maker.setErrorCode(SkDisplayXMLParserError::kInInclude); + } else { + maker.setErrorNoun(src); + maker.setErrorCode(SkDisplayXMLParserError::kIncludeNameUnknownOrMissing); + } + } + maker.fInInclude = false; +} diff --git a/skia/animator/SkDisplayInclude.h b/skia/animator/SkDisplayInclude.h new file mode 100644 index 0000000..af67942 --- /dev/null +++ b/skia/animator/SkDisplayInclude.h @@ -0,0 +1,34 @@ +/* libs/graphics/animator/SkDisplayInclude.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkDisplayInclude_DEFINED +#define SkDisplayInclude_DEFINED + +#include "SkDisplayable.h" +#include "SkMemberInfo.h" + +class SkInclude : public SkDisplayable { + DECLARE_MEMBER_INFO(Include); + virtual void onEndElement(SkAnimateMaker & ); + virtual bool enable(SkAnimateMaker & ); + virtual bool hasEnable() const; +protected: + SkString src; +}; + +#endif // SkDisplayInclude_DEFINED + diff --git a/skia/animator/SkDisplayInput.cpp b/skia/animator/SkDisplayInput.cpp new file mode 100644 index 0000000..bd034db --- /dev/null +++ b/skia/animator/SkDisplayInput.cpp @@ -0,0 +1,63 @@ +/* libs/graphics/animator/SkDisplayInput.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkDisplayInput.h" + +enum SkInput_Properties { + SK_PROPERTY(initialized) +}; + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkInput::fInfo[] = { + SK_MEMBER_ALIAS(float, fFloat, Float), + SK_MEMBER_PROPERTY(initialized, Boolean), + SK_MEMBER_ALIAS(int, fInt, Int), + SK_MEMBER(name, String), + SK_MEMBER(string, String) +}; + +#endif + +DEFINE_GET_MEMBER(SkInput); + +SkInput::SkInput() : fInt((int) SK_NaN32), fFloat(SK_ScalarNaN) {} + +SkDisplayable* SkInput::contains(const SkString& string) { + return string.equals(name) ? this : NULL; +} + +bool SkInput::enable(SkAnimateMaker & ) { + return true; +} + +bool SkInput::getProperty(int index, SkScriptValue* value) const { + switch (index) { + case SK_PROPERTY(initialized): + value->fType = SkType_Boolean; + value->fOperand.fS32 = fInt != (int) SK_NaN32 || + SkScalarIsNaN(fFloat) == false || string.size() > 0; + break; + default: + return false; + } + return true; +} + +bool SkInput::hasEnable() const { + return true; +} diff --git a/skia/animator/SkDisplayInput.h b/skia/animator/SkDisplayInput.h new file mode 100644 index 0000000..4693c36 --- /dev/null +++ b/skia/animator/SkDisplayInput.h @@ -0,0 +1,42 @@ +/* libs/graphics/animator/SkDisplayInput.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkDisplayInput_DEFINED +#define SkDisplayInput_DEFINED + +#include "SkDisplayable.h" +#include "SkMemberInfo.h" + +class SkInput : public SkDisplayable { + DECLARE_MEMBER_INFO(Input); + SkInput(); + virtual SkDisplayable* contains(const SkString& ); + virtual bool getProperty(int index, SkScriptValue* value) const; + virtual bool enable(SkAnimateMaker & ); + virtual bool hasEnable() const; +protected: + SkString name; + int32_t fInt; + SkScalar fFloat; + SkString string; +private: + friend class SkDisplayEvent; + friend class SkPost; +}; + +#endif // SkDisplayInput_DEFINED + diff --git a/skia/animator/SkDisplayList.cpp b/skia/animator/SkDisplayList.cpp new file mode 100644 index 0000000..84889d8 --- /dev/null +++ b/skia/animator/SkDisplayList.cpp @@ -0,0 +1,168 @@ +/* libs/graphics/animator/SkDisplayList.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkDisplayList.h" +#include "SkAnimateActive.h" +#include "SkAnimateBase.h" +#include "SkAnimateMaker.h" +#include "SkDisplayApply.h" +#include "SkDrawable.h" +#include "SkDrawGroup.h" +#include "SkDrawMatrix.h" +#include "SkInterpolator.h" +#include "SkTime.h" + +SkDisplayList::SkDisplayList() : fDrawBounds(true), fUnionBounds(false), fInTime(0) { +} + +SkDisplayList::~SkDisplayList() { +} + +void SkDisplayList::append(SkActive* active) { + *fActiveList.append() = active; +} + +bool SkDisplayList::draw(SkAnimateMaker& maker, SkMSec inTime) { + validate(); + fInTime = inTime; + bool result = false; + fInvalBounds.setEmpty(); + if (fDrawList.count()) { + for (SkActive** activePtr = fActiveList.begin(); activePtr < fActiveList.end(); activePtr++) { + SkActive* active = *activePtr; + active->reset(); + } + for (int index = 0; index < fDrawList.count(); index++) { + SkDrawable* draw = fDrawList[index]; + draw->initialize(); // allow matrices to reset themselves + SkASSERT(draw->isDrawable()); + validate(); + result |= draw->draw(maker); + } + } + validate(); + return result; +} + +int SkDisplayList::findGroup(SkDrawable* match, SkTDDrawableArray** list, + SkGroup** parent, SkGroup** found, SkTDDrawableArray**grandList) { + *parent = NULL; + *list = &fDrawList; + *grandList = &fDrawList; + return SearchForMatch(match, list, parent, found, grandList); +} + +void SkDisplayList::hardReset() { + fDrawList.reset(); + fActiveList.reset(); +} + +bool SkDisplayList::onIRect(const SkIRect& r) { + fBounds = r; + return fDrawBounds; +} + +int SkDisplayList::SearchForMatch(SkDrawable* match, SkTDDrawableArray** list, + SkGroup** parent, SkGroup** found, SkTDDrawableArray**grandList) { + *found = NULL; + for (int index = 0; index < (*list)->count(); index++) { + SkDrawable* draw = (**list)[index]; + if (draw == match) + return index; + if (draw->isApply()) { + SkApply* apply = (SkApply*) draw; + if (apply->scope == match) + return index; + if (apply->scope->isGroup() && SearchGroupForMatch(apply->scope, match, list, parent, found, grandList, index)) + return index; + if (apply->mode == SkApply::kMode_create) { + for (SkDrawable** ptr = apply->fScopes.begin(); ptr < apply->fScopes.end(); ptr++) { + SkDrawable* scope = *ptr; + if (scope == match) + return index; + //perhaps should call SearchGroupForMatch here as well (on scope) + } + } + } + if (draw->isGroup() && SearchGroupForMatch(draw, match, list, parent, found, grandList, index)) + return index; + + } + return -1; +} + +bool SkDisplayList::SearchGroupForMatch(SkDrawable* draw, SkDrawable* match, SkTDDrawableArray** list, + SkGroup** parent, SkGroup** found, SkTDDrawableArray** grandList, int &index) { + SkGroup* group = (SkGroup*) draw; + if (group->getOriginal() == match) + return true; + SkTDDrawableArray* saveList = *list; + int groupIndex = group->findGroup(match, list, parent, found, grandList); + if (groupIndex >= 0) { + *found = group; + index = groupIndex; + return true; + } + *list = saveList; + return false; + } + +void SkDisplayList::reset() { + for (int index = 0; index < fDrawList.count(); index++) { + SkDrawable* draw = fDrawList[index]; + if (draw->isApply() == false) + continue; + SkApply* apply = (SkApply*) draw; + apply->reset(); + } +} + +void SkDisplayList::remove(SkActive* active) { + int index = fActiveList.find(active); + SkASSERT(index >= 0); + fActiveList.remove(index); // !!! could use shuffle instead + SkASSERT(fActiveList.find(active) < 0); +} + +#ifdef SK_DUMP_ENABLED +int SkDisplayList::fDumpIndex; +int SkDisplayList::fIndent; + +void SkDisplayList::dump(SkAnimateMaker* maker) { + fIndent = 0; + dumpInner(maker); +} + +void SkDisplayList::dumpInner(SkAnimateMaker* maker) { + for (int index = 0; index < fDrawList.count(); index++) { + fDumpIndex = index; + fDrawList[fDumpIndex]->dump(maker); + } +} + +#endif + +#ifdef SK_DEBUG +void SkDisplayList::validate() { + for (int index = 0; index < fDrawList.count(); index++) { + SkDrawable* draw = fDrawList[index]; + draw->validate(); + } +} +#endif + + diff --git a/skia/animator/SkDisplayList.h b/skia/animator/SkDisplayList.h new file mode 100644 index 0000000..7f127ba --- /dev/null +++ b/skia/animator/SkDisplayList.h @@ -0,0 +1,79 @@ +/* libs/graphics/animator/SkDisplayList.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkDisplayList_DEFINED +#define SkDisplayList_DEFINED + +#include "SkOperand.h" +#include "SkIntArray.h" +#include "SkBounder.h" +#include "SkRect.h" + +class SkAnimateMaker; +class SkActive; +class SkApply; +class SkDrawable; +class SkGroup; + +class SkDisplayList : public SkBounder { +public: + SkDisplayList(); + virtual ~SkDisplayList(); + void append(SkActive* ); + void clear() { fDrawList.reset(); } + int count() { return fDrawList.count(); } + bool draw(SkAnimateMaker& , SkMSec time); +#ifdef SK_DUMP_ENABLED + void dump(SkAnimateMaker* maker); + void dumpInner(SkAnimateMaker* maker); + static int fIndent; + static int fDumpIndex; +#endif + int findGroup(SkDrawable* match, SkTDDrawableArray** list, + SkGroup** parent, SkGroup** found, SkTDDrawableArray** grandList); + SkDrawable* get(int index) { return fDrawList[index]; } + SkMSec getTime() { return fInTime; } + SkTDDrawableArray* getDrawList() { return &fDrawList; } + void hardReset(); + virtual bool onIRect(const SkIRect& r); + void reset(); + void remove(SkActive* ); +#ifdef SK_DEBUG + void validate(); +#else + void validate() {} +#endif + static int SearchForMatch(SkDrawable* match, SkTDDrawableArray** list, + SkGroup** parent, SkGroup** found, SkTDDrawableArray**grandList); + static bool SearchGroupForMatch(SkDrawable* draw, SkDrawable* match, + SkTDDrawableArray** list, SkGroup** parent, SkGroup** found, SkTDDrawableArray** grandList, + int &index); +public: + SkIRect fBounds; + SkIRect fInvalBounds; + bool fDrawBounds; + bool fHasUnion; + bool fUnionBounds; +private: + SkTDDrawableArray fDrawList; + SkTDActiveArray fActiveList; + SkMSec fInTime; + friend class SkEvents; +}; + +#endif // SkDisplayList_DEFINED + diff --git a/skia/animator/SkDisplayMath.cpp b/skia/animator/SkDisplayMath.cpp new file mode 100644 index 0000000..9facb9b --- /dev/null +++ b/skia/animator/SkDisplayMath.cpp @@ -0,0 +1,248 @@ +/* libs/graphics/animator/SkDisplayMath.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkDisplayMath.h" + +enum SkDisplayMath_Properties { + SK_PROPERTY(E), + SK_PROPERTY(LN10), + SK_PROPERTY(LN2), + SK_PROPERTY(LOG10E), + SK_PROPERTY(LOG2E), + SK_PROPERTY(PI), + SK_PROPERTY(SQRT1_2), + SK_PROPERTY(SQRT2) +}; + +const SkScalar SkDisplayMath::gConstants[] = { +#ifdef SK_SCALAR_IS_FLOAT + 2.718281828f, // E + 2.302585093f, // LN10 + 0.693147181f, // LN2 + 0.434294482f, // LOG10E + 1.442695041f, // LOG2E + 3.141592654f, // PI + 0.707106781f, // SQRT1_2 + 1.414213562f // SQRT2 +#else + 0x2B7E1, // E + 0x24D76, // LN10 + 0xB172, // LN2 + 0x6F2E, // LOG10E + 0x17154, // LOG2E + 0x3243F, // PI + 0xB505, // SQRT1_2 + 0x16A0A // SQRT2 +#endif +}; + +enum SkDisplayMath_Functions { + SK_FUNCTION(abs), + SK_FUNCTION(acos), + SK_FUNCTION(asin), + SK_FUNCTION(atan), + SK_FUNCTION(atan2), + SK_FUNCTION(ceil), + SK_FUNCTION(cos), + SK_FUNCTION(exp), + SK_FUNCTION(floor), + SK_FUNCTION(log), + SK_FUNCTION(max), + SK_FUNCTION(min), + SK_FUNCTION(pow), + SK_FUNCTION(random), + SK_FUNCTION(round), + SK_FUNCTION(sin), + SK_FUNCTION(sqrt), + SK_FUNCTION(tan) +}; + +const SkFunctionParamType SkDisplayMath::fFunctionParameters[] = { + (SkFunctionParamType) SkType_Float, // abs + (SkFunctionParamType) 0, + (SkFunctionParamType) SkType_Float, // acos + (SkFunctionParamType) 0, + (SkFunctionParamType) SkType_Float, // asin + (SkFunctionParamType) 0, + (SkFunctionParamType) SkType_Float, // atan + (SkFunctionParamType) 0, + (SkFunctionParamType) SkType_Float, // atan2 + (SkFunctionParamType) SkType_Float, + (SkFunctionParamType) 0, + (SkFunctionParamType) SkType_Float, // ceil + (SkFunctionParamType) 0, + (SkFunctionParamType) SkType_Float, // cos + (SkFunctionParamType) 0, + (SkFunctionParamType) SkType_Float, // exp + (SkFunctionParamType) 0, + (SkFunctionParamType) SkType_Float, // floor + (SkFunctionParamType) 0, + (SkFunctionParamType) SkType_Float, // log + (SkFunctionParamType) 0, + (SkFunctionParamType) SkType_Array, // max + (SkFunctionParamType) 0, + (SkFunctionParamType) SkType_Array, // min + (SkFunctionParamType) 0, + (SkFunctionParamType) SkType_Float, // pow + (SkFunctionParamType) SkType_Float, + (SkFunctionParamType) 0, + (SkFunctionParamType) SkType_Float, // random + (SkFunctionParamType) 0, + (SkFunctionParamType) SkType_Float, // round + (SkFunctionParamType) 0, + (SkFunctionParamType) SkType_Float, // sin + (SkFunctionParamType) 0, + (SkFunctionParamType) SkType_Float, // sqrt + (SkFunctionParamType) 0, + (SkFunctionParamType) SkType_Float, // tan + (SkFunctionParamType) 0 +}; + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkDisplayMath::fInfo[] = { + SK_MEMBER_PROPERTY(E, Float), + SK_MEMBER_PROPERTY(LN10, Float), + SK_MEMBER_PROPERTY(LN2, Float), + SK_MEMBER_PROPERTY(LOG10E, Float), + SK_MEMBER_PROPERTY(LOG2E, Float), + SK_MEMBER_PROPERTY(PI, Float), + SK_MEMBER_PROPERTY(SQRT1_2, Float), + SK_MEMBER_PROPERTY(SQRT2, Float), + SK_MEMBER_FUNCTION(abs, Float), + SK_MEMBER_FUNCTION(acos, Float), + SK_MEMBER_FUNCTION(asin, Float), + SK_MEMBER_FUNCTION(atan, Float), + SK_MEMBER_FUNCTION(atan2, Float), + SK_MEMBER_FUNCTION(ceil, Float), + SK_MEMBER_FUNCTION(cos, Float), + SK_MEMBER_FUNCTION(exp, Float), + SK_MEMBER_FUNCTION(floor, Float), + SK_MEMBER_FUNCTION(log, Float), + SK_MEMBER_FUNCTION(max, Float), + SK_MEMBER_FUNCTION(min, Float), + SK_MEMBER_FUNCTION(pow, Float), + SK_MEMBER_FUNCTION(random, Float), + SK_MEMBER_FUNCTION(round, Float), + SK_MEMBER_FUNCTION(sin, Float), + SK_MEMBER_FUNCTION(sqrt, Float), + SK_MEMBER_FUNCTION(tan, Float) +}; + +#endif + +DEFINE_GET_MEMBER(SkDisplayMath); + +void SkDisplayMath::executeFunction(SkDisplayable* target, int index, + SkTDArray<SkScriptValue>& parameters, SkDisplayTypes type, + SkScriptValue* scriptValue) { + if (scriptValue == NULL) + return; + SkASSERT(target == this); + SkScriptValue* array = parameters.begin(); + SkScriptValue* end = parameters.end(); + SkScalar input = parameters[0].fOperand.fScalar; + SkScalar scalarResult; + switch (index) { + case SK_FUNCTION(abs): + scalarResult = SkScalarAbs(input); + break; + case SK_FUNCTION(acos): + scalarResult = SkScalarACos(input); + break; + case SK_FUNCTION(asin): + scalarResult = SkScalarASin(input); + break; + case SK_FUNCTION(atan): + scalarResult = SkScalarATan2(input, SK_Scalar1); + break; + case SK_FUNCTION(atan2): + scalarResult = SkScalarATan2(input, parameters[1].fOperand.fScalar); + break; + case SK_FUNCTION(ceil): + scalarResult = SkIntToScalar(SkScalarCeil(input)); + break; + case SK_FUNCTION(cos): + scalarResult = SkScalarCos(input); + break; + case SK_FUNCTION(exp): + scalarResult = SkScalarExp(input); + break; + case SK_FUNCTION(floor): + scalarResult = SkIntToScalar(SkScalarFloor(input)); + break; + case SK_FUNCTION(log): + scalarResult = SkScalarLog(input); + break; + case SK_FUNCTION(max): + scalarResult = -SK_ScalarMax; + while (array < end) { + scalarResult = SkMaxScalar(scalarResult, array->fOperand.fScalar); + array++; + } + break; + case SK_FUNCTION(min): + scalarResult = SK_ScalarMax; + while (array < end) { + scalarResult = SkMinScalar(scalarResult, array->fOperand.fScalar); + array++; + } + break; + case SK_FUNCTION(pow): + // not the greatest -- but use x^y = e^(y * ln(x)) + scalarResult = SkScalarLog(input); + scalarResult = SkScalarMul(parameters[1].fOperand.fScalar, scalarResult); + scalarResult = SkScalarExp(scalarResult); + break; + case SK_FUNCTION(random): + scalarResult = fRandom.nextUScalar1(); + break; + case SK_FUNCTION(round): + scalarResult = SkIntToScalar(SkScalarRound(input)); + break; + case SK_FUNCTION(sin): + scalarResult = SkScalarSin(input); + break; + case SK_FUNCTION(sqrt): { + SkASSERT(parameters.count() == 1); + SkASSERT(type == SkType_Float); + scalarResult = SkScalarSqrt(input); + } break; + case SK_FUNCTION(tan): + scalarResult = SkScalarTan(input); + break; + default: + SkASSERT(0); + scalarResult = SK_ScalarNaN; + } + scriptValue->fOperand.fScalar = scalarResult; + scriptValue->fType = SkType_Float; +} + +const SkFunctionParamType* SkDisplayMath::getFunctionsParameters() { + return fFunctionParameters; +} + +bool SkDisplayMath::getProperty(int index, SkScriptValue* value) const { + if ((unsigned)index < SK_ARRAY_COUNT(gConstants)) { + value->fOperand.fScalar = gConstants[index]; + value->fType = SkType_Float; + return true; + } + SkASSERT(0); + return false; +} diff --git a/skia/animator/SkDisplayMath.h b/skia/animator/SkDisplayMath.h new file mode 100644 index 0000000..dc1fb9e --- /dev/null +++ b/skia/animator/SkDisplayMath.h @@ -0,0 +1,40 @@ +/* libs/graphics/animator/SkDisplayMath.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkDisplayMath_DEFINED +#define SkDisplayMath_DEFINED + +#include "SkDisplayable.h" +#include "SkMemberInfo.h" +#include "SkRandom.h" + +class SkDisplayMath : public SkDisplayable { + DECLARE_DISPLAY_MEMBER_INFO(Math); + virtual void executeFunction(SkDisplayable* , int index, + SkTDArray<SkScriptValue>& parameters, SkDisplayTypes type, + SkScriptValue* ); + virtual const SkFunctionParamType* getFunctionsParameters(); + virtual bool getProperty(int index, SkScriptValue* value) const; +private: + mutable SkRandom fRandom; + static const SkScalar gConstants[]; + static const SkFunctionParamType fFunctionParameters[]; + +}; + +#endif // SkDisplayMath_DEFINED + diff --git a/skia/animator/SkDisplayMovie.cpp b/skia/animator/SkDisplayMovie.cpp new file mode 100644 index 0000000..eaf73e0 --- /dev/null +++ b/skia/animator/SkDisplayMovie.cpp @@ -0,0 +1,138 @@ +/* libs/graphics/animator/SkDisplayMovie.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkDisplayMovie.h" +#include "SkAnimateMaker.h" +#include "SkCanvas.h" +#include "SkPaint.h" + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkDisplayMovie::fInfo[] = { + SK_MEMBER(src, String) +}; + +#endif + +DEFINE_GET_MEMBER(SkDisplayMovie); + +SkDisplayMovie::SkDisplayMovie() : fDecodedSuccessfully(false), fLoaded(false), fMovieBuilt(false) { + fMovie.fMaker->fInMovie = true; +} + +SkDisplayMovie::~SkDisplayMovie() { +} + +void SkDisplayMovie::buildMovie() { + if (fMovieBuilt) + return; + SkAnimateMaker* movieMaker = fMovie.fMaker; + SkAnimateMaker* parentMaker = movieMaker->fParentMaker; + if (src.size() == 0 || parentMaker == NULL) + return; + movieMaker->fPrefix.set(parentMaker->fPrefix); + fDecodedSuccessfully = fMovie.fMaker->decodeURI(src.c_str()); + if (fDecodedSuccessfully == false) { + + if (movieMaker->getErrorCode() != SkXMLParserError::kNoError || movieMaker->getNativeCode() != -1) { + movieMaker->setInnerError(parentMaker, src); + parentMaker->setErrorCode(SkDisplayXMLParserError::kInMovie); + } else { + parentMaker->setErrorNoun(src); + parentMaker->setErrorCode(SkDisplayXMLParserError::kMovieNameUnknownOrMissing); + } + } + fMovieBuilt = true; +} + +SkDisplayable* SkDisplayMovie::deepCopy(SkAnimateMaker* maker) { + SkDisplayMovie* copy = (SkDisplayMovie*) INHERITED::deepCopy(maker); + copy->fMovie.fMaker->fParentMaker = fMovie.fMaker->fParentMaker; + copy->fMovie.fMaker->fHostEventSinkID = fMovie.fMaker->fHostEventSinkID; + copy->fMovieBuilt = false; + *fMovie.fMaker->fParentMaker->fMovies.append() = copy; + return copy; +} + +void SkDisplayMovie::dirty() { + buildMovie(); +} + +bool SkDisplayMovie::doEvent(SkDisplayEvent::Kind kind, SkEventState* state) { + if (fLoaded == false) + return false; + fMovie.fMaker->fEnableTime = fMovie.fMaker->fParentMaker->fEnableTime; + return fMovie.fMaker->fEvents.doEvent(*fMovie.fMaker, kind, state); +} + +bool SkDisplayMovie::draw(SkAnimateMaker& maker) { + if (fDecodedSuccessfully == false) + return false; + if (fLoaded == false) + enable(maker); + maker.fCanvas->save(); + SkPaint local = SkPaint(*maker.fPaint); + bool result = fMovie.draw(maker.fCanvas, &local, + maker.fDisplayList.getTime()) != SkAnimator::kNotDifferent; + maker.fDisplayList.fInvalBounds.join(fMovie.fMaker->fDisplayList.fInvalBounds); + maker.fCanvas->restore(); + return result; +} + +#ifdef SK_DUMP_ENABLED +void SkDisplayMovie::dump(SkAnimateMaker* maker) { + dumpBase(maker); + SkDebugf("src=\"%s\"/>\n", src.c_str()); + SkAnimateMaker* movieMaker = fMovie.fMaker; + SkDisplayList::fIndent += 4; + movieMaker->fDisplayList.dumpInner(movieMaker); + SkDisplayList::fIndent -= 4; + dumpEnd(maker); +} + +void SkDisplayMovie::dumpEvents() { + fMovie.fMaker->fEvents.dump(*fMovie.fMaker); +} +#endif + +bool SkDisplayMovie::enable(SkAnimateMaker& maker) { + if (fDecodedSuccessfully == false) + return false; + SkAnimateMaker* movieMaker = fMovie.fMaker; + movieMaker->fEvents.doEvent(*movieMaker, SkDisplayEvent::kOnload, NULL); + movieMaker->fEvents.removeEvent(SkDisplayEvent::kOnload, NULL); + fLoaded = true; + movieMaker->fLoaded = true; + return false; +} + +bool SkDisplayMovie::hasEnable() const { + return true; +} + +void SkDisplayMovie::onEndElement(SkAnimateMaker& maker) { +#if defined SK_DEBUG && defined SK_DEBUG_ANIMATION_TIMING + fMovie.fMaker->fDebugTimeBase = maker.fDebugTimeBase; +#endif + fMovie.fMaker->fPrefix.set(maker.fPrefix); + fMovie.fMaker->fHostEventSinkID = maker.fHostEventSinkID; + fMovie.fMaker->fParentMaker = &maker; + buildMovie(); + *maker.fMovies.append() = this; +} + + diff --git a/skia/animator/SkDisplayMovie.h b/skia/animator/SkDisplayMovie.h new file mode 100644 index 0000000..9441343 --- /dev/null +++ b/skia/animator/SkDisplayMovie.h @@ -0,0 +1,60 @@ +/* libs/graphics/animator/SkDisplayMovie.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkDisplayMovie_DEFINED +#define SkDisplayMovie_DEFINED + +#include "SkAnimator.h" +#include "SkDrawable.h" +#include "SkMemberInfo.h" + +struct SkEventState; + +class SkDisplayMovie : public SkDrawable { + DECLARE_DISPLAY_MEMBER_INFO(Movie); + SkDisplayMovie(); + virtual ~SkDisplayMovie(); + void buildMovie(); + virtual SkDisplayable* deepCopy(SkAnimateMaker* ); + virtual void dirty(); + bool doEvent(const SkEvent& evt) { + return fLoaded && fMovie.doEvent(evt); + } + virtual bool doEvent(SkDisplayEvent::Kind , SkEventState* state ); + virtual bool draw(SkAnimateMaker& ); +#ifdef SK_DUMP_ENABLED + virtual void dump(SkAnimateMaker* ); + virtual void dumpEvents(); +#endif + virtual bool enable(SkAnimateMaker& ); + const SkAnimator* getAnimator() const { return &fMovie; } + virtual bool hasEnable() const; + virtual void onEndElement(SkAnimateMaker& ); +protected: + SkString src; + SkAnimator fMovie; + SkBool8 fDecodedSuccessfully; + SkBool8 fLoaded; + SkBool8 fMovieBuilt; + friend class SkAnimateMaker; + friend class SkPost; +private: + typedef SkDrawable INHERITED; +}; + +#endif // SkDisplayMovie_DEFINED + diff --git a/skia/animator/SkDisplayNumber.cpp b/skia/animator/SkDisplayNumber.cpp new file mode 100644 index 0000000..062fd4a --- /dev/null +++ b/skia/animator/SkDisplayNumber.cpp @@ -0,0 +1,67 @@ +/* libs/graphics/animator/SkDisplayNumber.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkDisplayNumber.h" + +enum SkDisplayNumber_Properties { + SK_PROPERTY(MAX_VALUE), + SK_PROPERTY(MIN_VALUE), + SK_PROPERTY(NEGATIVE_INFINITY), + SK_PROPERTY(NaN), + SK_PROPERTY(POSITIVE_INFINITY) +}; + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkDisplayNumber::fInfo[] = { + SK_MEMBER_PROPERTY(MAX_VALUE, Float), + SK_MEMBER_PROPERTY(MIN_VALUE, Float), + SK_MEMBER_PROPERTY(NEGATIVE_INFINITY, Float), + SK_MEMBER_PROPERTY(NaN, Float), + SK_MEMBER_PROPERTY(POSITIVE_INFINITY, Float) +}; + +#endif + +DEFINE_GET_MEMBER(SkDisplayNumber); + +bool SkDisplayNumber::getProperty(int index, SkScriptValue* value) const { + SkScalar constant; + switch (index) { + case SK_PROPERTY(MAX_VALUE): + constant = SK_ScalarMax; + break; + case SK_PROPERTY(MIN_VALUE): + constant = SK_ScalarMin; + break; + case SK_PROPERTY(NEGATIVE_INFINITY): + constant = -SK_ScalarInfinity; + break; + case SK_PROPERTY(NaN): + constant = SK_ScalarNaN; + break; + case SK_PROPERTY(POSITIVE_INFINITY): + constant = SK_ScalarInfinity; + break; + default: + SkASSERT(0); + return false; + } + value->fOperand.fScalar = constant; + value->fType = SkType_Float; + return true; +} diff --git a/skia/animator/SkDisplayNumber.h b/skia/animator/SkDisplayNumber.h new file mode 100644 index 0000000..5429aaf --- /dev/null +++ b/skia/animator/SkDisplayNumber.h @@ -0,0 +1,30 @@ +/* libs/graphics/animator/SkDisplayNumber.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkDisplayNumber_DEFINED +#define SkDisplayNumber_DEFINED + +#include "SkDisplayable.h" +#include "SkMemberInfo.h" + +class SkDisplayNumber : public SkDisplayable { + DECLARE_DISPLAY_MEMBER_INFO(Number); + virtual bool getProperty(int index, SkScriptValue* value) const; +private: +}; + +#endif // SkDisplayNumber_DEFINED diff --git a/skia/animator/SkDisplayPost.cpp b/skia/animator/SkDisplayPost.cpp new file mode 100644 index 0000000..464150b --- /dev/null +++ b/skia/animator/SkDisplayPost.cpp @@ -0,0 +1,315 @@ +/* libs/graphics/animator/SkDisplayPost.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkDisplayPost.h" +#include "SkAnimateMaker.h" +#include "SkAnimator.h" +#include "SkDisplayMovie.h" +#include "SkPostParts.h" +#include "SkScript.h" +#ifdef SK_DEBUG +#include "SkDump.h" +#include "SkTime.h" +#endif + +enum SkPost_Properties { + SK_PROPERTY(target), + SK_PROPERTY(type) +}; + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkPost::fInfo[] = { + SK_MEMBER(delay, MSec), +// SK_MEMBER(initialized, Boolean), + SK_MEMBER(mode, EventMode), + SK_MEMBER(sink, String), + SK_MEMBER_PROPERTY(target, String), + SK_MEMBER_PROPERTY(type, String) +}; + +#endif + +DEFINE_GET_MEMBER(SkPost); + +SkPost::SkPost() : delay(0), /*initialized(SkBool(-1)), */ mode(kImmediate), fMaker(NULL), + fSinkID(0), fTargetMaker(NULL), fChildHasID(false), fDirty(false) { +} + +SkPost::~SkPost() { + for (SkData** part = fParts.begin(); part < fParts.end(); part++) + delete *part; +} + +bool SkPost::add(SkAnimateMaker& , SkDisplayable* child) { + SkASSERT(child && child->isData()); + SkData* part = (SkData*) child; + *fParts.append() = part; + return true; +} + +bool SkPost::childrenNeedDisposing() const { + return false; +} + +void SkPost::dirty() { + fDirty = true; +} + +#ifdef SK_DUMP_ENABLED +void SkPost::dump(SkAnimateMaker* maker) { + dumpBase(maker); + SkString* eventType = new SkString(); + fEvent.getType(eventType); + if (eventType->equals("user")) { + const char* target = fEvent.findString("id"); + SkDebugf("target=\"%s\" ", target); + } + else + SkDebugf("type=\"%s\" ", eventType->c_str()); + delete eventType; + + if (delay > 0) { +#ifdef SK_CAN_USE_FLOAT + SkDebugf("delay=\"%g\" ", SkScalarToFloat(SkScalarDiv(delay, 1000))); +#else + SkDebugf("delay=\"%x\" ", SkScalarDiv(delay, 1000)); +#endif + } +// if (initialized == false) +// SkDebugf("(uninitialized) "); + SkString string; + SkDump::GetEnumString(SkType_EventMode, mode, &string); + if (!string.equals("immediate")) + SkDebugf("mode=\"%s\" ", string.c_str()); + // !!! could enhance this to search through make hierarchy to show name of sink + if (sink.size() > 0) { + SkDebugf("sink=\"%s\" sinkID=\"%d\" ", sink.c_str(), fSinkID); + } else if (fSinkID != maker->getAnimator()->getSinkID() && fSinkID != 0) { + SkDebugf("sinkID=\"%d\" ", fSinkID); + } + const SkMetaData& meta = fEvent.getMetaData(); + SkMetaData::Iter iter(meta); + SkMetaData::Type type; + int number; + const char* name; + bool closedYet = false; + SkDisplayList::fIndent += 4; + //this seems to work, but kinda hacky + //for some reason the last part is id, which i don't want + //and the parts seem to be in the reverse order from the one in which we find the + //data itself + //SkData** ptr = fParts.end(); + //SkData* data; + //const char* ID; + while ((name = iter.next(&type, &number)) != NULL) { + //ptr--; + if (strcmp(name, "id") == 0) + continue; + if (closedYet == false) { + SkDebugf(">\n"); + closedYet = true; + } + //data = *ptr; + //if (data->id) + // ID = data->id; + //else + // ID = ""; + SkDebugf("%*s<data name=\"%s\" ", SkDisplayList::fIndent, "", name); + switch (type) { + case SkMetaData::kS32_Type: { + int32_t s32; + meta.findS32(name, &s32); + SkDebugf("int=\"%d\" ", s32); + } break; + case SkMetaData::kScalar_Type: { + SkScalar scalar; + meta.findScalar(name, &scalar); +#ifdef SK_CAN_USE_FLOAT + SkDebugf("float=\"%g\" ", SkScalarToFloat(scalar)); +#else + SkDebugf("float=\"%x\" ", scalar); +#endif + } break; + case SkMetaData::kString_Type: + SkDebugf("string=\"%s\" ", meta.findString(name)); + break; + case SkMetaData::kPtr_Type: {//when do we have a pointer + void* ptr; + meta.findPtr(name, &ptr); + SkDebugf("0x%08x ", ptr); + } break; + case SkMetaData::kBool_Type: { + bool boolean; + meta.findBool(name, &boolean); + SkDebugf("boolean=\"%s\" ", boolean ? "true " : "false "); + } break; + default: + break; + } + SkDebugf("/>\n"); + //ptr++; +/* perhaps this should only be done in the case of a pointer? + SkDisplayable* displayable; + if (maker->find(name, &displayable)) + displayable->dump(maker); + else + SkDebugf("\n");*/ + } + SkDisplayList::fIndent -= 4; + if (closedYet) + dumpEnd(maker); + else + SkDebugf("/>\n"); + +} +#endif + +bool SkPost::enable(SkAnimateMaker& maker ) { + if (maker.hasError()) + return true; + if (fDirty) { + if (sink.size() > 0) + findSinkID(); + if (fChildHasID) { + SkString preserveID(fEvent.findString("id")); + fEvent.getMetaData().reset(); + if (preserveID.size() > 0) + fEvent.setString("id", preserveID); + for (SkData** part = fParts.begin(); part < fParts.end(); part++) { + if ((*part)->add()) + maker.setErrorCode(SkDisplayXMLParserError::kErrorAddingDataToPost); + } + } + fDirty = false; + } +#ifdef SK_DUMP_ENABLED + if (maker.fDumpPosts) { + SkDebugf("post enable: "); + dump(&maker); + } +#if defined SK_DEBUG_ANIMATION_TIMING + SkString debugOut; + SkMSec time = maker.getAppTime(); + debugOut.appendS32(time - maker.fDebugTimeBase); + debugOut.append(" post id="); + debugOut.append(_id); + debugOut.append(" enable="); + debugOut.appendS32(maker.fEnableTime - maker.fDebugTimeBase); + debugOut.append(" delay="); + debugOut.appendS32(delay); +#endif +#endif +// SkMSec adjustedDelay = maker.adjustDelay(maker.fEnableTime, delay); + SkMSec futureTime = maker.fEnableTime + delay; + fEvent.setFast32(futureTime); +#if defined SK_DEBUG && defined SK_DEBUG_ANIMATION_TIMING + debugOut.append(" future="); + debugOut.appendS32(futureTime - maker.fDebugTimeBase); + SkDebugf("%s\n", debugOut.c_str()); +#endif + SkEventSinkID targetID = fSinkID; + bool isAnimatorEvent = true; + SkAnimator* anim = maker.getAnimator(); + if (targetID == 0) { + isAnimatorEvent = fEvent.findString("id") != NULL; + if (isAnimatorEvent) + targetID = anim->getSinkID(); + else if (maker.fHostEventSinkID) + targetID = maker.fHostEventSinkID; + else + return true; + } else + anim = fTargetMaker->getAnimator(); + if (delay == 0) { + if (isAnimatorEvent && mode == kImmediate) + fTargetMaker->doEvent(fEvent); + else + anim->onEventPost(new SkEvent(fEvent), targetID); + } else + anim->onEventPostTime(new SkEvent(fEvent), targetID, futureTime); + return true; +} + +void SkPost::findSinkID() { + // get the next delimiter '.' if any + fTargetMaker = fMaker; + const char* ch = sink.c_str(); + do { + const char* end = strchr(ch, '.'); + size_t len = end ? end - ch : strlen(ch); + SkDisplayable* displayable = NULL; + if (SK_LITERAL_STR_EQUAL("parent", ch, len)) { + if (fTargetMaker->fParentMaker) + fTargetMaker = fTargetMaker->fParentMaker; + else { + fTargetMaker->setErrorCode(SkDisplayXMLParserError::kNoParentAvailable); + return; + } + } else { + fTargetMaker->find(ch, len, &displayable); + if (displayable == NULL || displayable->getType() != SkType_Movie) { + fTargetMaker->setErrorCode(SkDisplayXMLParserError::kExpectedMovie); + return; + } + SkDisplayMovie* movie = (SkDisplayMovie*) displayable; + fTargetMaker = movie->fMovie.fMaker; + } + if (end == NULL) + break; + ch = ++end; + } while (true); + SkAnimator* anim = fTargetMaker->getAnimator(); + fSinkID = anim->getSinkID(); +} + +bool SkPost::hasEnable() const { + return true; +} + +void SkPost::onEndElement(SkAnimateMaker& maker) { + fTargetMaker = fMaker = &maker; + if (fChildHasID == false) { + for (SkData** part = fParts.begin(); part < fParts.end(); part++) + delete *part; + fParts.reset(); + } +} + +void SkPost::setChildHasID() { + fChildHasID = true; +} + +bool SkPost::setProperty(int index, SkScriptValue& value) { + SkASSERT(value.fType == SkType_String); + SkString* string = value.fOperand.fString; + switch(index) { + case SK_PROPERTY(target): { + fEvent.setType("user"); + fEvent.setString("id", *string); + mode = kImmediate; + } break; + case SK_PROPERTY(type): + fEvent.setType(*string); + break; + default: + SkASSERT(0); + return false; + } + return true; +} + diff --git a/skia/animator/SkDisplayPost.h b/skia/animator/SkDisplayPost.h new file mode 100644 index 0000000..ab4ad60 --- /dev/null +++ b/skia/animator/SkDisplayPost.h @@ -0,0 +1,67 @@ +/* libs/graphics/animator/SkDisplayPost.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkDisplayPost_DEFINED +#define SkDisplayPost_DEFINED + +#include "SkDisplayable.h" +#include "SkEvent.h" +#include "SkEventSink.h" +#include "SkMemberInfo.h" +#include "SkIntArray.h" + +class SkData; +class SkAnimateMaker; + +class SkPost : public SkDisplayable { + DECLARE_MEMBER_INFO(Post); + enum Mode { + kDeferred, + kImmediate + }; + SkPost(); + virtual ~SkPost(); + virtual bool add(SkAnimateMaker& , SkDisplayable* child); + virtual bool childrenNeedDisposing() const; + virtual void dirty(); +#ifdef SK_DUMP_ENABLED + virtual void dump(SkAnimateMaker* ); +#endif + virtual bool enable(SkAnimateMaker& ); + virtual bool hasEnable() const; + virtual void onEndElement(SkAnimateMaker& ); + virtual void setChildHasID(); + virtual bool setProperty(int index, SkScriptValue& ); +protected: + SkMSec delay; + SkString sink; +// SkBool initialized; + Mode mode; + SkEvent fEvent; + SkAnimateMaker* fMaker; + SkTDDataArray fParts; + SkEventSinkID fSinkID; + SkAnimateMaker* fTargetMaker; + SkBool8 fChildHasID; + SkBool8 fDirty; +private: + void findSinkID(); + friend class SkData; + typedef SkDisplayable INHERITED; +}; + +#endif //SkDisplayPost_DEFINED diff --git a/skia/animator/SkDisplayRandom.cpp b/skia/animator/SkDisplayRandom.cpp new file mode 100644 index 0000000..dcdfdaf --- /dev/null +++ b/skia/animator/SkDisplayRandom.cpp @@ -0,0 +1,80 @@ +/* libs/graphics/animator/SkDisplayRandom.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkDisplayRandom.h" +#include "SkInterpolator.h" + +enum SkDisplayRandom_Properties { + SK_PROPERTY(random), + SK_PROPERTY(seed) +}; + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkDisplayRandom::fInfo[] = { + SK_MEMBER(blend, Float), + SK_MEMBER(max, Float), + SK_MEMBER(min, Float), + SK_MEMBER_DYNAMIC_PROPERTY(random, Float), + SK_MEMBER_PROPERTY(seed, Int) +}; + +#endif + +DEFINE_GET_MEMBER(SkDisplayRandom); + +SkDisplayRandom::SkDisplayRandom() : blend(0), min(0), max(SK_Scalar1) { +} + +#ifdef SK_DUMP_ENABLED +void SkDisplayRandom::dump(SkAnimateMaker* maker) { + dumpBase(maker); +#ifdef SK_CAN_USE_FLOAT + SkDebugf("min=\"%g\" ", SkScalarToFloat(min)); + SkDebugf("max=\"%g\" ", SkScalarToFloat(max)); + SkDebugf("blend=\"%g\" ", SkScalarToFloat(blend)); +#else + SkDebugf("min=\"%x\" ", min); + SkDebugf("max=\"%x\" ", max); + SkDebugf("blend=\"%x\" ", blend); +#endif + SkDebugf("/>\n"); +} +#endif + +bool SkDisplayRandom::getProperty(int index, SkScriptValue* value) const { + switch(index) { + case SK_PROPERTY(random): { + SkScalar random = fRandom.nextUScalar1(); + SkScalar relativeT = SkUnitCubicInterp(random, SK_Scalar1 - blend, 0, 0, SK_Scalar1 - blend); + value->fOperand.fScalar = min + SkScalarMul(max - min, relativeT); + value->fType = SkType_Float; + return true; + } + default: + SkASSERT(0); + } + return false; +} + +bool SkDisplayRandom::setProperty(int index, SkScriptValue& value) { + SkASSERT(index == SK_PROPERTY(seed)); + SkASSERT(value.fType == SkType_Int); + fRandom.setSeed(value.fOperand.fS32); + return true; +} + diff --git a/skia/animator/SkDisplayRandom.h b/skia/animator/SkDisplayRandom.h new file mode 100644 index 0000000..73f490b --- /dev/null +++ b/skia/animator/SkDisplayRandom.h @@ -0,0 +1,49 @@ +/* libs/graphics/animator/SkDisplayRandom.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkDisplayRandom_DEFINED +#define SkDisplayRandom_DEFINED + +#include "SkDisplayable.h" +#include "SkMemberInfo.h" +#include "SkRandom.h" + +#ifdef min +#undef min +#endif + +#ifdef max +#undef max +#endif + +class SkDisplayRandom : public SkDisplayable { + DECLARE_DISPLAY_MEMBER_INFO(Random); + SkDisplayRandom(); +#ifdef SK_DUMP_ENABLED + virtual void dump(SkAnimateMaker* ); +#endif + virtual bool getProperty(int index, SkScriptValue* value) const; + virtual bool setProperty(int index, SkScriptValue& ); +private: + SkScalar blend; + SkScalar min; + SkScalar max; + mutable SkRandom fRandom; +}; + +#endif // SkDisplayRandom_DEFINED + diff --git a/skia/animator/SkDisplayScreenplay.cpp b/skia/animator/SkDisplayScreenplay.cpp new file mode 100644 index 0000000..7591a13 --- /dev/null +++ b/skia/animator/SkDisplayScreenplay.cpp @@ -0,0 +1,30 @@ +/* libs/graphics/animator/SkDisplayScreenplay.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkDisplayScreenplay.h" + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkDisplayScreenplay::fInfo[] = { + SK_MEMBER(time, MSec) +}; + +#endif + +DEFINE_GET_MEMBER(SkDisplayScreenplay); + + diff --git a/skia/animator/SkDisplayScreenplay.h b/skia/animator/SkDisplayScreenplay.h new file mode 100644 index 0000000..3a7200d --- /dev/null +++ b/skia/animator/SkDisplayScreenplay.h @@ -0,0 +1,29 @@ +/* libs/graphics/animator/SkDisplayScreenplay.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkDisplayScreenplay_DEFINED +#define SkDisplayScreenplay_DEFINED + +#include "SkDisplayable.h" +#include "SkMemberInfo.h" + +class SkDisplayScreenplay : public SkDisplayable { + DECLARE_DISPLAY_MEMBER_INFO(Screenplay); + SkMSec time; +}; + +#endif // SkDisplayScreenplay_DEFINED diff --git a/skia/animator/SkDisplayType.cpp b/skia/animator/SkDisplayType.cpp new file mode 100644 index 0000000..0ecbfb4 --- /dev/null +++ b/skia/animator/SkDisplayType.cpp @@ -0,0 +1,774 @@ +/* libs/graphics/animator/SkDisplayType.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkDisplayType.h" +#include "SkAnimateMaker.h" +#include "SkAnimateSet.h" +#include "SkDisplayAdd.h" +#include "SkDisplayApply.h" +#include "SkDisplayBounds.h" +#include "SkDisplayEvent.h" +#include "SkDisplayInclude.h" +#ifdef SK_DEBUG +#include "SkDisplayList.h" +#endif +#include "SkDisplayMath.h" +#include "SkDisplayMovie.h" +#include "SkDisplayNumber.h" +#include "SkDisplayPost.h" +#include "SkDisplayRandom.h" +#include "SkDisplayTypes.h" +#include "SkDraw3D.h" +#include "SkDrawBitmap.h" +#include "SkDrawClip.h" +#include "SkDrawDash.h" +#include "SkDrawDiscrete.h" +#include "SkDrawEmboss.h" +#include "SkDrawFull.h" +#include "SkDrawGradient.h" +#include "SkDrawLine.h" +#include "SkDrawMatrix.h" +#include "SkDrawOval.h" +#include "SkDrawPaint.h" +#include "SkDrawPath.h" +#include "SkDrawPoint.h" +#include "SkDrawSaveLayer.h" +#include "SkDrawText.h" +#include "SkDrawTextBox.h" +#include "SkDrawTo.h" +#include "SkDrawTransparentShader.h" +#include "SkDump.h" +#include "SkExtras.h" +#include "SkHitClear.h" +#include "SkHitTest.h" +#include "SkMatrixParts.h" +#include "SkPathParts.h" +#include "SkPostParts.h" +#include "SkSnapshot.h" +#include "SkTextOnPath.h" +#include "SkTextToPath.h" +#include "SkTSearch.h" + +#define CASE_NEW(_class) \ + case SkType_##_class: result = new Sk##_class(); break +#define CASE_DRAW_NEW(_class) \ + case SkType_##_class: result = new SkDraw##_class(); break +#define CASE_DISPLAY_NEW(_class) \ + case SkType_##_class: result = new SkDisplay##_class(); break +#ifdef SK_DEBUG + #define CASE_DEBUG_RETURN_NIL(_class) \ + case SkType_##_class: return NULL +#else + #define CASE_DEBUG_RETURN_NIL(_class) +#endif + + +SkDisplayTypes SkDisplayType::gNewTypes = kNumberOfTypes; + +SkDisplayable* SkDisplayType::CreateInstance(SkAnimateMaker* maker, SkDisplayTypes type) { + SkDisplayable* result = NULL; + switch (type) { + // unknown + CASE_DISPLAY_NEW(Math); + CASE_DISPLAY_NEW(Number); + CASE_NEW(Add); + CASE_NEW(AddCircle); + // addgeom + CASE_DEBUG_RETURN_NIL(AddMode); + CASE_NEW(AddOval); + CASE_NEW(AddPath); + CASE_NEW(AddRect); + CASE_NEW(AddRoundRect); + CASE_DEBUG_RETURN_NIL(Align); + CASE_NEW(Animate); + // animatebase + CASE_NEW(Apply); + CASE_DEBUG_RETURN_NIL(ApplyMode); + CASE_DEBUG_RETURN_NIL(ApplyTransition); + CASE_DISPLAY_NEW(Array); + // argb + // base64 + // basebitmap + // baseclassinfo + CASE_DRAW_NEW(Bitmap); + // bitmapencoding + // bitmapformat + CASE_DRAW_NEW(BitmapShader); + CASE_DRAW_NEW(Blur); + CASE_DISPLAY_NEW(Boolean); + // boundable + CASE_DISPLAY_NEW(Bounds); + CASE_DEBUG_RETURN_NIL(Cap); + CASE_NEW(Clear); + CASE_DRAW_NEW(Clip); + CASE_NEW(Close); + CASE_DRAW_NEW(Color); + CASE_NEW(CubicTo); + CASE_NEW(Dash); + CASE_NEW(Data); + CASE_NEW(Discrete); + // displayable + // drawable + CASE_NEW(DrawTo); + CASE_NEW(Dump); + // dynamicstring + CASE_DRAW_NEW(Emboss); + CASE_DISPLAY_NEW(Event); + CASE_DEBUG_RETURN_NIL(EventCode); + CASE_DEBUG_RETURN_NIL(EventKind); + CASE_DEBUG_RETURN_NIL(EventMode); + // filltype + // filtertype + CASE_DISPLAY_NEW(Float); + CASE_NEW(FromPath); + CASE_DEBUG_RETURN_NIL(FromPathMode); + CASE_NEW(Full); + // gradient + CASE_NEW(Group); + CASE_NEW(HitClear); + CASE_NEW(HitTest); + CASE_NEW(Image); + CASE_NEW(Include); + CASE_NEW(Input); + CASE_DISPLAY_NEW(Int); + CASE_DEBUG_RETURN_NIL(Join); + CASE_NEW(Line); + CASE_NEW(LineTo); + CASE_NEW(LinearGradient); + CASE_DRAW_NEW(MaskFilter); + CASE_DEBUG_RETURN_NIL(MaskFilterBlurStyle); + // maskfilterlight + CASE_DRAW_NEW(Matrix); + // memberfunction + // memberproperty + CASE_NEW(Move); + CASE_NEW(MoveTo); + CASE_DISPLAY_NEW(Movie); + // msec + CASE_NEW(Oval); + CASE_DRAW_NEW(Paint); + CASE_DRAW_NEW(Path); + // pathdirection + CASE_DRAW_NEW(PathEffect); + // point + CASE_NEW(DrawPoint); + CASE_NEW(PolyToPoly); + CASE_NEW(Polygon); + CASE_NEW(Polyline); + CASE_NEW(Post); + CASE_NEW(QuadTo); + CASE_NEW(RCubicTo); + CASE_NEW(RLineTo); + CASE_NEW(RMoveTo); + CASE_NEW(RQuadTo); + CASE_NEW(RadialGradient); + CASE_DISPLAY_NEW(Random); + CASE_DRAW_NEW(Rect); + CASE_NEW(RectToRect); + CASE_NEW(Remove); + CASE_NEW(Replace); + CASE_NEW(Rotate); + CASE_NEW(RoundRect); + CASE_NEW(Save); + CASE_NEW(SaveLayer); + CASE_NEW(Scale); + // screenplay + CASE_NEW(Set); + CASE_DRAW_NEW(Shader); + CASE_NEW(Skew); + CASE_NEW(3D_Camera); + CASE_NEW(3D_Patch); + // 3dpoint + CASE_NEW(Snapshot); + CASE_DISPLAY_NEW(String); + // style + CASE_NEW(Text); + CASE_DRAW_NEW(TextBox); + // textboxalign + // textboxmode + CASE_NEW(TextOnPath); + CASE_NEW(TextToPath); + CASE_DEBUG_RETURN_NIL(TileMode); + CASE_NEW(Translate); + CASE_DRAW_NEW(TransparentShader); + CASE_DRAW_NEW(Typeface); + CASE_DEBUG_RETURN_NIL(Xfermode); + default: + SkExtras** end = maker->fExtras.end(); + for (SkExtras** extraPtr = maker->fExtras.begin(); extraPtr < end; extraPtr++) { + if ((result = (*extraPtr)->createInstance(type)) != NULL) + return result; + } + SkASSERT(0); + } + return result; +} + +#undef CASE_NEW +#undef CASE_DRAW_NEW +#undef CASE_DISPLAY_NEW + +#if SK_USE_CONDENSED_INFO == 0 + +#define CASE_GET_INFO(_class) case SkType_##_class: \ + info = Sk##_class::fInfo; infoCount = Sk##_class::fInfoCount; break +#define CASE_GET_DRAW_INFO(_class) case SkType_##_class: \ + info = SkDraw##_class::fInfo; infoCount = SkDraw##_class::fInfoCount; break +#define CASE_GET_DISPLAY_INFO(_class) case SkType_##_class: \ + info = SkDisplay##_class::fInfo; infoCount = SkDisplay##_class::fInfoCount; \ + break + +const SkMemberInfo* SkDisplayType::GetMembers(SkAnimateMaker* maker, + SkDisplayTypes type, int* infoCountPtr) { + const SkMemberInfo* info = NULL; + int infoCount = 0; + switch (type) { + // unknown + CASE_GET_DISPLAY_INFO(Math); + CASE_GET_DISPLAY_INFO(Number); + CASE_GET_INFO(Add); + CASE_GET_INFO(AddCircle); + CASE_GET_INFO(AddGeom); + // addmode + CASE_GET_INFO(AddOval); + CASE_GET_INFO(AddPath); + CASE_GET_INFO(AddRect); + CASE_GET_INFO(AddRoundRect); + // align + CASE_GET_INFO(Animate); + CASE_GET_INFO(AnimateBase); + CASE_GET_INFO(Apply); + // applymode + // applytransition + CASE_GET_DISPLAY_INFO(Array); + // argb + // base64 + CASE_GET_INFO(BaseBitmap); + // baseclassinfo + CASE_GET_DRAW_INFO(Bitmap); + // bitmapencoding + // bitmapformat + CASE_GET_DRAW_INFO(BitmapShader); + CASE_GET_DRAW_INFO(Blur); + CASE_GET_DISPLAY_INFO(Boolean); + // boundable + CASE_GET_DISPLAY_INFO(Bounds); + // cap + // clear + CASE_GET_DRAW_INFO(Clip); + // close + CASE_GET_DRAW_INFO(Color); + CASE_GET_INFO(CubicTo); + CASE_GET_INFO(Dash); + CASE_GET_INFO(Data); + CASE_GET_INFO(Discrete); + // displayable + // drawable + CASE_GET_INFO(DrawTo); + CASE_GET_INFO(Dump); + // dynamicstring + CASE_GET_DRAW_INFO(Emboss); + CASE_GET_DISPLAY_INFO(Event); + // eventcode + // eventkind + // eventmode + // filltype + // filtertype + CASE_GET_DISPLAY_INFO(Float); + CASE_GET_INFO(FromPath); + // frompathmode + // full + CASE_GET_INFO(Gradient); + CASE_GET_INFO(Group); + CASE_GET_INFO(HitClear); + CASE_GET_INFO(HitTest); + CASE_GET_INFO(Image); + CASE_GET_INFO(Include); + CASE_GET_INFO(Input); + CASE_GET_DISPLAY_INFO(Int); + // join + CASE_GET_INFO(Line); + CASE_GET_INFO(LineTo); + CASE_GET_INFO(LinearGradient); + // maskfilter + // maskfilterblurstyle + // maskfilterlight + CASE_GET_DRAW_INFO(Matrix); + // memberfunction + // memberproperty + CASE_GET_INFO(Move); + CASE_GET_INFO(MoveTo); + CASE_GET_DISPLAY_INFO(Movie); + // msec + CASE_GET_INFO(Oval); + CASE_GET_DRAW_INFO(Path); + CASE_GET_DRAW_INFO(Paint); + // pathdirection + // patheffect + case SkType_Point: info = Sk_Point::fInfo; infoCount = Sk_Point::fInfoCount; break; // no virtual flavor + CASE_GET_INFO(DrawPoint); // virtual flavor + CASE_GET_INFO(PolyToPoly); + CASE_GET_INFO(Polygon); + CASE_GET_INFO(Polyline); + CASE_GET_INFO(Post); + CASE_GET_INFO(QuadTo); + CASE_GET_INFO(RCubicTo); + CASE_GET_INFO(RLineTo); + CASE_GET_INFO(RMoveTo); + CASE_GET_INFO(RQuadTo); + CASE_GET_INFO(RadialGradient); + CASE_GET_DISPLAY_INFO(Random); + CASE_GET_DRAW_INFO(Rect); + CASE_GET_INFO(RectToRect); + CASE_GET_INFO(Remove); + CASE_GET_INFO(Replace); + CASE_GET_INFO(Rotate); + CASE_GET_INFO(RoundRect); + CASE_GET_INFO(Save); + CASE_GET_INFO(SaveLayer); + CASE_GET_INFO(Scale); + // screenplay + CASE_GET_INFO(Set); + CASE_GET_DRAW_INFO(Shader); + CASE_GET_INFO(Skew); + CASE_GET_INFO(3D_Camera); + CASE_GET_INFO(3D_Patch); + CASE_GET_INFO(3D_Point); + CASE_GET_INFO(Snapshot); + CASE_GET_DISPLAY_INFO(String); + // style + CASE_GET_INFO(Text); + CASE_GET_DRAW_INFO(TextBox); + // textboxalign + // textboxmode + CASE_GET_INFO(TextOnPath); + CASE_GET_INFO(TextToPath); + // tilemode + CASE_GET_INFO(Translate); + // transparentshader + CASE_GET_DRAW_INFO(Typeface); + // xfermode + // knumberoftypes + default: + if (maker) { + SkExtras** end = maker->fExtras.end(); + for (SkExtras** extraPtr = maker->fExtras.begin(); extraPtr < end; extraPtr++) { + if ((info = (*extraPtr)->getMembers(type, infoCountPtr)) != NULL) + return info; + } + } + return NULL; + } + if (infoCountPtr) + *infoCountPtr = infoCount; + return info; +} + +const SkMemberInfo* SkDisplayType::GetMember(SkAnimateMaker* maker, + SkDisplayTypes type, const char** matchPtr ) { + int infoCount; + const SkMemberInfo* info = GetMembers(maker, type, &infoCount); + info = SkMemberInfo::Find(info, infoCount, matchPtr); +// SkASSERT(info); + return info; +} + +#undef CASE_GET_INFO +#undef CASE_GET_DRAW_INFO +#undef CASE_GET_DISPLAY_INFO + +#endif // SK_USE_CONDENSED_INFO == 0 + +#if defined SK_DEBUG || defined SK_BUILD_CONDENSED + #define DRAW_NAME(_name, _type) {_name, _type, true, false } + #define DISPLAY_NAME(_name, _type) {_name, _type, false, true } + #define INIT_BOOL_FIELDS , false, false +#else + #define DRAW_NAME(_name, _type) {_name, _type } + #define DISPLAY_NAME(_name, _type) {_name, _type } + #define INIT_BOOL_FIELDS +#endif + +const TypeNames gTypeNames[] = { + // unknown + { "Math", SkType_Math INIT_BOOL_FIELDS }, + { "Number", SkType_Number INIT_BOOL_FIELDS }, + { "add", SkType_Add INIT_BOOL_FIELDS }, + { "addCircle", SkType_AddCircle INIT_BOOL_FIELDS }, + // addgeom + // addmode + { "addOval", SkType_AddOval INIT_BOOL_FIELDS }, + { "addPath", SkType_AddPath INIT_BOOL_FIELDS }, + { "addRect", SkType_AddRect INIT_BOOL_FIELDS }, + { "addRoundRect", SkType_AddRoundRect INIT_BOOL_FIELDS }, + // align + { "animate", SkType_Animate INIT_BOOL_FIELDS }, + // animateBase + { "apply", SkType_Apply INIT_BOOL_FIELDS }, + // applymode + // applytransition + { "array", SkType_Array INIT_BOOL_FIELDS }, + // argb + // base64 + // basebitmap + // baseclassinfo + DRAW_NAME("bitmap", SkType_Bitmap), + // bitmapencoding + // bitmapformat + DRAW_NAME("bitmapShader", SkType_BitmapShader), + DRAW_NAME("blur", SkType_Blur), + { "boolean", SkType_Boolean INIT_BOOL_FIELDS }, + // boundable + DISPLAY_NAME("bounds", SkType_Bounds), + // cap + { "clear", SkType_Clear INIT_BOOL_FIELDS }, + DRAW_NAME("clip", SkType_Clip), + { "close", SkType_Close INIT_BOOL_FIELDS }, + DRAW_NAME("color", SkType_Color), + { "cubicTo", SkType_CubicTo INIT_BOOL_FIELDS }, + { "dash", SkType_Dash INIT_BOOL_FIELDS }, + { "data", SkType_Data INIT_BOOL_FIELDS }, + { "discrete", SkType_Discrete INIT_BOOL_FIELDS }, + // displayable + // drawable + { "drawTo", SkType_DrawTo INIT_BOOL_FIELDS }, + { "dump", SkType_Dump INIT_BOOL_FIELDS }, + // dynamicstring + DRAW_NAME("emboss", SkType_Emboss), + DISPLAY_NAME("event", SkType_Event), + // eventcode + // eventkind + // eventmode + // filltype + // filtertype + { "float", SkType_Float INIT_BOOL_FIELDS }, + { "fromPath", SkType_FromPath INIT_BOOL_FIELDS }, + // frompathmode + { "full", SkType_Full INIT_BOOL_FIELDS }, + // gradient + { "group", SkType_Group INIT_BOOL_FIELDS }, + { "hitClear", SkType_HitClear INIT_BOOL_FIELDS }, + { "hitTest", SkType_HitTest INIT_BOOL_FIELDS }, + { "image", SkType_Image INIT_BOOL_FIELDS }, + { "include", SkType_Include INIT_BOOL_FIELDS }, + { "input", SkType_Input INIT_BOOL_FIELDS }, + { "int", SkType_Int INIT_BOOL_FIELDS }, + // join + { "line", SkType_Line INIT_BOOL_FIELDS }, + { "lineTo", SkType_LineTo INIT_BOOL_FIELDS }, + { "linearGradient", SkType_LinearGradient INIT_BOOL_FIELDS }, + { "maskFilter", SkType_MaskFilter INIT_BOOL_FIELDS }, + // maskfilterblurstyle + // maskfilterlight + DRAW_NAME("matrix", SkType_Matrix), + // memberfunction + // memberproperty + { "move", SkType_Move INIT_BOOL_FIELDS }, + { "moveTo", SkType_MoveTo INIT_BOOL_FIELDS }, + { "movie", SkType_Movie INIT_BOOL_FIELDS }, + // msec + { "oval", SkType_Oval INIT_BOOL_FIELDS }, + DRAW_NAME("paint", SkType_Paint), + DRAW_NAME("path", SkType_Path), + // pathdirection + { "pathEffect", SkType_PathEffect INIT_BOOL_FIELDS }, + // point + DRAW_NAME("point", SkType_DrawPoint), + { "polyToPoly", SkType_PolyToPoly INIT_BOOL_FIELDS }, + { "polygon", SkType_Polygon INIT_BOOL_FIELDS }, + { "polyline", SkType_Polyline INIT_BOOL_FIELDS }, + { "post", SkType_Post INIT_BOOL_FIELDS }, + { "quadTo", SkType_QuadTo INIT_BOOL_FIELDS }, + { "rCubicTo", SkType_RCubicTo INIT_BOOL_FIELDS }, + { "rLineTo", SkType_RLineTo INIT_BOOL_FIELDS }, + { "rMoveTo", SkType_RMoveTo INIT_BOOL_FIELDS }, + { "rQuadTo", SkType_RQuadTo INIT_BOOL_FIELDS }, + { "radialGradient", SkType_RadialGradient INIT_BOOL_FIELDS }, + DISPLAY_NAME("random", SkType_Random), + { "rect", SkType_Rect INIT_BOOL_FIELDS }, + { "rectToRect", SkType_RectToRect INIT_BOOL_FIELDS }, + { "remove", SkType_Remove INIT_BOOL_FIELDS }, + { "replace", SkType_Replace INIT_BOOL_FIELDS }, + { "rotate", SkType_Rotate INIT_BOOL_FIELDS }, + { "roundRect", SkType_RoundRect INIT_BOOL_FIELDS }, + { "save", SkType_Save INIT_BOOL_FIELDS }, + { "saveLayer", SkType_SaveLayer INIT_BOOL_FIELDS }, + { "scale", SkType_Scale INIT_BOOL_FIELDS }, + // screenplay + { "set", SkType_Set INIT_BOOL_FIELDS }, + { "shader", SkType_Shader INIT_BOOL_FIELDS }, + { "skew", SkType_Skew INIT_BOOL_FIELDS }, + { "skia3d:camera", SkType_3D_Camera INIT_BOOL_FIELDS }, + { "skia3d:patch", SkType_3D_Patch INIT_BOOL_FIELDS }, + // point + { "snapshot", SkType_Snapshot INIT_BOOL_FIELDS }, + { "string", SkType_String INIT_BOOL_FIELDS }, + // style + { "text", SkType_Text INIT_BOOL_FIELDS }, + { "textBox", SkType_TextBox INIT_BOOL_FIELDS }, + // textboxalign + // textboxmode + { "textOnPath", SkType_TextOnPath INIT_BOOL_FIELDS }, + { "textToPath", SkType_TextToPath INIT_BOOL_FIELDS }, + // tilemode + { "translate", SkType_Translate INIT_BOOL_FIELDS }, + DRAW_NAME("transparentShader", SkType_TransparentShader), + { "typeface", SkType_Typeface INIT_BOOL_FIELDS } + // xfermode + // knumberoftypes +}; + +const int kTypeNamesSize = SK_ARRAY_COUNT(gTypeNames); + +SkDisplayTypes SkDisplayType::Find(SkAnimateMaker* maker, const SkMemberInfo* match) { + for (int index = 0; index < kTypeNamesSize; index++) { + SkDisplayTypes type = gTypeNames[index].fType; + const SkMemberInfo* info = SkDisplayType::GetMembers(maker, type, NULL); + if (info == match) + return type; + } + return (SkDisplayTypes) -1; +} + +// !!! optimize this by replacing function with a byte-sized lookup table +SkDisplayTypes SkDisplayType::GetParent(SkAnimateMaker* maker, SkDisplayTypes base) { + if (base == SkType_Group || base == SkType_Save || base == SkType_SaveLayer) //!!! cheat a little until we have a lookup table + return SkType_Displayable; + if (base == SkType_Set) + return SkType_Animate; // another cheat until we have a lookup table + const SkMemberInfo* info = GetMembers(maker, base, NULL); // get info for this type + SkASSERT(info); + if (info->fType != SkType_BaseClassInfo) + return SkType_Unknown; // if no base, done + // !!! could change SK_MEMBER_INHERITED macro to take type, stuff in offset, so that + // this (and table builder) could know type without the following steps: + const SkMemberInfo* inherited = info->getInherited(); + SkDisplayTypes result = (SkDisplayTypes) (SkType_Unknown + 1); + for (; result <= SkType_Xfermode; result = (SkDisplayTypes) (result + 1)) { + const SkMemberInfo* match = GetMembers(maker, result, NULL); + if (match == inherited) + break; + } + SkASSERT(result <= SkType_Xfermode); + return result; +} + +SkDisplayTypes SkDisplayType::GetType(SkAnimateMaker* maker, const char match[], size_t len ) { + int index = SkStrSearch(&gTypeNames[0].fName, kTypeNamesSize, match, + len, sizeof(gTypeNames[0])); + if (index >= 0 && index < kTypeNamesSize) + return gTypeNames[index].fType; + SkExtras** end = maker->fExtras.end(); + for (SkExtras** extraPtr = maker->fExtras.begin(); extraPtr < end; extraPtr++) { + SkDisplayTypes result = (*extraPtr)->getType(match, len); + if (result != SkType_Unknown) + return result; + } + return (SkDisplayTypes) -1; +} + +bool SkDisplayType::IsEnum(SkAnimateMaker* , SkDisplayTypes type) { + switch (type) { + case SkType_AddMode: + case SkType_Align: + case SkType_ApplyMode: + case SkType_ApplyTransition: + case SkType_BitmapEncoding: + case SkType_BitmapFormat: + case SkType_Boolean: + case SkType_Cap: + case SkType_EventCode: + case SkType_EventKind: + case SkType_EventMode: + case SkType_FillType: + case SkType_FilterType: + case SkType_FontStyle: + case SkType_FromPathMode: + case SkType_Join: + case SkType_MaskFilterBlurStyle: + case SkType_PathDirection: + case SkType_Style: + case SkType_TextBoxAlign: + case SkType_TextBoxMode: + case SkType_TileMode: + case SkType_Xfermode: + return true; + default: // to avoid warnings + break; + } + return false; +} + +bool SkDisplayType::IsDisplayable(SkAnimateMaker* , SkDisplayTypes type) { + switch (type) { + case SkType_Add: + case SkType_AddCircle: + case SkType_AddOval: + case SkType_AddPath: + case SkType_AddRect: + case SkType_AddRoundRect: + case SkType_Animate: + case SkType_AnimateBase: + case SkType_Apply: + case SkType_BaseBitmap: + case SkType_Bitmap: + case SkType_BitmapShader: + case SkType_Blur: + case SkType_Clear: + case SkType_Clip: + case SkType_Close: + case SkType_Color: + case SkType_CubicTo: + case SkType_Dash: + case SkType_Data: + case SkType_Discrete: + case SkType_Displayable: + case SkType_Drawable: + case SkType_DrawTo: + case SkType_Emboss: + case SkType_Event: + case SkType_FromPath: + case SkType_Full: + case SkType_Group: + case SkType_Image: + case SkType_Input: + case SkType_Line: + case SkType_LineTo: + case SkType_LinearGradient: + case SkType_Matrix: + case SkType_Move: + case SkType_MoveTo: + case SkType_Movie: + case SkType_Oval: + case SkType_Paint: + case SkType_Path: + case SkType_PolyToPoly: + case SkType_Polygon: + case SkType_Polyline: + case SkType_Post: + case SkType_QuadTo: + case SkType_RCubicTo: + case SkType_RLineTo: + case SkType_RMoveTo: + case SkType_RQuadTo: + case SkType_RadialGradient: + case SkType_Random: + case SkType_Rect: + case SkType_RectToRect: + case SkType_Remove: + case SkType_Replace: + case SkType_Rotate: + case SkType_RoundRect: + case SkType_Save: + case SkType_SaveLayer: + case SkType_Scale: + case SkType_Set: + case SkType_Shader: + case SkType_Skew: + case SkType_3D_Camera: + case SkType_3D_Patch: + case SkType_Snapshot: + case SkType_Text: + case SkType_TextBox: + case SkType_TextOnPath: + case SkType_TextToPath: + case SkType_Translate: + case SkType_TransparentShader: + return true; + default: // to avoid warnings + break; + } + return false; +} + +bool SkDisplayType::IsStruct(SkAnimateMaker* , SkDisplayTypes type) { + switch (type) { + case SkType_Point: + case SkType_3D_Point: + return true; + default: // to avoid warnings + break; + } + return false; +} + + +SkDisplayTypes SkDisplayType::RegisterNewType() { + gNewTypes = (SkDisplayTypes) (gNewTypes + 1); + return gNewTypes; +} + + + +#ifdef SK_DEBUG +const char* SkDisplayType::GetName(SkAnimateMaker* maker, SkDisplayTypes type) { + for (int index = 0; index < kTypeNamesSize - 1; index++) { + if (gTypeNames[index].fType == type) + return gTypeNames[index].fName; + } + SkExtras** end = maker->fExtras.end(); + for (SkExtras** extraPtr = maker->fExtras.begin(); extraPtr < end; extraPtr++) { + const char* result = (*extraPtr)->getName(type); + if (result != NULL) + return result; + } + return NULL; +} +#endif + +#ifdef SK_SUPPORT_UNITTEST +void SkDisplayType::UnitTest() { + SkAnimator animator; + SkAnimateMaker* maker = animator.fMaker; + int index; + for (index = 0; index < kTypeNamesSize - 1; index++) { + SkASSERT(strcmp(gTypeNames[index].fName, gTypeNames[index + 1].fName) < 0); + SkASSERT(gTypeNames[index].fType < gTypeNames[index + 1].fType); + } + for (index = 0; index < kTypeNamesSize; index++) { + SkDisplayable* test = CreateInstance(maker, gTypeNames[index].fType); + if (test == NULL) + continue; +#if defined _WIN32 && _MSC_VER >= 1300 && defined _INC_CRTDBG // only on windows, only if using "crtdbg.h" + // we know that crtdbg puts 0xfdfdfdfd at the end of the block + // look for unitialized memory, signature 0xcdcdcdcd prior to that + int* start = (int*) test; + while (*start != 0xfdfdfdfd) { + SkASSERT(*start != 0xcdcdcdcd); + start++; + } +#endif + delete test; + } + for (index = 0; index < kTypeNamesSize; index++) { + int infoCount; + const SkMemberInfo* info = GetMembers(maker, gTypeNames[index].fType, &infoCount); + if (info == NULL) + continue; +#if SK_USE_CONDENSED_INFO == 0 + for (int inner = 0; inner < infoCount - 1; inner++) { + if (info[inner].fType == SkType_BaseClassInfo) + continue; + SkASSERT(strcmp(info[inner].fName, info[inner + 1].fName) < 0); + } +#endif + } +#if defined SK_DEBUG || defined SK_BUILD_CONDENSED + BuildCondensedInfo(maker); +#endif +} +#endif diff --git a/skia/animator/SkDisplayType.h b/skia/animator/SkDisplayType.h new file mode 100644 index 0000000..068fcddb --- /dev/null +++ b/skia/animator/SkDisplayType.h @@ -0,0 +1,216 @@ +/* libs/graphics/animator/SkDisplayType.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkDisplayType_DEFINED +#define SkDisplayType_DEFINED + +#include "SkMath.h" +#include "SkScalar.h" + +#ifdef SK_DEBUG + #ifdef SK_CAN_USE_FLOAT + #define SK_DUMP_ENABLED + #endif + #ifdef SK_BUILD_FOR_MAC + #define SK_FIND_LEAKS + #endif +#endif + +#define SK_LITERAL_STR_EQUAL(str, token, len) (sizeof(str) - 1 == len \ + && strncmp(str, token, sizeof(str) - 1) == 0) + +class SkAnimateMaker; +class SkDisplayable; +struct SkMemberInfo; + +enum SkDisplayTypes { + SkType_Unknown, + SkType_Math, // for ecmascript compatible Math functions and constants + SkType_Number, // for for ecmascript compatible Number functions and constants + SkType_Add, + SkType_AddCircle, + SkType_AddGeom, + SkType_AddMode, + SkType_AddOval, + SkType_AddPath, + SkType_AddRect, // path part + SkType_AddRoundRect, + SkType_Align, + SkType_Animate, + SkType_AnimateBase, // base type for animate, set + SkType_Apply, + SkType_ApplyMode, + SkType_ApplyTransition, + SkType_Array, + SkType_ARGB, + SkType_Base64, + SkType_BaseBitmap, + SkType_BaseClassInfo, + SkType_Bitmap, + SkType_BitmapEncoding, + SkType_BitmapFormat, + SkType_BitmapShader, + SkType_Blur, + SkType_Boolean, // can have values -1 (uninitialized), 0, 1 + SkType_Boundable, + SkType_Bounds, + SkType_Cap, + SkType_Clear, + SkType_Clip, + SkType_Close, + SkType_Color, + SkType_CubicTo, + SkType_Dash, + SkType_Data, + SkType_Discrete, + SkType_Displayable, + SkType_Drawable, + SkType_DrawTo, + SkType_Dump, + SkType_DynamicString, // evaluate at draw time + SkType_Emboss, + SkType_Event, + SkType_EventCode, + SkType_EventKind, + SkType_EventMode, + SkType_FillType, + SkType_FilterType, + SkType_Float, + SkType_FontStyle, + SkType_FromPath, + SkType_FromPathMode, + SkType_Full, + SkType_Gradient, + SkType_Group, + SkType_HitClear, + SkType_HitTest, + SkType_Image, + SkType_Include, + SkType_Input, + SkType_Int, + SkType_Join, + SkType_Line, // simple line primitive + SkType_LineTo, // used as part of path construction + SkType_LinearGradient, + SkType_MaskFilter, + SkType_MaskFilterBlurStyle, + SkType_MaskFilterLight, + SkType_Matrix, + SkType_MemberFunction, + SkType_MemberProperty, + SkType_Move, + SkType_MoveTo, + SkType_Movie, + SkType_MSec, + SkType_Oval, + SkType_Paint, + SkType_Path, + SkType_PathDirection, + SkType_PathEffect, + SkType_Point, // used inside other structures, no vtable + SkType_DrawPoint, // used to draw points, has a vtable + SkType_PolyToPoly, + SkType_Polygon, + SkType_Polyline, + SkType_Post, + SkType_QuadTo, + SkType_RCubicTo, + SkType_RLineTo, + SkType_RMoveTo, + SkType_RQuadTo, + SkType_RadialGradient, + SkType_Random, + SkType_Rect, + SkType_RectToRect, + SkType_Remove, + SkType_Replace, + SkType_Rotate, + SkType_RoundRect, + SkType_Save, + SkType_SaveLayer, + SkType_Scale, + SkType_Screenplay, + SkType_Set, + SkType_Shader, + SkType_Skew, + SkType_3D_Camera, + SkType_3D_Patch, + SkType_3D_Point, + SkType_Snapshot, + SkType_String, // pointer to SkString + SkType_Style, + SkType_Text, + SkType_TextBox, + SkType_TextBoxAlign, + SkType_TextBoxMode, + SkType_TextOnPath, + SkType_TextToPath, + SkType_TileMode, + SkType_Translate, + SkType_TransparentShader, + SkType_Typeface, + SkType_Xfermode, + kNumberOfTypes +}; + +struct TypeNames { + const char* fName; + SkDisplayTypes fType; +#if defined SK_DEBUG || defined SK_BUILD_CONDENSED + bool fDrawPrefix; + bool fDisplayPrefix; +#endif +}; + +#ifdef SK_DEBUG +typedef SkDisplayTypes SkFunctionParamType; +#else +typedef unsigned char SkFunctionParamType; +#endif + +extern const TypeNames gTypeNames[]; +extern const int kTypeNamesSize; + +class SkDisplayType { +public: + static SkDisplayTypes Find(SkAnimateMaker* , const SkMemberInfo* ); + static const SkMemberInfo* GetMember(SkAnimateMaker* , SkDisplayTypes , const char** ); + static const SkMemberInfo* GetMembers(SkAnimateMaker* , SkDisplayTypes , int* infoCountPtr); + static SkDisplayTypes GetParent(SkAnimateMaker* , SkDisplayTypes ); + static bool IsDisplayable(SkAnimateMaker* , SkDisplayTypes ); + static bool IsEnum(SkAnimateMaker* , SkDisplayTypes ); + static bool IsStruct(SkAnimateMaker* , SkDisplayTypes ); + static SkDisplayTypes RegisterNewType(); + static SkDisplayTypes Resolve(const char[] , const SkMemberInfo** ); +#ifdef SK_DEBUG + static bool IsAnimate(SkDisplayTypes type ) { return type == SkType_Animate || + type == SkType_Set; } + static const char* GetName(SkAnimateMaker* , SkDisplayTypes ); +#endif +#ifdef SK_SUPPORT_UNITTEST + static void UnitTest(); +#endif +#if defined SK_DEBUG || defined SK_BUILD_CONDENSED + static void BuildCondensedInfo(SkAnimateMaker* ); +#endif + static SkDisplayTypes GetType(SkAnimateMaker* , const char[] , size_t len); + static SkDisplayable* CreateInstance(SkAnimateMaker* , SkDisplayTypes ); +private: + static SkDisplayTypes gNewTypes; +}; + +#endif // SkDisplayType_DEFINED diff --git a/skia/animator/SkDisplayTypes.cpp b/skia/animator/SkDisplayTypes.cpp new file mode 100644 index 0000000..6c6ad08 --- /dev/null +++ b/skia/animator/SkDisplayTypes.cpp @@ -0,0 +1,229 @@ +/* libs/graphics/animator/SkDisplayTypes.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkDisplayTypes.h" +#include "SkAnimateBase.h" + +bool SkDisplayDepend::canContainDependents() const { + return true; +} + +void SkDisplayDepend::dirty() { + SkDisplayable** last = fDependents.end(); + for (SkDisplayable** depPtr = fDependents.begin(); depPtr < last; depPtr++) { + SkAnimateBase* animate = (SkAnimateBase* ) *depPtr; + animate->setChanged(true); + } +} + +// Boolean +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkDisplayBoolean::fInfo[] = { + SK_MEMBER(value, Boolean) +}; + +#endif + +DEFINE_GET_MEMBER(SkDisplayBoolean); + +SkDisplayBoolean::SkDisplayBoolean() : value(false) { +} + +#ifdef SK_DUMP_ENABLED +void SkDisplayBoolean::dump(SkAnimateMaker* maker){ + dumpBase(maker); + SkDebugf("value=\"%s\" />\n", value ? "true" : "false"); +} +#endif + +// int32_t +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkDisplayInt::fInfo[] = { + SK_MEMBER(value, Int) +}; + +#endif + +DEFINE_GET_MEMBER(SkDisplayInt); + +SkDisplayInt::SkDisplayInt() : value(0) { +} + +#ifdef SK_DUMP_ENABLED +void SkDisplayInt::dump(SkAnimateMaker* maker){ + dumpBase(maker); + SkDebugf("value=\"%d\" />\n", value); +} +#endif + +// SkScalar +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkDisplayFloat::fInfo[] = { + SK_MEMBER(value, Float) +}; + +#endif + +DEFINE_GET_MEMBER(SkDisplayFloat); + +SkDisplayFloat::SkDisplayFloat() : value(0) { +} + +#ifdef SK_DUMP_ENABLED +void SkDisplayFloat::dump(SkAnimateMaker* maker) { + dumpBase(maker); +#ifdef SK_CAN_USE_FLOAT + SkDebugf("value=\"%g\" />\n", SkScalarToFloat(value)); +#else + SkDebugf("value=\"%x\" />\n", value); +#endif +} +#endif + +// SkString +enum SkDisplayString_Functions { + SK_FUNCTION(slice) +}; + +enum SkDisplayString_Properties { + SK_PROPERTY(length) +}; + +const SkFunctionParamType SkDisplayString::fFunctionParameters[] = { + (SkFunctionParamType) SkType_Int, // slice + (SkFunctionParamType) SkType_Int, + (SkFunctionParamType) 0 +}; + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkDisplayString::fInfo[] = { + SK_MEMBER_PROPERTY(length, Int), + SK_MEMBER_FUNCTION(slice, String), + SK_MEMBER(value, String) +}; + +#endif + +DEFINE_GET_MEMBER(SkDisplayString); + +SkDisplayString::SkDisplayString() { +} + +SkDisplayString::SkDisplayString(SkString& copyFrom) : value(copyFrom) { +} + +void SkDisplayString::executeFunction(SkDisplayable* target, int index, + SkTDArray<SkScriptValue>& parameters, SkDisplayTypes type, + SkScriptValue* scriptValue) { + if (scriptValue == NULL) + return; + SkASSERT(target == this); + switch (index) { + case SK_FUNCTION(slice): + scriptValue->fType = SkType_String; + SkASSERT(parameters[0].fType == SkType_Int); + int start = parameters[0].fOperand.fS32; + if (start < 0) + start = (int) (value.size() - start); + int end = (int) value.size(); + if (parameters.count() > 1) { + SkASSERT(parameters[1].fType == SkType_Int); + end = parameters[1].fOperand.fS32; + } + //if (end >= 0 && end < (int) value.size()) + if (end >= 0 && end <= (int) value.size()) + scriptValue->fOperand.fString = new SkString(&value.c_str()[start], end - start); + else + scriptValue->fOperand.fString = new SkString(value); + break; + } +} + +const SkFunctionParamType* SkDisplayString::getFunctionsParameters() { + return fFunctionParameters; +} + +bool SkDisplayString::getProperty(int index, SkScriptValue* scriptValue) const { + switch (index) { + case SK_PROPERTY(length): + scriptValue->fType = SkType_Int; + scriptValue->fOperand.fS32 = (int32_t) value.size(); + break; + default: + SkASSERT(0); + return false; + } + return true; +} + + +// SkArray +#if 0 // !!! reason enough to qualify enum with class name or move typedArray into its own file +enum SkDisplayArray_Properties { + SK_PROPERTY(length) +}; +#endif + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkDisplayArray::fInfo[] = { + SK_MEMBER_PROPERTY(length, Int), + SK_MEMBER_ARRAY(values, Unknown) +}; + +#endif + +DEFINE_GET_MEMBER(SkDisplayArray); + +SkDisplayArray::SkDisplayArray() { +} + +SkDisplayArray::SkDisplayArray(SkTypedArray& copyFrom) : values(copyFrom) { + +} + +SkDisplayArray::~SkDisplayArray() { + if (values.getType() == SkType_String) { + for (int index = 0; index < values.count(); index++) + delete values[index].fString; + return; + } + if (values.getType() == SkType_Array) { + for (int index = 0; index < values.count(); index++) + delete values[index].fArray; + } +} + +bool SkDisplayArray::getProperty(int index, SkScriptValue* value) const { + switch (index) { + case SK_PROPERTY(length): + value->fType = SkType_Int; + value->fOperand.fS32 = values.count(); + break; + default: + SkASSERT(0); + return false; + } + return true; +} + + + diff --git a/skia/animator/SkDisplayTypes.h b/skia/animator/SkDisplayTypes.h new file mode 100644 index 0000000..738af18 --- /dev/null +++ b/skia/animator/SkDisplayTypes.h @@ -0,0 +1,115 @@ +/* libs/graphics/animator/SkDisplayTypes.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkDisplayTypes_DEFINED +#define SkDisplayTypes_DEFINED + +#include "SkDisplayable.h" +#include "SkMemberInfo.h" +#include "SkTypedArray.h" + +class SkOpArray; // compiled script experiment + + +class SkDisplayDepend : public SkDisplayable { +public: + virtual bool canContainDependents() const; + void addDependent(SkDisplayable* displayable) { + if (fDependents.find(displayable) < 0) + *fDependents.append() = displayable; + } + virtual void dirty(); +private: + SkTDDisplayableArray fDependents; + typedef SkDisplayable INHERITED; +}; + +class SkDisplayBoolean : public SkDisplayDepend { + DECLARE_DISPLAY_MEMBER_INFO(Boolean); + SkDisplayBoolean(); +#ifdef SK_DUMP_ENABLED + virtual void dump(SkAnimateMaker* ); +#endif + SkBool value; + friend class SkAnimatorScript; + friend class SkAnimatorScript_Box; + friend class SkAnimatorScript_Unbox; + typedef SkDisplayDepend INHERITED; +}; + +class SkDisplayInt : public SkDisplayDepend { + DECLARE_DISPLAY_MEMBER_INFO(Int); + SkDisplayInt(); +#ifdef SK_DUMP_ENABLED + virtual void dump(SkAnimateMaker* ); +#endif +private: + int32_t value; + friend class SkAnimatorScript; + friend class SkAnimatorScript_Box; + friend class SkAnimatorScript_Unbox; + typedef SkDisplayDepend INHERITED; +}; + +class SkDisplayFloat : public SkDisplayDepend { + DECLARE_DISPLAY_MEMBER_INFO(Float); + SkDisplayFloat(); +#ifdef SK_DUMP_ENABLED + virtual void dump(SkAnimateMaker* ); +#endif +private: + SkScalar value; + friend class SkAnimatorScript; + friend class SkAnimatorScript_Box; + friend class SkAnimatorScript_Unbox; + typedef SkDisplayDepend INHERITED; +}; + +class SkDisplayString : public SkDisplayDepend { + DECLARE_DISPLAY_MEMBER_INFO(String); + SkDisplayString(); + SkDisplayString(SkString& ); + virtual void executeFunction(SkDisplayable* , int index, + SkTDArray<SkScriptValue>& parameters, SkDisplayTypes type, + SkScriptValue* ); + virtual const SkFunctionParamType* getFunctionsParameters(); + virtual bool getProperty(int index, SkScriptValue* ) const; + SkString value; +private: + static const SkFunctionParamType fFunctionParameters[]; +}; + +class SkDisplayArray : public SkDisplayDepend { + DECLARE_DISPLAY_MEMBER_INFO(Array); + SkDisplayArray(); + SkDisplayArray(SkTypedArray& ); + SkDisplayArray(SkOpArray& ); // compiled script experiment + virtual ~SkDisplayArray(); + virtual bool getProperty(int index, SkScriptValue* ) const; +private: + SkTypedArray values; + friend class SkAnimator; + friend class SkAnimatorScript; + friend class SkAnimatorScript2; + friend class SkAnimatorScript_Unbox; + friend class SkDisplayable; + friend struct SkMemberInfo; + friend class SkScriptEngine; +}; + +#endif // SkDisplayTypes_DEFINED + diff --git a/skia/animator/SkDisplayXMLParser.cpp b/skia/animator/SkDisplayXMLParser.cpp new file mode 100644 index 0000000..39c4a39 --- /dev/null +++ b/skia/animator/SkDisplayXMLParser.cpp @@ -0,0 +1,318 @@ +/* libs/graphics/animator/SkDisplayXMLParser.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkDisplayXMLParser.h" +#include "SkAnimateMaker.h" +#include "SkDisplayApply.h" +#include "SkUtils.h" +#ifdef SK_DEBUG +#include "SkTime.h" +#endif + +static char const* const gErrorStrings[] = { + "unknown error ", + "apply scopes itself", + "display tree too deep (circular reference?) ", + "element missing parent ", + "element type not allowed in parent ", + "error adding <data> to <post> ", + "error adding to <matrix> ", + "error adding to <paint> ", + "error adding to <path> ", + "error in attribute value ", + "error in script ", + "expected movie in sink attribute ", + "field not in target ", + "number of offsets in gradient must match number of colors", + "no offset in gradient may be greater than one", + "last offset in gradient must be one", + "offsets in gradient must be increasing", + "first offset in gradient must be zero", + "gradient attribute \"points\" must have length of four", + "in include ", + "in movie ", + "include name unknown or missing ", + "index out of range ", + "movie name unknown or missing ", + "no parent available to resolve sink attribute ", + "parent element can't contain ", + "saveLayer must specify a bounds", + "target id not found ", + "unexpected type " +}; + +SkDisplayXMLParserError::~SkDisplayXMLParserError() { +} + +void SkDisplayXMLParserError::getErrorString(SkString* str) const { + if (fCode > kUnknownError) + str->set(gErrorStrings[fCode - kUnknownError]); + else + str->reset(); + INHERITED::getErrorString(str); +} + +void SkDisplayXMLParserError::setInnerError(SkAnimateMaker* parent, const SkString& src) { + SkString inner; + getErrorString(&inner); + inner.prepend(": "); + inner.prependS32(getLineNumber()); + inner.prepend(", line "); + inner.prepend(src); + parent->setErrorNoun(inner); +} + + +SkDisplayXMLParser::SkDisplayXMLParser(SkAnimateMaker& maker) + : INHERITED(&maker.fError), fMaker(maker), fInInclude(maker.fInInclude), + fInSkia(maker.fInInclude), fCurrDisplayable(NULL) +{ +} + +SkDisplayXMLParser::~SkDisplayXMLParser() { + if (fCurrDisplayable && fMaker.fChildren.find(fCurrDisplayable) < 0) + delete fCurrDisplayable; + for (Parent* parPtr = fParents.begin() + 1; parPtr < fParents.end(); parPtr++) { + SkDisplayable* displayable = parPtr->fDisplayable; + if (displayable == fCurrDisplayable) + continue; + SkASSERT(fMaker.fChildren.find(displayable) < 0); + if (fMaker.fHelpers.find(displayable) < 0) + delete displayable; + } +} + + + +bool SkDisplayXMLParser::onAddAttribute(const char name[], const char value[]) { + return onAddAttributeLen(name, value, strlen(value)); +} + +bool SkDisplayXMLParser::onAddAttributeLen(const char attrName[], const char attrValue[], + size_t attrValueLen) +{ + if (fCurrDisplayable == NULL) // this signals we should ignore attributes for this element + return strncmp(attrName, "xmlns", sizeof("xmlns") - 1) != 0; + SkDisplayable* displayable = fCurrDisplayable; + SkDisplayTypes type = fCurrType; + + if (strcmp(attrName, "id") == 0) { + if (fMaker.find(attrValue, attrValueLen, NULL)) { + fError->setNoun(attrValue, attrValueLen); + fError->setCode(SkXMLParserError::kDuplicateIDs); + return true; + } +#ifdef SK_DEBUG + displayable->_id.set(attrValue, attrValueLen); + displayable->id = displayable->_id.c_str(); +#endif + fMaker.idsSet(attrValue, attrValueLen, displayable); + int parentIndex = fParents.count() - 1; + if (parentIndex > 0) { + SkDisplayable* parent = fParents[parentIndex - 1].fDisplayable; + parent->setChildHasID(); + } + return false; + } + const char* name = attrName; + const SkMemberInfo* info = SkDisplayType::GetMember(&fMaker, type, &name); + if (info == NULL) { + fError->setNoun(name); + fError->setCode(SkXMLParserError::kUnknownAttributeName); + return true; + } + if (info->setValue(fMaker, NULL, 0, info->getCount(), displayable, info->getType(), attrValue, + attrValueLen)) + return false; + if (fMaker.fError.hasError()) { + fError->setNoun(attrValue, attrValueLen); + return true; + } + SkDisplayable* ref = NULL; + if (fMaker.find(attrValue, attrValueLen, &ref) == false) { + ref = fMaker.createInstance(attrValue, attrValueLen); + if (ref == NULL) { + fError->setNoun(attrValue, attrValueLen); + fError->setCode(SkXMLParserError::kErrorInAttributeValue); + return true; + } else + fMaker.helperAdd(ref); + } + if (info->fType != SkType_MemberProperty) { + fError->setNoun(name); + fError->setCode(SkXMLParserError::kUnknownAttributeName); + return true; + } + SkScriptValue scriptValue; + scriptValue.fOperand.fDisplayable = ref; + scriptValue.fType = ref->getType(); + displayable->setProperty(info->propertyIndex(), scriptValue); + return false; +} + +bool SkDisplayXMLParser::onEndElement(const char elem[]) +{ + int parentIndex = fParents.count() - 1; + if (parentIndex >= 0) { + Parent& container = fParents[parentIndex]; + SkDisplayable* displayable = container.fDisplayable; + fMaker.fEndDepth = parentIndex; + displayable->onEndElement(fMaker); + if (fMaker.fError.hasError()) + return true; + if (parentIndex > 0) { + SkDisplayable* parent = fParents[parentIndex - 1].fDisplayable; + bool result = parent->add(fMaker, displayable); + if (fMaker.hasError()) + return true; + if (result == false) { + int infoCount; + const SkMemberInfo* info = + SkDisplayType::GetMembers(&fMaker, fParents[parentIndex - 1].fType, &infoCount); + const SkMemberInfo* foundInfo; + if ((foundInfo = searchContainer(info, infoCount)) != NULL) { + parent->setReference(foundInfo, displayable); + // if (displayable->isHelper() == false) + fMaker.helperAdd(displayable); + } else { + fMaker.setErrorCode(SkDisplayXMLParserError::kElementTypeNotAllowedInParent); + return true; + } + } + if (parent->childrenNeedDisposing()) + delete displayable; + } + fParents.remove(parentIndex); + } + fCurrDisplayable = NULL; + if (fInInclude == false && strcasecmp(elem, "screenplay") == 0) { + if (fMaker.fInMovie == false) { + fMaker.fEnableTime = fMaker.getAppTime(); +#if defined SK_DEBUG && defined SK_DEBUG_ANIMATION_TIMING + if (fMaker.fDebugTimeBase == (SkMSec) -1) + fMaker.fDebugTimeBase = fMaker.fEnableTime; + SkString debugOut; + SkMSec time = fMaker.getAppTime(); + debugOut.appendS32(time - fMaker.fDebugTimeBase); + debugOut.append(" onLoad enable="); + debugOut.appendS32(fMaker.fEnableTime - fMaker.fDebugTimeBase); + SkDebugf("%s\n", debugOut.c_str()); +#endif + fMaker.fEvents.doEvent(fMaker, SkDisplayEvent::kOnload, NULL); + if (fMaker.fError.hasError()) + return true; + fMaker.fEvents.removeEvent(SkDisplayEvent::kOnload, NULL); + + } + fInSkia = false; + } + return false; +} + +bool SkDisplayXMLParser::onStartElement(const char name[]) +{ + return onStartElementLen(name, strlen(name)); +} + +bool SkDisplayXMLParser::onStartElementLen(const char name[], size_t len) { + fCurrDisplayable = NULL; // init so we'll ignore attributes if we exit early + + if (strncasecmp(name, "screenplay", len) == 0) { + fInSkia = true; + if (fInInclude == false) + fMaker.idsSet(name, len, &fMaker.fScreenplay); + return false; + } + if (fInSkia == false) + return false; + + SkDisplayable* displayable = fMaker.createInstance(name, len); + if (displayable == NULL) { + fError->setNoun(name, len); + fError->setCode(SkXMLParserError::kUnknownElement); + return true; + } + SkDisplayTypes type = displayable->getType(); + Parent record = { displayable, type }; + *fParents.append() = record; + if (fParents.count() == 1) + fMaker.childrenAdd(displayable); + else { + Parent* parent = fParents.end() - 2; + if (displayable->setParent(parent->fDisplayable)) { + fError->setNoun(name, len); + getError()->setCode(SkDisplayXMLParserError::kParentElementCantContain); + return true; + } + } + + // set these for subsequent calls to addAttribute() + fCurrDisplayable = displayable; + fCurrType = type; + return false; +} + +const SkMemberInfo* SkDisplayXMLParser::searchContainer(const SkMemberInfo* infoBase, + int infoCount) { + const SkMemberInfo* bestDisplayable = NULL; + const SkMemberInfo* lastResort = NULL; + for (int index = 0; index < infoCount; index++) { + const SkMemberInfo* info = &infoBase[index]; + if (info->fType == SkType_BaseClassInfo) { + const SkMemberInfo* inherited = info->getInherited(); + const SkMemberInfo* result = searchContainer(inherited, info->fCount); + if (result != NULL) + return result; + continue; + } + Parent* container = fParents.end() - 1; + SkDisplayTypes type = (SkDisplayTypes) info->fType; + if (type == SkType_MemberProperty) + type = info->propertyType(); + SkDisplayTypes containerType = container->fType; + if (type == containerType && (type == SkType_Rect || type == SkType_Polygon || + type == SkType_Array || type == SkType_Int || type == SkType_Bitmap)) + goto rectNext; + while (type != containerType) { + if (containerType == SkType_Displayable) + goto next; + containerType = SkDisplayType::GetParent(&fMaker, containerType); + if (containerType == SkType_Unknown) + goto next; + } + return info; +next: + if (type == SkType_Drawable || type == SkType_Displayable && + container->fDisplayable->isDrawable()) { +rectNext: + if (fParents.count() > 1) { + Parent* parent = fParents.end() - 2; + if (info == parent->fDisplayable->preferredChild(type)) + bestDisplayable = info; + else + lastResort = info; + } + } + } + if (bestDisplayable) + return bestDisplayable; + if (lastResort) + return lastResort; + return NULL; +} + + diff --git a/skia/animator/SkDisplayXMLParser.h b/skia/animator/SkDisplayXMLParser.h new file mode 100644 index 0000000..bddc809 --- /dev/null +++ b/skia/animator/SkDisplayXMLParser.h @@ -0,0 +1,101 @@ +/* libs/graphics/animator/SkDisplayXMLParser.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkDisplayXMLParser_DEFINED +#define SkDisplayXMLParser_DEFINED + +#include "SkIntArray.h" +#include "SkTDict.h" +#include "SkDisplayType.h" +#include "SkXMLParser.h" + +class SkAnimateMaker; +class SkDisplayable; + +class SkDisplayXMLParserError : public SkXMLParserError { +public: + enum ErrorCode { + kApplyScopesItself = kUnknownError + 1, + kDisplayTreeTooDeep, + kElementMissingParent, + kElementTypeNotAllowedInParent, + kErrorAddingDataToPost, + kErrorAddingToMatrix, + kErrorAddingToPaint, + kErrorAddingToPath, + kErrorInAttributeValue, + kErrorInScript, + kExpectedMovie, + kFieldNotInTarget, + kGradientOffsetsDontMatchColors, + kGradientOffsetsMustBeNoMoreThanOne, + kGradientOffsetsMustEndWithOne, + kGradientOffsetsMustIncrease, + kGradientOffsetsMustStartWithZero, + kGradientPointsLengthMustBeFour, + kInInclude, + kInMovie, + kIncludeNameUnknownOrMissing, + kIndexOutOfRange, + kMovieNameUnknownOrMissing, + kNoParentAvailable, + kParentElementCantContain, + kSaveLayerNeedsBounds, + kTargetIDNotFound, + kUnexpectedType + }; + virtual ~SkDisplayXMLParserError(); + virtual void getErrorString(SkString* str) const; + void setCode(ErrorCode code) { INHERITED::setCode((INHERITED::ErrorCode) code); } + void setInnerError(SkAnimateMaker* maker, const SkString& str); + typedef SkXMLParserError INHERITED; + friend class SkDisplayXMLParser; +}; + +class SkDisplayXMLParser : public SkXMLParser { +public: + SkDisplayXMLParser(SkAnimateMaker& maker); + virtual ~SkDisplayXMLParser(); +protected: + virtual bool onAddAttribute(const char name[], const char value[]); + bool onAddAttributeLen(const char name[], const char value[], size_t len); + virtual bool onEndElement(const char elem[]); + virtual bool onStartElement(const char elem[]); + bool onStartElementLen(const char elem[], size_t len); +private: + struct Parent { + SkDisplayable* fDisplayable; + SkDisplayTypes fType; + }; + SkTDArray<Parent> fParents; + SkDisplayXMLParser& operator= (const SkDisplayXMLParser& ); + SkDisplayXMLParserError* getError() { return (SkDisplayXMLParserError*) fError; } + const SkMemberInfo* searchContainer(const SkMemberInfo* , + int infoCount); + SkAnimateMaker& fMaker; + SkBool fInInclude; + SkBool fInSkia; + // local state between onStartElement and onAddAttribute + SkDisplayable* fCurrDisplayable; + SkDisplayTypes fCurrType; + friend class SkXMLAnimatorWriter; + typedef SkXMLParser INHERITED; +}; + +#endif // SkDisplayXMLParser_DEFINED + + diff --git a/skia/animator/SkDisplayable.cpp b/skia/animator/SkDisplayable.cpp new file mode 100644 index 0000000..83182e5 --- /dev/null +++ b/skia/animator/SkDisplayable.cpp @@ -0,0 +1,566 @@ +/* libs/graphics/animator/SkDisplayable.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkDisplayable.h" +#include "SkDisplayApply.h" +#include "SkParse.h" +#ifdef SK_DEBUG +#include "SkDisplayList.h" +#endif +#include "SkDisplayTypes.h" + +#ifdef SK_FIND_LEAKS +// int SkDisplayable::fAllocationCount; +SkTDDisplayableArray SkDisplayable::fAllocations; +#endif + +#ifdef SK_DEBUG +SkDisplayable::SkDisplayable() { + id = _id.c_str(); +#ifdef SK_FIND_LEAKS + // fAllocationCount++; + *fAllocations.append() = this; +#endif +} +#endif + +SkDisplayable::~SkDisplayable() { +#ifdef SK_FIND_LEAKS + // fAllocationCount--; + int index = fAllocations.find(this); + SkASSERT(index >= 0); + fAllocations.remove(index); +#endif +} + +bool SkDisplayable::add(SkAnimateMaker& , SkDisplayable* child) { + return false; +} + +//void SkDisplayable::apply(SkAnimateMaker& , const SkMemberInfo* , +// SkDisplayable* , SkScalar [], int count) { +// SkASSERT(0); +//} + +bool SkDisplayable::canContainDependents() const { + return false; +} + +bool SkDisplayable::childrenNeedDisposing() const { + return false; +} + +void SkDisplayable::clearBounder() { +} + +bool SkDisplayable::contains(SkDisplayable* ) { + return false; +} + +SkDisplayable* SkDisplayable::contains(const SkString& ) { + return NULL; +} + +SkDisplayable* SkDisplayable::deepCopy(SkAnimateMaker* maker) { + SkDisplayTypes type = getType(); + if (type == SkType_Unknown) { + SkASSERT(0); + return NULL; + } + SkDisplayable* copy = SkDisplayType::CreateInstance(maker, type); + int index = -1; + int propIndex = 0; + const SkMemberInfo* info; + do { + info = copy->getMember(++index); + if (info == NULL) + break; + if (info->fType == SkType_MemberProperty) { + SkScriptValue value; + if (getProperty(propIndex, &value)) + copy->setProperty(propIndex, value); + propIndex++; + continue; + } + if (info->fType == SkType_MemberFunction) + continue; + if (info->fType == SkType_Array) { + SkTDOperandArray* array = (SkTDOperandArray*) info->memberData(this); + int arrayCount; + if (array == NULL || (arrayCount = array->count()) == 0) + continue; + SkTDOperandArray* copyArray = (SkTDOperandArray*) info->memberData(copy); + copyArray->setCount(arrayCount); + SkDisplayTypes elementType; + if (type == SkType_Array) { + SkDisplayArray* dispArray = (SkDisplayArray*) this; + elementType = dispArray->values.getType(); + } else + elementType = info->arrayType(); + size_t elementSize = SkMemberInfo::GetSize(elementType); + size_t byteSize = elementSize * arrayCount; + memcpy(copyArray->begin(), array->begin(), byteSize); + continue; + } + if (SkDisplayType::IsDisplayable(maker, info->fType)) { + SkDisplayable** displayable = (SkDisplayable**) info->memberData(this); + if (*displayable == NULL || *displayable == (SkDisplayable*) -1) + continue; + SkDisplayable* deeper = (*displayable)->deepCopy(maker); + info->setMemberData(copy, deeper, sizeof(deeper)); + continue; + } + if (info->fType == SkType_String || info->fType == SkType_DynamicString) { + SkString* string; + info->getString(this, &string); + info->setString(copy, string); + continue; + } + void* data = info->memberData(this); + size_t size = SkMemberInfo::GetSize(info->fType); + info->setMemberData(copy, data, size); + } while (true); + copy->dirty(); + return copy; +} + +void SkDisplayable::dirty() { +} + +#ifdef SK_DUMP_ENABLED +void SkDisplayable::dump(SkAnimateMaker* maker) { + dumpBase(maker); +#if SK_USE_CONDENSED_INFO == 0 + this->dumpAttrs(maker); + this->dumpChildren(maker); +#endif +} + +void SkDisplayable::dumpAttrs(SkAnimateMaker* maker) { + SkDisplayTypes type = getType(); + if (type == SkType_Unknown) { + //SkDebugf("/>\n"); + return; + } + SkDisplayable* blankCopy = SkDisplayType::CreateInstance(maker, type); + + int index = -1; + int propIndex = 0; + const SkMemberInfo* info; + const SkMemberInfo* blankInfo; + SkScriptValue value; + SkScriptValue blankValue; + SkOperand values[2]; + SkOperand blankValues[2]; + do { + info = this->getMember(++index); + if (NULL == info) { + //SkDebugf("\n"); + break; + } + if (SkType_MemberProperty == info->fType) { + if (getProperty(propIndex, &value)) { + blankCopy->getProperty(propIndex, &blankValue); + //last two are dummies + dumpValues(info, value.fType, value.fOperand, blankValue.fOperand, value.fOperand, blankValue.fOperand); + } + + propIndex++; + continue; + } + if (SkDisplayType::IsDisplayable(maker, info->fType)) { + continue; + } + + if (info->fType == SkType_MemberFunction) + continue; + + + if (info->fType == SkType_Array) { + SkTDOperandArray* array = (SkTDOperandArray*) info->memberData(this); + int arrayCount; + if (array == NULL || (arrayCount = array->count()) == 0) + continue; + SkDisplayTypes elementType; + if (type == SkType_Array) { + SkDisplayArray* dispArray = (SkDisplayArray*) this; + elementType = dispArray->values.getType(); + } else + elementType = info->arrayType(); + bool firstElem = true; + SkDebugf("%s=\"[", info->fName); + for (SkOperand* op = array->begin(); op < array->end(); op++) { + if (!firstElem) SkDebugf(","); + switch (elementType) { + case SkType_Displayable: + SkDebugf("%s", op->fDisplayable->id); + break; + case SkType_Int: + SkDebugf("%d", op->fS32); + break; + case SkType_Float: +#ifdef SK_CAN_USE_FLOAT + SkDebugf("%g", SkScalarToFloat(op->fScalar)); +#else + SkDebugf("%x", op->fScalar); +#endif + break; + case SkType_String: + case SkType_DynamicString: + SkDebugf("%s", op->fString->c_str()); + break; + default: + break; + } + firstElem = false; + } + SkDebugf("]\" "); + continue; + } + + if (info->fType == SkType_String || info->fType == SkType_DynamicString) { + SkString* string; + info->getString(this, &string); + if (string->isEmpty() == false) + SkDebugf("%s=\"%s\"\t", info->fName, string->c_str()); + continue; + } + + + blankInfo = blankCopy->getMember(index); + int i = info->fCount; + info->getValue(this, values, i); + blankInfo->getValue(blankCopy, blankValues, i); + dumpValues(info, info->fType, values[0], blankValues[0], values[1], blankValues[1]); + } while (true); + delete blankCopy; +} + +void SkDisplayable::dumpBase(SkAnimateMaker* maker) { + SkDisplayTypes type = getType(); + const char* elementName = "(unknown)"; + if (type != SkType_Unknown && type != SkType_Screenplay) + elementName = SkDisplayType::GetName(maker, type); + SkDebugf("%*s", SkDisplayList::fIndent, ""); + if (SkDisplayList::fDumpIndex != 0 && SkDisplayList::fIndent == 0) + SkDebugf("%d: ", SkDisplayList::fDumpIndex); + SkDebugf("<%s ", elementName); + if (strcmp(id,"") != 0) + SkDebugf("id=\"%s\" ", id); +} + +void SkDisplayable::dumpChildren(SkAnimateMaker* maker, bool closedAngle) { + + int index = -1; + const SkMemberInfo* info; + index = -1; + SkDisplayList::fIndent += 4; + do { + info = this->getMember(++index); + if (NULL == info) { + break; + } + if (SkDisplayType::IsDisplayable(maker, info->fType)) { + SkDisplayable** displayable = (SkDisplayable**) info->memberData(this); + if (*displayable == NULL || *displayable == (SkDisplayable*) -1) + continue; + if (closedAngle == false) { + SkDebugf(">\n"); + closedAngle = true; + } + (*displayable)->dump(maker); + } + } while (true); + SkDisplayList::fIndent -= 4; + if (closedAngle) + dumpEnd(maker); + else + SkDebugf("/>\n"); +} + +void SkDisplayable::dumpEnd(SkAnimateMaker* maker) { + SkDisplayTypes type = getType(); + const char* elementName = "(unknown)"; + if (type != SkType_Unknown && type != SkType_Screenplay) + elementName = SkDisplayType::GetName(maker, type); + SkDebugf("%*s", SkDisplayList::fIndent, ""); + SkDebugf("</%s>\n", elementName); +} + +void SkDisplayable::dumpEvents() { +} + +void SkDisplayable::dumpValues(const SkMemberInfo* info, SkDisplayTypes type, SkOperand op, SkOperand blankOp, + SkOperand op2, SkOperand blankOp2) { + switch (type) { + case SkType_BitmapEncoding: + switch (op.fS32) { + case 0 : SkDebugf("type=\"jpeg\" "); + break; + case 1 : SkDebugf("type=\"png\" "); + break; + default: SkDebugf("type=\"UNDEFINED\" "); + } + break; + //should make this a separate case in dump attrs, rather than make dump values have a larger signature + case SkType_Point: + if (op.fScalar != blankOp.fScalar || op2.fScalar != blankOp.fScalar) { +#ifdef SK_CAN_USE_FLOAT + SkDebugf("%s=\"[%g,%g]\" ", info->fName, SkScalarToFloat(op.fScalar), SkScalarToFloat(op2.fScalar)); +#else + SkDebugf("%s=\"[%x,%x]\" ", info->fName, op.fScalar, op2.fScalar); +#endif + } + break; + case SkType_FromPathMode: + switch (op.fS32) { + case 0: + //don't want to print anything for 0, just adding it to remove it from default: + break; + case 1: + SkDebugf("%s=\"%s\" ", info->fName, "angle"); + break; + case 2: + SkDebugf("%s=\"%s\" ", info->fName, "position"); + break; + default: + SkDebugf("%s=\"INVALID\" ", info->fName); + } + break; + case SkType_MaskFilterBlurStyle: + switch (op.fS32) { + case 0: + break; + case 1: + SkDebugf("%s=\"%s\" ", info->fName, "solid"); + break; + case 2: + SkDebugf("%s=\"%s\" ", info->fName, "outer"); + break; + case 3: + SkDebugf("%s=\"%s\" ", info->fName, "inner"); + break; + default: + SkDebugf("%s=\"INVALID\" ", info->fName); + } + break; + case SkType_FilterType: + if (op.fS32 == 1) + SkDebugf("%s=\"%s\" ", info->fName, "bilinear"); + break; + case SkType_PathDirection: + SkDebugf("%s=\"%s\" ", info->fName, op.fS32 == 0 ? "cw" : "ccw"); + break; + case SkType_FillType: + SkDebugf("%s=\"%s\" ", info->fName, op.fS32 == 0 ? "winding" : "evenOdd"); + break; + case SkType_TileMode: + //correct to look at the S32? + if (op.fS32 != blankOp.fS32) + SkDebugf("%s=\"%s\" ", info->fName, op.fS32 == 0 ? "clamp" : op.fS32 == 1 ? "repeat" : "mirror"); + break; + case SkType_Boolean: + if (op.fS32 != blankOp.fS32) + SkDebugf("%s=\"%s\" ", info->fName, op.fS32 == 0 ? "false" : "true"); + break; + case SkType_Int: + if (op.fS32 != blankOp.fS32) + SkDebugf(" %s=\"%d\" ", info->fName, op.fS32); + break; + case SkType_Float: + if (op.fScalar != blankOp.fScalar) { //or /65536? +#ifdef SK_CAN_USE_FLOAT + SkDebugf("%s=\"%g\" ", info->fName, SkScalarToFloat(op.fScalar)); +#else + SkDebugf("%s=\"%x\" ", info->fName, op.fScalar); +#endif + } + break; + case SkType_String: + case SkType_DynamicString: + if (op.fString->size() > 0) + SkDebugf("%s=\"%s\" ", info->fName, op.fString->c_str()); + break; + case SkType_MSec: + if (op.fS32 != blankOp.fS32) { +#ifdef SK_CAN_USE_FLOAT + SkDebugf(" %s=\"%g\" ", info->fName, SkScalarToFloat(SkScalarDiv(op.fS32, 1000))); +#else + SkDebugf(" %s=\"%x\" ", info->fName, SkScalarDiv(op.fS32, 1000)); +#endif + } + default: + SkDebugf(""); + } +} + +#endif + +bool SkDisplayable::enable( SkAnimateMaker& ) { + return false; +} + +void SkDisplayable::enableBounder() { +} + +void SkDisplayable::executeFunction(SkDisplayable* , int index, + SkTDArray<SkScriptValue>& , SkDisplayTypes, SkScriptValue* ) { + SkASSERT(0); +} + +void SkDisplayable::executeFunction(SkDisplayable* target, + const SkMemberInfo* info, SkTypedArray* values, SkScriptValue* value) { + SkTDArray<SkScriptValue> typedValues; + for (SkOperand* op = values->begin(); op < values->end(); op++) { + SkScriptValue temp; + temp.fType = values->getType(); + temp.fOperand = *op; + *typedValues.append() = temp; + } + executeFunction(target, info->functionIndex(), typedValues, info->getType(), value); +} + +void SkDisplayable::executeFunction2(SkDisplayable* , int index, + SkOpArray* params, SkDisplayTypes, SkOperand2* ) { + SkASSERT(0); +} + +void SkDisplayable::getBounds(SkRect* rect) { + SkASSERT(rect); + rect->fLeft = rect->fTop = SK_ScalarMax; + rect->fRight= rect->fBottom = -SK_ScalarMax; +} + +const SkFunctionParamType* SkDisplayable::getFunctionsParameters() { + return NULL; +} + +const SkMemberInfo* SkDisplayable::getMember(int index) { + return NULL; +} + +const SkMemberInfo* SkDisplayable::getMember(const char name[]) { + return NULL; +} + +const SkFunctionParamType* SkDisplayable::getParameters(const SkMemberInfo* info, + int* paramCount) { + const SkFunctionParamType* params = getFunctionsParameters(); + SkASSERT(params != NULL); + int funcIndex = info->functionIndex(); + // !!! eventually break traversing params into an external function (maybe this whole function) + int index = funcIndex; + int offset = 0; + while (--index >= 0) { + while (params[offset] != 0) + offset++; + offset++; + } + int count = 0; + while (params[offset] != 0) { + count++; + offset++; + } + *paramCount = count; + return ¶ms[offset - count]; +} + +SkDisplayable* SkDisplayable::getParent() const { + return NULL; +} + +bool SkDisplayable::getProperty(int index, SkScriptValue* ) const { +// SkASSERT(0); + return false; +} + +bool SkDisplayable::getProperty2(int index, SkOperand2* value) const { + SkASSERT(0); + return false; +} + +SkDisplayTypes SkDisplayable::getType() const { + return SkType_Unknown; +} + +bool SkDisplayable::hasEnable() const { + return false; +} + +bool SkDisplayable::isDrawable() const { + return false; +} + +void SkDisplayable::onEndElement(SkAnimateMaker& ) {} + +const SkMemberInfo* SkDisplayable::preferredChild(SkDisplayTypes type) { + return NULL; +} + +bool SkDisplayable::resolveIDs(SkAnimateMaker& maker, SkDisplayable* original, SkApply* apply) { + return false; +} + +//SkDisplayable* SkDisplayable::resolveTarget(SkAnimateMaker& ) { +// return this; +//} + +void SkDisplayable::setChildHasID() { +} + +bool SkDisplayable::setParent(SkDisplayable* ) { + return false; +} + +bool SkDisplayable::setProperty(int index, SkScriptValue& ) { + //SkASSERT(0); + return false; +} + +void SkDisplayable::setReference(const SkMemberInfo* info, SkDisplayable* displayable) { + if (info->fType == SkType_MemberProperty) { + SkScriptValue scriptValue; + scriptValue.fOperand.fDisplayable = displayable; + scriptValue.fType = displayable->getType(); + setProperty(info->propertyIndex(), scriptValue); + } else if (info->fType == SkType_Array) { + SkASSERT(displayable->getType() == SkType_Array); + SkDisplayArray* dispArray = (SkDisplayArray*) displayable; + SkTDScalarArray* array = (SkTDScalarArray* ) info->memberData(this); + array->setCount(dispArray->values.count()); + memcpy(array->begin(), dispArray->values.begin(), dispArray->values.count() * sizeof(int)); + // + + // !!! need a way for interpreter engine to own array + // !!! probably need to replace all scriptable arrays with single bigger array + // that has operand and type on every element -- or + // when array is dirtied, need to get parent to reparse to local array + } else { + void* storage = info->memberData(this); + memcpy(storage, &displayable, sizeof(SkDisplayable*)); + } +// !!! unclear why displayable is dirtied here +// if this is called, this breaks fromPath.xml +// displayable->dirty(); +} + +#ifdef SK_DEBUG +void SkDisplayable::validate() { +} +#endif + + diff --git a/skia/animator/SkDisplayable.h b/skia/animator/SkDisplayable.h new file mode 100644 index 0000000..0c35d10 --- /dev/null +++ b/skia/animator/SkDisplayable.h @@ -0,0 +1,120 @@ +/* libs/graphics/animator/SkDisplayable.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkDisplayable_DEFINED +#define SkDisplayable_DEFINED + +#include "SkOperand.h" +#ifdef SK_DEBUG +#include "SkString.h" +#endif +#include "SkIntArray.h" +#include "SkRect.h" +#include "SkTDArray.h" + +class SkAnimateMaker; +class SkApply; +class SkEvents; +struct SkMemberInfo; +struct SkScriptValue; +class SkOpArray; // compiled scripting experiment +union SkOperand2; // compiled scripting experiment + +class SkDisplayable { +public: +#ifdef SK_DEBUG + SkDisplayable(); +#endif + virtual ~SkDisplayable(); + virtual bool add(SkAnimateMaker& , SkDisplayable* child); + virtual bool canContainDependents() const; + virtual bool childrenNeedDisposing() const; + virtual void clearBounder(); + virtual bool contains(SkDisplayable* ); + virtual SkDisplayable* contains(const SkString& ); + virtual SkDisplayable* deepCopy(SkAnimateMaker* ); + virtual void dirty(); +#ifdef SK_DUMP_ENABLED + virtual void dump(SkAnimateMaker* ); + void dumpAttrs(SkAnimateMaker* ); + void dumpBase(SkAnimateMaker* ); + void dumpChildren(SkAnimateMaker* maker, bool closedAngle = false ); + void dumpEnd(SkAnimateMaker* ); + virtual void dumpEvents(); +#endif + virtual bool enable( SkAnimateMaker& ); + virtual void enableBounder(); + virtual void executeFunction(SkDisplayable* , int functionIndex, + SkTDArray<SkScriptValue>& , SkDisplayTypes , SkScriptValue* ); + void executeFunction(SkDisplayable* , const SkMemberInfo* , + SkTypedArray* , SkScriptValue* ); + virtual void executeFunction2(SkDisplayable* , int functionIndex, + SkOpArray* params , SkDisplayTypes , SkOperand2* ); // compiled scripting experiment + virtual void getBounds(SkRect* ); + virtual const SkFunctionParamType* getFunctionsParameters(); + virtual const SkMemberInfo* getMember(int index); + virtual const SkMemberInfo* getMember(const char name[]); + const SkFunctionParamType* getParameters(const SkMemberInfo* info, + int* paramCount); + virtual SkDisplayable* getParent() const; + virtual bool getProperty(int index, SkScriptValue* value) const; + virtual bool getProperty2(int index, SkOperand2* value) const; // compiled scripting experiment + virtual SkDisplayTypes getType() const; + virtual bool hasEnable() const; + bool isAnimate() const { + SkDisplayTypes type = getType(); + return type == SkType_Animate || type == SkType_Set; } + bool isApply() const { return getType() == SkType_Apply; } + bool isColor() const { return getType() == SkType_Color; } + virtual bool isDrawable() const; + bool isGroup() const { return getType() == SkType_Group || + getType() == SkType_Save || getType() == SkType_DrawTo || + getType() == SkType_SaveLayer; } + bool isMatrix() const { return getType() == SkType_Matrix; } + virtual bool isPaint() const { return getType() == SkType_Paint; } + virtual bool isPath() const { return false; } + bool isPost() const { return getType() == SkType_Post; } + virtual void onEndElement(SkAnimateMaker& ); + virtual const SkMemberInfo* preferredChild(SkDisplayTypes type); + virtual bool resolveIDs(SkAnimateMaker& maker, SkDisplayable* original, SkApply* ); + virtual void setChildHasID(); + virtual bool setParent(SkDisplayable* ); + virtual bool setProperty(int index, SkScriptValue& ); + void setReference(const SkMemberInfo* info, SkDisplayable* ref); +#ifdef SK_DEBUG + bool isData() const { return getType() == SkType_Data; }; + bool isEvent() const { return getType() == SkType_Event; } + virtual bool isMatrixPart() const { return false; } + bool isPatch() const { return getType() == SkType_3D_Patch; } + virtual bool isPaintPart() const { return false; } + virtual bool isPathPart() const { return false; } + virtual void validate(); + SkString _id; + const char* id; +// static int fAllocationCount; + static SkTDDisplayableArray fAllocations; +#else + void validate() {} +#endif +#ifdef SK_DUMP_ENABLED +private: + void dumpValues(const SkMemberInfo* info, SkDisplayTypes type, SkOperand op, SkOperand blankOp, + SkOperand op2, SkOperand blankOp2); +#endif +}; + +#endif // SkDisplayable_DEFINED diff --git a/skia/animator/SkDraw3D.cpp b/skia/animator/SkDraw3D.cpp new file mode 100644 index 0000000..d90cf0a --- /dev/null +++ b/skia/animator/SkDraw3D.cpp @@ -0,0 +1,117 @@ +/* libs/graphics/animator/SkDraw3D.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkDraw3D.h" +#include "SkAnimateMaker.h" +#include "SkCanvas.h" +#include "SkTypedArray.h" + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo Sk3D_Point::fInfo[] = { + SK_MEMBER_ALIAS(x, fPoint.fX, Float), + SK_MEMBER_ALIAS(y, fPoint.fY, Float), + SK_MEMBER_ALIAS(z, fPoint.fZ, Float) +}; + +#endif + +DEFINE_NO_VIRTUALS_GET_MEMBER(Sk3D_Point); + +Sk3D_Point::Sk3D_Point() { + fPoint.set(0, 0, 0); +} + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo Sk3D_Camera::fInfo[] = { + SK_MEMBER_ALIAS(axis, fCamera.fAxis, 3D_Point), + SK_MEMBER(hackHeight, Float), + SK_MEMBER(hackWidth, Float), + SK_MEMBER_ALIAS(location, fCamera.fLocation, 3D_Point), + SK_MEMBER_ALIAS(observer, fCamera.fObserver, 3D_Point), + SK_MEMBER(patch, 3D_Patch), + SK_MEMBER_ALIAS(zenith, fCamera.fZenith, 3D_Point), +}; + +#endif + +DEFINE_GET_MEMBER(Sk3D_Camera); + +Sk3D_Camera::Sk3D_Camera() : hackWidth(0), hackHeight(0), patch(NULL) { +} + +Sk3D_Camera::~Sk3D_Camera() { +} + +bool Sk3D_Camera::draw(SkAnimateMaker& maker) { + fCamera.update(); + SkMatrix matrix; + fCamera.patchToMatrix(patch->fPatch, &matrix); + matrix.preTranslate(hackWidth / 2, -hackHeight / 2); + matrix.postTranslate(hackWidth / 2, hackHeight / 2); + maker.fCanvas->concat(matrix); + return false; +} + + +enum Sk3D_Patch_Functions { + SK_FUNCTION(rotateDegrees) +}; + +const SkFunctionParamType Sk3D_Patch::fFunctionParameters[] = { + (SkFunctionParamType) SkType_Float, + (SkFunctionParamType) SkType_Float, + (SkFunctionParamType) SkType_Float, + (SkFunctionParamType) 0 // terminator for parameter list (there may be multiple parameter lists) +}; + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo Sk3D_Patch::fInfo[] = { + SK_MEMBER_ALIAS(origin, fPatch.fOrigin, 3D_Point), + SK_MEMBER_FUNCTION(rotateDegrees, Float), + SK_MEMBER_ALIAS(u, fPatch.fU, 3D_Point), + SK_MEMBER_ALIAS(v, fPatch.fV, 3D_Point) +}; + +#endif + +DEFINE_GET_MEMBER(Sk3D_Patch); + +void Sk3D_Patch::executeFunction(SkDisplayable* target, int index, + SkTDArray<SkScriptValue>& parameters, SkDisplayTypes type, + SkScriptValue* ) { + SkASSERT(target == this); + switch (index) { + case SK_FUNCTION(rotateDegrees): + SkASSERT(parameters.count() == 3); + SkASSERT(type == SkType_Float); + fPatch.rotateDegrees(parameters[0].fOperand.fScalar, + parameters[1].fOperand.fScalar, parameters[2].fOperand.fScalar); + break; + default: + SkASSERT(0); + } +} + +const SkFunctionParamType* Sk3D_Patch::getFunctionsParameters() { + return fFunctionParameters; +} + + + diff --git a/skia/animator/SkDraw3D.h b/skia/animator/SkDraw3D.h new file mode 100644 index 0000000..b9be37e --- /dev/null +++ b/skia/animator/SkDraw3D.h @@ -0,0 +1,59 @@ +/* libs/graphics/animator/SkDraw3D.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkDraw3D_DEFINED +#define SkDraw3D_DEFINED + +#include "SkCamera.h" +#include "SkDrawable.h" +#include "SkMemberInfo.h" + +class Sk3D_Patch; + +struct Sk3D_Point { + DECLARE_NO_VIRTUALS_MEMBER_INFO(3D_Point); + Sk3D_Point(); +private: + SkPoint3D fPoint; +}; + +class Sk3D_Camera : public SkDrawable { + DECLARE_MEMBER_INFO(3D_Camera); + Sk3D_Camera(); + virtual ~Sk3D_Camera(); + virtual bool draw(SkAnimateMaker& ); +private: + SkScalar hackWidth; + SkScalar hackHeight; + SkCamera3D fCamera; + Sk3D_Patch* patch; +}; + +class Sk3D_Patch : public SkDisplayable { + DECLARE_MEMBER_INFO(3D_Patch); +private: + virtual void executeFunction(SkDisplayable* , int index, + SkTDArray<SkScriptValue>& parameters, SkDisplayTypes type, + SkScriptValue* ); + virtual const SkFunctionParamType* getFunctionsParameters(); + SkPatch3D fPatch; + static const SkFunctionParamType fFunctionParameters[]; + friend class Sk3D_Camera; +}; + +#endif // SkDraw3D_DEFINED + diff --git a/skia/animator/SkDrawBitmap.cpp b/skia/animator/SkDrawBitmap.cpp new file mode 100644 index 0000000..f5102ab --- /dev/null +++ b/skia/animator/SkDrawBitmap.cpp @@ -0,0 +1,206 @@ +/* libs/graphics/animator/SkDrawBitmap.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkDrawBitmap.h" +#include "SkAnimateMaker.h" +#include "SkCanvas.h" +#include "SkImageDecoder.h" +#include "SkPaint.h" +#include "SkStream.h" + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkBaseBitmap::fInfo[] = { + SK_MEMBER(x, Float), + SK_MEMBER(y, Float) +}; + +#endif + +DEFINE_GET_MEMBER(SkBaseBitmap); + +SkBaseBitmap::SkBaseBitmap() : x(0), y(0) { +} + +SkBaseBitmap::~SkBaseBitmap() { +} + +bool SkBaseBitmap::draw(SkAnimateMaker& maker) { + SkBoundableAuto boundable(this, maker); + maker.fCanvas->drawBitmap(fBitmap, x, y, maker.fPaint); + return false; +} + +enum SkDrawBitmap_Properties { + SK_PROPERTY(erase) +}; + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkDrawBitmap::fInfo[] = { + SK_MEMBER_INHERITED, + SK_MEMBER_PROPERTY(erase, ARGB), + SK_MEMBER(format, BitmapFormat), + SK_MEMBER(height, Int), + SK_MEMBER(rowBytes, Int), + SK_MEMBER(width, Int), +}; + +#endif + +DEFINE_GET_MEMBER(SkDrawBitmap); + +SkDrawBitmap::SkDrawBitmap() : format((SkBitmap::Config) -1), height(-1), + rowBytes(0), width(-1), fColor(0), fColorSet(false) { +} + +SkDrawBitmap::~SkDrawBitmap() { +} + +#ifdef SK_DUMP_ENABLED +void SkDrawBitmap::dump(SkAnimateMaker* maker) { + dumpBase(maker); + dumpAttrs(maker); + if (fColorSet) + SkDebugf("erase=\"argb(%d,%d,%d,%d)\" ", SkColorGetA(fColor)/255, SkColorGetR(fColor), + SkColorGetG(fColor), SkColorGetB(fColor)); + if (rowBytes > 0) + SkDebugf("rowBytes=\"%d\" ", rowBytes); + const char* formatName; + switch (format) { + case 0: formatName = "none"; break; + case 1: formatName = "A1"; break; + case 2: formatName = "A8"; break; + case 3: formatName = "Index8"; break; + case 4: formatName = "RGB16"; break; + case 5: formatName = "RGB32"; break; + } + SkDebugf("format=\"%s\" />\n", formatName); +} +#endif + +void SkDrawBitmap::onEndElement(SkAnimateMaker& maker) { + SkASSERT(format != (SkBitmap::Config) -1); + SkASSERT(width != -1); + SkASSERT(height != -1); + SkASSERT(rowBytes >= 0); + fBitmap.setConfig((SkBitmap::Config) format, width, height, rowBytes); + fBitmap.allocPixels(); + if (fColorSet) + fBitmap.eraseColor(fColor); +} + +bool SkDrawBitmap::setProperty(int index, SkScriptValue& value) +{ + switch (index) { + case SK_PROPERTY(erase): + SkASSERT(value.fType == SkType_ARGB); + fColor = value.fOperand.fS32; + fColorSet = true; + break; + default: + SkASSERT(0); + return false; + } + return true; +} + + +enum SkImage_Properties { + SK_PROPERTY(height), + SK_PROPERTY(width) +}; + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkImage::fInfo[] = { + SK_MEMBER_INHERITED, + SK_MEMBER(base64, Base64), + SK_MEMBER_PROPERTY(height, Int), + SK_MEMBER(src, String), + SK_MEMBER_PROPERTY(width, Int) +}; + +#endif + +DEFINE_GET_MEMBER(SkImage); + +SkImage::SkImage() : fDirty(true), fUriBase(NULL) { + base64.fData = NULL; + base64.fLength = 0; +} + +SkImage::~SkImage() { + delete[] base64.fData; +} + +SkDisplayable* SkImage::deepCopy(SkAnimateMaker* maker) { + SkDisplayable* copy = INHERITED::deepCopy(maker); + ((SkImage*) copy)->fUriBase = ((SkImage*) this)->fUriBase; + return copy; +} + +void SkImage::dirty() { + fDirty = true; +} + +bool SkImage::draw(SkAnimateMaker& maker) { + if (fDirty) + resolve(); + return INHERITED::draw(maker); +} + +bool SkImage::getProperty(int index, SkScriptValue* value) const { + if (fDirty) + resolve(); + switch (index) { + case SK_PROPERTY(height): + value->fOperand.fS32 = fBitmap.height(); + break; + case SK_PROPERTY(width): + value->fOperand.fS32 = fBitmap.width(); + break; + default: + SkASSERT(0); + return false; + } + value->fType = SkType_Int; + return true; +} + +void SkImage::onEndElement(SkAnimateMaker& maker) { + fUriBase = maker.fPrefix.c_str(); +} + +void SkImage::resolve() { + fDirty = false; + if (base64.fData) { + fBitmap.reset(); + SkImageDecoder::DecodeMemory(base64.fData, base64.fLength, &fBitmap); + } else if (src.size()) { + if (fLast.equals(src)) + return; + fLast.set(src); + fBitmap.reset(); + + //SkStream* stream = SkStream::GetURIStream(fUriBase, src.c_str()); + SkStream* stream = new SkFILEStream(src.c_str()); + + SkAutoTDelete<SkStream> autoDel(stream); + SkImageDecoder::DecodeStream(stream, &fBitmap); + } +} diff --git a/skia/animator/SkDrawBitmap.h b/skia/animator/SkDrawBitmap.h new file mode 100644 index 0000000..28ec054 --- /dev/null +++ b/skia/animator/SkDrawBitmap.h @@ -0,0 +1,82 @@ +/* libs/graphics/animator/SkDrawBitmap.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkDrawBitmap_DEFINED +#define SkDrawBitmap_DEFINED + +#include "SkBoundable.h" +#include "SkBase64.h" +#include "SkBitmap.h" +// #include "SkImageDecoder.h" +#include "SkMemberInfo.h" + +class SkBaseBitmap : public SkBoundable { + DECLARE_MEMBER_INFO(BaseBitmap); + SkBaseBitmap(); + virtual ~SkBaseBitmap(); + virtual bool draw(SkAnimateMaker& ); +protected: + SkBitmap fBitmap; + SkScalar x; + SkScalar y; +private: + friend class SkDrawTo; + friend class SkDrawBitmapShader; + typedef SkBoundable INHERITED; +}; + +class SkDrawBitmap : public SkBaseBitmap { + DECLARE_DRAW_MEMBER_INFO(Bitmap); + SkDrawBitmap(); + virtual ~SkDrawBitmap(); +#ifdef SK_DUMP_ENABLED + virtual void dump(SkAnimateMaker* ); +#endif + virtual void onEndElement(SkAnimateMaker& ); + virtual bool setProperty(int index, SkScriptValue& value); +protected: + int /*SkBitmap::Config*/ format; + int32_t height; + int32_t rowBytes; + int32_t width; + SkColor fColor; + SkBool fColorSet; + typedef SkBaseBitmap INHERITED; +}; + +class SkImage : public SkBaseBitmap { + DECLARE_MEMBER_INFO(Image); + SkImage(); + virtual ~SkImage(); + virtual SkDisplayable* deepCopy(SkAnimateMaker* ); + virtual void dirty(); + virtual bool draw(SkAnimateMaker& ); + virtual bool getProperty(int index, SkScriptValue* value) const; + virtual void onEndElement(SkAnimateMaker& maker); +private: + void resolve() const { (const_cast<SkImage*>(this))->resolve(); } + void resolve(); +protected: + SkBase64 base64; + SkString src; + SkString fLast; // cache of src so that stream isn't unnecessarily decoded + SkBool fDirty; + const char* fUriBase; + typedef SkBaseBitmap INHERITED; +}; + +#endif // SkDrawBitmap_DEFINED diff --git a/skia/animator/SkDrawBlur.cpp b/skia/animator/SkDrawBlur.cpp new file mode 100644 index 0000000..618a590 --- /dev/null +++ b/skia/animator/SkDrawBlur.cpp @@ -0,0 +1,40 @@ +/* libs/graphics/animator/SkDrawBlur.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkDrawBlur.h" + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkDrawBlur::fInfo[] = { + SK_MEMBER(blurStyle, MaskFilterBlurStyle), + SK_MEMBER(radius, Float) +}; + +#endif + +DEFINE_GET_MEMBER(SkDrawBlur); + +SkDrawBlur::SkDrawBlur() : radius(-1), + blurStyle(SkBlurMaskFilter::kNormal_BlurStyle) { +} + +SkMaskFilter* SkDrawBlur::getMaskFilter() { + if (radius < 0) + return NULL; + return SkBlurMaskFilter::Create(radius, (SkBlurMaskFilter::BlurStyle) blurStyle); +} + diff --git a/skia/animator/SkDrawBlur.h b/skia/animator/SkDrawBlur.h new file mode 100644 index 0000000..5a6e8b3 --- /dev/null +++ b/skia/animator/SkDrawBlur.h @@ -0,0 +1,34 @@ +/* libs/graphics/animator/SkDrawBlur.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkDrawBlur_DEFINED +#define SkDrawBlur_DEFINED + +#include "SkPaintParts.h" +#include "SkBlurMaskFilter.h" + +class SkDrawBlur : public SkDrawMaskFilter { + DECLARE_DRAW_MEMBER_INFO(Blur); + SkDrawBlur(); + virtual SkMaskFilter* getMaskFilter(); +protected: + SkScalar radius; + int /*SkBlurMaskFilter::BlurStyle*/ blurStyle; +}; + +#endif // SkDrawBlur_DEFINED + diff --git a/skia/animator/SkDrawClip.cpp b/skia/animator/SkDrawClip.cpp new file mode 100644 index 0000000..147c3a8 --- /dev/null +++ b/skia/animator/SkDrawClip.cpp @@ -0,0 +1,48 @@ +/* libs/graphics/animator/SkDrawClip.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkDrawClip.h" +#include "SkAnimateMaker.h" +#include "SkCanvas.h" +#include "SkDrawRectangle.h" +#include "SkDrawPath.h" + + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkDrawClip::fInfo[] = { + SK_MEMBER(path, Path), + SK_MEMBER(rect, Rect) +}; + +#endif + +DEFINE_GET_MEMBER(SkDrawClip); + +SkDrawClip::SkDrawClip() : rect(NULL), path(NULL) { +} + +bool SkDrawClip::draw(SkAnimateMaker& maker ) { + if (rect != NULL) + maker.fCanvas->clipRect(rect->fRect); + else { + SkASSERT(path != NULL); + maker.fCanvas->clipPath(path->fPath); + } + return false; +} + diff --git a/skia/animator/SkDrawClip.h b/skia/animator/SkDrawClip.h new file mode 100644 index 0000000..c19694b --- /dev/null +++ b/skia/animator/SkDrawClip.h @@ -0,0 +1,37 @@ +/* libs/graphics/animator/SkDrawClip.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkDrawClip_DEFINED +#define SkDrawClip_DEFINED + +#include "SkDrawable.h" +#include "SkMemberInfo.h" +#include "SkRegion.h" + +class SkDrawPath; +class SkDrawRect; + +class SkDrawClip : public SkDrawable { + DECLARE_DRAW_MEMBER_INFO(Clip); + SkDrawClip(); + virtual bool draw(SkAnimateMaker& ); +private: + SkDrawRect* rect; + SkDrawPath* path; +}; + +#endif // SkDrawClip_DEFINED diff --git a/skia/animator/SkDrawColor.cpp b/skia/animator/SkDrawColor.cpp new file mode 100644 index 0000000..d7934a9 --- /dev/null +++ b/skia/animator/SkDrawColor.cpp @@ -0,0 +1,278 @@ +/* libs/graphics/animator/SkDrawColor.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkDrawColor.h" +#ifdef SK_DEBUG +#include "SkDisplayList.h" +#endif +#include "SkDrawPaint.h" +#include "SkParse.h" +#include "SkScript.h" + +enum HSV_Choice { + kGetHue, + kGetSaturation, + kGetValue +}; + +static SkScalar RGB_to_HSV(SkColor color, HSV_Choice choice) { + SkScalar red = SkIntToScalar(SkColorGetR(color)); + SkScalar green = SkIntToScalar(SkColorGetG(color)); + SkScalar blue = SkIntToScalar(SkColorGetB(color)); + SkScalar min = SkMinScalar(SkMinScalar(red, green), blue); + SkScalar value = SkMaxScalar(SkMaxScalar(red, green), blue); + if (choice == kGetValue) + return value/255; + SkScalar delta = value - min; + SkScalar saturation = value == 0 ? 0 : SkScalarDiv(delta, value); + if (choice == kGetSaturation) + return saturation; + SkScalar hue; + if (saturation == 0) + hue = 0; + else { + SkScalar part60 = SkScalarDiv(60 * SK_Scalar1, delta); + if (red == value) { + hue = SkScalarMul(green - blue, part60); + if (hue < 0) + hue += 360 * SK_Scalar1; + } + else if (green == value) + hue = 120 * SK_Scalar1 + SkScalarMul(blue - red, part60); + else // blue == value + hue = 240 * SK_Scalar1 + SkScalarMul(red - green, part60); + } + SkASSERT(choice == kGetHue); + return hue; +} + +#if defined _WIN32 && _MSC_VER >= 1300 // disable 'red', etc. may be used without having been initialized +#pragma warning ( push ) +#pragma warning ( disable : 4701 ) +#endif + +static SkColor HSV_to_RGB(SkColor color, HSV_Choice choice, SkScalar hsv) { + SkScalar hue = choice == kGetHue ? hsv : RGB_to_HSV(color, kGetHue); + SkScalar saturation = choice == kGetSaturation ? hsv : RGB_to_HSV(color, kGetSaturation); + SkScalar value = choice == kGetValue ? hsv : RGB_to_HSV(color, kGetValue); + value *= 255; + SkScalar red SK_INIT_TO_AVOID_WARNING; + SkScalar green SK_INIT_TO_AVOID_WARNING; + SkScalar blue SK_INIT_TO_AVOID_WARNING; + if (saturation == 0) // color is on black-and-white center line + red = green = blue = value; + else { + //SkScalar fraction = SkScalarMod(hue, 60 * SK_Scalar1); + int sextant = SkScalarFloor(hue / 60); + SkScalar fraction = hue / 60 - SkIntToScalar(sextant); + SkScalar p = SkScalarMul(value , SK_Scalar1 - saturation); + SkScalar q = SkScalarMul(value, SK_Scalar1 - SkScalarMul(saturation, fraction)); + SkScalar t = SkScalarMul(value, SK_Scalar1 - + SkScalarMul(saturation, SK_Scalar1 - fraction)); + switch (sextant % 6) { + case 0: red = value; green = t; blue = p; break; + case 1: red = q; green = value; blue = p; break; + case 2: red = p; green = value; blue = t; break; + case 3: red = p; green = q; blue = value; break; + case 4: red = t; green = p; blue = value; break; + case 5: red = value; green = p; blue = q; break; + } + } + //used to say SkToU8((U8CPU) red) etc + return SkColorSetARGB(SkColorGetA(color), SkScalarRound(red), + SkScalarRound(green), SkScalarRound(blue)); +} + +#if defined _WIN32 && _MSC_VER >= 1300 +#pragma warning ( pop ) +#endif + +enum SkDrawColor_Properties { + SK_PROPERTY(alpha), + SK_PROPERTY(blue), + SK_PROPERTY(green), + SK_PROPERTY(hue), + SK_PROPERTY(red), + SK_PROPERTY(saturation), + SK_PROPERTY(value) +}; + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkDrawColor::fInfo[] = { + SK_MEMBER_PROPERTY(alpha, Float), + SK_MEMBER_PROPERTY(blue, Float), + SK_MEMBER(color, ARGB), + SK_MEMBER_PROPERTY(green, Float), + SK_MEMBER_PROPERTY(hue, Float), + SK_MEMBER_PROPERTY(red, Float), + SK_MEMBER_PROPERTY(saturation, Float), + SK_MEMBER_PROPERTY(value, Float), +}; + +#endif + +DEFINE_GET_MEMBER(SkDrawColor); + +SkDrawColor::SkDrawColor() : fDirty(false) { + color = SK_ColorBLACK; + fHue = fSaturation = fValue = SK_ScalarNaN; +} + +bool SkDrawColor::add() { + if (fPaint->color != NULL) + return true; // error (probably color in paint as attribute as well) + fPaint->color = this; + fPaint->fOwnsColor = true; + return false; +} + +SkDisplayable* SkDrawColor::deepCopy(SkAnimateMaker* maker) { + SkDrawColor* copy = new SkDrawColor(); + copy->color = color; + copy->fHue = fHue; + copy->fSaturation = fSaturation; + copy->fValue = fValue; + copy->fDirty = fDirty; + return copy; +} + +void SkDrawColor::dirty(){ + fDirty = true; +} + +#ifdef SK_DUMP_ENABLED +void SkDrawColor::dump(SkAnimateMaker* maker) { + dumpBase(maker); + SkDebugf("alpha=\"%d\" red=\"%d\" green=\"%d\" blue=\"%d\" />\n", + SkColorGetA(color)/255, SkColorGetR(color), + SkColorGetG(color), SkColorGetB(color)); +} +#endif + +SkColor SkDrawColor::getColor() { + if (fDirty) { + if (SkScalarIsNaN(fValue) == false) + color = HSV_to_RGB(color, kGetValue, fValue); + if (SkScalarIsNaN(fSaturation) == false) + color = HSV_to_RGB(color, kGetSaturation, fSaturation); + if (SkScalarIsNaN(fHue) == false) + color = HSV_to_RGB(color, kGetHue, fHue); + fDirty = false; + } + return color; +} + +SkDisplayable* SkDrawColor::getParent() const { + return fPaint; +} + +bool SkDrawColor::getProperty(int index, SkScriptValue* value) const { + value->fType = SkType_Float; + SkScalar result; + switch(index) { + case SK_PROPERTY(alpha): + result = SkIntToScalar(SkColorGetA(color)) / 255; + break; + case SK_PROPERTY(blue): + result = SkIntToScalar(SkColorGetB(color)); + break; + case SK_PROPERTY(green): + result = SkIntToScalar(SkColorGetG(color)); + break; + case SK_PROPERTY(hue): + result = RGB_to_HSV(color, kGetHue); + break; + case SK_PROPERTY(red): + result = SkIntToScalar(SkColorGetR(color)); + break; + case SK_PROPERTY(saturation): + result = RGB_to_HSV(color, kGetSaturation); + break; + case SK_PROPERTY(value): + result = RGB_to_HSV(color, kGetValue); + break; + default: + SkASSERT(0); + return false; + } + value->fOperand.fScalar = result; + return true; +} + +void SkDrawColor::onEndElement(SkAnimateMaker& maker){ + fDirty = true; +} + +bool SkDrawColor::setParent(SkDisplayable* parent) { + SkASSERT(parent != NULL); + if (parent->getType() == SkType_LinearGradient || parent->getType() == SkType_RadialGradient) + return false; + if (parent->isPaint() == false) + return true; + fPaint = (SkDrawPaint*) parent; + return false; +} + +bool SkDrawColor::setProperty(int index, SkScriptValue& value) { + SkASSERT(value.fType == SkType_Float); + SkScalar scalar = value.fOperand.fScalar; + switch (index) { + case SK_PROPERTY(alpha): + uint8_t alpha; + #ifdef SK_SCALAR_IS_FLOAT + alpha = scalar == SK_Scalar1 ? 255 : SkToU8((U8CPU) (scalar * 256)); + #else + alpha = SkToU8((scalar - (scalar >= SK_ScalarHalf)) >> 8); + #endif + color = SkColorSetARGB(alpha, SkColorGetR(color), + SkColorGetG(color), SkColorGetB(color)); + break; + case SK_PROPERTY(blue): + scalar = SkScalarClampMax(scalar, 255 * SK_Scalar1); + color = SkColorSetARGB(SkColorGetA(color), SkColorGetR(color), + SkColorGetG(color), SkToU8((U8CPU) scalar)); + break; + case SK_PROPERTY(green): + scalar = SkScalarClampMax(scalar, 255 * SK_Scalar1); + color = SkColorSetARGB(SkColorGetA(color), SkColorGetR(color), + SkToU8((U8CPU) scalar), SkColorGetB(color)); + break; + case SK_PROPERTY(hue): + fHue = scalar;//RGB_to_HSV(color, kGetHue); + fDirty = true; + break; + case SK_PROPERTY(red): + scalar = SkScalarClampMax(scalar, 255 * SK_Scalar1); + color = SkColorSetARGB(SkColorGetA(color), SkToU8((U8CPU) scalar), + SkColorGetG(color), SkColorGetB(color)); + break; + case SK_PROPERTY(saturation): + fSaturation = scalar;//RGB_to_HSV(color, kGetSaturation); + fDirty = true; + break; + case SK_PROPERTY(value): + fValue = scalar;//RGB_to_HSV(color, kGetValue); + fDirty = true; + break; + default: + SkASSERT(0); + return false; + } + return true; +} + diff --git a/skia/animator/SkDrawColor.h b/skia/animator/SkDrawColor.h new file mode 100644 index 0000000..f1e6d2b --- /dev/null +++ b/skia/animator/SkDrawColor.h @@ -0,0 +1,50 @@ +/* libs/graphics/animator/SkDrawColor.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkDrawColor_DEFINED +#define SkDrawColor_DEFINED + +#include "SkPaintParts.h" +#include "SkColor.h" + +class SkDrawColor : public SkPaintPart { + DECLARE_DRAW_MEMBER_INFO(Color); + SkDrawColor(); + virtual bool add(); + virtual void dirty(); +#ifdef SK_DUMP_ENABLED + virtual void dump(SkAnimateMaker* ); +#endif + SkColor getColor(); + virtual SkDisplayable* deepCopy(SkAnimateMaker* ); + virtual SkDisplayable* getParent() const; + virtual bool getProperty(int index, SkScriptValue* value) const; + virtual void onEndElement(SkAnimateMaker& ); + virtual bool setParent(SkDisplayable* parent); + virtual bool setProperty(int index, SkScriptValue&); +protected: + SkColor color; + SkScalar fHue; + SkScalar fSaturation; + SkScalar fValue; + SkBool fDirty; +private: + friend class SkGradient; + typedef SkPaintPart INHERITED; +}; + +#endif // SkDrawColor_DEFINED diff --git a/skia/animator/SkDrawDash.cpp b/skia/animator/SkDrawDash.cpp new file mode 100644 index 0000000..1a76064 --- /dev/null +++ b/skia/animator/SkDrawDash.cpp @@ -0,0 +1,44 @@ +/* libs/graphics/animator/SkDrawDash.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkDrawDash.h" +#include "SkDashPathEffect.h" + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkDash::fInfo[] = { + SK_MEMBER_ARRAY(intervals, Float), + SK_MEMBER(phase, Float) +}; + +#endif + +DEFINE_GET_MEMBER(SkDash); + +SkDash::SkDash() : phase(0) { +} + +SkDash::~SkDash() { +} + +SkPathEffect* SkDash::getPathEffect() { + int count = intervals.count(); + if (count == 0) + return NULL; + return new SkDashPathEffect(intervals.begin(), count, phase); +} + diff --git a/skia/animator/SkDrawDash.h b/skia/animator/SkDrawDash.h new file mode 100644 index 0000000..39a13ea --- /dev/null +++ b/skia/animator/SkDrawDash.h @@ -0,0 +1,35 @@ +/* libs/graphics/animator/SkDrawDash.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkDrawDash_DEFINED +#define SkDrawDash_DEFINED + +#include "SkPaintParts.h" +#include "SkIntArray.h" + +class SkDash : public SkDrawPathEffect { + DECLARE_MEMBER_INFO(Dash); + SkDash(); + virtual ~SkDash(); + virtual SkPathEffect* getPathEffect(); +private: + SkTDScalarArray intervals; + SkScalar phase; +}; + +#endif // SkDrawDash_DEFINED + diff --git a/skia/animator/SkDrawDiscrete.cpp b/skia/animator/SkDrawDiscrete.cpp new file mode 100644 index 0000000..0afe8f5 --- /dev/null +++ b/skia/animator/SkDrawDiscrete.cpp @@ -0,0 +1,43 @@ +/* libs/graphics/animator/SkDrawDiscrete.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkDrawDiscrete.h" +#include "SkAnimateMaker.h" +#include "SkPaint.h" +#include "SkDiscretePathEffect.h" + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkDiscrete::fInfo[] = { + SK_MEMBER(deviation, Float), + SK_MEMBER(segLength, Float) +}; + +#endif + +DEFINE_GET_MEMBER(SkDiscrete); + +SkDiscrete::SkDiscrete() : deviation(0), segLength(0) { +} + +SkPathEffect* SkDiscrete::getPathEffect() { + if (deviation <= 0 || segLength <= 0) + return NULL; + else + return new SkDiscretePathEffect(segLength, deviation); +} + diff --git a/skia/animator/SkDrawDiscrete.h b/skia/animator/SkDrawDiscrete.h new file mode 100644 index 0000000..9652766 --- /dev/null +++ b/skia/animator/SkDrawDiscrete.h @@ -0,0 +1,32 @@ +/* libs/graphics/animator/SkDrawDiscrete.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkDrawDiscrete_DEFINED +#define SkDrawDiscrete_DEFINED + +#include "SkPaintParts.h" + +class SkDiscrete : public SkDrawPathEffect { + DECLARE_MEMBER_INFO(Discrete); + SkDiscrete(); + virtual SkPathEffect* getPathEffect(); +private: + SkScalar deviation; + SkScalar segLength; +}; + +#endif //SkDrawDiscrete_DEFINED diff --git a/skia/animator/SkDrawEmboss.cpp b/skia/animator/SkDrawEmboss.cpp new file mode 100644 index 0000000..ed1433c --- /dev/null +++ b/skia/animator/SkDrawEmboss.cpp @@ -0,0 +1,42 @@ +/* libs/graphics/animator/SkDrawEmboss.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkDrawEmboss.h" + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkDrawEmboss::fInfo[] = { + SK_MEMBER(ambient, Float), + SK_MEMBER_ARRAY(direction, Float), + SK_MEMBER(radius, Float), + SK_MEMBER(specular, Float) +}; + +#endif + +DEFINE_GET_MEMBER(SkDrawEmboss); + +SkDrawEmboss::SkDrawEmboss() : radius(-1) { + direction.setCount(3); +} + +SkMaskFilter* SkDrawEmboss::getMaskFilter() { + if (radius < 0 || direction.count() !=3) + return NULL; + return SkBlurMaskFilter::CreateEmboss(direction.begin(), ambient, specular, radius); +} + diff --git a/skia/animator/SkDrawEmboss.h b/skia/animator/SkDrawEmboss.h new file mode 100644 index 0000000..51c60a1 --- /dev/null +++ b/skia/animator/SkDrawEmboss.h @@ -0,0 +1,33 @@ +/* libs/graphics/animator/SkDrawEmboss.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkDrawEmboss_DEFINED +#define SkDrawEmboss_DEFINED + +#include "SkDrawBlur.h" + +class SkDrawEmboss : public SkDrawMaskFilter { + DECLARE_DRAW_MEMBER_INFO(Emboss); + SkDrawEmboss(); + virtual SkMaskFilter* getMaskFilter(); +protected: + SkTDScalarArray direction; + SkScalar radius, ambient, specular; +}; + +#endif // SkDrawEmboss_DEFINED + diff --git a/skia/animator/SkDrawExtraPathEffect.cpp b/skia/animator/SkDrawExtraPathEffect.cpp new file mode 100644 index 0000000..2a9cb2f --- /dev/null +++ b/skia/animator/SkDrawExtraPathEffect.cpp @@ -0,0 +1,519 @@ +/* libs/graphics/animator/SkDrawExtraPathEffect.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkDrawExtraPathEffect.h" +#include "SkDrawPath.h" +#include "Sk1DPathEffect.h" +#include "Sk2DPathEffect.h" +#include "SkMemberInfo.h" +#include "SkPaintParts.h" +#include "SkPathEffect.h" +#include "SkCornerPathEffect.h" + +#include "SkDashPathEffect.h" + +class SkDrawShapePathEffect : public SkDrawPathEffect { + DECLARE_PRIVATE_MEMBER_INFO(DrawShapePathEffect); + SkDrawShapePathEffect(); + virtual ~SkDrawShapePathEffect(); + virtual bool add(SkAnimateMaker& , SkDisplayable* ); + virtual SkPathEffect* getPathEffect(); +protected: + SkDrawable* addPath; + SkDrawable* addMatrix; + SkDrawPath* path; + SkPathEffect* fPathEffect; + friend class SkShape1DPathEffect; + friend class SkShape2DPathEffect; +}; + +class SkDrawShape1DPathEffect : public SkDrawShapePathEffect { + DECLARE_EXTRAS_MEMBER_INFO(SkDrawShape1DPathEffect); + SkDrawShape1DPathEffect(SkDisplayTypes ); + virtual ~SkDrawShape1DPathEffect(); + virtual void onEndElement(SkAnimateMaker& ); +private: + SkString phase; + SkString spacing; + friend class SkShape1DPathEffect; + typedef SkDrawShapePathEffect INHERITED; +}; + +class SkDrawShape2DPathEffect : public SkDrawShapePathEffect { + DECLARE_EXTRAS_MEMBER_INFO(SkDrawShape2DPathEffect); + SkDrawShape2DPathEffect(SkDisplayTypes ); + virtual ~SkDrawShape2DPathEffect(); + virtual void onEndElement(SkAnimateMaker& ); +private: + SkDrawMatrix* matrix; + friend class SkShape2DPathEffect; + typedef SkDrawShapePathEffect INHERITED; +}; + +class SkDrawComposePathEffect : public SkDrawPathEffect { + DECLARE_EXTRAS_MEMBER_INFO(SkDrawComposePathEffect); + SkDrawComposePathEffect(SkDisplayTypes ); + virtual ~SkDrawComposePathEffect(); + virtual bool add(SkAnimateMaker& , SkDisplayable* ); + virtual SkPathEffect* getPathEffect(); + virtual bool isPaint() const; +private: + SkDrawPathEffect* effect1; + SkDrawPathEffect* effect2; +}; + +class SkDrawCornerPathEffect : public SkDrawPathEffect { + DECLARE_EXTRAS_MEMBER_INFO(SkDrawCornerPathEffect); + SkDrawCornerPathEffect(SkDisplayTypes ); + virtual ~SkDrawCornerPathEffect(); + virtual SkPathEffect* getPathEffect(); +private: + SkScalar radius; +}; + +//////////// SkShape1DPathEffect + +#include "SkAnimateMaker.h" +#include "SkAnimatorScript.h" +#include "SkDisplayApply.h" +#include "SkDrawMatrix.h" +#include "SkPaint.h" + +class SkShape1DPathEffect : public Sk1DPathEffect { +public: + SkShape1DPathEffect(SkDrawShape1DPathEffect* draw, SkAnimateMaker* maker) : + fDraw(draw), fMaker(maker) { + } + +protected: + virtual SkScalar begin(SkScalar contourLength) + { + SkScriptValue value; + SkAnimatorScript engine(*fMaker, NULL, SkType_Float); + engine.propertyCallBack(GetContourLength, &contourLength); + value.fOperand.fScalar = 0; + engine.evaluate(fDraw->phase.c_str(), &value, SkType_Float); + return value.fOperand.fScalar; + } + + virtual SkScalar next(SkPath* dst, SkScalar distance, SkPathMeasure& ) + { + fMaker->setExtraPropertyCallBack(fDraw->fType, GetDistance, &distance); + SkDrawPath* drawPath = NULL; + if (fDraw->addPath->isPath()) { + drawPath = (SkDrawPath*) fDraw->addPath; + } else { + SkApply* apply = (SkApply*) fDraw->addPath; + apply->refresh(*fMaker); + apply->activate(*fMaker); + apply->interpolate(*fMaker, SkScalarMulRound(distance, 1000)); + drawPath = (SkDrawPath*) apply->getScope(); + } + SkMatrix m; + m.reset(); + if (fDraw->addMatrix) { + SkDrawMatrix* matrix; + if (fDraw->addMatrix->getType() == SkType_Matrix) + matrix = (SkDrawMatrix*) fDraw->addMatrix; + else { + SkApply* apply = (SkApply*) fDraw->addMatrix; + apply->refresh(*fMaker); + apply->activate(*fMaker); + apply->interpolate(*fMaker, SkScalarMulRound(distance, 1000)); + matrix = (SkDrawMatrix*) apply->getScope(); + } + } + SkScalar result = 0; + SkAnimatorScript::EvaluateFloat(*fMaker, NULL, fDraw->spacing.c_str(), &result); + if (drawPath) + dst->addPath(drawPath->getPath(), m); + fMaker->clearExtraPropertyCallBack(fDraw->fType); + return result; + } + +private: + virtual void flatten(SkFlattenableWriteBuffer& ) {} + virtual Factory getFactory() { return NULL; } + + static bool GetContourLength(const char* token, size_t len, void* clen, SkScriptValue* value) { + if (SK_LITERAL_STR_EQUAL("contourLength", token, len)) { + value->fOperand.fScalar = *(SkScalar*) clen; + value->fType = SkType_Float; + return true; + } + return false; + } + + static bool GetDistance(const char* token, size_t len, void* dist, SkScriptValue* value) { + if (SK_LITERAL_STR_EQUAL("distance", token, len)) { + value->fOperand.fScalar = *(SkScalar*) dist; + value->fType = SkType_Float; + return true; + } + return false; + } + + SkDrawShape1DPathEffect* fDraw; + SkAnimateMaker* fMaker; +}; + +//////////// SkDrawShapePathEffect + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkDrawShapePathEffect::fInfo[] = { + SK_MEMBER(addMatrix, Drawable), // either matrix or apply + SK_MEMBER(addPath, Drawable), // either path or apply + SK_MEMBER(path, Path), +}; + +#endif + +DEFINE_GET_MEMBER(SkDrawShapePathEffect); + +SkDrawShapePathEffect::SkDrawShapePathEffect() : + addPath(NULL), addMatrix(NULL), path(NULL), fPathEffect(NULL) { +} + +SkDrawShapePathEffect::~SkDrawShapePathEffect() { + fPathEffect->safeUnref(); +} + +bool SkDrawShapePathEffect::add(SkAnimateMaker& , SkDisplayable* child) { + path = (SkDrawPath*) child; + return true; +} + +SkPathEffect* SkDrawShapePathEffect::getPathEffect() { + fPathEffect->ref(); + return fPathEffect; +} + +//////////// SkDrawShape1DPathEffect + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkDrawShape1DPathEffect::fInfo[] = { + SK_MEMBER_INHERITED, + SK_MEMBER(phase, String), + SK_MEMBER(spacing, String), +}; + +#endif + +DEFINE_GET_MEMBER(SkDrawShape1DPathEffect); + +SkDrawShape1DPathEffect::SkDrawShape1DPathEffect(SkDisplayTypes type) : fType(type) { +} + +SkDrawShape1DPathEffect::~SkDrawShape1DPathEffect() { +} + +void SkDrawShape1DPathEffect::onEndElement(SkAnimateMaker& maker) { + if (addPath == NULL || (addPath->isPath() == false && addPath->isApply() == false)) + maker.setErrorCode(SkDisplayXMLParserError::kUnknownError); // !!! add error + else + fPathEffect = new SkShape1DPathEffect(this, &maker); +} + +////////// SkShape2DPathEffect + +class SkShape2DPathEffect : public Sk2DPathEffect { +public: + SkShape2DPathEffect(SkDrawShape2DPathEffect* draw, SkAnimateMaker* maker, + const SkMatrix& matrix) : Sk2DPathEffect(matrix), fDraw(draw), fMaker(maker) { + } + +protected: + virtual void begin(const SkIRect& uvBounds, SkPath* ) + { + fUVBounds.set(SkIntToScalar(uvBounds.fLeft), SkIntToScalar(uvBounds.fTop), + SkIntToScalar(uvBounds.fRight), SkIntToScalar(uvBounds.fBottom)); + } + + virtual void next(const SkPoint& loc, int u, int v, SkPath* dst) + { + fLoc = loc; + fU = u; + fV = v; + SkDrawPath* drawPath; + fMaker->setExtraPropertyCallBack(fDraw->fType, Get2D, this); + if (fDraw->addPath->isPath()) { + drawPath = (SkDrawPath*) fDraw->addPath; + } else { + SkApply* apply = (SkApply*) fDraw->addPath; + apply->refresh(*fMaker); + apply->activate(*fMaker); + apply->interpolate(*fMaker, v); + drawPath = (SkDrawPath*) apply->getScope(); + } + if (drawPath == NULL) + goto clearCallBack; + if (fDraw->matrix) { + SkDrawMatrix* matrix; + if (fDraw->matrix->getType() == SkType_Matrix) + matrix = (SkDrawMatrix*) fDraw->matrix; + else { + SkApply* apply = (SkApply*) fDraw->matrix; + apply->activate(*fMaker); + apply->interpolate(*fMaker, v); + matrix = (SkDrawMatrix*) apply->getScope(); + } + if (matrix) { + dst->addPath(drawPath->getPath(), matrix->getMatrix()); + goto clearCallBack; + } + } + dst->addPath(drawPath->getPath()); +clearCallBack: + fMaker->clearExtraPropertyCallBack(fDraw->fType); + } + +private: + + static bool Get2D(const char* token, size_t len, void* s2D, SkScriptValue* value) { + static const char match[] = "locX|locY|left|top|right|bottom|u|v" ; + SkShape2DPathEffect* shape2D = (SkShape2DPathEffect*) s2D; + int index; + if (SkAnimatorScript::MapEnums(match, token, len, &index) == false) + return false; + SkASSERT((sizeof(SkPoint) + sizeof(SkRect)) / sizeof(SkScalar) == 6); + if (index < 6) { + value->fType = SkType_Float; + value->fOperand.fScalar = (&shape2D->fLoc.fX)[index]; + } else { + value->fType = SkType_Int; + value->fOperand.fS32 = (&shape2D->fU)[index - 6]; + } + return true; + } + + SkPoint fLoc; + SkRect fUVBounds; + int32_t fU; + int32_t fV; + SkDrawShape2DPathEffect* fDraw; + SkAnimateMaker* fMaker; + + // illegal + SkShape2DPathEffect(const SkShape2DPathEffect&); + SkShape2DPathEffect& operator=(const SkShape2DPathEffect&); +}; + +////////// SkDrawShape2DPathEffect + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkDrawShape2DPathEffect::fInfo[] = { + SK_MEMBER_INHERITED, + SK_MEMBER(matrix, Matrix) +}; + +#endif + +DEFINE_GET_MEMBER(SkDrawShape2DPathEffect); + +SkDrawShape2DPathEffect::SkDrawShape2DPathEffect(SkDisplayTypes type) : fType(type) { +} + +SkDrawShape2DPathEffect::~SkDrawShape2DPathEffect() { +} + +void SkDrawShape2DPathEffect::onEndElement(SkAnimateMaker& maker) { + if (addPath == NULL || (addPath->isPath() == false && addPath->isApply() == false) || + matrix == NULL) + maker.setErrorCode(SkDisplayXMLParserError::kUnknownError); // !!! add error + else + fPathEffect = new SkShape2DPathEffect(this, &maker, matrix->getMatrix()); +} + +////////// SkDrawComposePathEffect + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkDrawComposePathEffect::fInfo[] = { + SK_MEMBER(effect1, PathEffect), + SK_MEMBER(effect2, PathEffect) +}; + +#endif + +DEFINE_GET_MEMBER(SkDrawComposePathEffect); + +SkDrawComposePathEffect::SkDrawComposePathEffect(SkDisplayTypes type) : fType(type), + effect1(NULL), effect2(NULL) { +} + +SkDrawComposePathEffect::~SkDrawComposePathEffect() { + delete effect1; + delete effect2; +} + +bool SkDrawComposePathEffect::add(SkAnimateMaker& , SkDisplayable* child) { + if (effect1 == NULL) + effect1 = (SkDrawPathEffect*) child; + else + effect2 = (SkDrawPathEffect*) child; + return true; +} + +SkPathEffect* SkDrawComposePathEffect::getPathEffect() { + SkPathEffect* e1 = effect1->getPathEffect(); + SkPathEffect* e2 = effect2->getPathEffect(); + SkPathEffect* composite = new SkComposePathEffect(e1, e2); + e1->unref(); + e2->unref(); + return composite; +} + +bool SkDrawComposePathEffect::isPaint() const { + return true; +} + +//////////// SkDrawCornerPathEffect + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkDrawCornerPathEffect::fInfo[] = { + SK_MEMBER(radius, Float) +}; + +#endif + +DEFINE_GET_MEMBER(SkDrawCornerPathEffect); + +SkDrawCornerPathEffect::SkDrawCornerPathEffect(SkDisplayTypes type): + fType(type), radius(0) { +} + +SkDrawCornerPathEffect::~SkDrawCornerPathEffect() { +} + +SkPathEffect* SkDrawCornerPathEffect::getPathEffect() { + return new SkCornerPathEffect(radius); +} + +///////// + +#include "SkExtras.h" + +const char kDrawShape1DPathEffectName[] = "pathEffect:shape1D"; +const char kDrawShape2DPathEffectName[] = "pathEffect:shape2D"; +const char kDrawComposePathEffectName[] = "pathEffect:compose"; +const char kDrawCornerPathEffectName[] = "pathEffect:corner"; + +class SkExtraPathEffects : public SkExtras { +public: + SkExtraPathEffects(SkAnimator* animator) : + skDrawShape1DPathEffectType(SkType_Unknown), + skDrawShape2DPathEffectType(SkType_Unknown), + skDrawComposePathEffectType(SkType_Unknown), + skDrawCornerPathEffectType(SkType_Unknown) { + } + + virtual SkDisplayable* createInstance(SkDisplayTypes type) { + SkDisplayable* result = NULL; + if (skDrawShape1DPathEffectType == type) + result = new SkDrawShape1DPathEffect(type); + else if (skDrawShape2DPathEffectType == type) + result = new SkDrawShape2DPathEffect(type); + else if (skDrawComposePathEffectType == type) + result = new SkDrawComposePathEffect(type); + else if (skDrawCornerPathEffectType == type) + result = new SkDrawCornerPathEffect(type); + return result; + } + + virtual bool definesType(SkDisplayTypes type) { + return type == skDrawShape1DPathEffectType || + type == skDrawShape2DPathEffectType || + type == skDrawComposePathEffectType || + type == skDrawCornerPathEffectType; + } + +#if SK_USE_CONDENSED_INFO == 0 + virtual const SkMemberInfo* getMembers(SkDisplayTypes type, int* infoCountPtr) { + const SkMemberInfo* info = NULL; + int infoCount = 0; + if (skDrawShape1DPathEffectType == type) { + info = SkDrawShape1DPathEffect::fInfo; + infoCount = SkDrawShape1DPathEffect::fInfoCount; + } else if (skDrawShape2DPathEffectType == type) { + info = SkDrawShape2DPathEffect::fInfo; + infoCount = SkDrawShape2DPathEffect::fInfoCount; + } else if (skDrawComposePathEffectType == type) { + info = SkDrawComposePathEffect::fInfo; + infoCount = SkDrawShape1DPathEffect::fInfoCount; + } else if (skDrawCornerPathEffectType == type) { + info = SkDrawCornerPathEffect::fInfo; + infoCount = SkDrawCornerPathEffect::fInfoCount; + } + if (infoCountPtr) + *infoCountPtr = infoCount; + return info; + } +#endif + +#ifdef SK_DEBUG + virtual const char* getName(SkDisplayTypes type) { + if (skDrawShape1DPathEffectType == type) + return kDrawShape1DPathEffectName; + else if (skDrawShape2DPathEffectType == type) + return kDrawShape2DPathEffectName; + else if (skDrawComposePathEffectType == type) + return kDrawComposePathEffectName; + else if (skDrawCornerPathEffectType == type) + return kDrawCornerPathEffectName; + return NULL; + } +#endif + + virtual SkDisplayTypes getType(const char name[], size_t len ) { + SkDisplayTypes* type = NULL; + if (SK_LITERAL_STR_EQUAL(kDrawShape1DPathEffectName, name, len)) + type = &skDrawShape1DPathEffectType; + else if (SK_LITERAL_STR_EQUAL(kDrawShape2DPathEffectName, name, len)) + type = &skDrawShape2DPathEffectType; + else if (SK_LITERAL_STR_EQUAL(kDrawComposePathEffectName, name, len)) + type = &skDrawComposePathEffectType; + else if (SK_LITERAL_STR_EQUAL(kDrawCornerPathEffectName, name, len)) + type = &skDrawCornerPathEffectType; + if (type) { + if (*type == SkType_Unknown) + *type = SkDisplayType::RegisterNewType(); + return *type; + } + return SkType_Unknown; + } + +private: + SkDisplayTypes skDrawShape1DPathEffectType; + SkDisplayTypes skDrawShape2DPathEffectType; + SkDisplayTypes skDrawComposePathEffectType; + SkDisplayTypes skDrawCornerPathEffectType; +}; + + +void InitializeSkExtraPathEffects(SkAnimator* animator) { + animator->addExtras(new SkExtraPathEffects(animator)); +} + +//////////////// + + +SkExtras::SkExtras() : fExtraCallBack(NULL), fExtraStorage(NULL) { +} diff --git a/skia/animator/SkDrawFull.cpp b/skia/animator/SkDrawFull.cpp new file mode 100644 index 0000000..9c964eb3 --- /dev/null +++ b/skia/animator/SkDrawFull.cpp @@ -0,0 +1,27 @@ +/* libs/graphics/animator/SkDrawFull.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkDrawFull.h" +#include "SkAnimateMaker.h" +#include "SkCanvas.h" + +bool SkFull::draw(SkAnimateMaker& maker) { + SkBoundableAuto boundable(this, maker); + maker.fCanvas->drawPaint(*maker.fPaint); + return false; +} + diff --git a/skia/animator/SkDrawFull.h b/skia/animator/SkDrawFull.h new file mode 100644 index 0000000..68fe130 --- /dev/null +++ b/skia/animator/SkDrawFull.h @@ -0,0 +1,30 @@ +/* libs/graphics/animator/SkDrawFull.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkDrawFull_DEFINED +#define SkDrawFull_DEFINED + +#include "SkBoundable.h" + +class SkFull : public SkBoundable { + DECLARE_EMPTY_MEMBER_INFO(Full); + virtual bool draw(SkAnimateMaker& ); +private: + typedef SkBoundable INHERITED; +}; + +#endif // SkDrawFull_DEFINED diff --git a/skia/animator/SkDrawGradient.cpp b/skia/animator/SkDrawGradient.cpp new file mode 100644 index 0000000..4ae7b4f --- /dev/null +++ b/skia/animator/SkDrawGradient.cpp @@ -0,0 +1,235 @@ +/* libs/graphics/animator/SkDrawGradient.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkDrawGradient.h" +#include "SkAnimateMaker.h" +#include "SkAnimatorScript.h" +#include "SkGradientShader.h" +#include "SkUnitMapper.h" + +SkScalar SkUnitToScalar(U16CPU x) { +#ifdef SK_SCALAR_IS_FLOAT + return x / 65535.0f; +#else + return x + (x >> 8); +#endif +} + +U16CPU SkScalarToUnit(SkScalar x) { + SkScalar pin = SkScalarPin(x, 0, SK_Scalar1); +#ifdef SK_SCALAR_IS_FLOAT + return (int) (pin * 65535.0f); +#else + return pin - (pin >= 32768); +#endif +} + +class SkGradientUnitMapper : public SkUnitMapper { +public: + SkGradientUnitMapper(SkAnimateMaker* maker, const char* script) : fMaker(maker), fScript(script) { + } + + // overrides for SkFlattenable + virtual Factory getFactory() { return NULL; } + +protected: + virtual uint16_t mapUnit16(uint16_t x) { + fUnit = SkUnitToScalar(x); + SkScriptValue value; + SkAnimatorScript engine(*fMaker, NULL, SkType_Float); + engine.propertyCallBack(GetUnitValue, &fUnit); + if (engine.evaluate(fScript, &value, SkType_Float)) + x = SkScalarToUnit(value.fOperand.fScalar); + return x; + } + + static bool GetUnitValue(const char* token, size_t len, void* unitPtr, SkScriptValue* value) { + if (SK_LITERAL_STR_EQUAL("unit", token, len)) { + value->fOperand.fScalar = *(SkScalar*) unitPtr; + value->fType = SkType_Float; + return true; + } + return false; + } + + SkAnimateMaker* fMaker; + const char* fScript; + SkScalar fUnit; +}; + + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkGradient::fInfo[] = { + SK_MEMBER_INHERITED, + SK_MEMBER_ARRAY(offsets, Float), + SK_MEMBER(unitMapper, String) +}; + +#endif + +DEFINE_GET_MEMBER(SkGradient); + +SkGradient::SkGradient() : fUnitMapper(NULL) { +} + +SkGradient::~SkGradient() { + for (int index = 0; index < fDrawColors.count(); index++) + delete fDrawColors[index]; + delete fUnitMapper; +} + +bool SkGradient::add(SkAnimateMaker& , SkDisplayable* child) { + SkASSERT(child); + if (child->isColor()) { + SkDrawColor* color = (SkDrawColor*) child; + *fDrawColors.append() = color; + return true; + } + return false; +} + +int SkGradient::addPrelude() { + int count = fDrawColors.count(); + fColors.setCount(count); + for (int index = 0; index < count; index++) + fColors[index] = fDrawColors[index]->color; + return count; +} + +#ifdef SK_DUMP_ENABLED +void SkGradient::dumpRest(SkAnimateMaker* maker) { + dumpAttrs(maker); + //can a gradient have no colors? + bool closedYet = false; + SkDisplayList::fIndent += 4; + for (SkDrawColor** ptr = fDrawColors.begin(); ptr < fDrawColors.end(); ptr++) { + if (closedYet == false) { + SkDebugf(">\n"); + closedYet = true; + } + SkDrawColor* color = *ptr; + color->dump(maker); + } + SkDisplayList::fIndent -= 4; + dumpChildren(maker, closedYet); //dumps the matrix if it has one +} +#endif + +void SkGradient::onEndElement(SkAnimateMaker& maker) { + if (offsets.count() != 0) { + if (offsets.count() != fDrawColors.count()) { + maker.setErrorCode(SkDisplayXMLParserError::kGradientOffsetsDontMatchColors); + return; + } + if (offsets[0] != 0) { + maker.setErrorCode(SkDisplayXMLParserError::kGradientOffsetsMustStartWithZero); + return; + } + if (offsets[offsets.count()-1] != SK_Scalar1) { + maker.setErrorCode(SkDisplayXMLParserError::kGradientOffsetsMustEndWithOne); + return; + } + for (int i = 1; i < offsets.count(); i++) { + if (offsets[i] <= offsets[i-1]) { + maker.setErrorCode(SkDisplayXMLParserError::kGradientOffsetsMustIncrease); + return; + } + if (offsets[i] > SK_Scalar1) { + maker.setErrorCode(SkDisplayXMLParserError::kGradientOffsetsMustBeNoMoreThanOne); + return; + } + } + } + if (unitMapper.size() > 0) + fUnitMapper = new SkGradientUnitMapper(&maker, unitMapper.c_str()); + INHERITED::onEndElement(maker); +} + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkLinearGradient::fInfo[] = { + SK_MEMBER_INHERITED, + SK_MEMBER_ARRAY(points, Float), +}; + +#endif + +DEFINE_GET_MEMBER(SkLinearGradient); + +SkLinearGradient::SkLinearGradient() { +} + +void SkLinearGradient::onEndElement(SkAnimateMaker& maker) +{ + if (points.count() != 4) + maker.setErrorCode(SkDisplayXMLParserError::kGradientPointsLengthMustBeFour); + INHERITED::onEndElement(maker); +} + +#ifdef SK_DUMP_ENABLED +void SkLinearGradient::dump(SkAnimateMaker* maker) { + dumpBase(maker); + dumpRest(maker); + } +#endif + +SkShader* SkLinearGradient::getShader() { + if (addPrelude() == 0 || points.count() != 4) + return NULL; + SkShader* shader = SkGradientShader::CreateLinear((SkPoint*)points.begin(), + fColors.begin(), offsets.begin(), fColors.count(), (SkShader::TileMode) tileMode, fUnitMapper); + SkAutoTDelete<SkShader> autoDel(shader); + addPostlude(shader); + (void)autoDel.detach(); + return shader; +} + + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkRadialGradient::fInfo[] = { + SK_MEMBER_INHERITED, + SK_MEMBER(center, Point), + SK_MEMBER(radius, Float) +}; + +#endif + +DEFINE_GET_MEMBER(SkRadialGradient); + +SkRadialGradient::SkRadialGradient() : radius(0) { + center.set(0, 0); +} + +#ifdef SK_DUMP_ENABLED +void SkRadialGradient::dump(SkAnimateMaker* maker) { + dumpBase(maker); + dumpRest(maker); +} +#endif + +SkShader* SkRadialGradient::getShader() { + if (addPrelude() == 0) + return NULL; + SkShader* shader = SkGradientShader::CreateRadial(center, + radius, fColors.begin(), offsets.begin(), fColors.count(), (SkShader::TileMode) tileMode, fUnitMapper); + SkAutoTDelete<SkShader> autoDel(shader); + addPostlude(shader); + (void)autoDel.detach(); + return shader; +} diff --git a/skia/animator/SkDrawGradient.h b/skia/animator/SkDrawGradient.h new file mode 100644 index 0000000..4b4a3cd --- /dev/null +++ b/skia/animator/SkDrawGradient.h @@ -0,0 +1,76 @@ +/* libs/graphics/animator/SkDrawGradient.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkDrawGradient_DEFINED +#define SkDrawGradient_DEFINED + +#include "SkDrawColor.h" +#include "SkDrawShader.h" +#include "SkIntArray.h" + +class SkUnitMapper; + +class SkGradient : public SkDrawShader { + DECLARE_PRIVATE_MEMBER_INFO(Gradient); + SkGradient(); + virtual ~SkGradient(); + virtual bool add(SkAnimateMaker& , SkDisplayable* child); +#ifdef SK_DUMP_ENABLED + virtual void dumpRest(SkAnimateMaker*); +#endif + virtual void onEndElement(SkAnimateMaker& ); +protected: + SkTDScalarArray offsets; + SkString unitMapper; + SkTDColorArray fColors; + SkTDDrawColorArray fDrawColors; + SkUnitMapper* fUnitMapper; + int addPrelude(); +private: + typedef SkDrawShader INHERITED; +}; + +class SkLinearGradient : public SkGradient { + DECLARE_MEMBER_INFO(LinearGradient); + SkLinearGradient(); + virtual void onEndElement(SkAnimateMaker& ); +#ifdef SK_DUMP_ENABLED + virtual void dump(SkAnimateMaker*); +#endif + virtual SkShader* getShader(); +protected: + SkTDScalarArray points; +private: + typedef SkGradient INHERITED; +}; + +class SkRadialGradient : public SkGradient { + DECLARE_MEMBER_INFO(RadialGradient); + SkRadialGradient(); +#ifdef SK_DUMP_ENABLED + virtual void dump(SkAnimateMaker*); +#endif + virtual SkShader* getShader(); +protected: + SkPoint center; + SkScalar radius; +private: + typedef SkGradient INHERITED; +}; + +#endif // SkDrawGradient_DEFINED + diff --git a/skia/animator/SkDrawGroup.cpp b/skia/animator/SkDrawGroup.cpp new file mode 100644 index 0000000..4c215a1 --- /dev/null +++ b/skia/animator/SkDrawGroup.cpp @@ -0,0 +1,331 @@ +/* libs/graphics/animator/SkDrawGroup.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkDrawGroup.h" +#include "SkAnimateMaker.h" +#include "SkAnimatorScript.h" +#include "SkCanvas.h" +#include "SkDisplayApply.h" +#include "SkPaint.h" +#ifdef SK_DEBUG +#include "SkDisplayList.h" +#endif + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkGroup::fInfo[] = { + SK_MEMBER(condition, String), + SK_MEMBER(enableCondition, String) +}; + +#endif + +DEFINE_GET_MEMBER(SkGroup); + +SkGroup::SkGroup() : fParentList(NULL), fOriginal(NULL) { +} + +SkGroup::~SkGroup() { + if (fOriginal) // has been copied + return; + int index = 0; + int max = fCopies.count() << 5; + for (SkDrawable** ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) { + if (index >= max || markedForDelete(index)) + delete *ptr; +// else { +// SkApply* apply = (SkApply*) *ptr; +// SkASSERT(apply->isApply()); +// SkASSERT(apply->getScope()); +// delete apply->getScope(); +// } + index++; + } +} + +bool SkGroup::add(SkAnimateMaker& , SkDisplayable* child) { + SkASSERT(child); +// SkASSERT(child->isDrawable()); + *fChildren.append() = (SkDrawable*) child; + if (child->isGroup()) { + SkGroup* groupie = (SkGroup*) child; + SkASSERT(groupie->fParentList == NULL); + groupie->fParentList = &fChildren; + } + return true; +} + +bool SkGroup::contains(SkDisplayable* match) { + for (SkDrawable** ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) { + SkDrawable* drawable = *ptr; + if (drawable == match || drawable->contains(match)) + return true; + } + return false; +} + +SkGroup* SkGroup::copy() { + SkGroup* result = new SkGroup(); + result->fOriginal = this; + result->fChildren = fChildren; + return result; +} + +SkBool SkGroup::copySet(int index) { + return (fCopies[index >> 5] & 1 << (index & 0x1f)) != 0; +} + +SkDisplayable* SkGroup::deepCopy(SkAnimateMaker* maker) { + SkDisplayable* copy = INHERITED::deepCopy(maker); + for (SkDrawable** ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) { + SkDisplayable* displayable = (SkDisplayable*)*ptr; + SkDisplayable* deeperCopy = displayable->deepCopy(maker); + ((SkGroup*)copy)->add(*maker, deeperCopy); + } + return copy; +} + +bool SkGroup::doEvent(SkDisplayEvent::Kind kind, SkEventState* state) { + bool handled = false; + for (SkDrawable** ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) { + SkDrawable* drawable = *ptr; + if (drawable->isDrawable() == false) + continue; + handled |= drawable->doEvent(kind, state); + } + return handled; +} + +bool SkGroup::draw(SkAnimateMaker& maker) { + bool conditionTrue = ifCondition(maker, this, condition); + bool result = false; + for (SkDrawable** ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) { + SkDrawable* drawable = *ptr; + if (drawable->isDrawable() == false) + continue; + if (conditionTrue == false) { + if (drawable->isApply()) + ((SkApply*) drawable)->disable(); + continue; + } + maker.validate(); + result |= drawable->draw(maker); + maker.validate(); + } + return result; +} + +#ifdef SK_DUMP_ENABLED +void SkGroup::dump(SkAnimateMaker* maker) { + dumpBase(maker); + if (condition.size() > 0) + SkDebugf("condition=\"%s\" ", condition.c_str()); + if (enableCondition.size() > 0) + SkDebugf("enableCondition=\"%s\" ", enableCondition.c_str()); + dumpDrawables(maker); +} + +void SkGroup::dumpDrawables(SkAnimateMaker* maker) { + SkDisplayList::fIndent += 4; + int save = SkDisplayList::fDumpIndex; + SkDisplayList::fDumpIndex = 0; + bool closedYet = false; + for (SkDrawable** ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) { + if (closedYet == false) { + closedYet = true; + SkDebugf(">\n"); + } + SkDrawable* drawable = *ptr; + drawable->dump(maker); + SkDisplayList::fDumpIndex++; + } + SkDisplayList::fIndent -= 4; + SkDisplayList::fDumpIndex = save; + if (closedYet) //we had children, now it's time to close the group + dumpEnd(maker); + else //no children + SkDebugf("/>\n"); +} + +void SkGroup::dumpEvents() { + for (SkDrawable** ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) { + SkDrawable* drawable = *ptr; + drawable->dumpEvents(); + } +} +#endif + +bool SkGroup::enable(SkAnimateMaker& maker ) { + reset(); + for (SkDrawable** ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) { + SkDrawable* drawable = *ptr; + if (ifCondition(maker, drawable, enableCondition) == false) + continue; + drawable->enable(maker); + } + return true; // skip add; already added so that scope is findable by children +} + +int SkGroup::findGroup(SkDrawable* match, SkTDDrawableArray** list, + SkGroup** parent, SkGroup** found, SkTDDrawableArray** grandList) { + *list = &fChildren; + for (SkDrawable** ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) { + SkDrawable* drawable = *ptr; + if (drawable->isGroup()) { + SkGroup* childGroup = (SkGroup*) drawable; + if (childGroup->fOriginal == match) + goto foundMatch; + } + if (drawable == match) { +foundMatch: + *parent = this; + return (int) (ptr - fChildren.begin()); + } + } + *grandList = &fChildren; + return SkDisplayList::SearchForMatch(match, list, parent, found, grandList); +} + +bool SkGroup::hasEnable() const { + return true; +} + +bool SkGroup::ifCondition(SkAnimateMaker& maker, SkDrawable* drawable, + SkString& conditionString) { + if (conditionString.size() == 0) + return true; + int32_t result; + bool success = SkAnimatorScript::EvaluateInt(maker, this, conditionString.c_str(), &result); +#ifdef SK_DUMP_ENABLED + if (maker.fDumpGConditions) { + SkDebugf("group: "); + dumpBase(&maker); + SkDebugf("condition=%s ", conditionString.c_str()); + if (success == false) + SkDebugf("(script failed)\n"); + else + SkDebugf("success=%s\n", result != 0 ? "true" : "false"); + } +#endif + return success && result != 0; +} + +void SkGroup::initialize() { + for (SkDrawable** ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) { + SkDrawable* drawable = *ptr; + if (drawable->isDrawable() == false) + continue; + drawable->initialize(); + } +} + +void SkGroup::markCopyClear(int index) { + if (index < 0) + index = fChildren.count(); + fCopies[index >> 5] &= ~(1 << (index & 0x1f)); +} + +void SkGroup::markCopySet(int index) { + if (index < 0) + index = fChildren.count(); + fCopies[index >> 5] |= 1 << (index & 0x1f); +} + +void SkGroup::markCopySize(int index) { + if (index < 0) + index = fChildren.count() + 1; + int oldLongs = fCopies.count(); + int newLongs = (index >> 5) + 1; + if (oldLongs < newLongs) { + fCopies.setCount(newLongs); + memset(&fCopies[oldLongs], 0, (newLongs - oldLongs) << 2); + } +} + +void SkGroup::reset() { + if (fOriginal) // has been copied + return; + int index = 0; + int max = fCopies.count() << 5; + for (SkDrawable** ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) { + if (index >= max || copySet(index) == false) + continue; + SkApply* apply = (SkApply*) *ptr; + SkASSERT(apply->isApply()); + SkASSERT(apply->getScope()); + *ptr = apply->getScope(); + markCopyClear(index); + index++; + } +} + +bool SkGroup::resolveIDs(SkAnimateMaker& maker, SkDisplayable* orig, SkApply* apply) { + SkGroup* original = (SkGroup*) orig; + SkTDDrawableArray& originalChildren = original->fChildren; + SkDrawable** originalPtr = originalChildren.begin(); + SkDrawable** ptr = fChildren.begin(); + SkDrawable** end = fChildren.end(); + SkDrawable** origChild = ((SkGroup*) orig)->fChildren.begin(); + while (ptr < end) { + SkDrawable* drawable = *ptr++; + maker.resolveID(drawable, *origChild++); + if (drawable->resolveIDs(maker, *originalPtr++, apply) == true) + return true; // failed + } + return false; +} + +void SkGroup::setSteps(int steps) { + for (SkDrawable** ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) { + SkDrawable* drawable = *ptr; + if (drawable->isDrawable() == false) + continue; + drawable->setSteps(steps); + } +} + +#ifdef SK_DEBUG +void SkGroup::validate() { + for (SkDrawable** ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) { + SkDrawable* drawable = *ptr; + drawable->validate(); + } +} +#endif + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkSave::fInfo[] = { + SK_MEMBER_INHERITED +}; + +#endif + +DEFINE_GET_MEMBER(SkSave); + +bool SkSave::draw(SkAnimateMaker& maker) { + maker.fCanvas->save(); + SkPaint* save = maker.fPaint; + SkPaint local = SkPaint(*maker.fPaint); + maker.fPaint = &local; + bool result = INHERITED::draw(maker); + maker.fPaint = save; + maker.fCanvas->restore(); + return result; +} + + diff --git a/skia/animator/SkDrawGroup.h b/skia/animator/SkDrawGroup.h new file mode 100644 index 0000000..da153a0e1 --- /dev/null +++ b/skia/animator/SkDrawGroup.h @@ -0,0 +1,80 @@ +/* libs/graphics/animator/SkDrawGroup.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkDrawGroup_DEFINED +#define SkDrawGroup_DEFINED + +#include "SkDrawable.h" +#include "SkIntArray.h" +#include "SkMemberInfo.h" + +class SkGroup : public SkDrawable { //interface for schema element <g> +public: + DECLARE_MEMBER_INFO(Group); + SkGroup(); + virtual ~SkGroup(); + virtual bool add(SkAnimateMaker& , SkDisplayable* child); + virtual bool contains(SkDisplayable* ); + SkGroup* copy(); + SkBool copySet(int index); + virtual SkDisplayable* deepCopy(SkAnimateMaker* ); + virtual bool doEvent(SkDisplayEvent::Kind , SkEventState* state ); + virtual bool draw(SkAnimateMaker& ); +#ifdef SK_DUMP_ENABLED + virtual void dump(SkAnimateMaker* ); + virtual void dumpDrawables(SkAnimateMaker* ); + virtual void dumpEvents(); +#endif + int findGroup(SkDrawable* drawable, SkTDDrawableArray** list, + SkGroup** parent, SkGroup** found, SkTDDrawableArray** grandList); + virtual bool enable(SkAnimateMaker& ); + SkTDDrawableArray* getChildren() { return &fChildren; } + SkGroup* getOriginal() { return fOriginal; } + virtual bool hasEnable() const; + virtual void initialize(); + SkBool isACopy() { return fOriginal != NULL; } + void markCopyClear(int index); + void markCopySet(int index); + void markCopySize(int index); + bool markedForDelete(int index) const { return (fCopies[index >> 5] & 1 << (index & 0x1f)) == 0; } + void reset(); + bool resolveIDs(SkAnimateMaker& maker, SkDisplayable* original, SkApply* ); + virtual void setSteps(int steps); +#ifdef SK_DEBUG + virtual void validate(); +#endif +protected: + bool ifCondition(SkAnimateMaker& maker, SkDrawable* drawable, + SkString& conditionString); + SkString condition; + SkString enableCondition; + SkTDDrawableArray fChildren; + SkTDDrawableArray* fParentList; + SkTDIntArray fCopies; + SkGroup* fOriginal; +private: + typedef SkDrawable INHERITED; +}; + +class SkSave: public SkGroup { + DECLARE_MEMBER_INFO(Save); + virtual bool draw(SkAnimateMaker& ); +private: + typedef SkGroup INHERITED; +}; + +#endif // SkDrawGroup_DEFINED diff --git a/skia/animator/SkDrawLine.cpp b/skia/animator/SkDrawLine.cpp new file mode 100644 index 0000000..4fee464 --- /dev/null +++ b/skia/animator/SkDrawLine.cpp @@ -0,0 +1,43 @@ +/* libs/graphics/animator/SkDrawLine.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkDrawLine.h" +#include "SkAnimateMaker.h" +#include "SkCanvas.h" +#include "SkPaint.h" + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkLine::fInfo[] = { + SK_MEMBER(x1, Float), + SK_MEMBER(x2, Float), + SK_MEMBER(y1, Float), + SK_MEMBER(y2, Float) +}; + +#endif + +DEFINE_GET_MEMBER(SkLine); + +SkLine::SkLine() : x1(0), x2(0), y1(0), y2(0) { +} + +bool SkLine::draw(SkAnimateMaker& maker) { + SkBoundableAuto boundable(this, maker); + maker.fCanvas->drawLine(x1, y1, x2, y2, *maker.fPaint); + return false; +} diff --git a/skia/animator/SkDrawLine.h b/skia/animator/SkDrawLine.h new file mode 100644 index 0000000..80451e3 --- /dev/null +++ b/skia/animator/SkDrawLine.h @@ -0,0 +1,37 @@ +/* libs/graphics/animator/SkDrawLine.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkDrawLine_DEFINED +#define SkDrawLine_DEFINED + +#include "SkBoundable.h" +#include "SkMemberInfo.h" + +class SkLine : public SkBoundable { + DECLARE_MEMBER_INFO(Line); + SkLine(); + virtual bool draw(SkAnimateMaker& ); +private: + SkScalar x1; + SkScalar x2; + SkScalar y1; + SkScalar y2; + typedef SkBoundable INHERITED; +}; + +#endif // SkDrawLine_DEFINED + diff --git a/skia/animator/SkDrawMatrix.cpp b/skia/animator/SkDrawMatrix.cpp new file mode 100644 index 0000000..8ccd9c1 --- /dev/null +++ b/skia/animator/SkDrawMatrix.cpp @@ -0,0 +1,290 @@ +/* libs/graphics/animator/SkDrawMatrix.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkDrawMatrix.h" +#include "SkAnimateMaker.h" +#include "SkCanvas.h" +#include "SkPaint.h" +#include "SkParse.h" +#include "SkMatrixParts.h" +#include "SkScript.h" +#include "SkTypedArray.h" + +enum SkDrawMatrix_Properties { + SK_PROPERTY(perspectX), + SK_PROPERTY(perspectY), + SK_PROPERTY(rotate), + SK_PROPERTY(scale), + SK_PROPERTY(scaleX), + SK_PROPERTY(scaleY), + SK_PROPERTY(skewX), + SK_PROPERTY(skewY), + SK_PROPERTY(translate), + SK_PROPERTY(translateX), + SK_PROPERTY(translateY) +}; + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkDrawMatrix::fInfo[] = { + SK_MEMBER_ARRAY(matrix, Float), + SK_MEMBER_PROPERTY(perspectX, Float), + SK_MEMBER_PROPERTY(perspectY, Float), + SK_MEMBER_PROPERTY(rotate, Float), + SK_MEMBER_PROPERTY(scale, Float), + SK_MEMBER_PROPERTY(scaleX, Float), + SK_MEMBER_PROPERTY(scaleY, Float), + SK_MEMBER_PROPERTY(skewX, Float), + SK_MEMBER_PROPERTY(skewY, Float), + SK_MEMBER_PROPERTY(translate, Point), + SK_MEMBER_PROPERTY(translateX, Float), + SK_MEMBER_PROPERTY(translateY, Float) +}; + +#endif + +DEFINE_GET_MEMBER(SkDrawMatrix); + +SkDrawMatrix::SkDrawMatrix() : fChildHasID(false), fDirty(false) { + fConcat.reset(); + fMatrix.reset(); +} + +SkDrawMatrix::~SkDrawMatrix() { + for (SkMatrixPart** part = fParts.begin(); part < fParts.end(); part++) + delete *part; +} + +bool SkDrawMatrix::add(SkAnimateMaker& maker, SkDisplayable* child) { + SkASSERT(child && child->isMatrixPart()); + SkMatrixPart* part = (SkMatrixPart*) child; + *fParts.append() = part; + if (part->add()) + maker.setErrorCode(SkDisplayXMLParserError::kErrorAddingToMatrix); + return true; +} + +bool SkDrawMatrix::childrenNeedDisposing() const { + return false; +} + +SkDisplayable* SkDrawMatrix::deepCopy(SkAnimateMaker* maker) { + SkDrawMatrix* copy = (SkDrawMatrix*) + SkDisplayType::CreateInstance(maker, SkType_Matrix); + SkASSERT(fParts.count() == 0); + copy->fMatrix = fMatrix; + copy->fConcat = fConcat; + return copy; +} + +void SkDrawMatrix::dirty() { + fDirty = true; +} + +bool SkDrawMatrix::draw(SkAnimateMaker& maker) { + SkMatrix& concat = getMatrix(); + maker.fCanvas->concat(concat); + return false; +} + +#ifdef SK_DUMP_ENABLED +void SkDrawMatrix::dump(SkAnimateMaker* maker) { + dumpBase(maker); + if (fMatrix.isIdentity()) { + SkDebugf("matrix=\"identity\"/>\n"); + return; + } + SkScalar result; + result = fMatrix[SkMatrix::kMScaleX]; + if (result != SK_Scalar1) + SkDebugf("sx=\"%g\" ", SkScalarToFloat(result)); + result = fMatrix.getScaleY(); + if (result != SK_Scalar1) + SkDebugf("sy=\"%g\" ", SkScalarToFloat(result)); + result = fMatrix.getSkewX(); + if (result) + SkDebugf("skew-x=\"%g\" ", SkScalarToFloat(result)); + result = fMatrix.getSkewY(); + if (result) + SkDebugf("skew-y=\"%g\" ", SkScalarToFloat(result)); + result = fMatrix.getTranslateX(); + if (result) + SkDebugf("tx=\"%g\" ", SkScalarToFloat(result)); + result = fMatrix.getTranslateY(); + if (result) + SkDebugf("ty=\"%g\" ", SkScalarToFloat(result)); + result = fMatrix.getPerspX(); + if (result) + SkDebugf("perspect-x=\"%g\" ", SkScalarToFloat(result)); + result = fMatrix.getPerspY(); + if (result) + SkDebugf("perspect-y=\"%g\" ", SkScalarToFloat(result)); + SkDebugf("/>\n"); +} +#endif + +SkMatrix& SkDrawMatrix::getMatrix() { + if (fDirty == false) + return fConcat; + fMatrix.reset(); + for (SkMatrixPart** part = fParts.begin(); part < fParts.end(); part++) { + (*part)->add(); + fConcat = fMatrix; + } + fDirty = false; + return fConcat; +} + +bool SkDrawMatrix::getProperty(int index, SkScriptValue* value) const { + value->fType = SkType_Float; + SkScalar result; + switch (index) { + case SK_PROPERTY(perspectX): + result = fMatrix.getPerspX(); + break; + case SK_PROPERTY(perspectY): + result = fMatrix.getPerspY(); + break; + case SK_PROPERTY(scaleX): + result = fMatrix.getScaleX(); + break; + case SK_PROPERTY(scaleY): + result = fMatrix.getScaleY(); + break; + case SK_PROPERTY(skewX): + result = fMatrix.getSkewX(); + break; + case SK_PROPERTY(skewY): + result = fMatrix.getSkewY(); + break; + case SK_PROPERTY(translateX): + result = fMatrix.getTranslateX(); + break; + case SK_PROPERTY(translateY): + result = fMatrix.getTranslateY(); + break; + default: +// SkASSERT(0); + return false; + } + value->fOperand.fScalar = result; + return true; +} + +void SkDrawMatrix::initialize() { + fConcat = fMatrix; +} + +void SkDrawMatrix::onEndElement(SkAnimateMaker& ) { + if (matrix.count() > 0) { + SkScalar* vals = matrix.begin(); + fMatrix.setScaleX(vals[0]); + fMatrix.setSkewX(vals[1]); + fMatrix.setTranslateX(vals[2]); + fMatrix.setSkewY(vals[3]); + fMatrix.setScaleY(vals[4]); + fMatrix.setTranslateY(vals[5]); +#ifdef SK_SCALAR_IS_FIXED + fMatrix.setPerspX(SkFixedToFract(vals[6])); + fMatrix.setPerspY(SkFixedToFract(vals[7])); +#else + fMatrix.setPerspX(vals[6]); + fMatrix.setPerspY(vals[7]); +#endif +// fMatrix.setPerspW(vals[8]); + goto setConcat; + } + if (fChildHasID == false) { + { + for (SkMatrixPart** part = fParts.begin(); part < fParts.end(); part++) + delete *part; + } + fParts.reset(); +setConcat: + fConcat = fMatrix; + fDirty = false; + } +} + +void SkDrawMatrix::setChildHasID() { + fChildHasID = true; +} + +bool SkDrawMatrix::setProperty(int index, SkScriptValue& scriptValue) { + SkScalar number = scriptValue.fOperand.fScalar; + switch (index) { + case SK_PROPERTY(translate): + // SkScalar xy[2]; + SkASSERT(scriptValue.fType == SkType_Array); + SkASSERT(scriptValue.fOperand.fArray->getType() == SkType_Float); + SkASSERT(scriptValue.fOperand.fArray->count() == 2); + // SkParse::FindScalars(scriptValue.fOperand.fString->c_str(), xy, 2); + fMatrix.setTranslateX((*scriptValue.fOperand.fArray)[0].fScalar); + fMatrix.setTranslateY((*scriptValue.fOperand.fArray)[1].fScalar); + return true; + case SK_PROPERTY(perspectX): +#ifdef SK_SCALAR_IS_FIXED + fMatrix.setPerspX(SkFixedToFract(number)); +#else + fMatrix.setPerspX(number); +#endif + break; + case SK_PROPERTY(perspectY): +#ifdef SK_SCALAR_IS_FIXED + fMatrix.setPerspY(SkFixedToFract(number)); +#else + fMatrix.setPerspY(number); +#endif + break; + case SK_PROPERTY(rotate): { + SkMatrix temp; + temp.setRotate(number, 0, 0); + fMatrix.setScaleX(temp.getScaleX()); + fMatrix.setScaleY(temp.getScaleY()); + fMatrix.setSkewX(temp.getSkewX()); + fMatrix.setSkewY(temp.getSkewY()); + } break; + case SK_PROPERTY(scale): + fMatrix.setScaleX(number); + fMatrix.setScaleY(number); + break; + case SK_PROPERTY(scaleX): + fMatrix.setScaleX(number); + break; + case SK_PROPERTY(scaleY): + fMatrix.setScaleY(number); + break; + case SK_PROPERTY(skewX): + fMatrix.setSkewX(number); + break; + case SK_PROPERTY(skewY): + fMatrix.setSkewY(number); + break; + case SK_PROPERTY(translateX): + fMatrix.setTranslateX(number); + break; + case SK_PROPERTY(translateY): + fMatrix.setTranslateY(number); + break; + default: + SkASSERT(0); + return false; + } + fConcat = fMatrix; + return true; +} + diff --git a/skia/animator/SkDrawMatrix.h b/skia/animator/SkDrawMatrix.h new file mode 100644 index 0000000..1cf00ff --- /dev/null +++ b/skia/animator/SkDrawMatrix.h @@ -0,0 +1,82 @@ +/* libs/graphics/animator/SkDrawMatrix.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkDrawMatrix_DEFINED +#define SkDrawMatrix_DEFINED + +#include "SkDrawable.h" +#include "SkMatrix.h" +#include "SkMemberInfo.h" +#include "SkIntArray.h" + +class SkMatrixPart; + +class SkDrawMatrix : public SkDrawable { + DECLARE_DRAW_MEMBER_INFO(Matrix); + SkDrawMatrix(); + virtual ~SkDrawMatrix(); + virtual bool add(SkAnimateMaker& , SkDisplayable* child); + virtual bool childrenNeedDisposing() const; + virtual void dirty(); + virtual bool draw(SkAnimateMaker& ); +#ifdef SK_DUMP_ENABLED + virtual void dump(SkAnimateMaker* ); +#endif + SkMatrix& getMatrix(); + virtual bool getProperty(int index, SkScriptValue* value) const; + virtual void initialize(); + virtual void onEndElement(SkAnimateMaker& ); + virtual void setChildHasID(); + virtual bool setProperty(int index, SkScriptValue& ); + + void concat(SkMatrix& inMatrix) { + fConcat.preConcat(inMatrix); + } + + virtual SkDisplayable* deepCopy(SkAnimateMaker* ); + + + void rotate(SkScalar degrees, SkPoint& center) { + fMatrix.preRotate(degrees, center.fX, center.fY); + } + + void set(SkMatrix& src) { + fMatrix.preConcat(src); + } + + void scale(SkScalar scaleX, SkScalar scaleY, SkPoint& center) { + fMatrix.preScale(scaleX, scaleY, center.fX, center.fY); + } + + void skew(SkScalar skewX, SkScalar skewY, SkPoint& center) { + fMatrix.preSkew(skewX, skewY, center.fX, center.fY); + } + + void translate(SkScalar x, SkScalar y) { + fMatrix.preTranslate(x, y); + } +private: + SkTDScalarArray matrix; + SkMatrix fConcat; + SkMatrix fMatrix; + SkTDMatrixPartArray fParts; + SkBool8 fChildHasID; + SkBool8 fDirty; + typedef SkDrawable INHERITED; +}; + +#endif // SkDrawMatrix_DEFINED diff --git a/skia/animator/SkDrawOval.cpp b/skia/animator/SkDrawOval.cpp new file mode 100644 index 0000000..734bcad --- /dev/null +++ b/skia/animator/SkDrawOval.cpp @@ -0,0 +1,37 @@ +/* libs/graphics/animator/SkDrawOval.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkDrawOval.h" +#include "SkAnimateMaker.h" +#include "SkCanvas.h" + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkOval::fInfo[] = { + SK_MEMBER_INHERITED, +}; + +#endif + +DEFINE_GET_MEMBER(SkOval); + +bool SkOval::draw(SkAnimateMaker& maker) { + SkBoundableAuto boundable(this, maker); + maker.fCanvas->drawOval(fRect, *maker.fPaint); + return false; +} + diff --git a/skia/animator/SkDrawOval.h b/skia/animator/SkDrawOval.h new file mode 100644 index 0000000..2bfec9d --- /dev/null +++ b/skia/animator/SkDrawOval.h @@ -0,0 +1,31 @@ +/* libs/graphics/animator/SkDrawOval.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkDrawOval_DEFINED +#define SkDrawOval_DEFINED + +#include "SkDrawRectangle.h" + +class SkOval : public SkDrawRect { + DECLARE_MEMBER_INFO(Oval); + virtual bool draw(SkAnimateMaker& ); +private: + typedef SkDrawRect INHERITED; +}; + +#endif // SkDrawOval_DEFINED + diff --git a/skia/animator/SkDrawPaint.cpp b/skia/animator/SkDrawPaint.cpp new file mode 100644 index 0000000..a1b67f5 --- /dev/null +++ b/skia/animator/SkDrawPaint.cpp @@ -0,0 +1,278 @@ +/* libs/graphics/animator/SkDrawPaint.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkDrawPaint.h" +#include "SkAnimateMaker.h" +#include "SkDrawColor.h" +#include "SkDrawShader.h" +#include "SkMaskFilter.h" +#include "SkPaintParts.h" +#include "SkPathEffect.h" + +enum SkPaint_Functions { + SK_FUNCTION(measureText) +}; + +enum SkPaint_Properties { + SK_PROPERTY(ascent), + SK_PROPERTY(descent) +}; + +// !!! in the future, this could be compiled by build-condensed-info into an array of parameters +// with a lookup table to find the first parameter -- for now, it is iteratively searched through +const SkFunctionParamType SkDrawPaint::fFunctionParameters[] = { + (SkFunctionParamType) SkType_String, + (SkFunctionParamType) 0 // terminator for parameter list (there may be multiple parameter lists) +}; + + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkDrawPaint::fInfo[] = { + SK_MEMBER(antiAlias, Boolean), + SK_MEMBER_PROPERTY(ascent, Float), + SK_MEMBER(color, Color), + SK_MEMBER_PROPERTY(descent, Float), + SK_MEMBER(fakeBold, Boolean), + SK_MEMBER(filterBitmap, Boolean), + SK_MEMBER(linearText, Boolean), + SK_MEMBER(maskFilter, MaskFilter), + SK_MEMBER_FUNCTION(measureText, Float), + SK_MEMBER(pathEffect, PathEffect), + SK_MEMBER(shader, Shader), + SK_MEMBER(strikeThru, Boolean), + SK_MEMBER(stroke, Boolean), + SK_MEMBER(strokeCap, Cap), + SK_MEMBER(strokeJoin, Join), + SK_MEMBER(strokeMiter, Float), + SK_MEMBER(strokeWidth, Float), + SK_MEMBER(style, Style), + SK_MEMBER(textAlign, Align), + SK_MEMBER(textScaleX, Float), + SK_MEMBER(textSize, Float), + SK_MEMBER(textSkewX, Float), + SK_MEMBER(typeface, Typeface), + SK_MEMBER(underline, Boolean), + SK_MEMBER(xfermode, Xfermode) +}; + +#endif + +DEFINE_GET_MEMBER(SkDrawPaint); + +SkDrawPaint::SkDrawPaint() : antiAlias(-1), color(NULL), fakeBold(-1), filterBitmap(-1), + linearText(-1), maskFilter((SkDrawMaskFilter*) -1), pathEffect((SkDrawPathEffect*) -1), + shader((SkDrawShader*) -1), strikeThru(-1), stroke(-1), + strokeCap((SkPaint::Cap) -1), strokeJoin((SkPaint::Join) -1), strokeMiter(SK_ScalarNaN), + strokeWidth(SK_ScalarNaN), style((SkPaint::Style) -1), + textAlign((SkPaint::Align) -1), textScaleX(SK_ScalarNaN), textSize(SK_ScalarNaN), + textSkewX(SK_ScalarNaN), typeface((SkDrawTypeface*) -1), + underline(-1), xfermode((SkPorterDuff::Mode) -1), fOwnsColor(false), fOwnsMaskFilter(false), + fOwnsPathEffect(false), fOwnsShader(false), fOwnsTypeface(false) { +} + +SkDrawPaint::~SkDrawPaint() { + if (fOwnsColor) + delete color; + if (fOwnsMaskFilter) + delete maskFilter; + if (fOwnsPathEffect) + delete pathEffect; + if (fOwnsShader) + delete shader; + if (fOwnsTypeface) + delete typeface; +} + +bool SkDrawPaint::add(SkAnimateMaker& maker, SkDisplayable* child) { + SkASSERT(child && child->isPaintPart()); + SkPaintPart* part = (SkPaintPart*) child; + if (part->add()) + maker.setErrorCode(SkDisplayXMLParserError::kErrorAddingToPaint); + return true; +} + +SkDisplayable* SkDrawPaint::deepCopy(SkAnimateMaker* maker) { + SkDrawColor* tempColor = color; + color = NULL; + SkDrawPaint* copy = (SkDrawPaint*) INHERITED::deepCopy(maker); + color = tempColor; + tempColor = (SkDrawColor*) color->deepCopy(maker); + tempColor->setParent(copy); + tempColor->add(); + copy->fOwnsColor = true; + return copy; +} + +bool SkDrawPaint::draw(SkAnimateMaker& maker) { + SkPaint* paint = maker.fPaint; + setupPaint(paint); + return false; +} + +#ifdef SK_DUMP_ENABLED +void SkDrawPaint::dump(SkAnimateMaker* maker) { + dumpBase(maker); + dumpAttrs(maker); + bool closedYet = false; + SkDisplayList::fIndent +=4; + //should i say if (maskFilter && ...? + if (maskFilter != (SkDrawMaskFilter*)-1) { + SkDebugf(">\n"); + maskFilter->dump(maker); + closedYet = true; + } + if (pathEffect != (SkDrawPathEffect*) -1) { + if (closedYet == false) { + SkDebugf(">\n"); + closedYet = true; + } + pathEffect->dump(maker); + } + if (fOwnsTypeface) { + if (closedYet == false) { + SkDebugf(">\n"); + closedYet = true; + } + typeface->dump(maker); + } + SkDisplayList::fIndent -= 4; + dumpChildren(maker, closedYet); +} +#endif + +void SkDrawPaint::executeFunction(SkDisplayable* target, int index, + SkTDArray<SkScriptValue>& parameters, SkDisplayTypes type, + SkScriptValue* scriptValue) { + if (scriptValue == NULL) + return; + SkASSERT(target == this); + switch (index) { + case SK_FUNCTION(measureText): { + SkASSERT(parameters.count() == 1); + SkASSERT(type == SkType_Float); + SkPaint paint; + setupPaint(&paint); + scriptValue->fType = SkType_Float; + SkASSERT(parameters[0].fType == SkType_String); + scriptValue->fOperand.fScalar = paint.measureText(parameters[0].fOperand.fString->c_str(), + parameters[0].fOperand.fString->size()); +// SkDebugf("measureText: %s = %g\n", parameters[0].fOperand.fString->c_str(), +// scriptValue->fOperand.fScalar / 65536.0f); + } break; + default: + SkASSERT(0); + } +} + +const SkFunctionParamType* SkDrawPaint::getFunctionsParameters() { + return fFunctionParameters; +} + +bool SkDrawPaint::getProperty(int index, SkScriptValue* value) const { + SkPaint::FontMetrics metrics; + SkPaint paint; + setupPaint(&paint); + paint.getFontMetrics(&metrics); + switch (index) { + case SK_PROPERTY(ascent): + value->fOperand.fScalar = metrics.fAscent; + break; + case SK_PROPERTY(descent): + value->fOperand.fScalar = metrics.fDescent; + break; + // should consider returning fLeading as well (or roll it into ascent/descent somehow <reed> + default: + SkASSERT(0); + return false; + } + value->fType = SkType_Float; + return true; +} + +bool SkDrawPaint::resolveIDs(SkAnimateMaker& maker, SkDisplayable* origDisp, SkApply* ) { + SkASSERT(origDisp->isPaint()); + SkDrawPaint* original = (SkDrawPaint*) origDisp; + if (fOwnsColor && maker.resolveID(color, original->color) == false) + return true; + if (fOwnsMaskFilter && maker.resolveID(maskFilter, original->maskFilter) == false) + return true; + if (fOwnsPathEffect && maker.resolveID(pathEffect, original->pathEffect) == false) + return true; + if (fOwnsShader && maker.resolveID(shader, original->shader) == false) + return true; + if (fOwnsTypeface && maker.resolveID(typeface, original->typeface) == false) + return true; + return false; // succeeded +} + +void SkDrawPaint::setupPaint(SkPaint* paint) const { + if (antiAlias != -1) + paint->setAntiAlias(SkToBool(antiAlias)); + if (color != NULL) + paint->setColor(color->getColor()); + if (fakeBold != -1) + paint->setFakeBoldText(SkToBool(fakeBold)); + if (filterBitmap != -1) + paint->setFilterBitmap(SkToBool(filterBitmap)); + // stroke is legacy; style setting if present overrides stroke + if (stroke != -1) + paint->setStyle(SkToBool(stroke) ? SkPaint::kStroke_Style : SkPaint::kFill_Style); + if (style != (SkPaint::Style) -1) + paint->setStyle((SkPaint::Style) style); + if (linearText != -1) + paint->setLinearText(SkToBool(linearText)); + if (maskFilter == NULL) + paint->setMaskFilter(NULL); + else if (maskFilter != (SkDrawMaskFilter*) -1) + paint->setMaskFilter(maskFilter->getMaskFilter())->safeUnref(); + if (pathEffect == NULL) + paint->setPathEffect(NULL); + else if (pathEffect != (SkDrawPathEffect*) -1) + paint->setPathEffect(pathEffect->getPathEffect())->safeUnref(); + if (shader == NULL) + paint->setShader(NULL); + else if (shader != (SkDrawShader*) -1) + paint->setShader(shader->getShader())->safeUnref(); + if (strikeThru != -1) + paint->setStrikeThruText(SkToBool(strikeThru)); + if (strokeCap != (SkPaint::Cap) -1) + paint->setStrokeCap((SkPaint::Cap) strokeCap); + if (strokeJoin != (SkPaint::Join) -1) + paint->setStrokeJoin((SkPaint::Join) strokeJoin); + if (SkScalarIsNaN(strokeMiter) == false) + paint->setStrokeMiter(strokeMiter); + if (SkScalarIsNaN(strokeWidth) == false) + paint->setStrokeWidth(strokeWidth); + if (textAlign != (SkPaint::Align) -1) + paint->setTextAlign((SkPaint::Align) textAlign); + if (SkScalarIsNaN(textScaleX) == false) + paint->setTextScaleX(textScaleX); + if (SkScalarIsNaN(textSize) == false) + paint->setTextSize(textSize); + if (SkScalarIsNaN(textSkewX) == false) + paint->setTextSkewX(textSkewX); + if (typeface == NULL) + paint->setTypeface(NULL); + else if (typeface != (SkDrawTypeface*) -1) + paint->setTypeface(typeface->getTypeface())->safeUnref(); + if (underline != -1) + paint->setUnderlineText(SkToBool(underline)); + if (xfermode != (SkPorterDuff::Mode) -1) + paint->setPorterDuffXfermode((SkPorterDuff::Mode) xfermode); +} + diff --git a/skia/animator/SkDrawPaint.h b/skia/animator/SkDrawPaint.h new file mode 100644 index 0000000..9c4df61 --- /dev/null +++ b/skia/animator/SkDrawPaint.h @@ -0,0 +1,88 @@ +/* libs/graphics/animator/SkDrawPaint.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkDrawPaint_DEFINED +#define SkDrawPaint_DEFINED + +#include "SkDrawable.h" +#include "SkIntArray.h" +#include "SkMemberInfo.h" +#include "SkPaint.h" +#include "SkXfermode.h" + +class SkDrawMaskFilter; +class SkDrawPathEffect; +class SkDrawShader; +class SkTransferMode; +class SkDrawTypeface; + +class SkDrawPaint : public SkDrawable { + DECLARE_DRAW_MEMBER_INFO(Paint); + SkDrawPaint(); + virtual ~SkDrawPaint(); + virtual bool add(SkAnimateMaker& , SkDisplayable* child); + virtual SkDisplayable* deepCopy(SkAnimateMaker* ); + virtual bool draw(SkAnimateMaker& ); +#ifdef SK_DUMP_ENABLED + virtual void dump(SkAnimateMaker* ); +#endif + virtual void executeFunction(SkDisplayable* target, int index, + SkTDArray<SkScriptValue>& parameters, SkDisplayTypes type, + SkScriptValue* ); + virtual const SkFunctionParamType* getFunctionsParameters(); + virtual bool getProperty(int index, SkScriptValue* value) const; + virtual bool resolveIDs(SkAnimateMaker& maker, SkDisplayable* original, SkApply* apply); +protected: + static const SkFunctionParamType fFunctionParameters[]; + void setupPaint(SkPaint* paint) const; +public: + SkBool antiAlias; + SkDrawColor* color; + SkBool fakeBold; + SkBool filterBitmap; + SkBool linearText; + SkDrawMaskFilter* maskFilter; + SkDrawPathEffect* pathEffect; + SkDrawShader* shader; + SkBool strikeThru; + SkBool stroke; + int /*SkPaint::Cap*/ strokeCap; + int /*SkPaint::Join */ strokeJoin; + SkScalar strokeMiter; + SkScalar strokeWidth; + int /* SkPaint::Style */ style; + int /* SkPaint::Align */ textAlign; + SkScalar textScaleX; + SkScalar textSize; + SkScalar textSkewX; + SkDrawTypeface* typeface; + SkBool underline; + int /*SkXfermode::Modes*/ xfermode; + SkBool8 fOwnsColor; + SkBool8 fOwnsMaskFilter; + SkBool8 fOwnsPathEffect; + SkBool8 fOwnsShader; + SkBool8 fOwnsTransferMode; + SkBool8 fOwnsTypeface; +private: + typedef SkDrawable INHERITED; + friend class SkTextToPath; + friend class SkSaveLayer; +}; + +#endif // SkDrawPaint_DEFINED + diff --git a/skia/animator/SkDrawPath.cpp b/skia/animator/SkDrawPath.cpp new file mode 100644 index 0000000..c1cbad5 --- /dev/null +++ b/skia/animator/SkDrawPath.cpp @@ -0,0 +1,229 @@ +/* libs/graphics/animator/SkDrawPath.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkDrawPath.h" +#include "SkAnimateMaker.h" +#include "SkCanvas.h" +#include "SkMath.h" +#include "SkMatrixParts.h" +#include "SkPaint.h" +#include "SkPathParts.h" + +enum SkPath_Properties { + SK_PROPERTY(fillType), + SK_PROPERTY(length) +}; + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkDrawPath::fInfo[] = { + SK_MEMBER(d, String), + SK_MEMBER_PROPERTY(fillType, FillType), + SK_MEMBER_PROPERTY(length, Float) +}; + +#endif + +DEFINE_GET_MEMBER(SkDrawPath); + +SkDrawPath::SkDrawPath() +{ + fParent = NULL; + fLength = SK_ScalarNaN; + fChildHasID = false; + fDirty = false; +} + +SkDrawPath::~SkDrawPath() { + for (SkPathPart** part = fParts.begin(); part < fParts.end(); part++) + delete *part; +} + +bool SkDrawPath::add(SkAnimateMaker& maker, SkDisplayable* child) { + SkASSERT(child && child->isPathPart()); + SkPathPart* part = (SkPathPart*) child; + *fParts.append() = part; + if (part->add()) + maker.setErrorCode(SkDisplayXMLParserError::kErrorAddingToPath); + fDirty = false; + return true; +} + +bool SkDrawPath::childrenNeedDisposing() const { + return false; +} + +void SkDrawPath::dirty() { + fDirty = true; + fLength = SK_ScalarNaN; + if (fParent) + fParent->dirty(); +} + +bool SkDrawPath::draw(SkAnimateMaker& maker) { + SkPath& path = getPath(); + SkBoundableAuto boundable(this, maker); + maker.fCanvas->drawPath(path, *maker.fPaint); + return false; +} + +SkDisplayable* SkDrawPath::getParent() const { + return fParent; +} + +#ifdef SK_DUMP_ENABLED +void SkDrawPath::dump(SkAnimateMaker* maker) { + dumpBase(maker); + dumpAttrs(maker); + bool closedYet = false; + SkDisplayList::fIndent += 4; + for(SkPathPart** part = fParts.begin(); part < fParts.end(); part++) { + if (closedYet == false) { + SkDebugf(">\n"); + closedYet = true; + } + (*part)->dump(maker); + } + SkDisplayList::fIndent -= 4; + if (closedYet) + dumpEnd(maker); + else + SkDebugf("/>\n"); +} +#endif + +SkPath& SkDrawPath::getPath() { + if (fDirty == false) + return fPath; + if (d.size() > 0) + { + parseSVG(); + d.reset(); + } + else + { + fPath.reset(); + for (SkPathPart** part = fParts.begin(); part < fParts.end(); part++) + (*part)->add(); + } + fDirty = false; + return fPath; +} + +void SkDrawPath::onEndElement(SkAnimateMaker& ) { + if (d.size() > 0) { + parseSVG(); + d.reset(); + fDirty = false; + return; + } + if (fChildHasID == false) { + for (SkPathPart** part = fParts.begin(); part < fParts.end(); part++) + delete *part; + fParts.reset(); + fDirty = false; + } +} + +bool SkDrawPath::getProperty(int index, SkScriptValue* value) const { + switch (index) { + case SK_PROPERTY(length): + if (SkScalarIsNaN(fLength)) { + const SkPath& path = ((SkDrawPath*) this)->getPath(); + SkPathMeasure pathMeasure(path, false); + fLength = pathMeasure.getLength(); + } + value->fType = SkType_Float; + value->fOperand.fScalar = fLength; + break; + case SK_PROPERTY(fillType): + value->fType = SkType_FillType; + value->fOperand.fS32 = (int) fPath.getFillType(); + break; + default: + SkASSERT(0); + return false; + } + return true; +} + +void SkDrawPath::setChildHasID() { + fChildHasID = true; +} + +bool SkDrawPath::setParent(SkDisplayable* parent) { + fParent = parent; + return false; +} + +bool SkDrawPath::setProperty(int index, SkScriptValue& value) +{ + switch (index) { + case SK_PROPERTY(fillType): + SkASSERT(value.fType == SkType_FillType); + SkASSERT(value.fOperand.fS32 >= SkPath::kWinding_FillType && + value.fOperand.fS32 <= SkPath::kEvenOdd_FillType); + fPath.setFillType((SkPath::FillType) value.fOperand.fS32); + break; + default: + SkASSERT(0); + return false; + } + return true; +} + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkPolyline::fInfo[] = { + SK_MEMBER_ARRAY(points, Float) +}; + +#endif + +DEFINE_GET_MEMBER(SkPolyline); + +bool SkPolyline::add(SkAnimateMaker& , SkDisplayable*) const { + return false; +} + +void SkPolyline::onEndElement(SkAnimateMaker& maker) { + INHERITED::onEndElement(maker); + if (points.count() <= 0) + return; + fPath.reset(); + fPath.moveTo(points[0], points[1]); + int count = points.count(); + for (int index = 2; index < count; index += 2) + fPath.lineTo(points[index], points[index+1]); +} + + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkPolygon::fInfo[] = { + SK_MEMBER_INHERITED +}; + +#endif + +DEFINE_GET_MEMBER(SkPolygon); + +void SkPolygon::onEndElement(SkAnimateMaker& maker) { + INHERITED::onEndElement(maker); + fPath.close(); +} + diff --git a/skia/animator/SkDrawPath.h b/skia/animator/SkDrawPath.h new file mode 100644 index 0000000..719b3461 --- /dev/null +++ b/skia/animator/SkDrawPath.h @@ -0,0 +1,77 @@ +/* libs/graphics/animator/SkDrawPath.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkDrawPath_DEFINED +#define SkDrawPath_DEFINED + +#include "SkBoundable.h" +#include "SkIntArray.h" +#include "SkMemberInfo.h" +#include "SkPath.h" + +class SkDrawPath : public SkBoundable { + DECLARE_DRAW_MEMBER_INFO(Path); + SkDrawPath(); + virtual ~SkDrawPath(); + virtual bool add(SkAnimateMaker& , SkDisplayable* child); + bool childHasID() { return SkToBool(fChildHasID); } + virtual bool childrenNeedDisposing() const; + virtual void dirty(); + virtual bool draw(SkAnimateMaker& ); + virtual SkDisplayable* getParent() const; +#ifdef SK_DUMP_ENABLED + virtual void dump(SkAnimateMaker* ); +#endif + SkPath& getPath(); + virtual bool getProperty(int index, SkScriptValue* value) const; + virtual bool setProperty(int index, SkScriptValue& value); + virtual void onEndElement(SkAnimateMaker& ); + virtual void setChildHasID(); + virtual bool setParent(SkDisplayable* parent); + virtual bool isPath() const { return true; } +public: + SkPath fPath; +protected: + void parseSVG(); + SkString d; + SkTDPathPartArray fParts; + mutable SkScalar fLength; + SkDisplayable* fParent; // SkPolyToPoly or SkFromPath, for instance + SkBool8 fChildHasID; + SkBool8 fDirty; +private: + typedef SkBoundable INHERITED; +}; + +class SkPolyline : public SkDrawPath { + DECLARE_MEMBER_INFO(Polyline); + virtual bool add(SkAnimateMaker& , SkDisplayable*) const; + virtual void onEndElement(SkAnimateMaker& ); +protected: + SkTDScalarArray points; +private: + typedef SkDrawPath INHERITED; +}; + +class SkPolygon : public SkPolyline { + DECLARE_MEMBER_INFO(Polygon); + virtual void onEndElement(SkAnimateMaker& ); +private: + typedef SkPolyline INHERITED; +}; + +#endif // SkDrawPath_DEFINED diff --git a/skia/animator/SkDrawPoint.cpp b/skia/animator/SkDrawPoint.cpp new file mode 100644 index 0000000..8654e9a --- /dev/null +++ b/skia/animator/SkDrawPoint.cpp @@ -0,0 +1,54 @@ +/* libs/graphics/animator/SkDrawPoint.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkDrawPoint.h" +#include "SkAnimateMaker.h" +#include "SkCanvas.h" +#include "SkPaint.h" + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo Sk_Point::fInfo[] = { + SK_MEMBER_ALIAS(x, fPoint.fX, Float), + SK_MEMBER_ALIAS(y, fPoint.fY, Float) +}; + +#endif + +DEFINE_NO_VIRTUALS_GET_MEMBER(Sk_Point); + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkDrawPoint::fInfo[] = { + SK_MEMBER_ALIAS(x, fPoint.fX, Float), + SK_MEMBER_ALIAS(y, fPoint.fY, Float) +}; + +#endif + +DEFINE_GET_MEMBER(SkDrawPoint); + +SkDrawPoint::SkDrawPoint() { + fPoint.set(0, 0); +} + +void SkDrawPoint::getBounds(SkRect* rect ) { + rect->fLeft = rect->fRight = fPoint.fX; + rect->fTop = rect->fBottom = fPoint.fY; +} + + diff --git a/skia/animator/SkDrawPoint.h b/skia/animator/SkDrawPoint.h new file mode 100644 index 0000000..30fd243 --- /dev/null +++ b/skia/animator/SkDrawPoint.h @@ -0,0 +1,41 @@ +/* libs/graphics/animator/SkDrawPoint.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkDrawPoint_DEFINED +#define SkDrawPoint_DEFINED + +#include "SkBoundable.h" +#include "SkMemberInfo.h" +#include "SkPoint.h" + +struct Sk_Point { + DECLARE_NO_VIRTUALS_MEMBER_INFO(_Point); + Sk_Point(); +private: + SkPoint fPoint; +}; + +class SkDrawPoint : public SkDisplayable { + DECLARE_MEMBER_INFO(DrawPoint); + SkDrawPoint(); + virtual void getBounds(SkRect* ); +private: + SkPoint fPoint; + typedef SkDisplayable INHERITED; +}; + +#endif // SkDrawPoint_DEFINED diff --git a/skia/animator/SkDrawRectangle.cpp b/skia/animator/SkDrawRectangle.cpp new file mode 100644 index 0000000..2971e8c --- /dev/null +++ b/skia/animator/SkDrawRectangle.cpp @@ -0,0 +1,153 @@ +/* libs/graphics/animator/SkDrawRectangle.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkDrawRectangle.h" +#include "SkAnimateMaker.h" +#include "SkCanvas.h" +#include "SkMatrixParts.h" +#include "SkPaint.h" +#include "SkScript.h" + +enum SkRectangle_Properties { + SK_PROPERTY(height), + SK_PROPERTY(needsRedraw), + SK_PROPERTY(width) +}; + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkDrawRect::fInfo[] = { + SK_MEMBER_ALIAS(bottom, fRect.fBottom, Float), + SK_MEMBER_PROPERTY(height, Float), + SK_MEMBER_ALIAS(left, fRect.fLeft, Float), + SK_MEMBER_PROPERTY(needsRedraw, Boolean), + SK_MEMBER_ALIAS(right, fRect.fRight, Float), + SK_MEMBER_ALIAS(top, fRect.fTop, Float), + SK_MEMBER_PROPERTY(width, Float) +}; + +#endif + +DEFINE_GET_MEMBER(SkDrawRect); + +SkDrawRect::SkDrawRect() : fParent(NULL) { + fRect.setEmpty(); +} + +void SkDrawRect::dirty() { + if (fParent) + fParent->dirty(); +} + +bool SkDrawRect::draw(SkAnimateMaker& maker) { + SkBoundableAuto boundable(this, maker); + maker.fCanvas->drawRect(fRect, *maker.fPaint); + return false; +} + +#ifdef SK_DUMP_ENABLED +void SkDrawRect::dump(SkAnimateMaker* maker) { + dumpBase(maker); + SkDebugf("left=\"%g\" top=\"%g\" right=\"%g\" bottom=\"%g\" />\n", + SkScalarToFloat(fRect.fLeft), SkScalarToFloat(fRect.fTop), SkScalarToFloat(fRect.fRight), + SkScalarToFloat(fRect.fBottom)); +} +#endif + +SkDisplayable* SkDrawRect::getParent() const { + return fParent; +} + +bool SkDrawRect::getProperty(int index, SkScriptValue* value) const { + SkScalar result; + switch (index) { + case SK_PROPERTY(height): + result = fRect.height(); + break; + case SK_PROPERTY(needsRedraw): + value->fType = SkType_Boolean; + value->fOperand.fS32 = fBounds.isEmpty() == false; + return true; + case SK_PROPERTY(width): + result = fRect.width(); + break; + default: + SkASSERT(0); + return false; + } + value->fType = SkType_Float; + value->fOperand.fScalar = result; + return true; +} + + +bool SkDrawRect::setParent(SkDisplayable* parent) { + fParent = parent; + return false; +} + +bool SkDrawRect::setProperty(int index, SkScriptValue& value) { + SkScalar scalar = value.fOperand.fScalar; + switch (index) { + case SK_PROPERTY(height): + SkASSERT(value.fType == SkType_Float); + fRect.fBottom = scalar + fRect.fTop; + return true; + case SK_PROPERTY(needsRedraw): + return false; + case SK_PROPERTY(width): + SkASSERT(value.fType == SkType_Float); + fRect.fRight = scalar + fRect.fLeft; + return true; + default: + SkASSERT(0); + } + return false; +} + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkRoundRect::fInfo[] = { + SK_MEMBER_INHERITED, + SK_MEMBER(rx, Float), + SK_MEMBER(ry, Float), +}; + +#endif + +DEFINE_GET_MEMBER(SkRoundRect); + +SkRoundRect::SkRoundRect() : rx(0), ry(0) { +} + +bool SkRoundRect::draw(SkAnimateMaker& maker) { + SkBoundableAuto boundable(this, maker); + maker.fCanvas->drawRoundRect(fRect, rx, ry, *maker.fPaint); + return false; +} + +#ifdef SK_DUMP_ENABLED +void SkRoundRect::dump(SkAnimateMaker* maker) { + dumpBase(maker); + SkDebugf("left=\"%g\" top=\"%g\" right=\"%g\" bottom=\"%g\" rx=\"%g\" ry=\"%g\" />\n", + SkScalarToFloat(fRect.fLeft), SkScalarToFloat(fRect.fTop), SkScalarToFloat(fRect.fRight), + SkScalarToFloat(fRect.fBottom), SkScalarToFloat(rx), SkScalarToFloat(ry)); +} +#endif + + + diff --git a/skia/animator/SkDrawRectangle.h b/skia/animator/SkDrawRectangle.h new file mode 100644 index 0000000..51edcc5 --- /dev/null +++ b/skia/animator/SkDrawRectangle.h @@ -0,0 +1,64 @@ +/* libs/graphics/animator/SkDrawRectangle.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkDrawRectangle_DEFINED +#define SkDrawRectangle_DEFINED + +#include "SkBoundable.h" +#include "SkMemberInfo.h" +#include "SkRect.h" + +class SkRectToRect; + +class SkDrawRect : public SkBoundable { + DECLARE_DRAW_MEMBER_INFO(Rect); + SkDrawRect(); + virtual void dirty(); + virtual bool draw(SkAnimateMaker& ); +#ifdef SK_DUMP_ENABLED + virtual void dump(SkAnimateMaker* ); +#endif + virtual SkDisplayable* getParent() const; + virtual bool getProperty(int index, SkScriptValue* value) const; + virtual bool setParent(SkDisplayable* parent); + virtual bool setProperty(int index, SkScriptValue& ); +protected: + SkRect fRect; + SkDisplayable* fParent; +private: + friend class SkDrawClip; + friend class SkRectToRect; + friend class SkSaveLayer; + typedef SkBoundable INHERITED; +}; + +class SkRoundRect : public SkDrawRect { + DECLARE_MEMBER_INFO(RoundRect); + SkRoundRect(); + virtual bool draw(SkAnimateMaker& ); +#ifdef SK_DUMP_ENABLED + virtual void dump(SkAnimateMaker* ); +#endif +protected: + SkScalar rx; + SkScalar ry; +private: + typedef SkDrawRect INHERITED; +}; + +#endif // SkDrawRectangle_DEFINED + diff --git a/skia/animator/SkDrawSaveLayer.cpp b/skia/animator/SkDrawSaveLayer.cpp new file mode 100644 index 0000000..c78ef30 --- /dev/null +++ b/skia/animator/SkDrawSaveLayer.cpp @@ -0,0 +1,86 @@ +/* libs/graphics/animator/SkDrawSaveLayer.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkDrawSaveLayer.h" +#include "SkAnimateMaker.h" +#include "SkCanvas.h" +#include "SkDrawPaint.h" +#include "SkDrawRectangle.h" + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkSaveLayer::fInfo[] = { + SK_MEMBER(bounds, Rect), + SK_MEMBER(paint, Paint) +}; + +#endif + +DEFINE_GET_MEMBER(SkSaveLayer); + +SkSaveLayer::SkSaveLayer() : paint(NULL), bounds(NULL) { +} + +SkSaveLayer::~SkSaveLayer(){ +} + +bool SkSaveLayer::draw(SkAnimateMaker& maker) +{ + if (!bounds) { + return false; + } + SkPaint* save = maker.fPaint; + //paint is an SkDrawPaint + if (paint) + { + SkPaint realPaint; + paint->setupPaint(&realPaint); + maker.fCanvas->saveLayer(&bounds->fRect, &realPaint, SkCanvas::kHasAlphaLayer_SaveFlag); + } + else + maker.fCanvas->saveLayer(&bounds->fRect, save, SkCanvas::kHasAlphaLayer_SaveFlag); + SkPaint local = SkPaint(*maker.fPaint); + maker.fPaint = &local; + bool result = INHERITED::draw(maker); + maker.fPaint = save; + maker.fCanvas->restore(); + return result; +} + +#ifdef SK_DUMP_ENABLED +void SkSaveLayer::dump(SkAnimateMaker* maker) +{ + dumpBase(maker); + //would dump enabled be defined but not debug? +#ifdef SK_DEBUG + if (paint) + SkDebugf("paint=\"%s\" ", paint->id); + if (bounds) + SkDebugf("bounds=\"%s\" ", bounds->id); +#endif + dumpDrawables(maker); +} +#endif + +void SkSaveLayer::onEndElement(SkAnimateMaker& maker) +{ + if (!bounds) + maker.setErrorCode(SkDisplayXMLParserError::kSaveLayerNeedsBounds); + INHERITED::onEndElement(maker); +} + + diff --git a/skia/animator/SkDrawSaveLayer.h b/skia/animator/SkDrawSaveLayer.h new file mode 100644 index 0000000..c247219 --- /dev/null +++ b/skia/animator/SkDrawSaveLayer.h @@ -0,0 +1,44 @@ +/* libs/graphics/animator/SkDrawSaveLayer.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkDrawSaveLayer_DEFINED +#define SkDrawSaveLayer_DEFINED + +#include "SkDrawGroup.h" +#include "SkMemberInfo.h" + +class SkDrawPaint; +class SkDrawRect; + +class SkSaveLayer : public SkGroup { + DECLARE_MEMBER_INFO(SaveLayer); + SkSaveLayer(); + virtual ~SkSaveLayer(); + virtual bool draw(SkAnimateMaker& ); +#ifdef SK_DUMP_ENABLED + virtual void dump(SkAnimateMaker* ); +#endif + virtual void onEndElement(SkAnimateMaker& ); +protected: + SkDrawPaint* paint; + SkDrawRect* bounds; +private: + typedef SkGroup INHERITED; + +}; + +#endif //SkDrawSaveLayer_DEFINED diff --git a/skia/animator/SkDrawShader.cpp b/skia/animator/SkDrawShader.cpp new file mode 100644 index 0000000..2ff225b --- /dev/null +++ b/skia/animator/SkDrawShader.cpp @@ -0,0 +1,92 @@ +/* libs/graphics/animator/SkDrawShader.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkDrawShader.h" +#include "SkDrawBitmap.h" +#include "SkDrawMatrix.h" +#include "SkDrawPaint.h" +#include "SkTemplates.h" + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkDrawShader::fInfo[] = { + SK_MEMBER(matrix, Matrix), + SK_MEMBER(tileMode, TileMode) +}; + +#endif + +DEFINE_GET_MEMBER(SkDrawShader); + +SkDrawShader::SkDrawShader() : matrix(NULL), + tileMode(SkShader::kClamp_TileMode) { +} + +bool SkDrawShader::add() { + if (fPaint->shader != (SkDrawShader*) -1) + return true; + fPaint->shader = this; + fPaint->fOwnsShader = true; + return false; +} + +void SkDrawShader::addPostlude(SkShader* shader) { + if (matrix) + shader->setLocalMatrix(matrix->getMatrix()); +} + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkDrawBitmapShader::fInfo[] = { + SK_MEMBER_INHERITED, + SK_MEMBER(filterBitmap, Boolean), + SK_MEMBER(image, BaseBitmap) +}; + +#endif + +DEFINE_GET_MEMBER(SkDrawBitmapShader); + +SkDrawBitmapShader::SkDrawBitmapShader() : filterBitmap(-1), image(NULL) {} + +bool SkDrawBitmapShader::add() { + if (fPaint->shader != (SkDrawShader*) -1) + return true; + fPaint->shader = this; + fPaint->fOwnsShader = true; + return false; +} + +SkShader* SkDrawBitmapShader::getShader() { + if (image == NULL) + return NULL; + + // note: bitmap shader now supports independent tile modes for X and Y + // we pass the same to both, but later we should extend this flexibility + // to the xml (e.g. tileModeX="repeat" tileModeY="clmap") + // <reed> + // oops, bitmapshader no longer takes filterBitmap, but deduces it at + // draw-time from the paint <reed> + SkShader* shader = SkShader::CreateBitmapShader(image->fBitmap, + (SkShader::TileMode) tileMode, + (SkShader::TileMode) tileMode); + SkAutoTDelete<SkShader> autoDel(shader); + addPostlude(shader); + (void)autoDel.detach(); + return shader; +} + diff --git a/skia/animator/SkDrawShader.h b/skia/animator/SkDrawShader.h new file mode 100644 index 0000000..9fd94a5 --- /dev/null +++ b/skia/animator/SkDrawShader.h @@ -0,0 +1,38 @@ +/* libs/graphics/animator/SkDrawShader.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkDrawShader_DEFINED +#define SkDrawShader_DEFINED + +#include "SkPaintParts.h" +#include "SkShader.h" + +class SkBaseBitmap; + +class SkDrawBitmapShader : public SkDrawShader { + DECLARE_DRAW_MEMBER_INFO(BitmapShader); + SkDrawBitmapShader(); + virtual bool add(); + virtual SkShader* getShader(); +protected: + SkBool filterBitmap; + SkBaseBitmap* image; +private: + typedef SkDrawShader INHERITED; +}; + +#endif // SkDrawShader_DEFINED diff --git a/skia/animator/SkDrawText.cpp b/skia/animator/SkDrawText.cpp new file mode 100644 index 0000000..d2f3992 --- /dev/null +++ b/skia/animator/SkDrawText.cpp @@ -0,0 +1,64 @@ +/* libs/graphics/animator/SkDrawText.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkDrawText.h" +#include "SkAnimateMaker.h" +#include "SkCanvas.h" +#include "SkPaint.h" + +enum SkText_Properties { + SK_PROPERTY(length) +}; + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkText::fInfo[] = { + SK_MEMBER_PROPERTY(length, Int), + SK_MEMBER(text, String), + SK_MEMBER(x, Float), + SK_MEMBER(y, Float) +}; + +#endif + +DEFINE_GET_MEMBER(SkText); + +SkText::SkText() : x(0), y(0) { +} + +SkText::~SkText() { +} + +bool SkText::draw(SkAnimateMaker& maker) { + SkBoundableAuto boundable(this, maker); + maker.fCanvas->drawText(text.c_str(), text.size(), x, y, *maker.fPaint); + return false; +} + +#ifdef SK_DUMP_ENABLED +void SkText::dump(SkAnimateMaker* maker) { + INHERITED::dump(maker); +} +#endif + +bool SkText::getProperty(int index, SkScriptValue* value) const { + SkASSERT(index == SK_PROPERTY(length)); + value->fType = SkType_Int; + value->fOperand.fS32 = (int32_t) text.size(); + return true; +} + diff --git a/skia/animator/SkDrawText.h b/skia/animator/SkDrawText.h new file mode 100644 index 0000000..cc5ce80 --- /dev/null +++ b/skia/animator/SkDrawText.h @@ -0,0 +1,44 @@ +/* libs/graphics/animator/SkDrawText.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkDrawText_DEFINED +#define SkDrawText_DEFINED + +#include "SkBoundable.h" +#include "SkMemberInfo.h" + +class SkText : public SkBoundable { + DECLARE_MEMBER_INFO(Text); + SkText(); + virtual ~SkText(); + virtual bool draw(SkAnimateMaker& ); +#ifdef SK_DUMP_ENABLED + virtual void dump(SkAnimateMaker* ); +#endif + virtual bool getProperty(int index, SkScriptValue* value) const ; + const char* getText() { return text.c_str(); } + size_t getSize() { return text.size(); } +protected: + SkString text; + SkScalar x; + SkScalar y; +private: + friend class SkTextToPath; + typedef SkBoundable INHERITED; +}; + +#endif // SkDrawText_DEFINED diff --git a/skia/animator/SkDrawTextBox.cpp b/skia/animator/SkDrawTextBox.cpp new file mode 100644 index 0000000..2c3108f --- /dev/null +++ b/skia/animator/SkDrawTextBox.cpp @@ -0,0 +1,90 @@ +/* libs/graphics/animator/SkDrawTextBox.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkDrawTextBox.h" +#include "SkAnimateMaker.h" +#include "SkCanvas.h" +#include "SkPaint.h" + +enum SkDrawTextBox_Properties { + foo = 100, + SK_PROPERTY(spacingAlign), + SK_PROPERTY(mode) +}; + + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkDrawTextBox::fInfo[] = { + SK_MEMBER_INHERITED, + SK_MEMBER(mode, TextBoxMode), + SK_MEMBER_ALIAS(spacingAdd, fSpacingAdd, Float), + SK_MEMBER(spacingAlign, TextBoxAlign), + SK_MEMBER_ALIAS(spacingMul, fSpacingMul, Float), + SK_MEMBER_ALIAS(text, fText, String) +}; + +#endif + +DEFINE_GET_MEMBER(SkDrawTextBox); + +SkDrawTextBox::SkDrawTextBox() +{ + fSpacingMul = SK_Scalar1; + fSpacingAdd = 0; + spacingAlign = SkTextBox::kStart_SpacingAlign; + mode = SkTextBox::kLineBreak_Mode; +} + +#ifdef SK_DUMP_ENABLED +void SkDrawTextBox::dump(SkAnimateMaker* maker) +{ + dumpBase(maker); + dumpAttrs(maker); + if (mode == 0) + SkDebugf("mode=\"oneLine\" "); + if (spacingAlign == 1) + SkDebugf("spacingAlign=\"center\" "); + else if (spacingAlign == 2) + SkDebugf("spacingAlign=\"end\" "); + SkDebugf("/>\n"); +} +#endif + +bool SkDrawTextBox::getProperty(int index, SkScriptValue* value) const +{ + return this->INHERITED::getProperty(index, value); +} + +bool SkDrawTextBox::setProperty(int index, SkScriptValue& scriptValue) +{ + return this->INHERITED::setProperty(index, scriptValue); +} + +bool SkDrawTextBox::draw(SkAnimateMaker& maker) +{ + SkTextBox box; + box.setMode((SkTextBox::Mode) mode); + box.setSpacingAlign((SkTextBox::SpacingAlign) spacingAlign); + box.setBox(fRect); + box.setSpacing(fSpacingMul, fSpacingAdd); + SkBoundableAuto boundable(this, maker); + box.draw(maker.fCanvas, fText.c_str(), fText.size(), *maker.fPaint); + return false; +} + + diff --git a/skia/animator/SkDrawTextBox.h b/skia/animator/SkDrawTextBox.h new file mode 100644 index 0000000..6396eac --- /dev/null +++ b/skia/animator/SkDrawTextBox.h @@ -0,0 +1,47 @@ +/* libs/graphics/animator/SkDrawTextBox.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkDrawTextBox_DEFINED +#define SkDrawTextBox_DEFINED + +#include "SkDrawRectangle.h" +#include "SkTextBox.h" + +class SkDrawTextBox : public SkDrawRect { + DECLARE_DRAW_MEMBER_INFO(TextBox); + SkDrawTextBox(); + + // overrides + virtual bool draw(SkAnimateMaker& ); +#ifdef SK_DUMP_ENABLED + virtual void dump(SkAnimateMaker* ); +#endif + virtual bool getProperty(int index, SkScriptValue* value) const; + virtual bool setProperty(int index, SkScriptValue& ); + +private: + SkString fText; + SkScalar fSpacingMul; + SkScalar fSpacingAdd; + int /*SkTextBox::Mode*/ mode; + int /*SkTextBox::SpacingAlign*/ spacingAlign; + + typedef SkDrawRect INHERITED; +}; + +#endif // SkDrawTextBox_DEFINED + diff --git a/skia/animator/SkDrawTo.cpp b/skia/animator/SkDrawTo.cpp new file mode 100644 index 0000000..7176e40 --- /dev/null +++ b/skia/animator/SkDrawTo.cpp @@ -0,0 +1,64 @@ +/* libs/graphics/animator/SkDrawTo.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkDrawTo.h" +#include "SkAnimateMaker.h" +#include "SkCanvas.h" +#include "SkDrawBitmap.h" + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkDrawTo::fInfo[] = { + SK_MEMBER(drawOnce, Boolean), + SK_MEMBER(use, Bitmap) +}; + +#endif + +DEFINE_GET_MEMBER(SkDrawTo); + +SkDrawTo::SkDrawTo() : drawOnce(false), use(NULL), fDrawnOnce(false) { +} + +#if 0 +SkDrawTo::~SkDrawTo() { + SkASSERT(0); +} +#endif + +bool SkDrawTo::draw(SkAnimateMaker& maker) { + if (fDrawnOnce) + return false; + SkCanvas canvas(use->fBitmap); + SkCanvas* save = maker.fCanvas; + maker.fCanvas = &canvas; + INHERITED::draw(maker); + maker.fCanvas = save; + fDrawnOnce = drawOnce; + return false; +} + +#ifdef SK_DUMP_ENABLED +void SkDrawTo::dump(SkAnimateMaker* maker) { + dumpBase(maker); + dumpAttrs(maker); + if (use) + SkDebugf("use=\"%s\" ", use->id); + dumpDrawables(maker); +} +#endif + diff --git a/skia/animator/SkDrawTo.h b/skia/animator/SkDrawTo.h new file mode 100644 index 0000000..c9268ae --- /dev/null +++ b/skia/animator/SkDrawTo.h @@ -0,0 +1,42 @@ +/* libs/graphics/animator/SkDrawTo.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkDrawTo_DEFINED +#define SkDrawTo_DEFINED + +#include "SkDrawGroup.h" +#include "SkMemberInfo.h" + +class SkDrawBitmap; + +class SkDrawTo : public SkGroup { + DECLARE_MEMBER_INFO(DrawTo); + SkDrawTo(); +// virtual ~SkDrawTo(); + virtual bool draw(SkAnimateMaker& ); +#ifdef SK_DUMP_ENABLED + virtual void dump(SkAnimateMaker* ); +#endif +protected: + SkBool drawOnce; + SkDrawBitmap* use; +private: + typedef SkGroup INHERITED; + SkBool fDrawnOnce; +}; + +#endif // SkDrawTo_DEFINED diff --git a/skia/animator/SkDrawTransparentShader.cpp b/skia/animator/SkDrawTransparentShader.cpp new file mode 100644 index 0000000..8282248 --- /dev/null +++ b/skia/animator/SkDrawTransparentShader.cpp @@ -0,0 +1,24 @@ +/* libs/graphics/animator/SkDrawTransparentShader.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkDrawTransparentShader.h" +#include "SkTransparentShader.h" + +SkShader* SkDrawTransparentShader::getShader() { + return new SkTransparentShader(); +} + diff --git a/skia/animator/SkDrawTransparentShader.h b/skia/animator/SkDrawTransparentShader.h new file mode 100644 index 0000000..8c0c49e --- /dev/null +++ b/skia/animator/SkDrawTransparentShader.h @@ -0,0 +1,29 @@ +/* libs/graphics/animator/SkDrawTransparentShader.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkDrawTransparentShader_DEFINED +#define SkDrawTransparentShader_DEFINED + +#include "SkPaintParts.h" + +class SkDrawTransparentShader : public SkDrawShader { + DECLARE_EMPTY_MEMBER_INFO(TransparentShader); + virtual SkShader* getShader(); +}; + +#endif // SkDrawTransparentShader_DEFINED + diff --git a/skia/animator/SkDrawable.cpp b/skia/animator/SkDrawable.cpp new file mode 100644 index 0000000..631ebb1 --- /dev/null +++ b/skia/animator/SkDrawable.cpp @@ -0,0 +1,33 @@ +/* libs/graphics/animator/SkDrawable.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkDrawable.h" + +bool SkDrawable::doEvent(SkDisplayEvent::Kind , SkEventState* ) { + return false; +} + +bool SkDrawable::isDrawable() const { + return true; +} + +void SkDrawable::initialize() { +} + +void SkDrawable::setSteps(int steps) { +} + diff --git a/skia/animator/SkDrawable.h b/skia/animator/SkDrawable.h new file mode 100644 index 0000000..1f932ad --- /dev/null +++ b/skia/animator/SkDrawable.h @@ -0,0 +1,36 @@ +/* libs/graphics/animator/SkDrawable.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkDrawable_DEFINED +#define SkDrawable_DEFINED + +#include "SkDisplayable.h" +#include "SkDisplayEvent.h" +#include "SkMath.h" + +struct SkEventState; + +class SkDrawable : public SkDisplayable { +public: + virtual bool doEvent(SkDisplayEvent::Kind , SkEventState* state ); + virtual bool draw(SkAnimateMaker& ) = 0; + virtual void initialize(); + virtual bool isDrawable() const; + virtual void setSteps(int steps); +}; + +#endif // SkDrawable_DEFINED diff --git a/skia/animator/SkDump.cpp b/skia/animator/SkDump.cpp new file mode 100644 index 0000000..5e8b92d --- /dev/null +++ b/skia/animator/SkDump.cpp @@ -0,0 +1,158 @@ +/* libs/graphics/animator/SkDump.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkDump.h" + +#ifdef SK_DUMP_ENABLED + +#include "SkAnimateMaker.h" +#include "SkAnimatorScript.h" +#include "SkDisplayEvents.h" +#include "SkDisplayList.h" +#include "SkString.h" + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkDump::fInfo[] = { + SK_MEMBER(displayList, Boolean), + SK_MEMBER(eventList, Boolean), + SK_MEMBER(events, Boolean), + SK_MEMBER(groups, Boolean), + SK_MEMBER(name, String), + SK_MEMBER(posts, Boolean), + SK_MEMBER(script, String) +}; + +#endif + +DEFINE_GET_MEMBER(SkDump); + +SkDump::SkDump() : displayList(-1), eventList(-1), events(-1), groups(-1), posts(-1) { +} + +bool SkDump::enable(SkAnimateMaker& maker ) { + if (script.size() > 0) + return evaluate(maker); + bool hasAttr = false; + if (events > 0) + hasAttr |= maker.fDumpEvents = true; + if (posts > 0) + hasAttr |= maker.fDumpPosts = true; + if (groups > 0) + hasAttr |= maker.fDumpGConditions = true; + if ((hasAttr |= (eventList > 0)) == true) + maker.fEvents.dump(maker); + if ((hasAttr |= (name.size() > 0)) == true) + maker.dump(name.c_str()); + if (displayList > 0 || displayList != 0 && hasAttr == false) + maker.fDisplayList.dump(&maker); + return true; +} + +bool SkDump::evaluate(SkAnimateMaker &maker) { + SkAnimatorScript scriptEngine(maker, NULL, SkType_Int); + SkScriptValue value; + const char* cScript = script.c_str(); + bool success = scriptEngine.evaluateScript(&cScript, &value); + SkDebugf("%*s<dump script=\"%s\" answer=\" ", SkDisplayList::fIndent, "", script.c_str()); + if (success == false) { + SkDebugf("INVALID\" />\n"); + return false; + } + switch (value.fType) { + case SkType_Float: + SkDebugf("%g\" />\n", SkScalarToFloat(value.fOperand.fScalar)); + break; + case SkType_Int: + SkDebugf("%d\" />\n", value.fOperand.fS32); + break; + case SkType_String: + SkDebugf("%s\" />\n", value.fOperand.fString->c_str()); + break; + default: + return false; + } + return true; +} + +bool SkDump::hasEnable() const { + return true; +} + +void SkDump::GetEnumString(SkDisplayTypes type, int index, SkString* result) { + int badEnum = index; + const SkDisplayEnumMap& map = SkAnimatorScript::GetEnumValues(type); + const char* str = map.fValues; + while (--index >= 0) { + str = strchr(str, '|'); + if (str == NULL) { + result->reset(); + result->appendS32(badEnum); + return; + } + str += 1; + } + const char* end = strchr(str, '|'); + if (end == NULL) + end = str + strlen(str); + result->set(str, end - str); +} + +#else + +// in the release version, <dump> is allowed, and its attributes are defined, but +// are not stored and have no effect + +#if SK_USE_CONDENSED_INFO == 0 + +enum SkDump_Properties { + SK_PROPERTY(displayList), + SK_PROPERTY(eventList), + SK_PROPERTY(events), + SK_PROPERTY(groups), + SK_PROPERTY(name), + SK_PROPERTY(posts), + SK_PROPERTY(script) +}; + +const SkMemberInfo SkDump::fInfo[] = { + SK_MEMBER_PROPERTY(displayList, Boolean), + SK_MEMBER_PROPERTY(eventList, Boolean), + SK_MEMBER_PROPERTY(events, Boolean), + SK_MEMBER_PROPERTY(groups, Boolean), + SK_MEMBER_PROPERTY(name, String), + SK_MEMBER_PROPERTY(posts, Boolean), + SK_MEMBER_PROPERTY(script, String) +}; + +#endif + +DEFINE_GET_MEMBER(SkDump); + +bool SkDump::enable(SkAnimateMaker& maker ) { + return true; +} + +bool SkDump::hasEnable() const { + return true; +} + +bool SkDump::setProperty(int index, SkScriptValue& ) { + return index <= SK_PROPERTY(posts); +} + +#endif diff --git a/skia/animator/SkDump.h b/skia/animator/SkDump.h new file mode 100644 index 0000000..8b38f35 --- /dev/null +++ b/skia/animator/SkDump.h @@ -0,0 +1,51 @@ +/* libs/graphics/animator/SkDump.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkDump_DEFINED +#define SkDump_DEFINED + +#include "SkDisplayable.h" +#include "SkMemberInfo.h" + +class SkAnimateMaker; +class SkString; + +class SkDump : public SkDisplayable { + DECLARE_MEMBER_INFO(Dump); +#ifdef SK_DUMP_ENABLED + SkDump(); + virtual bool enable(SkAnimateMaker & ); + bool evaluate(SkAnimateMaker &); + virtual bool hasEnable() const; + static void GetEnumString(SkDisplayTypes , int index, SkString* result); + SkBool displayList; + SkBool eventList; + SkBool events; + SkString name; + SkBool groups; + SkBool posts; + SkString script; +#else + virtual bool enable(SkAnimateMaker & ); + virtual bool hasEnable() const; + virtual bool setProperty(int index, SkScriptValue& ); +#endif +}; + + +#endif // SkDump_DEFINED + diff --git a/skia/animator/SkExtraPathEffects.xsd b/skia/animator/SkExtraPathEffects.xsd new file mode 100644 index 0000000..8d1172d --- /dev/null +++ b/skia/animator/SkExtraPathEffects.xsd @@ -0,0 +1,33 @@ +<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
+xmlns:Sk="urn:screenplay"
+xmlns:extra="urn:extraPathEffects" targetNamespace="urn:extraPathEffects" >
+ <xs:import namespace="urn:screenplay"
+ schemaLocation="SkAnimateSchema.xsd" />
+
+ <xs:element name="composePathEffect" >
+ <xs:complexType>
+ <xs:choice maxOccurs="1">
+ <xs:element ref="Sk:dash"/>
+ <xs:element ref="extra:shape1DPathEffect"/>
+ </xs:choice>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <xs:element name="shape1DPathEffect" >
+ <xs:complexType>
+ <xs:choice maxOccurs="1">
+ <xs:element ref="Sk:matrix"/>
+ <xs:element ref="Sk:path"/>
+ </xs:choice>
+ <xs:attribute name="addPath" type="Sk:DynamicString" />
+ <xs:attribute name="matrix" type="Sk:DynamicString" />
+ <xs:attribute name="path" type="Sk:Path" />
+ <xs:attribute name="phase" type="Sk:DynamicString"/>
+ <xs:attribute name="spacing" type="Sk:DynamicString"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+</xs:schema>
+
diff --git a/skia/animator/SkExtras.h b/skia/animator/SkExtras.h new file mode 100644 index 0000000..b792758 --- /dev/null +++ b/skia/animator/SkExtras.h @@ -0,0 +1,42 @@ +/* libs/graphics/animator/SkExtras.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkExtras_DEFINED +#define SkExtras_DEFINED + +#include "SkScript.h" + +class SkExtras { +public: + SkExtras(); + virtual ~SkExtras() {} + + virtual SkDisplayable* createInstance(SkDisplayTypes type) = 0; + virtual bool definesType(SkDisplayTypes type) = 0; +#if SK_USE_CONDENSED_INFO == 0 + virtual const SkMemberInfo* getMembers(SkDisplayTypes type, int* infoCountPtr) = 0; +#endif +#ifdef SK_DEBUG + virtual const char* getName(SkDisplayTypes type) = 0; +#endif + virtual SkDisplayTypes getType(const char match[], size_t len ) = 0; + + SkScriptEngine::_propertyCallBack fExtraCallBack; + void* fExtraStorage; +}; + +#endif // SkExtras_DEFINED diff --git a/skia/animator/SkGetCondensedInfo.cpp b/skia/animator/SkGetCondensedInfo.cpp new file mode 100644 index 0000000..35f7171 --- /dev/null +++ b/skia/animator/SkGetCondensedInfo.cpp @@ -0,0 +1,130 @@ +/* libs/graphics/animator/SkGetCondensedInfo.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkMemberInfo.h" + +#if SK_USE_CONDENSED_INFO == 1 + +// SkCondensed.cpp is auto-generated +// To generate it, execute SkDisplayType::BuildCondensedInfo() +#ifdef SK_DEBUG +#include "SkCondensedDebug.cpp" +#else +#include "SkCondensedRelease.cpp" +#endif + +static int _searchByName(const unsigned char* lengths, int count, const char* strings, const char target[]) { + int lo = 0; + int hi = count - 1; + while (lo < hi) { + int mid = (hi + lo) >> 1; + if (strcmp(&strings[lengths[mid << 2]], target) < 0) + lo = mid + 1; + else + hi = mid; + } + if (strcmp(&strings[lengths[hi << 2]], target) != 0) + return -1; + return hi; +} + +static int _searchByType(SkDisplayTypes type) { + unsigned char match = (unsigned char) type; + int lo = 0; + int hi = kTypeIDs - 1; + while (lo < hi) { + int mid = (hi + lo) >> 1; + if (gTypeIDs[mid] < match) + lo = mid + 1; + else + hi = mid; + } + if (gTypeIDs[hi] != type) + return -1; + return hi; +} + +const SkMemberInfo* SkDisplayType::GetMembers(SkAnimateMaker* , SkDisplayTypes type, int* infoCountPtr) { + int lookup = _searchByType(type); + if (lookup < 0) + return NULL; + if (infoCountPtr) + *infoCountPtr = gInfoCounts[lookup]; + return gInfoTables[lookup]; +} + +// !!! replace with inline +const SkMemberInfo* SkDisplayType::GetMember(SkAnimateMaker* , SkDisplayTypes type, const char** matchPtr ) { + const SkMemberInfo* info = SkMemberInfo::Find(type, matchPtr); + SkASSERT(info); + return info; +} + +static const SkMemberInfo* _lookup(int lookup, const char** matchPtr) { + int count = gInfoCounts[lookup]; + const SkMemberInfo* info = gInfoTables[lookup]; + if (info->fType == SkType_BaseClassInfo) { + int baseTypeLookup = info->fOffset; + const SkMemberInfo* result = _lookup(baseTypeLookup, matchPtr); + if (result != NULL) + return result; + if (--count == 0) + return NULL; + info++; + } + SkASSERT(info->fType != SkType_BaseClassInfo); + const char* match = *matchPtr; + const char* strings = gInfoNames[lookup]; + int index = _searchByName(&info->fName, count, strings, match); + if (index < 0) + return NULL; + return &info[index]; +} + +const SkMemberInfo* SkMemberInfo::Find(SkDisplayTypes type, int* index) { + int count = gInfoCounts[lookup]; + const SkMemberInfo* info = gInfoTables[lookup]; + if (info->fType == SkType_BaseClassInfo) { + int baseTypeLookup = info->fOffset; + const SkMemberInfo* result = Find(baseTypeLookup, index); + if (result != NULL) + return result; + if (--count == 0) + return NULL; + info++; + } + SkASSERT(info->fType != SkType_BaseClassInfo); + if (*index >= count) { + *index -= count; + return NULL; + } + return &info[index]; +} + +const SkMemberInfo* SkMemberInfo::Find(SkDisplayTypes type, const char** matchPtr) { + int lookup = _searchByType(type); + SkASSERT(lookup >= 0); + return _lookup(lookup, matchPtr); +} + +const SkMemberInfo* SkMemberInfo::getInherited() const { + int baseTypeLookup = fOffset; + return gInfoTables[baseTypeLookup]; +} + +#endif + diff --git a/skia/animator/SkHitClear.cpp b/skia/animator/SkHitClear.cpp new file mode 100644 index 0000000..e88ab92 --- /dev/null +++ b/skia/animator/SkHitClear.cpp @@ -0,0 +1,41 @@ +/* libs/graphics/animator/SkHitClear.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkHitClear.h" + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkHitClear::fInfo[] = { + SK_MEMBER_ARRAY(targets, Displayable) +}; + +#endif + +DEFINE_GET_MEMBER(SkHitClear); + +bool SkHitClear::enable(SkAnimateMaker& maker) { + for (int tIndex = 0; tIndex < targets.count(); tIndex++) { + SkDisplayable* target = targets[tIndex]; + target->clearBounder(); + } + return true; +} + +bool SkHitClear::hasEnable() const { + return true; +} + diff --git a/skia/animator/SkHitClear.h b/skia/animator/SkHitClear.h new file mode 100644 index 0000000..8a41e0c --- /dev/null +++ b/skia/animator/SkHitClear.h @@ -0,0 +1,33 @@ +/* libs/graphics/animator/SkHitClear.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkHitClear_DEFINED +#define SkHitClear_DEFINED + +#include "SkDisplayable.h" +#include "SkMemberInfo.h" +#include "SkTypedArray.h" + +class SkHitClear : public SkDisplayable { + DECLARE_MEMBER_INFO(HitClear); + virtual bool enable(SkAnimateMaker& ); + virtual bool hasEnable() const; +private: + SkTDDisplayableArray targets; +}; + +#endif // SkHitClear_DEFINED diff --git a/skia/animator/SkHitTest.cpp b/skia/animator/SkHitTest.cpp new file mode 100644 index 0000000..f54ca9a --- /dev/null +++ b/skia/animator/SkHitTest.cpp @@ -0,0 +1,83 @@ +/* libs/graphics/animator/SkHitTest.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkHitTest.h" + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkHitTest::fInfo[] = { + SK_MEMBER_ARRAY(bullets, Displayable), + SK_MEMBER_ARRAY(hits, Int), + SK_MEMBER_ARRAY(targets, Displayable), + SK_MEMBER(value, Boolean) +}; + +#endif + +DEFINE_GET_MEMBER(SkHitTest); + +SkHitTest::SkHitTest() : value(false) { +} + +bool SkHitTest::draw(SkAnimateMaker& maker) { + hits.setCount(bullets.count()); + value = false; + int bulletCount = bullets.count(); + int targetCount = targets.count(); + for (int bIndex = 0; bIndex < bulletCount; bIndex++) { + SkDisplayable* bullet = bullets[bIndex]; + SkRect bBounds; + bullet->getBounds(&bBounds); + hits[bIndex] = -1; + if (bBounds.fLeft == (int16_t)0x8000U) + continue; + for (int tIndex = 0; tIndex < targetCount; tIndex++) { + SkDisplayable* target = targets[tIndex]; + SkRect tBounds; + target->getBounds(&tBounds); + if (bBounds.intersect(tBounds)) { + hits[bIndex] = tIndex; + value = true; + break; + } + } + } + return false; +} + +bool SkHitTest::enable(SkAnimateMaker& maker) { + for (int bIndex = 0; bIndex < bullets.count(); bIndex++) { + SkDisplayable* bullet = bullets[bIndex]; + bullet->enableBounder(); + } + for (int tIndex = 0; tIndex < targets.count(); tIndex++) { + SkDisplayable* target = targets[tIndex]; + target->enableBounder(); + } + return false; +} + +bool SkHitTest::hasEnable() const { + return true; +} + +const SkMemberInfo* SkHitTest::preferredChild(SkDisplayTypes type) { + if (bullets.count() == 0) + return getMember("bullets"); + return getMember("targets"); // !!! cwap! need to refer to member through enum like kScope instead +} + diff --git a/skia/animator/SkHitTest.h b/skia/animator/SkHitTest.h new file mode 100644 index 0000000..a786cb2 --- /dev/null +++ b/skia/animator/SkHitTest.h @@ -0,0 +1,38 @@ +/* libs/graphics/animator/SkHitTest.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkHitTest_DEFINED +#define SkHitTest_DEFINED + +#include "SkDrawable.h" +#include "SkTypedArray.h" + +class SkHitTest : public SkDrawable { + DECLARE_MEMBER_INFO(HitTest); + SkHitTest(); + virtual bool draw(SkAnimateMaker& ); + virtual bool enable(SkAnimateMaker& ); + virtual bool hasEnable() const; + virtual const SkMemberInfo* preferredChild(SkDisplayTypes type); +private: + SkTDDisplayableArray bullets; + SkTDIntArray hits; + SkTDDisplayableArray targets; + SkBool value; +}; + +#endif // SkHitTest_DEFINED diff --git a/skia/animator/SkIntArray.h b/skia/animator/SkIntArray.h new file mode 100644 index 0000000..bb58541 --- /dev/null +++ b/skia/animator/SkIntArray.h @@ -0,0 +1,66 @@ +/* libs/graphics/animator/SkIntArray.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkIntArray_DEFINED +#define SkIntArray_DEFINED + +#include "SkColor.h" +#include "SkDisplayType.h" +#include "SkMath.h" +#include "SkTDArray_Experimental.h" + +class SkActive; +class SkAnimateBase; +class SkData; +class SkDisplayable; +class SkDisplayEvent; +class SkDrawable; +class SkDrawColor; +class SkMatrixPart; +struct SkMemberInfo; +class SkPathPart; +class SkPaintPart; +class SkTypedArray; +class SkString; +union SkOperand; + +typedef SkIntArray(int) SkTDIntArray; +typedef SkIntArray(SkColor) SkTDColorArray; +typedef SkIntArray(SkDisplayTypes) SkTDDisplayTypesArray; +typedef SkIntArray(SkMSec) SkTDMSecArray; +typedef SkIntArray(SkScalar) SkTDScalarArray; + +typedef SkLongArray(SkActive*) SkTDActiveArray; +typedef SkLongArray(SkAnimateBase*) SkTDAnimateArray; +typedef SkLongArray(SkData*) SkTDDataArray; +typedef SkLongArray(SkDisplayable*) SkTDDisplayableArray; +typedef SkLongArray(SkDisplayEvent*) SkTDDisplayEventArray; +typedef SkLongArray(SkDrawable*) SkTDDrawableArray; +typedef SkLongArray(SkDrawColor*) SkTDDrawColorArray; +typedef SkLongArray(SkMatrixPart*) SkTDMatrixPartArray; +typedef SkLongArray(const SkMemberInfo*) SkTDMemberInfoArray; +typedef SkLongArray(SkPaintPart*) SkTDPaintPartArray; +typedef SkLongArray(SkPathPart*) SkTDPathPartArray; +typedef SkLongArray(SkTypedArray*) SkTDTypedArrayArray; +typedef SkLongArray(SkString*) SkTDStringArray; +typedef SkLongArray(SkOperand) SkTDOperandArray; +typedef SkLongArray(SkOperand*) SkTDOperandPtrArray; + +#endif // SkIntArray_DEFINED + + + diff --git a/skia/animator/SkMatrixParts.cpp b/skia/animator/SkMatrixParts.cpp new file mode 100644 index 0000000..d5bdcc1 --- /dev/null +++ b/skia/animator/SkMatrixParts.cpp @@ -0,0 +1,302 @@ +/* libs/graphics/animator/SkMatrixParts.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkMatrixParts.h" +#include "SkAnimateMaker.h" +#include "SkDrawMatrix.h" +#include "SkDrawRectangle.h" +#include "SkDrawPath.h" + +SkMatrixPart::SkMatrixPart() : fMatrix(NULL) { +} + +void SkMatrixPart::dirty() { + fMatrix->dirty(); +} + +SkDisplayable* SkMatrixPart::getParent() const { + return fMatrix; +} + +bool SkMatrixPart::setParent(SkDisplayable* parent) { + SkASSERT(parent != NULL); + if (parent->isMatrix() == false) + return true; + fMatrix = (SkDrawMatrix*) parent; + return false; +} + + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkRotate::fInfo[] = { + SK_MEMBER(center, Point), + SK_MEMBER(degrees, Float) +}; + +#endif + +DEFINE_GET_MEMBER(SkRotate); + +SkRotate::SkRotate() : degrees(0) { + center.fX = center.fY = 0; +} + +bool SkRotate::add() { + fMatrix->rotate(degrees, center); + return false; +} + + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkScale::fInfo[] = { + SK_MEMBER(center, Point), + SK_MEMBER(x, Float), + SK_MEMBER(y, Float) +}; + +#endif + +DEFINE_GET_MEMBER(SkScale); + +SkScale::SkScale() : x(SK_Scalar1), y(SK_Scalar1) { + center.fX = center.fY = 0; +} + +bool SkScale::add() { + fMatrix->scale(x, y, center); + return false; +} + + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkSkew::fInfo[] = { + SK_MEMBER(center, Point), + SK_MEMBER(x, Float), + SK_MEMBER(y, Float) +}; + +#endif + +DEFINE_GET_MEMBER(SkSkew); + +SkSkew::SkSkew() : x(0), y(0) { + center.fX = center.fY = 0; +} + +bool SkSkew::add() { + fMatrix->skew(x, y, center); + return false; +} + + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkTranslate::fInfo[] = { + SK_MEMBER(x, Float), + SK_MEMBER(y, Float) +}; + +#endif + +DEFINE_GET_MEMBER(SkTranslate); + +SkTranslate::SkTranslate() : x(0), y(0) { +} + +bool SkTranslate::add() { + fMatrix->translate(x, y); + return false; +} + + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkFromPath::fInfo[] = { + SK_MEMBER(mode, FromPathMode), + SK_MEMBER(offset, Float), + SK_MEMBER(path, Path) +}; + +#endif + +DEFINE_GET_MEMBER(SkFromPath); + +SkFromPath::SkFromPath() : + mode(0), offset(0), path(NULL) { +} + +SkFromPath::~SkFromPath() { +} + +bool SkFromPath::add() { + if (path == NULL) + return true; + static const uint8_t gFlags[] = { + SkPathMeasure::kGetPosAndTan_MatrixFlag, // normal + SkPathMeasure::kGetTangent_MatrixFlag, // angle + SkPathMeasure::kGetPosition_MatrixFlag // position + }; + if ((unsigned)mode >= SK_ARRAY_COUNT(gFlags)) + return true; + SkMatrix result; + fPathMeasure.setPath(&path->getPath(), false); + if (fPathMeasure.getMatrix(offset, &result, (SkPathMeasure::MatrixFlags)gFlags[mode])) + fMatrix->set(result); + return false; +} + + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkRectToRect::fInfo[] = { + SK_MEMBER(destination, Rect), + SK_MEMBER(source, Rect) +}; + +#endif + +DEFINE_GET_MEMBER(SkRectToRect); + +SkRectToRect::SkRectToRect() : + source(NULL), destination(NULL) { +} + +SkRectToRect::~SkRectToRect() { +} + +bool SkRectToRect::add() { + if (source == NULL || destination == NULL) + return true; + SkMatrix temp; + temp.setRectToRect(source->fRect, destination->fRect, + SkMatrix::kFill_ScaleToFit); + fMatrix->set(temp); + return false; +} + +#ifdef SK_DUMP_ENABLED +void SkRectToRect::dump(SkAnimateMaker* maker) { + dumpBase(maker); + SkDebugf("/>\n"); + SkDisplayList::fIndent += 4; + if (source) { + SkDebugf("%*s<source>\n", SkDisplayList::fIndent, ""); + SkDisplayList::fIndent += 4; + source->dump(maker); + SkDisplayList::fIndent -= 4; + SkDebugf("%*s</source>\n", SkDisplayList::fIndent, ""); + } + if (destination) { + SkDebugf("%*s<destination>\n", SkDisplayList::fIndent, ""); + SkDisplayList::fIndent += 4; + destination->dump(maker); + SkDisplayList::fIndent -= 4; + SkDebugf("%*s</destination>\n", SkDisplayList::fIndent, ""); + } + SkDisplayList::fIndent -= 4; + dumpEnd(maker); +} +#endif + +const SkMemberInfo* SkRectToRect::preferredChild(SkDisplayTypes ) { + if (source == NULL) + return getMember("source"); // !!! cwap! need to refer to member through enum like kScope instead + else { + SkASSERT(destination == NULL); + return getMember("destination"); + } +} + + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkPolyToPoly::fInfo[] = { + SK_MEMBER(destination, Polygon), + SK_MEMBER(source, Polygon) +}; + +#endif + +DEFINE_GET_MEMBER(SkPolyToPoly); + +SkPolyToPoly::SkPolyToPoly() : source(NULL), destination(NULL) { +} + +SkPolyToPoly::~SkPolyToPoly() { +} + +bool SkPolyToPoly::add() { + SkASSERT(source); + SkASSERT(destination); + SkPoint src[4]; + SkPoint dst[4]; + SkPath& sourcePath = source->getPath(); + int srcPts = sourcePath.getPoints(src, 4); + SkPath& destPath = destination->getPath(); + int dstPts = destPath.getPoints(dst, 4); + if (srcPts != dstPts) + return true; + SkMatrix temp; + temp.setPolyToPoly(src, dst, srcPts); + fMatrix->set(temp); + return false; +} + +#ifdef SK_DUMP_ENABLED +void SkPolyToPoly::dump(SkAnimateMaker* maker) { + dumpBase(maker); + SkDebugf("/>\n"); + SkDisplayList::fIndent += 4; + if (source) { + SkDebugf("%*s<source>\n", SkDisplayList::fIndent, ""); + SkDisplayList::fIndent += 4; + source->dump(maker); + SkDisplayList::fIndent -= 4; + SkDebugf("%*s</source>\n", SkDisplayList::fIndent, ""); + } + if (destination) { + SkDebugf("%*s<destination>\n", SkDisplayList::fIndent, ""); + SkDisplayList::fIndent += 4; + destination->dump(maker); + SkDisplayList::fIndent -= 4; + SkDebugf("%*s</destination>\n", SkDisplayList::fIndent, ""); + } + SkDisplayList::fIndent -= 4; + dumpEnd(maker); +} +#endif + +void SkPolyToPoly::onEndElement(SkAnimateMaker& ) { + SkASSERT(source); + SkASSERT(destination); + if (source->childHasID() || destination->childHasID()) + fMatrix->setChildHasID(); +} + +const SkMemberInfo* SkPolyToPoly::preferredChild(SkDisplayTypes ) { + if (source == NULL) + return getMember("source"); // !!! cwap! need to refer to member through enum like kScope instead + else { + SkASSERT(destination == NULL); + return getMember("destination"); + } +} + + diff --git a/skia/animator/SkMatrixParts.h b/skia/animator/SkMatrixParts.h new file mode 100644 index 0000000..5fcf115 --- /dev/null +++ b/skia/animator/SkMatrixParts.h @@ -0,0 +1,127 @@ +/* libs/graphics/animator/SkMatrixParts.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkMatrixParts_DEFINED +#define SkMatrixParts_DEFINED + +#include "SkDisplayable.h" +#include "SkMemberInfo.h" +#include "SkPathMeasure.h" + +class SkDrawPath; +class SkDrawRect; +class SkPolygon; + +class SkDrawMatrix; +// class SkMatrix; + +class SkMatrixPart : public SkDisplayable { +public: + SkMatrixPart(); + virtual bool add() = 0; + virtual void dirty(); + virtual SkDisplayable* getParent() const; + virtual bool setParent(SkDisplayable* parent); +#ifdef SK_DEBUG + virtual bool isMatrixPart() const { return true; } +#endif +protected: + SkDrawMatrix* fMatrix; +}; + +class SkRotate : public SkMatrixPart { + DECLARE_MEMBER_INFO(Rotate); + SkRotate(); +protected: + virtual bool add(); + SkScalar degrees; + SkPoint center; +}; + +class SkScale : public SkMatrixPart { + DECLARE_MEMBER_INFO(Scale); + SkScale(); +protected: + virtual bool add(); + SkScalar x; + SkScalar y; + SkPoint center; +}; + +class SkSkew : public SkMatrixPart { + DECLARE_MEMBER_INFO(Skew); + SkSkew(); +protected: + virtual bool add(); + SkScalar x; + SkScalar y; + SkPoint center; +}; + +class SkTranslate : public SkMatrixPart { + DECLARE_MEMBER_INFO(Translate); + SkTranslate(); +protected: + virtual bool add(); + SkScalar x; + SkScalar y; +}; + +class SkFromPath : public SkMatrixPart { + DECLARE_MEMBER_INFO(FromPath); + SkFromPath(); + virtual ~SkFromPath(); +protected: + virtual bool add(); + int32_t mode; + SkScalar offset; + SkDrawPath* path; + SkPathMeasure fPathMeasure; +}; + +class SkRectToRect : public SkMatrixPart { + DECLARE_MEMBER_INFO(RectToRect); + SkRectToRect(); + virtual ~SkRectToRect(); +#ifdef SK_DUMP_ENABLED + virtual void dump(SkAnimateMaker* ); +#endif + virtual const SkMemberInfo* preferredChild(SkDisplayTypes type); +protected: + virtual bool add(); + SkDrawRect* source; + SkDrawRect* destination; +}; + +class SkPolyToPoly : public SkMatrixPart { + DECLARE_MEMBER_INFO(PolyToPoly); + SkPolyToPoly(); + virtual ~SkPolyToPoly(); +#ifdef SK_DUMP_ENABLED + virtual void dump(SkAnimateMaker* ); +#endif + virtual void onEndElement(SkAnimateMaker& ); + virtual const SkMemberInfo* preferredChild(SkDisplayTypes type); +protected: + virtual bool add(); + SkPolygon* source; + SkPolygon* destination; +}; + +// !!! add concat matrix ? + +#endif // SkMatrixParts_DEFINED diff --git a/skia/animator/SkMemberInfo.cpp b/skia/animator/SkMemberInfo.cpp new file mode 100644 index 0000000..d4f4e51 --- /dev/null +++ b/skia/animator/SkMemberInfo.cpp @@ -0,0 +1,568 @@ +/* libs/graphics/animator/SkMemberInfo.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkMemberInfo.h" +#include "SkAnimateMaker.h" +#include "SkAnimatorScript.h" +#include "SkBase64.h" +#include "SkCamera.h" +#include "SkDisplayable.h" +#include "SkDisplayTypes.h" +#include "SkDraw3D.h" +#include "SkDrawColor.h" +#include "SkParse.h" +#include "SkScript.h" +#include "SkTSearch.h" +#include "SkTypedArray.h" + +size_t SkMemberInfo::GetSize(SkDisplayTypes type) { // size of simple types only + size_t byteSize; + switch (type) { + case SkType_ARGB: + byteSize = sizeof(SkColor); + break; + case SkType_AddMode: + case SkType_Align: + case SkType_ApplyMode: + case SkType_ApplyTransition: + case SkType_BitmapEncoding: + case SkType_Boolean: + case SkType_Cap: + case SkType_EventCode: + case SkType_EventKind: + case SkType_EventMode: + case SkType_FilterType: + case SkType_FontStyle: + case SkType_FromPathMode: + case SkType_Join: + case SkType_MaskFilterBlurStyle: + case SkType_PathDirection: + case SkType_Style: + case SkType_TileMode: + case SkType_Xfermode: + byteSize = sizeof(int); + break; + case SkType_Base64: // assume base64 data is always const, copied by ref + case SkType_Displayable: + case SkType_Drawable: + case SkType_Matrix: + byteSize = sizeof(void*); + break; + case SkType_MSec: + byteSize = sizeof(SkMSec); + break; + case SkType_Point: + byteSize = sizeof(SkPoint); + break; + case SkType_3D_Point: + byteSize = sizeof(Sk3D_Point); + break; + case SkType_Int: + byteSize = sizeof(int32_t); + break; + case SkType_Float: + byteSize = sizeof(SkScalar); + break; + case SkType_DynamicString: + case SkType_String: + byteSize = sizeof(SkString); // assume we'll copy by reference, not value + break; + default: +// SkASSERT(0); + byteSize = 0; + } + return byteSize; +} + +bool SkMemberInfo::getArrayValue(const SkDisplayable* displayable, int index, SkOperand* value) const { + SkASSERT(fType != SkType_String && fType != SkType_MemberProperty); + char* valuePtr = (char*) *(SkOperand**) memberData(displayable); + SkDisplayTypes type = (SkDisplayTypes) 0; + if (displayable->getType() == SkType_Array) { + SkDisplayArray* dispArray = (SkDisplayArray*) displayable; + if (dispArray->values.count() <= index) + return false; + type = dispArray->values.getType(); + } else + SkASSERT(0); // incomplete + size_t byteSize = GetSize(type); + memcpy(value, valuePtr + index * byteSize, byteSize); + return true; +} + +size_t SkMemberInfo::getSize(const SkDisplayable* displayable) const { + size_t byteSize; + switch (fType) { + case SkType_MemberProperty: + byteSize = GetSize(propertyType()); + break; + case SkType_Array: { + SkDisplayTypes type; + if (displayable == NULL) + return sizeof(int); + if (displayable->getType() == SkType_Array) { + SkDisplayArray* dispArray = (SkDisplayArray*) displayable; + type = dispArray->values.getType(); + } else + type = propertyType(); + SkTDOperandArray* array = (SkTDOperandArray*) memberData(displayable); + byteSize = GetSize(type) * array->count(); + } break; + default: + byteSize = GetSize((SkDisplayTypes) fType); + } + return byteSize; +} + +void SkMemberInfo::getString(const SkDisplayable* displayable, SkString** string) const { + if (fType == SkType_MemberProperty) { + SkScriptValue value; + displayable->getProperty(propertyIndex(), &value); + SkASSERT(value.fType == SkType_String); + *string = value.fOperand.fString; + return; + } + SkASSERT(fCount == sizeof(SkString) / sizeof(SkScalar)); + SkASSERT(fType == SkType_String || fType == SkType_DynamicString); + void* valuePtr = memberData(displayable); + *string = (SkString*) valuePtr; +} + +void SkMemberInfo::getValue(const SkDisplayable* displayable, SkOperand value[], int count) const { + SkASSERT(fType != SkType_String && fType != SkType_MemberProperty); + SkASSERT(count == fCount); + void* valuePtr = memberData(displayable); + size_t byteSize = getSize(displayable); + SkASSERT(sizeof(value[0].fScalar) == sizeof(value[0])); // no support for 64 bit pointers, yet + memcpy(value, valuePtr, byteSize); +} + +void SkMemberInfo::setString(SkDisplayable* displayable, SkString* value) const { + SkString* string = (SkString*) memberData(displayable); + string->set(*value); + displayable->dirty(); +} + +void SkMemberInfo::setValue(SkDisplayable* displayable, const SkOperand values[], + int count) const { + SkASSERT(sizeof(values[0].fScalar) == sizeof(values[0])); // no support for 64 bit pointers, yet + char* dst = (char*) memberData(displayable); + if (fType == SkType_Array) { + SkTDScalarArray* array = (SkTDScalarArray* ) dst; + array->setCount(count); + dst = (char*) array->begin(); + } + memcpy(dst, values, count * sizeof(SkOperand)); + displayable->dirty(); +} + + +static inline bool is_between(int c, int min, int max) +{ + return (unsigned)(c - min) <= (unsigned)(max - min); +} + +static inline bool is_hex(int c) +{ + if (is_between(c, '0', '9')) + return true; + c |= 0x20; // make us lower-case + if (is_between(c, 'a', 'f')) + return true; + return false; +} + + +bool SkMemberInfo::setValue(SkAnimateMaker& maker, SkTDOperandArray* arrayStorage, + int storageOffset, int maxStorage, SkDisplayable* displayable, SkDisplayTypes outType, + const char rawValue[], size_t rawValueLen) const +{ + SkString valueStr(rawValue, rawValueLen); + SkScriptValue scriptValue; + scriptValue.fType = SkType_Unknown; + scriptValue.fOperand.fS32 = 0; + SkDisplayTypes type = getType(); + SkAnimatorScript engine(maker, displayable, type); + if (arrayStorage) + displayable = NULL; + bool success = true; + void* untypedStorage = NULL; + if (displayable && fType != SkType_MemberProperty && fType != SkType_MemberFunction) + untypedStorage = (SkTDOperandArray*) memberData(displayable); + + if (type == SkType_ARGB) { + // for both SpiderMonkey and SkiaScript, substitute any #xyz or #xxyyzz first + // it's enough to expand the colors into 0xFFxxyyzz + const char* poundPos; + while ((poundPos = strchr(valueStr.c_str(), '#')) != NULL) { + size_t offset = poundPos - valueStr.c_str(); + if (valueStr.size() - offset < 4) + break; + char r = poundPos[1]; + char g = poundPos[2]; + char b = poundPos[3]; + if (is_hex(r) == false || is_hex(g) == false || is_hex(b) == false) + break; + char hex = poundPos[4]; + if (is_hex(hex) == false) { + valueStr.insertUnichar(offset + 1, r); + valueStr.insertUnichar(offset + 3, g); + valueStr.insertUnichar(offset + 5, b); + } + *(char*) poundPos = '0'; // overwrite '#' + valueStr.insert(offset + 1, "xFF"); + } + } + if (SkDisplayType::IsDisplayable(&maker, type) || SkDisplayType::IsEnum(&maker, type) || type == SkType_ARGB) + goto scriptCommon; + switch (type) { + case SkType_String: +#if 0 + if (displayable && displayable->isAnimate()) { + + goto noScriptString; + } + if (strncmp(rawValue, "#string:", sizeof("#string:") - 1) == 0) { + SkASSERT(sizeof("string") == sizeof("script")); + char* stringHeader = valueStr.writable_str(); + memcpy(&stringHeader[1], "script", sizeof("script") - 1); + rawValue = valueStr.c_str(); + goto noScriptString; + } else +#endif + if (strncmp(rawValue, "#script:", sizeof("#script:") - 1) != 0) + goto noScriptString; + valueStr.remove(0, 8); + case SkType_Unknown: + case SkType_Int: + case SkType_MSec: // for the purposes of script, MSec is treated as a Scalar + case SkType_Point: + case SkType_3D_Point: + case SkType_Float: + case SkType_Array: +scriptCommon: { + const char* script = valueStr.c_str(); + success = engine.evaluateScript(&script, &scriptValue); + if (success == false) { + maker.setScriptError(engine); + return false; + } + } + SkASSERT(success); + if (scriptValue.fType == SkType_Displayable) { + if (type == SkType_String) { + const char* charPtr; + maker.findKey(scriptValue.fOperand.fDisplayable, &charPtr); + scriptValue.fOperand.fString = new SkString(charPtr); + scriptValue.fType = SkType_String; + engine.SkScriptEngine::track(scriptValue.fOperand.fString); + break; + } + SkASSERT(SkDisplayType::IsDisplayable(&maker, type)); + if (displayable) + displayable->setReference(this, scriptValue.fOperand.fDisplayable); + else + arrayStorage->begin()[0].fDisplayable = scriptValue.fOperand.fDisplayable; + return true; + } + if (type != scriptValue.fType) { + if (scriptValue.fType == SkType_Array) { + engine.forget(scriptValue.getArray()); + goto writeStruct; // real structs have already been written by script + } + switch (type) { + case SkType_String: + success = engine.convertTo(SkType_String, &scriptValue); + break; + case SkType_MSec: + case SkType_Float: + success = engine.convertTo(SkType_Float, &scriptValue); + break; + case SkType_Int: + success = engine.convertTo(SkType_Int, &scriptValue); + break; + case SkType_Array: + success = engine.convertTo(arrayType(), &scriptValue); + // !!! incomplete; create array of appropriate type and add scriptValue to it + SkASSERT(0); + break; + case SkType_Displayable: + case SkType_Drawable: + return false; // no way to convert other types to this + default: // to avoid warnings + break; + } + if (success == false) + return false; + } + if (type == SkType_MSec) + scriptValue.fOperand.fMSec = SkScalarMulRound(scriptValue.fOperand.fScalar, 1000); + scriptValue.fType = type; + break; + noScriptString: + case SkType_DynamicString: + if (fType == SkType_MemberProperty && displayable) { + SkString string(rawValue, rawValueLen); + SkScriptValue scriptValue; + scriptValue.fOperand.fString = &string; + scriptValue.fType = SkType_String; + displayable->setProperty(propertyIndex(), scriptValue); + } else if (displayable) { + SkString* string = (SkString*) memberData(displayable); + string->set(rawValue, rawValueLen); + } else { + SkASSERT(arrayStorage->count() == 1); + arrayStorage->begin()->fString->set(rawValue, rawValueLen); + } + goto dirty; + case SkType_Base64: { + SkBase64 base64; + base64.decode(rawValue, rawValueLen); + *(SkBase64* ) untypedStorage = base64; + } goto dirty; + default: + SkASSERT(0); + break; + } +// if (SkDisplayType::IsStruct(type) == false) + { +writeStruct: + if (writeValue(displayable, arrayStorage, storageOffset, maxStorage, + untypedStorage, outType, scriptValue)) { + maker.setErrorCode(SkDisplayXMLParserError::kUnexpectedType); + return false; + } + } +dirty: + if (displayable) + displayable->dirty(); + return true; +} + +bool SkMemberInfo::setValue(SkAnimateMaker& maker, SkTDOperandArray* arrayStorage, + int storageOffset, int maxStorage, SkDisplayable* displayable, SkDisplayTypes outType, + SkString& raw) const { + return setValue(maker, arrayStorage, storageOffset, maxStorage, displayable, outType, raw.c_str(), + raw.size()); +} + +bool SkMemberInfo::writeValue(SkDisplayable* displayable, SkTDOperandArray* arrayStorage, + int storageOffset, int maxStorage, void* untypedStorage, SkDisplayTypes outType, + SkScriptValue& scriptValue) const +{ + SkOperand* storage = untypedStorage ? (SkOperand*) untypedStorage : arrayStorage ? + arrayStorage->begin() : NULL; + if (storage) + storage += storageOffset; + SkDisplayTypes type = getType(); + if (fType == SkType_MemberProperty) { + if(displayable) + displayable->setProperty(propertyIndex(), scriptValue); + else { + SkASSERT(storageOffset < arrayStorage->count()); + switch (scriptValue.fType) { + case SkType_Boolean: + case SkType_Float: + case SkType_Int: + memcpy(&storage->fScalar, &scriptValue.fOperand.fScalar, sizeof(SkScalar)); + break; + case SkType_Array: + memcpy(&storage->fScalar, scriptValue.fOperand.fArray->begin(), scriptValue.fOperand.fArray->count() * sizeof(SkScalar)); + break; + case SkType_String: + storage->fString->set(*scriptValue.fOperand.fString); + break; + default: + SkASSERT(0); // type isn't handled yet + } + } + } else if (fType == SkType_MemberFunction) { + SkASSERT(scriptValue.fType == SkType_Array); + if (displayable) + displayable->executeFunction(displayable, this, scriptValue.fOperand.fArray, NULL); + else { + int count = scriptValue.fOperand.fArray->count(); + // SkASSERT(maxStorage == 0 || count == maxStorage); + if (arrayStorage->count() == 2) + arrayStorage->setCount(2 * count); + else { + storageOffset *= count; + SkASSERT(count + storageOffset <= arrayStorage->count()); + } + memcpy(&(*arrayStorage)[storageOffset], scriptValue.fOperand.fArray->begin(), count * sizeof(SkOperand)); + } + + } else if (fType == SkType_Array) { + SkTypedArray* destArray = (SkTypedArray*) (untypedStorage ? untypedStorage : arrayStorage); + SkASSERT(destArray); + // destArray->setCount(0); + if (scriptValue.fType != SkType_Array) { + SkASSERT(type == scriptValue.fType); + // SkASSERT(storageOffset + 1 <= maxStorage); + destArray->setCount(storageOffset + 1); + (*destArray)[storageOffset] = scriptValue.fOperand; + } else { + if (type == SkType_Unknown) { + type = scriptValue.fOperand.fArray->getType(); + destArray->setType(type); + } + SkASSERT(type == scriptValue.fOperand.fArray->getType()); + int count = scriptValue.fOperand.fArray->count(); + // SkASSERT(storageOffset + count <= maxStorage); + destArray->setCount(storageOffset + count); + memcpy(destArray->begin() + storageOffset, scriptValue.fOperand.fArray->begin(), sizeof(SkOperand) * count); + } + } else if (type == SkType_String) { + SkString* string = untypedStorage ? (SkString*) untypedStorage : (*arrayStorage)[storageOffset].fString; + string->set(*scriptValue.fOperand.fString); + } else if (type == SkType_ARGB && outType == SkType_Float) { + SkTypedArray* array = scriptValue.fOperand.fArray; + SkASSERT(scriptValue.fType == SkType_Int || scriptValue.fType == SkType_ARGB || + scriptValue.fType == SkType_Array); + SkASSERT(scriptValue.fType != SkType_Array || (array != NULL && + array->getType() == SkType_Int)); + int numberOfColors = scriptValue.fType == SkType_Array ? array->count() : 1; + int numberOfComponents = numberOfColors * 4; + // SkASSERT(maxStorage == 0 || maxStorage == numberOfComponents); + if (maxStorage == 0) + arrayStorage->setCount(numberOfComponents); + for (int index = 0; index < numberOfColors; index++) { + SkColor color = scriptValue.fType == SkType_Array ? + (SkColor) array->begin()[index].fS32 : (SkColor) scriptValue.fOperand.fS32; + storage[0].fScalar = SkIntToScalar(SkColorGetA(color)); + storage[1].fScalar = SkIntToScalar(SkColorGetR(color)); + storage[2].fScalar = SkIntToScalar(SkColorGetG(color)); + storage[3].fScalar = SkIntToScalar(SkColorGetB(color)); + storage += 4; + } + } else if (SkDisplayType::IsStruct(NULL /* !!! maker*/, type)) { + if (scriptValue.fType != SkType_Array) + return true; // error + SkASSERT(sizeof(SkScalar) == sizeof(SkOperand)); // !!! no 64 bit pointer support yet + int count = scriptValue.fOperand.fArray->count(); + if (count > 0) { + SkASSERT(fCount == count); + memcpy(storage, scriptValue.fOperand.fArray->begin(), count * sizeof(SkOperand)); + } + } else if (scriptValue.fType == SkType_Array) { + SkASSERT(scriptValue.fOperand.fArray->getType() == type); + SkASSERT(scriptValue.fOperand.fArray->count() == getCount()); + memcpy(storage, scriptValue.fOperand.fArray->begin(), getCount() * sizeof(SkOperand)); + } else { + memcpy(storage, &scriptValue.fOperand, sizeof(SkOperand)); + } + return false; +} + + +//void SkMemberInfo::setValue(SkDisplayable* displayable, const char value[], const char name[]) const { +// void* valuePtr = (void*) ((char*) displayable + fOffset); +// switch (fType) { +// case SkType_Point3D: { +// static const char xyz[] = "x|y|z"; +// int index = find_one(xyz, name); +// SkASSERT(index >= 0); +// valuePtr = (void*) ((char*) valuePtr + index * sizeof(SkScalar)); +// } break; +// default: +// SkASSERT(0); +// } +// SkParse::FindScalar(value, (SkScalar*) valuePtr); +// displayable->dirty(); +//} + +#if SK_USE_CONDENSED_INFO == 0 + +// Find Nth memberInfo +const SkMemberInfo* SkMemberInfo::Find(const SkMemberInfo info[], int count, int* index) { + SkASSERT(*index >= 0); + if (info->fType == SkType_BaseClassInfo) { + const SkMemberInfo* inherited = (SkMemberInfo*) info->fName; + const SkMemberInfo* result = SkMemberInfo::Find(inherited, info->fCount, index); + if (result != NULL) + return result; + if (--count == 0) + return NULL; + info++; + } + SkASSERT(info->fName); + SkASSERT(info->fType != SkType_BaseClassInfo); + if (*index >= count) { + *index -= count; + return NULL; + } + return &info[*index]; +} + +// Find named memberinfo +const SkMemberInfo* SkMemberInfo::Find(const SkMemberInfo info[], int count, const char** matchPtr) { + const char* match = *matchPtr; + if (info->fType == SkType_BaseClassInfo) { + const SkMemberInfo* inherited = (SkMemberInfo*) info->fName; + const SkMemberInfo* result = SkMemberInfo::Find(inherited, info->fCount, matchPtr); + if (result != NULL) + return result; + if (--count == 0) + return NULL; + info++; + } + SkASSERT(info->fName); + SkASSERT(info->fType != SkType_BaseClassInfo); + int index = SkStrSearch(&info->fName, count, match, sizeof(*info)); + if (index < 0 || index >= count) + return NULL; + return &info[index]; +} + +const SkMemberInfo* SkMemberInfo::getInherited() const { + return (SkMemberInfo*) fName; +} + +#endif // SK_USE_CONDENSED_INFO == 0 + +#if 0 +bool SkMemberInfo::SetValue(void* valuePtr, const char value[], SkDisplayTypes type, + int count) { + switch (type) { + case SkType_Animate: + case SkType_BaseBitmap: + case SkType_Bitmap: + case SkType_Dash: + case SkType_Displayable: + case SkType_Drawable: + case SkType_Matrix: + case SkType_Path: + case SkType_Text: + case SkType_3D_Patch: + return false; // ref to object; caller must resolve + case SkType_MSec: { + SkParse::FindMSec(value, (SkMSec*) valuePtr); + } break; + case SkType_3D_Point: + case SkType_Point: + // case SkType_PointArray: + case SkType_ScalarArray: + SkParse::FindScalars(value, (SkScalar*) valuePtr, count); + break; + default: + SkASSERT(0); + } + return true; +} +#endif + + diff --git a/skia/animator/SkMemberInfo.h b/skia/animator/SkMemberInfo.h new file mode 100644 index 0000000..00dd7b6 --- /dev/null +++ b/skia/animator/SkMemberInfo.h @@ -0,0 +1,283 @@ +/* libs/graphics/animator/SkMemberInfo.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkMemberInfo_DEFINED +#define SkMemberInfo_DEFINED + +#if defined SK_BUILD_CONDENSED + #define SK_USE_CONDENSED_INFO 0 +#elif defined SK_BUILD_FOR_BREW + #define SK_USE_CONDENSED_INFO 1 /* required by BREW to handle its lack of writable globals */ +#else + #define SK_USE_CONDENSED_INFO 0 /* optional, but usually 1 unless Cary is testing something */ +#endif + +#include "SkDisplayType.h" +#include "SkScript.h" +#include "SkString.h" +#include "SkIntArray.h" + +class SkAnimateMaker; +class SkDisplayable; +class SkScriptEngine; + +// temporary hacks until name change is more complete +#define SkFloat SkScalar +#define SkInt SkS32 + +struct SkMemberInfo { + //!!! alternative: + // if fCount == 0, record is member property + // then fType can be type, so caller doesn't have to check +#if SK_USE_CONDENSED_INFO == 0 + const char* fName; // may be NULL for anonymous functions + size_t fOffset; // if negative, is index into member pointer table (for properties and functions) + SkDisplayTypes fType; + int fCount; // for properties, actual type (count is always assumed to be 1) +#else + unsigned char fName; + signed char fOffset; + unsigned char fType; + signed char fCount; +#endif + SkDisplayTypes arrayType() const { + SkASSERT(fType == SkType_Array); + return (SkDisplayTypes) fCount; // hack, but worth it? + } + int functionIndex() const { + SkASSERT(fType == SkType_MemberFunction); + return (signed) fOffset > 0 ? -1 + (int) fOffset : -1 - (int) fOffset; + } + bool getArrayValue(const SkDisplayable* displayable, int index, SkOperand* value) const; + int getCount() const { + return fType == SkType_MemberProperty || fType == SkType_Array || + fType == SkType_MemberFunction ? 1 : fCount; + } + const SkMemberInfo* getInherited() const; + size_t getSize(const SkDisplayable* ) const; + void getString(const SkDisplayable* , SkString** string) const; + SkDisplayTypes getType() const { + return fType == SkType_MemberProperty || fType == SkType_Array || + fType == SkType_MemberFunction ? (SkDisplayTypes) fCount : (SkDisplayTypes) fType; + } + void getValue(const SkDisplayable* , SkOperand values[], int count) const; + bool isEnum() const; + const char* mapEnums(const char* match, int* value) const; + void* memberData(const SkDisplayable* displayable) const { + SkASSERT(fType != SkType_MemberProperty && fType != SkType_MemberFunction); + return (void*) ((const char*) displayable + fOffset); + } + int propertyIndex() const { + SkASSERT(fType == SkType_MemberProperty); + return (signed) fOffset > 0 ? -1 + (int) fOffset : -1 - (int) fOffset; + } + SkDisplayTypes propertyType() const { + SkASSERT(fType == SkType_MemberProperty || fType == SkType_Array); + return (SkDisplayTypes) fCount; // hack, but worth it? + } + void setMemberData(SkDisplayable* displayable, const void* child, size_t size) const { + SkASSERT(fType != SkType_MemberProperty && fType != SkType_MemberFunction); + memcpy((char*) displayable + fOffset, child, size); + } + void setString(SkDisplayable* , SkString* ) const; + void setValue(SkDisplayable* , const SkOperand values[], int count) const; + bool setValue(SkAnimateMaker& , SkTDOperandArray* storage, + int storageOffset, int maxStorage, SkDisplayable* , + SkDisplayTypes outType, const char value[], size_t len) const; + bool setValue(SkAnimateMaker& , SkTDOperandArray* storage, + int storageOffset, int maxStorage, SkDisplayable* , + SkDisplayTypes outType, SkString& str) const; +// void setValue(SkDisplayable* , const char value[], const char name[]) const; + bool writeValue(SkDisplayable* displayable, SkTDOperandArray* arrayStorage, + int storageOffset, int maxStorage, void* untypedStorage, SkDisplayTypes outType, + SkScriptValue& scriptValue) const; +#if SK_USE_CONDENSED_INFO == 0 + static const SkMemberInfo* Find(const SkMemberInfo [], int count, int* index); + static const SkMemberInfo* Find(const SkMemberInfo [], int count, const char** name); +#else + static const SkMemberInfo* Find(SkDisplayTypes type, int* index); + static const SkMemberInfo* Find(SkDisplayTypes type, const char** name); +#endif + static size_t GetSize(SkDisplayTypes type); // size of simple types only +// static bool SetValue(void* value, const char* name, SkDisplayTypes , int count); +}; + +#define SK_MEMBER(_member, _type) \ + { #_member, SK_OFFSETOF(BASE_CLASS, _member), SkType_##_type, \ + sizeof(((BASE_CLASS*) 1)->_member) / sizeof(SkScalar) } + +#define SK_MEMBER_ALIAS(_member, _alias, _type) \ + { #_member, SK_OFFSETOF(BASE_CLASS, _alias), SkType_##_type, \ + sizeof(((BASE_CLASS*) 1)->_alias) / sizeof(SkScalar) } + +#define SK_MEMBER_ARRAY(_member, _type) \ + { #_member, SK_OFFSETOF(BASE_CLASS, _member), SkType_Array, \ + (int) SkType_##_type } + +#define SK_MEMBER_INHERITED \ + { (const char*) INHERITED::fInfo, 0, SkType_BaseClassInfo, INHERITED::fInfoCount } + +// #define SK_MEMBER_KEY_TYPE(_member, _type) +// {#_member, (size_t) -1, SkType_##_type, 0} + +#define SK_FUNCTION(_member) \ + k_##_member##Function + +#define SK_PROPERTY(_member) \ + k_##_member##Property + +#define SK_MEMBER_DYNAMIC_FUNCTION(_member, _type) \ + {#_member, (size_t) (+1 + SK_FUNCTION(_member)), SkType_MemberFunction, \ + (int) SkType_##_type } + +#define SK_MEMBER_DYNAMIC_PROPERTY(_member, _type) \ + {#_member, (size_t) (1 + SK_PROPERTY(_member)), SkType_MemberProperty, \ + (int) SkType_##_type } + +#define SK_MEMBER_FUNCTION(_member, _type) \ + {#_member, (size_t) (-1 - SK_FUNCTION(_member)), SkType_MemberFunction, \ + (int) SkType_##_type } + +#define SK_MEMBER_PROPERTY(_member, _type) \ + {#_member, (size_t) (-1 - SK_PROPERTY(_member)), SkType_MemberProperty, \ + (int) SkType_##_type } + +#if SK_USE_CONDENSED_INFO == 0 + +#define DECLARE_PRIVATE_MEMBER_INFO(_type) \ +public: \ + static const SkMemberInfo fInfo[]; \ + static const int fInfoCount; \ + virtual const SkMemberInfo* getMember(int index); \ + virtual const SkMemberInfo* getMember(const char name[]); \ + typedef Sk##_type BASE_CLASS + +#define DECLARE_MEMBER_INFO(_type) \ +public: \ + static const SkMemberInfo fInfo[]; \ + static const int fInfoCount; \ + virtual const SkMemberInfo* getMember(int index); \ + virtual const SkMemberInfo* getMember(const char name[]); \ + virtual SkDisplayTypes getType() const { return SkType_##_type; } \ + typedef Sk##_type BASE_CLASS + +#define DECLARE_DRAW_MEMBER_INFO(_type) \ +public: \ + static const SkMemberInfo fInfo[]; \ + static const int fInfoCount; \ + virtual const SkMemberInfo* getMember(int index); \ + virtual const SkMemberInfo* getMember(const char name[]); \ + virtual SkDisplayTypes getType() const { return SkType_##_type; } \ + typedef SkDraw##_type BASE_CLASS + +#define DECLARE_DISPLAY_MEMBER_INFO(_type) \ +public: \ + static const SkMemberInfo fInfo[]; \ + static const int fInfoCount; \ + virtual const SkMemberInfo* getMember(int index); \ + virtual const SkMemberInfo* getMember(const char name[]); \ + virtual SkDisplayTypes getType() const { return SkType_##_type; } \ + typedef SkDisplay##_type BASE_CLASS + +#define DECLARE_EMPTY_MEMBER_INFO(_type) \ +public: \ + virtual SkDisplayTypes getType() const { return SkType_##_type; } + +#define DECLARE_EXTRAS_MEMBER_INFO(_type) \ +public: \ + static const SkMemberInfo fInfo[]; \ + static const int fInfoCount; \ + virtual const SkMemberInfo* getMember(int index); \ + virtual const SkMemberInfo* getMember(const char name[]); \ + SkDisplayTypes fType; \ + virtual SkDisplayTypes getType() const { return fType; } \ + typedef _type BASE_CLASS + +#define DECLARE_NO_VIRTUALS_MEMBER_INFO(_type) \ +public: \ + static const SkMemberInfo fInfo[]; \ + static const int fInfoCount; \ + typedef Sk##_type BASE_CLASS + +#define DEFINE_GET_MEMBER(_class) \ + const SkMemberInfo* _class::getMember(int index) { \ + const SkMemberInfo* result = SkMemberInfo::Find(fInfo, SK_ARRAY_COUNT(fInfo), &index); \ + return result; \ + } \ + const SkMemberInfo* _class::getMember(const char name[]) { \ + const SkMemberInfo* result = SkMemberInfo::Find(fInfo, SK_ARRAY_COUNT(fInfo), &name); \ + return result; \ + } \ + const int _class::fInfoCount = SK_ARRAY_COUNT(fInfo) + +#define DEFINE_NO_VIRTUALS_GET_MEMBER(_class) \ + const int _class::fInfoCount = SK_ARRAY_COUNT(fInfo) + +#else + +#define DECLARE_PRIVATE_MEMBER_INFO(_type) \ +public: \ + typedef Sk##_type BASE_CLASS + +#define DECLARE_MEMBER_INFO(_type) \ +public: \ + virtual const SkMemberInfo* getMember(int index) { \ + return SkDisplayType::GetMember(NULL, SkType_##_type, &index); } \ + virtual const SkMemberInfo* getMember(const char name[]) { \ + return SkDisplayType::GetMember(NULL, SkType_##_type, &name); } \ + virtual SkDisplayTypes getType() const { return SkType_##_type; } \ + typedef Sk##_type BASE_CLASS + +#define DECLARE_DRAW_MEMBER_INFO(_type) \ +public: \ + virtual const SkMemberInfo* getMember(int index) { \ + return SkDisplayType::GetMember(NULL, SkType_##_type, &index); } \ + virtual const SkMemberInfo* getMember(const char name[]) { \ + return SkDisplayType::GetMember(NULL, SkType_##_type, &name); } \ + virtual SkDisplayTypes getType() const { return SkType_##_type; } \ + typedef SkDraw##_type BASE_CLASS + +#define DECLARE_DISPLAY_MEMBER_INFO(_type) \ +public: \ + virtual const SkMemberInfo* getMember(int index) { \ + return SkDisplayType::GetMember(NULL, SkType_##_type, &index); } \ + virtual const SkMemberInfo* getMember(const char name[]) { \ + return SkDisplayType::GetMember(NULL, SkType_##_type, &name); } \ + virtual SkDisplayTypes getType() const { return SkType_##_type; } \ + typedef SkDisplay##_type BASE_CLASS + +#define DECLARE_EXTRAS_MEMBER_INFO(_type) \ +public: \ + virtual const SkMemberInfo* getMember(int index) { \ + return SkDisplayType::GetMember(NULL, SkType_##_type, &index); } \ + virtual const SkMemberInfo* getMember(const char name[]) { \ + return SkDisplayType::GetMember(NULL, fType, &name); } \ + SkDisplayTypes fType; \ + virtual SkDisplayTypes getType() const { return fType; } \ + typedef _type BASE_CLASS + +#define DECLARE_NO_VIRTUALS_MEMBER_INFO(_type) \ +public: \ + typedef Sk##_type BASE_CLASS + +#define DEFINE_GET_MEMBER(_class) +#define DEFINE_NO_VIRTUALS_GET_MEMBER(_class) + +#endif + +#endif // SkMemberInfo_DEFINED + diff --git a/skia/animator/SkOpArray.cpp b/skia/animator/SkOpArray.cpp new file mode 100644 index 0000000..eb3fcec --- /dev/null +++ b/skia/animator/SkOpArray.cpp @@ -0,0 +1,16 @@ +#include "SkOpArray.h" + +SkOpArray::SkOpArray() : fType(SkOperand2::kNoType) { +} + +SkOpArray::SkOpArray(SkOperand2::OpType type) : fType(type) { +} + +bool SkOpArray::getIndex(int index, SkOperand2* operand) { + if (index >= count()) { + SkASSERT(0); + return false; + } + *operand = begin()[index]; + return true; +} diff --git a/skia/animator/SkOpArray.h b/skia/animator/SkOpArray.h new file mode 100644 index 0000000..5c511c0 --- /dev/null +++ b/skia/animator/SkOpArray.h @@ -0,0 +1,22 @@ +#ifndef SkOpArray_DEFINED +#define SkOpArray_DEFINED + +#include "SkOperand2.h" +#include "SkTDArray_Experimental.h" + +typedef SkLongArray(SkOperand2) SkTDOperand2Array; + +class SkOpArray : public SkTDOperand2Array { +public: + SkOpArray(); + SkOpArray(SkOperand2::OpType type); + bool getIndex(int index, SkOperand2* operand); + SkOperand2::OpType getType() { return fType; } + void setType(SkOperand2::OpType type) { + fType = type; + } +protected: + SkOperand2::OpType fType; +}; + +#endif // SkOpArray_DEFINED diff --git a/skia/animator/SkOperand.h b/skia/animator/SkOperand.h new file mode 100644 index 0000000..65ee592 --- /dev/null +++ b/skia/animator/SkOperand.h @@ -0,0 +1,54 @@ +/* libs/graphics/animator/SkOperand.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkOperand_DEFINED +#define SkOperand_DEFINED + +#include "SkDisplayType.h" + +class SkTypedArray; +class SkDisplayable; +class SkDrawable; +class SkString; + +union SkOperand { +// SkOperand() {} +// SkOperand(SkScalar scalar) : fScalar(scalar) {} + SkTypedArray* fArray; + SkDisplayable* fDisplayable; + SkDrawable* fDrawable; + void* fObject; + int32_t fS32; + SkMSec fMSec; + SkScalar fScalar; + SkString* fString; +}; + +struct SkScriptValue { + SkOperand fOperand; + SkDisplayTypes fType; + SkTypedArray* getArray() { SkASSERT(fType == SkType_Array); return fOperand.fArray; } + SkDisplayable* getDisplayable() { SkASSERT(fType == SkType_Displayable); return fOperand.fDisplayable; } + SkDrawable* getDrawable() { SkASSERT(fType == SkType_Drawable); return fOperand.fDrawable; } + int32_t getS32(SkAnimateMaker* maker) { SkASSERT(fType == SkType_Int || fType == SkType_Boolean || + SkDisplayType::IsEnum(maker, fType)); return fOperand.fS32; } + SkMSec getMSec() { SkASSERT(fType == SkType_MSec); return fOperand.fMSec; } + SkScalar getScalar() { SkASSERT(fType == SkType_Float); return fOperand.fScalar; } + SkString* getString() { SkASSERT(fType == SkType_String); return fOperand.fString; } +}; + +#endif // SkOperand_DEFINED diff --git a/skia/animator/SkOperand2.h b/skia/animator/SkOperand2.h new file mode 100644 index 0000000..f482e66 --- /dev/null +++ b/skia/animator/SkOperand2.h @@ -0,0 +1,47 @@ +#ifndef SkOperand2_DEFINED +#define SkOperand2_DEFINED + +#include "SkScalar.h" + +class SkOpArray; +class SkString; + +union SkOperand2 { + enum OpType { + kNoType, + kS32 = 1, + kScalar = 2, + kString = 4, + kArray = 8, + kObject = 16 + }; + SkOpArray* fArray; + void* fObject; + size_t fReference; + int32_t fS32; + SkScalar fScalar; + SkString* fString; +}; + +struct SkScriptValue2 { + enum IsConstant { + kConstant, + kVariable + }; + enum IsWritten { + kUnwritten, + kWritten + }; + SkOperand2 fOperand; + SkOperand2::OpType fType : 8; + IsConstant fIsConstant : 8; + IsWritten fIsWritten : 8; + SkOpArray* getArray() { SkASSERT(fType == SkOperand2::kArray); return fOperand.fArray; } + void* getObject() { SkASSERT(fType == SkOperand2::kObject); return fOperand.fObject; } + int32_t getS32() { SkASSERT(fType == SkOperand2::kS32); return fOperand.fS32; } + SkScalar getScalar() { SkASSERT(fType == SkOperand2::kScalar); return fOperand.fScalar; } + SkString* getString() { SkASSERT(fType == SkOperand2::kString); return fOperand.fString; } + bool isConstant() const { return fIsConstant == kConstant; } +}; + +#endif // SkOperand2_DEFINED diff --git a/skia/animator/SkOperandInterpolator.h b/skia/animator/SkOperandInterpolator.h new file mode 100644 index 0000000..d623475 --- /dev/null +++ b/skia/animator/SkOperandInterpolator.h @@ -0,0 +1,56 @@ +/* libs/graphics/animator/SkOperandInterpolator.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkOperandInterpolator_DEFINED +#define SkOperandInterpolator_DEFINED + +#include "SkDisplayType.h" +#include "SkInterpolator.h" +#include "SkOperand.h" + +class SkOperandInterpolator : public SkInterpolatorBase { +public: + SkOperandInterpolator(); + SkOperandInterpolator(int elemCount, int frameCount, SkDisplayTypes type); + SkOperand* getValues() { return fValues; } + int getValuesCount() { return fFrameCount * fElemCount; } + void reset(int elemCount, int frameCount, SkDisplayTypes type); + + /** Add or replace a key frame, copying the values[] data into the interpolator. + @param index The index of this frame (frames must be ordered by time) + @param time The millisecond time for this frame + @param values The array of values [elemCount] for this frame. The data is copied + into the interpolator. + @param blend A positive scalar specifying how to blend between this and the next key frame. + [0...1) is a cubic lag/log/lag blend (slow to change at the beginning and end) + 1 is a linear blend (default) + (1...inf) is a cubic log/lag/log blend (fast to change at the beginning and end) + */ + bool setKeyFrame(int index, SkMSec time, const SkOperand values[], SkScalar blend = SK_Scalar1); + Result timeToValues(SkMSec time, SkOperand values[]) const; + SkDEBUGCODE(static void UnitTest();) +private: + SkDisplayTypes fType; + SkOperand* fValues; // pointer into fStorage +#ifdef SK_DEBUG + SkOperand(* fValuesArray)[10]; +#endif + typedef SkInterpolatorBase INHERITED; +}; + +#endif // SkOperandInterpolator_DEFINED + diff --git a/skia/animator/SkOperandIterpolator.cpp b/skia/animator/SkOperandIterpolator.cpp new file mode 100644 index 0000000..0204221 --- /dev/null +++ b/skia/animator/SkOperandIterpolator.cpp @@ -0,0 +1,159 @@ +/* libs/graphics/animator/SkOperandIterpolator.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkOperandInterpolator.h" +#include "SkScript.h" + +SkOperandInterpolator::SkOperandInterpolator() { + INHERITED::reset(0, 0); + fType = SkType_Unknown; +} + +SkOperandInterpolator::SkOperandInterpolator(int elemCount, int frameCount, + SkDisplayTypes type) +{ + this->reset(elemCount, frameCount, type); +} + +void SkOperandInterpolator::reset(int elemCount, int frameCount, SkDisplayTypes type) +{ +// SkASSERT(type == SkType_String || type == SkType_Float || type == SkType_Int || +// type == SkType_Displayable || type == SkType_Drawable); + INHERITED::reset(elemCount, frameCount); + fType = type; + fStorage = sk_malloc_throw((sizeof(SkOperand) * elemCount + sizeof(SkTimeCode)) * frameCount); + fTimes = (SkTimeCode*) fStorage; + fValues = (SkOperand*) ((char*) fStorage + sizeof(SkTimeCode) * frameCount); +#ifdef SK_DEBUG + fTimesArray = (SkTimeCode(*)[10]) fTimes; + fValuesArray = (SkOperand(*)[10]) fValues; +#endif +} + +bool SkOperandInterpolator::setKeyFrame(int index, SkMSec time, const SkOperand values[], SkScalar blend) +{ + SkASSERT(values != NULL); + blend = SkScalarPin(blend, 0, SK_Scalar1); + + bool success = ~index == SkTSearch<SkMSec>(&fTimes->fTime, index, time, sizeof(SkTimeCode)); + SkASSERT(success); + if (success) { + SkTimeCode* timeCode = &fTimes[index]; + timeCode->fTime = time; + timeCode->fBlend[0] = SK_Scalar1 - blend; + timeCode->fBlend[1] = 0; + timeCode->fBlend[2] = 0; + timeCode->fBlend[3] = SK_Scalar1 - blend; + SkOperand* dst = &fValues[fElemCount * index]; + memcpy(dst, values, fElemCount * sizeof(SkOperand)); + } + return success; +} + +SkInterpolatorBase::Result SkOperandInterpolator::timeToValues(SkMSec time, SkOperand values[]) const +{ + SkScalar T; + int index; + SkBool exact; + Result result = timeToT(time, &T, &index, &exact); + if (values) + { + const SkOperand* nextSrc = &fValues[index * fElemCount]; + + if (exact) + memcpy(values, nextSrc, fElemCount * sizeof(SkScalar)); + else + { + SkASSERT(index > 0); + + const SkOperand* prevSrc = nextSrc - fElemCount; + + if (fType == SkType_Float || fType == SkType_3D_Point) { + for (int i = fElemCount - 1; i >= 0; --i) + values[i].fScalar = SkScalarInterp(prevSrc[i].fScalar, nextSrc[i].fScalar, T); + } else if (fType == SkType_Int || fType == SkType_MSec) { + for (int i = fElemCount - 1; i >= 0; --i) { + int32_t a = prevSrc[i].fS32; + int32_t b = nextSrc[i].fS32; + values[i].fS32 = a + SkScalarRound((b - a) * T); + } + } else + memcpy(values, prevSrc, sizeof(SkOperand) * fElemCount); + } + } + return result; +} + +/////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_DEBUG + +#ifdef SK_SUPPORT_UNITTEST + static SkOperand* iset(SkOperand array[3], int a, int b, int c) + { + array[0].fScalar = SkIntToScalar(a); + array[1].fScalar = SkIntToScalar(b); + array[2].fScalar = SkIntToScalar(c); + return array; + } +#endif + +void SkOperandInterpolator::UnitTest() +{ +#ifdef SK_SUPPORT_UNITTEST + SkOperandInterpolator inter(3, 2, SkType_Float); + SkOperand v1[3], v2[3], v[3], vv[3]; + Result result; + + inter.setKeyFrame(0, 100, iset(v1, 10, 20, 30), 0); + inter.setKeyFrame(1, 200, iset(v2, 110, 220, 330)); + + result = inter.timeToValues(0, v); + SkASSERT(result == kFreezeStart_Result); + SkASSERT(memcmp(v, v1, sizeof(v)) == 0); + + result = inter.timeToValues(99, v); + SkASSERT(result == kFreezeStart_Result); + SkASSERT(memcmp(v, v1, sizeof(v)) == 0); + + result = inter.timeToValues(100, v); + SkASSERT(result == kNormal_Result); + SkASSERT(memcmp(v, v1, sizeof(v)) == 0); + + result = inter.timeToValues(200, v); + SkASSERT(result == kNormal_Result); + SkASSERT(memcmp(v, v2, sizeof(v)) == 0); + + result = inter.timeToValues(201, v); + SkASSERT(result == kFreezeEnd_Result); + SkASSERT(memcmp(v, v2, sizeof(v)) == 0); + + result = inter.timeToValues(150, v); + SkASSERT(result == kNormal_Result); + SkASSERT(memcmp(v, iset(vv, 60, 120, 180), sizeof(v)) == 0); + + result = inter.timeToValues(125, v); + SkASSERT(result == kNormal_Result); + result = inter.timeToValues(175, v); + SkASSERT(result == kNormal_Result); +#endif +} + +#endif + + diff --git a/skia/animator/SkPaintParts.cpp b/skia/animator/SkPaintParts.cpp new file mode 100644 index 0000000..a6c0fa1 --- /dev/null +++ b/skia/animator/SkPaintParts.cpp @@ -0,0 +1,111 @@ +/* libs/graphics/animator/SkPaintParts.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkPaintParts.h" +#include "SkDrawPaint.h" +#ifdef SK_DUMP_ENABLED +#include "SkDisplayList.h" +#include "SkDump.h" +#endif + +SkPaintPart::SkPaintPart() : fPaint(NULL) { +} + +SkDisplayable* SkPaintPart::getParent() const { + return fPaint; +} + +bool SkPaintPart::setParent(SkDisplayable* parent) { + SkASSERT(parent != NULL); + if (parent->isPaint() == false) + return true; + fPaint = (SkDrawPaint*) parent; + return false; +} + + +// SkDrawMaskFilter +bool SkDrawMaskFilter::add() { + if (fPaint->maskFilter != (SkDrawMaskFilter*) -1) + return true; + fPaint->maskFilter = this; + fPaint->fOwnsMaskFilter = true; + return false; +} + +SkMaskFilter* SkDrawMaskFilter::getMaskFilter() { + return NULL; +} + + +// SkDrawPathEffect +bool SkDrawPathEffect::add() { + if (fPaint->isPaint()) { + if (fPaint->pathEffect != (SkDrawPathEffect*) -1) + return true; + fPaint->pathEffect = this; + fPaint->fOwnsPathEffect = true; + return false; + } + fPaint->add(*(SkAnimateMaker*) NULL, this); + return false; +} + +SkPathEffect* SkDrawPathEffect::getPathEffect() { + return NULL; +} + + +// SkDrawShader +SkShader* SkDrawShader::getShader() { + return NULL; +} + + +// Typeface +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkDrawTypeface::fInfo[] = { + SK_MEMBER(fontName, String), + SK_MEMBER(style, FontStyle) +}; + +#endif + +DEFINE_GET_MEMBER(SkDrawTypeface); + +SkDrawTypeface::SkDrawTypeface() : style (SkTypeface::kNormal){ +} + +bool SkDrawTypeface::add() { + if (fPaint->typeface != (SkDrawTypeface*) -1) + return true; + fPaint->typeface = this; + fPaint->fOwnsTypeface = true; + return false; +} + +#ifdef SK_DUMP_ENABLED +void SkDrawTypeface::dump(SkAnimateMaker* maker) { + SkDebugf("%*s<typeface fontName=\"%s\" ", SkDisplayList::fIndent, "", fontName.c_str()); + SkString string; + SkDump::GetEnumString(SkType_FontStyle, style, &string); + SkDebugf("style=\"%s\" />\n", string.c_str()); +} +#endif + + diff --git a/skia/animator/SkPaintParts.h b/skia/animator/SkPaintParts.h new file mode 100644 index 0000000..615a028 --- /dev/null +++ b/skia/animator/SkPaintParts.h @@ -0,0 +1,83 @@ +/* libs/graphics/animator/SkPaintParts.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkPaintParts_DEFINED +#define SkPaintParts_DEFINED + +#include "SkDisplayable.h" +#include "SkMemberInfo.h" +#include "SkPaint.h" +#include "SkShader.h" +#include "SkTypeface.h" +#include "SkXfermode.h" + +class SkDrawPaint; +class SkDrawMatrix; + +class SkPaintPart : public SkDisplayable { +public: + SkPaintPart(); + virtual bool add() = 0; + virtual SkDisplayable* getParent() const; + virtual bool setParent(SkDisplayable* parent); +#ifdef SK_DEBUG + virtual bool isPaintPart() const { return true; } +#endif +protected: + SkDrawPaint* fPaint; +}; + +class SkDrawMaskFilter : public SkPaintPart { + DECLARE_EMPTY_MEMBER_INFO(MaskFilter); + virtual SkMaskFilter* getMaskFilter(); +protected: + virtual bool add(); +}; + +class SkDrawPathEffect : public SkPaintPart { + DECLARE_EMPTY_MEMBER_INFO(PathEffect); + virtual SkPathEffect* getPathEffect(); +protected: + virtual bool add(); +}; + +class SkDrawShader : public SkPaintPart { + DECLARE_DRAW_MEMBER_INFO(Shader); + SkDrawShader(); + virtual SkShader* getShader(); +protected: + virtual bool add(); + void addPostlude(SkShader* shader); + SkDrawMatrix* matrix; + int /*SkShader::TileMode*/ tileMode; +}; + +class SkDrawTypeface : public SkPaintPart { + DECLARE_DRAW_MEMBER_INFO(Typeface); + SkDrawTypeface(); +#ifdef SK_DUMP_ENABLED + virtual void dump(SkAnimateMaker *); +#endif + SkTypeface* getTypeface() { + return SkTypeface::Create(fontName.c_str(), style); } +protected: + virtual bool add(); + SkString fontName; + SkTypeface::Style style; +}; + +#endif // SkPaintParts_DEFINED diff --git a/skia/animator/SkPathParts.cpp b/skia/animator/SkPathParts.cpp new file mode 100644 index 0000000..e015d66 --- /dev/null +++ b/skia/animator/SkPathParts.cpp @@ -0,0 +1,328 @@ +/* libs/graphics/animator/SkPathParts.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkPathParts.h" +#include "SkAnimateMaker.h" +#include "SkDrawMatrix.h" +#include "SkDrawRectangle.h" +#include "SkDrawPath.h" + +SkPathPart::SkPathPart() : fPath(NULL) { +} + +void SkPathPart::dirty() { + fPath->dirty(); +} + +SkDisplayable* SkPathPart::getParent() const { + return fPath; +} + +bool SkPathPart::setParent(SkDisplayable* parent) { + SkASSERT(parent != NULL); + if (parent->isPath() == false) + return true; + fPath = (SkDrawPath*) parent; + return false; +} + +// MoveTo +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkMoveTo::fInfo[] = { + SK_MEMBER(x, Float), + SK_MEMBER(y, Float) +}; + +#endif + +DEFINE_GET_MEMBER(SkMoveTo); + +SkMoveTo::SkMoveTo() : x(0), y(0) { +} + +bool SkMoveTo::add() { + fPath->fPath.moveTo(x, y); + return false; +} + + +// RMoveTo +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkRMoveTo::fInfo[] = { + SK_MEMBER_INHERITED +}; + +#endif + +DEFINE_GET_MEMBER(SkRMoveTo); + +bool SkRMoveTo::add() { + fPath->fPath.rMoveTo(x, y); + return false; +} + + +// LineTo +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkLineTo::fInfo[] = { + SK_MEMBER(x, Float), + SK_MEMBER(y, Float) +}; + +#endif + +DEFINE_GET_MEMBER(SkLineTo); + +SkLineTo::SkLineTo() : x(0), y(0) { +} + +bool SkLineTo::add() { + fPath->fPath.lineTo(x, y); + return false; +} + + +// RLineTo +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkRLineTo::fInfo[] = { + SK_MEMBER_INHERITED +}; + +#endif + +DEFINE_GET_MEMBER(SkRLineTo); + +bool SkRLineTo::add() { + fPath->fPath.rLineTo(x, y); + return false; +} + + +// QuadTo +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkQuadTo::fInfo[] = { + SK_MEMBER(x1, Float), + SK_MEMBER(x2, Float), + SK_MEMBER(y1, Float), + SK_MEMBER(y2, Float) +}; + +#endif + +DEFINE_GET_MEMBER(SkQuadTo); + +SkQuadTo::SkQuadTo() : x1(0), y1(0), x2(0), y2(0) { +} + +bool SkQuadTo::add() { + fPath->fPath.quadTo(x1, y1, x2, y2); + return false; +} + + +// RQuadTo +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkRQuadTo::fInfo[] = { + SK_MEMBER_INHERITED +}; + +#endif + +DEFINE_GET_MEMBER(SkRQuadTo); + +bool SkRQuadTo::add() { + fPath->fPath.rQuadTo(x1, y1, x2, y2); + return false; +} + + +// CubicTo +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkCubicTo::fInfo[] = { + SK_MEMBER(x1, Float), + SK_MEMBER(x2, Float), + SK_MEMBER(x3, Float), + SK_MEMBER(y1, Float), + SK_MEMBER(y2, Float), + SK_MEMBER(y3, Float) +}; + +#endif + +DEFINE_GET_MEMBER(SkCubicTo); + +SkCubicTo::SkCubicTo() : x1(0), y1(0), x2(0), y2(0), x3(0), y3(0) { +} + +bool SkCubicTo::add() { + fPath->fPath.cubicTo(x1, y1, x2, y2, x3, y3); + return false; +} + + +// RCubicTo +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkRCubicTo::fInfo[] = { + SK_MEMBER_INHERITED +}; + +#endif + +DEFINE_GET_MEMBER(SkRCubicTo); + +bool SkRCubicTo::add() { + fPath->fPath.rCubicTo(x1, y1, x2, y2, x3, y3); + return false; +} + + +// SkClose +bool SkClose::add() { + fPath->fPath.close(); + return false; +} + + +// SkAddGeom +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkAddGeom::fInfo[] = { + SK_MEMBER(direction, PathDirection) +}; + +#endif + +DEFINE_GET_MEMBER(SkAddGeom); + +SkAddGeom::SkAddGeom() : direction(SkPath::kCCW_Direction) { +} + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkAddRect::fInfo[] = { + SK_MEMBER_INHERITED, + SK_MEMBER_ALIAS(bottom, fRect.fBottom, Float), + SK_MEMBER_ALIAS(left, fRect.fLeft, Float), + SK_MEMBER_ALIAS(right, fRect.fRight, Float), + SK_MEMBER_ALIAS(top, fRect.fTop, Float) +}; + +#endif + +DEFINE_GET_MEMBER(SkAddRect); + +SkAddRect::SkAddRect() { + fRect.setEmpty(); +} + +bool SkAddRect::add() { + fPath->fPath.addRect(fRect, (SkPath::Direction) direction); + return false; +} + + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkAddOval::fInfo[] = { + SK_MEMBER_INHERITED +}; + +#endif + +DEFINE_GET_MEMBER(SkAddOval); + +bool SkAddOval::add() { + fPath->fPath.addOval(fRect, (SkPath::Direction) direction); + return false; +} + + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkAddCircle::fInfo[] = { + SK_MEMBER_INHERITED, + SK_MEMBER(radius, Float), + SK_MEMBER(x, Float), + SK_MEMBER(y, Float) +}; + +#endif + +DEFINE_GET_MEMBER(SkAddCircle); + +SkAddCircle::SkAddCircle() : radius(0), x(0), y(0) { +} + +bool SkAddCircle::add() { + fPath->fPath.addCircle(x, y, radius, (SkPath::Direction) direction); + return false; +} + + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkAddRoundRect::fInfo[] = { + SK_MEMBER_INHERITED, + SK_MEMBER(rx, Float), + SK_MEMBER(ry, Float) +}; + +#endif + +DEFINE_GET_MEMBER(SkAddRoundRect); + +SkAddRoundRect::SkAddRoundRect() : rx(0), ry(0) { +} + +bool SkAddRoundRect::add() { + fPath->fPath.addRoundRect(fRect, rx, ry, (SkPath::Direction) direction); + return false; +} + + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkAddPath::fInfo[] = { + SK_MEMBER(matrix, Matrix), + SK_MEMBER(path, Path) +}; + +#endif + +DEFINE_GET_MEMBER(SkAddPath); + +SkAddPath::SkAddPath() : matrix(NULL), path(NULL) { +} + +bool SkAddPath::add() { + SkASSERT (path != NULL); + if (matrix) + fPath->fPath.addPath(path->fPath, matrix->getMatrix()); + else + fPath->fPath.addPath(path->fPath); + return false; +} + + diff --git a/skia/animator/SkPathParts.h b/skia/animator/SkPathParts.h new file mode 100644 index 0000000..4af0295b --- /dev/null +++ b/skia/animator/SkPathParts.h @@ -0,0 +1,173 @@ +/* libs/graphics/animator/SkPathParts.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkPathParts_DEFINED +#define SkPathParts_DEFINED + +#include "SkDisplayable.h" +#include "SkMemberInfo.h" +#include "SkPath.h" + +class SkDrawPath; +class SkDrawMatrix; + +class SkPathPart : public SkDisplayable { +public: + SkPathPart(); + virtual bool add() = 0; + virtual void dirty(); + virtual SkDisplayable* getParent() const; + virtual bool setParent(SkDisplayable* parent); +#ifdef SK_DEBUG + virtual bool isPathPart() const { return true; } +#endif +protected: + SkDrawPath* fPath; +}; + +class SkMoveTo : public SkPathPart { + DECLARE_MEMBER_INFO(MoveTo); + SkMoveTo(); + virtual bool add(); +protected: + SkScalar x; + SkScalar y; +}; + +class SkRMoveTo : public SkMoveTo { + DECLARE_MEMBER_INFO(RMoveTo); + virtual bool add(); +private: + typedef SkMoveTo INHERITED; +}; + +class SkLineTo : public SkPathPart { + DECLARE_MEMBER_INFO(LineTo); + SkLineTo(); + virtual bool add(); +protected: + SkScalar x; + SkScalar y; +}; + +class SkRLineTo : public SkLineTo { + DECLARE_MEMBER_INFO(RLineTo); + virtual bool add(); +private: + typedef SkLineTo INHERITED; +}; + +class SkQuadTo : public SkPathPart { + DECLARE_MEMBER_INFO(QuadTo); + SkQuadTo(); + virtual bool add(); +protected: + SkScalar x1; + SkScalar y1; + SkScalar x2; + SkScalar y2; +}; + +class SkRQuadTo : public SkQuadTo { + DECLARE_MEMBER_INFO(RQuadTo); + virtual bool add(); +private: + typedef SkQuadTo INHERITED; +}; + +class SkCubicTo : public SkPathPart { + DECLARE_MEMBER_INFO(CubicTo); + SkCubicTo(); + virtual bool add(); +protected: + SkScalar x1; + SkScalar y1; + SkScalar x2; + SkScalar y2; + SkScalar x3; + SkScalar y3; +}; + +class SkRCubicTo : public SkCubicTo { + DECLARE_MEMBER_INFO(RCubicTo); + virtual bool add(); +private: + typedef SkCubicTo INHERITED; +}; + +class SkClose : public SkPathPart { + DECLARE_EMPTY_MEMBER_INFO(Close); + virtual bool add(); +}; + +class SkAddGeom : public SkPathPart { + DECLARE_PRIVATE_MEMBER_INFO(AddGeom); + SkAddGeom(); +protected: + int /*SkPath::Direction*/ direction; +}; + +class SkAddRect : public SkAddGeom { + DECLARE_MEMBER_INFO(AddRect); + SkAddRect(); + virtual bool add(); +protected: + SkRect fRect; +private: + typedef SkAddGeom INHERITED; +}; + +class SkAddOval : public SkAddRect { + DECLARE_MEMBER_INFO(AddOval); + virtual bool add(); +private: + typedef SkAddRect INHERITED; +}; + +class SkAddCircle : public SkAddGeom { + DECLARE_MEMBER_INFO(AddCircle); + SkAddCircle(); + virtual bool add(); +private: + SkScalar radius; + SkScalar x; + SkScalar y; + typedef SkAddGeom INHERITED; +}; + +class SkAddRoundRect : public SkAddRect { + DECLARE_MEMBER_INFO(AddRoundRect); + SkAddRoundRect(); + virtual bool add(); +private: + SkScalar rx; + SkScalar ry; + typedef SkAddRect INHERITED; +}; + +class SkAddPath : public SkPathPart { + DECLARE_MEMBER_INFO(AddPath); + SkAddPath(); + virtual bool add(); +private: + typedef SkPathPart INHERITED; + SkDrawMatrix* matrix; + SkDrawPath* path; +}; + +#endif // SkPathParts_DEFINED + diff --git a/skia/animator/SkPostParts.cpp b/skia/animator/SkPostParts.cpp new file mode 100644 index 0000000..b1469fc --- /dev/null +++ b/skia/animator/SkPostParts.cpp @@ -0,0 +1,65 @@ +/* libs/graphics/animator/SkPostParts.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkPostParts.h" +#include "SkDisplayPost.h" + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkData::fInfo[] = { + SK_MEMBER_INHERITED +}; + +#endif + +DEFINE_GET_MEMBER(SkData); + +SkData::SkData() : fParent(NULL) {} + +bool SkData::add() { + SkASSERT(name.size() > 0); + const char* dataName = name.c_str(); + if (fInt != (int) SK_NaN32) + fParent->fEvent.setS32(dataName, fInt); + else if (SkScalarIsNaN(fFloat) == false) + fParent->fEvent.setScalar(dataName, fFloat); + else if (string.size() > 0) + fParent->fEvent.setString(dataName, string); +// else +// SkASSERT(0); + return false; +} + +void SkData::dirty() { + fParent->dirty(); +} + +SkDisplayable* SkData::getParent() const { + return fParent; +} + +bool SkData::setParent(SkDisplayable* displayable) { + if (displayable->isPost() == false) + return true; + fParent = (SkPost*) displayable; + return false; +} + +void SkData::onEndElement(SkAnimateMaker&) { + add(); +} + diff --git a/skia/animator/SkPostParts.h b/skia/animator/SkPostParts.h new file mode 100644 index 0000000..f02b4b0 --- /dev/null +++ b/skia/animator/SkPostParts.h @@ -0,0 +1,39 @@ +/* libs/graphics/animator/SkPostParts.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkPostParts_DEFINED +#define SkPostParts_DEFINED + +#include "SkDisplayInput.h" + +class SkPost; + +class SkData: public SkInput { + DECLARE_MEMBER_INFO(Data); + SkData(); + bool add(); + virtual void dirty(); + virtual SkDisplayable* getParent() const; + virtual void onEndElement(SkAnimateMaker& ); + virtual bool setParent(SkDisplayable* ); +protected: + SkPost* fParent; + typedef SkInput INHERITED; + friend class SkPost; +}; + +#endif // SkPostParts_DEFINED diff --git a/skia/animator/SkSVGPath.cpp b/skia/animator/SkSVGPath.cpp new file mode 100644 index 0000000..e1c83c0 --- /dev/null +++ b/skia/animator/SkSVGPath.cpp @@ -0,0 +1,243 @@ +/* libs/graphics/animator/SkSVGPath.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include <ctype.h> +#include "SkDrawPath.h" +#include "SkParse.h" +#include "SkPoint.h" +#include "SkUtils.h" +#define QUADRATIC_APPROXIMATION 1 + +#if QUADRATIC_APPROXIMATION +//////////////////////////////////////////////////////////////////////////////////// +//functions to approximate a cubic using two quadratics + +// midPt sets the first argument to be the midpoint of the other two +// it is used by quadApprox +static inline void midPt(SkPoint& dest,const SkPoint& a,const SkPoint& b) +{ + dest.set(SkScalarAve(a.fX, b.fX),SkScalarAve(a.fY, b.fY)); +} +// quadApprox - makes an approximation, which we hope is faster +static void quadApprox(SkPath &fPath, const SkPoint &p0, const SkPoint &p1, const SkPoint &p2) +{ + //divide the cubic up into two cubics, then convert them into quadratics + //define our points + SkPoint c,j,k,l,m,n,o,p,q, mid; + fPath.getLastPt(&c); + midPt(j, p0, c); + midPt(k, p0, p1); + midPt(l, p1, p2); + midPt(o, j, k); + midPt(p, k, l); + midPt(q, o, p); + //compute the first half + m.set(SkScalarHalf(3*j.fX - c.fX), SkScalarHalf(3*j.fY - c.fY)); + n.set(SkScalarHalf(3*o.fX -q.fX), SkScalarHalf(3*o.fY - q.fY)); + midPt(mid,m,n); + fPath.quadTo(mid,q); + c = q; + //compute the second half + m.set(SkScalarHalf(3*p.fX - c.fX), SkScalarHalf(3*p.fY - c.fY)); + n.set(SkScalarHalf(3*l.fX -p2.fX),SkScalarHalf(3*l.fY -p2.fY)); + midPt(mid,m,n); + fPath.quadTo(mid,p2); +} +#endif + + +static inline bool is_between(int c, int min, int max) +{ + return (unsigned)(c - min) <= (unsigned)(max - min); +} + +static inline bool is_ws(int c) +{ + return is_between(c, 1, 32); +} + +static inline bool is_digit(int c) +{ + return is_between(c, '0', '9'); +} + +static inline bool is_sep(int c) +{ + return is_ws(c) || c == ','; +} + +static const char* skip_ws(const char str[]) +{ + SkASSERT(str); + while (is_ws(*str)) + str++; + return str; +} + +static const char* skip_sep(const char str[]) +{ + SkASSERT(str); + while (is_sep(*str)) + str++; + return str; +} + +static const char* find_points(const char str[], SkPoint value[], int count, + bool isRelative, SkPoint* relative) +{ + str = SkParse::FindScalars(str, &value[0].fX, count * 2); + if (isRelative) { + for (int index = 0; index < count; index++) { + value[index].fX += relative->fX; + value[index].fY += relative->fY; + } + } + return str; +} + +static const char* find_scalar(const char str[], SkScalar* value, + bool isRelative, SkScalar relative) +{ + str = SkParse::FindScalar(str, value); + if (isRelative) + *value += relative; + return str; +} + +void SkDrawPath::parseSVG() { + fPath.reset(); + const char* data = d.c_str(); + SkPoint f = {0, 0}; + SkPoint c = {0, 0}; + SkPoint lastc = {0, 0}; + SkPoint points[3]; + char op = '\0'; + char previousOp = '\0'; + bool relative = false; + do { + data = skip_ws(data); + if (data[0] == '\0') + break; + char ch = data[0]; + if (is_digit(ch) || ch == '-' || ch == '+') { + if (op == '\0') + return; + } + else { + op = ch; + relative = false; + if (islower(op)) { + op = (char) toupper(op); + relative = true; + } + data++; + data = skip_sep(data); + } + switch (op) { + case 'M': + data = find_points(data, points, 1, relative, &c); + fPath.moveTo(points[0]); + op = 'L'; + c = points[0]; + break; + case 'L': + data = find_points(data, points, 1, relative, &c); + fPath.lineTo(points[0]); + c = points[0]; + break; + case 'H': { + SkScalar x; + data = find_scalar(data, &x, relative, c.fX); + fPath.lineTo(x, c.fY); + c.fX = x; + } + break; + case 'V': { + SkScalar y; + data = find_scalar(data, &y, relative, c.fY); + fPath.lineTo(c.fX, y); + c.fY = y; + } + break; + case 'C': + data = find_points(data, points, 3, relative, &c); + goto cubicCommon; + case 'S': + data = find_points(data, &points[1], 2, relative, &c); + points[0] = c; + if (previousOp == 'C' || previousOp == 'S') { + points[0].fX -= lastc.fX - c.fX; + points[0].fY -= lastc.fY - c.fY; + } + cubicCommon: + // if (data[0] == '\0') + // return; +#if QUADRATIC_APPROXIMATION + quadApprox(fPath, points[0], points[1], points[2]); +#else //this way just does a boring, slow old cubic + fPath.cubicTo(points[0], points[1], points[2]); +#endif + //if we are using the quadApprox, lastc is what it would have been if we had used + //cubicTo + lastc = points[1]; + c = points[2]; + break; + case 'Q': // Quadratic Bezier Curve + data = find_points(data, points, 2, relative, &c); + goto quadraticCommon; + case 'T': + data = find_points(data, &points[1], 1, relative, &c); + points[0] = points[1]; + if (previousOp == 'Q' || previousOp == 'T') { + points[0].fX = c.fX * 2 - lastc.fX; + points[0].fY = c.fY * 2 - lastc.fY; + } + quadraticCommon: + fPath.quadTo(points[0], points[1]); + lastc = points[0]; + c = points[1]; + break; + case 'Z': + fPath.close(); +#if 0 // !!! still a bug? + if (fPath.isEmpty() && (f.fX != 0 || f.fY != 0)) { + c.fX -= SkScalar.Epsilon; // !!! enough? + fPath.moveTo(c); + fPath.lineTo(f); + fPath.close(); + } +#endif + c = f; + op = '\0'; + break; + case '~': { + SkPoint args[2]; + data = find_points(data, args, 2, false, NULL); + fPath.moveTo(args[0].fX, args[0].fY); + fPath.lineTo(args[1].fX, args[1].fY); + } + break; + default: + SkASSERT(0); + return; + } + if (previousOp == 0) + f = c; + previousOp = op; + } while (data[0] > 0); +} + diff --git a/skia/animator/SkScript.cpp b/skia/animator/SkScript.cpp new file mode 100644 index 0000000..c6c9b73 --- /dev/null +++ b/skia/animator/SkScript.cpp @@ -0,0 +1,1918 @@ +/* libs/graphics/animator/SkScript.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkScript.h" +#include "SkMath.h" +#include "SkParse.h" +#include "SkString.h" +#include "SkTypedArray.h" + +/* things to do + ? re-enable support for struct literals (e.g., for initializing points or rects) + {x:1, y:2} + ? use standard XML / script notation like document.getElementById("canvas"); + finish support for typed arrays + ? allow indexing arrays by string + this could map to the 'name' attribute of a given child of an array + ? allow multiple types in the array + remove SkDisplayType.h // from SkOperand.h + merge type and operand arrays into scriptvalue array +*/ + +#ifdef SK_DEBUG +static const char* errorStrings[] = { + "array index of out bounds", // kArrayIndexOutOfBounds + "could not find reference id", // kCouldNotFindReferencedID + "dot operator expects object", // kDotOperatorExpectsObject + "error in array index", // kErrorInArrrayIndex + "error in function parameters", // kErrorInFunctionParameters + "expected array", // kExpectedArray + "expected boolean expression", // kExpectedBooleanExpression + "expected field name", // kExpectedFieldName + "expected hex", // kExpectedHex + "expected int for condition operator", // kExpectedIntForConditionOperator + "expected number", // kExpectedNumber + "expected number for array index", // kExpectedNumberForArrayIndex + "expected operator", // kExpectedOperator + "expected token", // kExpectedToken + "expected token before dot operator", // kExpectedTokenBeforeDotOperator + "expected value", // kExpectedValue + "handle member failed", // kHandleMemberFailed + "handle member function failed", // kHandleMemberFunctionFailed + "handle unbox failed", // kHandleUnboxFailed + "index out of range", // kIndexOutOfRange + "mismatched array brace", // kMismatchedArrayBrace + "mismatched brackets", // kMismatchedBrackets + "no function handler found", // kNoFunctionHandlerFound + "premature end", // kPrematureEnd + "too many parameters", // kTooManyParameters + "type conversion failed", // kTypeConversionFailed + "unterminated string" // kUnterminatedString +}; +#endif + +const SkScriptEngine::SkOperatorAttributes SkScriptEngine::gOpAttributes[] = { + { kNoType, kNoType, kNoBias }, // kUnassigned, + { SkOpType(kInt | kScalar | kString), SkOpType(kInt | kScalar | kString), kTowardsString }, // kAdd + // kAddInt = kAdd, + { kNoType, kNoType, kNoBias }, // kAddScalar, + { kNoType, kNoType, kNoBias }, // kAddString, + { kNoType, kNoType, kNoBias }, // kArrayOp, + { kInt, kInt, kNoBias }, // kBitAnd + { kNoType, kInt, kNoBias }, // kBitNot + { kInt, kInt, kNoBias }, // kBitOr + { SkOpType(kInt | kScalar), SkOpType(kInt | kScalar), kNoBias }, // kDivide + // kDivideInt = kDivide + { kNoType, kNoType, kNoBias }, // kDivideScalar + { kNoType, kNoType, kNoBias }, // kElse + { SkOpType(kInt | kScalar | kString), SkOpType(kInt | kScalar | kString), kTowardsNumber }, // kEqual + // kEqualInt = kEqual + { kNoType, kNoType, kNoBias }, // kEqualScalar + { kNoType, kNoType, kNoBias }, // kEqualString + { kInt, kNoType, kNoBias }, // kFlipOps + { SkOpType(kInt | kScalar | kString), SkOpType(kInt | kScalar | kString), kTowardsNumber }, // kGreaterEqual + // kGreaterEqualInt = kGreaterEqual + { kNoType, kNoType, kNoBias }, // kGreaterEqualScalar + { kNoType, kNoType, kNoBias }, // kGreaterEqualString + { kNoType, kNoType, kNoBias }, // kIf + { kNoType, kInt, kNoBias }, // kLogicalAnd (really, ToBool) + { kNoType, kInt, kNoBias }, // kLogicalNot + { kInt, kInt, kNoBias }, // kLogicalOr + { kNoType, SkOpType(kInt | kScalar), kNoBias }, // kMinus + // kMinusInt = kMinus + { kNoType, kNoType, kNoBias }, // kMinusScalar + { SkOpType(kInt | kScalar), SkOpType(kInt | kScalar), kNoBias }, // kModulo + // kModuloInt = kModulo + { kNoType, kNoType, kNoBias }, // kModuloScalar + { SkOpType(kInt | kScalar), SkOpType(kInt | kScalar), kNoBias }, // kMultiply + // kMultiplyInt = kMultiply + { kNoType, kNoType, kNoBias }, // kMultiplyScalar + { kNoType, kNoType, kNoBias }, // kParen + { kInt, kInt, kNoBias }, // kShiftLeft + { kInt, kInt, kNoBias }, // kShiftRight + { SkOpType(kInt | kScalar), SkOpType(kInt | kScalar), kNoBias }, // kSubtract + // kSubtractInt = kSubtract + { kNoType, kNoType, kNoBias }, // kSubtractScalar + { kInt, kInt, kNoBias } // kXor +}; + +// Note that the real precedence for () [] is '2' +// but here, precedence means 'while an equal or smaller precedence than the current operator +// is on the stack, process it. This allows 3+5*2 to defer the add until after the multiply +// is preformed, since the add precedence is not smaller than multiply. +// But, (3*4 does not process the '(', since brackets are greater than all other precedences +#define kBracketPrecedence 16 +#define kIfElsePrecedence 15 + +const signed char SkScriptEngine::gPrecedence[] = { + -1, // kUnassigned, + 6, // kAdd, + // kAddInt = kAdd, + 6, // kAddScalar, + 6, // kAddString, // string concat + kBracketPrecedence, // kArrayOp, + 10, // kBitAnd, + 4, // kBitNot, + 12, // kBitOr, + 5, // kDivide, + // kDivideInt = kDivide, + 5, // kDivideScalar, + kIfElsePrecedence, // kElse, + 9, // kEqual, + // kEqualInt = kEqual, + 9, // kEqualScalar, + 9, // kEqualString, + -1, // kFlipOps, + 8, // kGreaterEqual, + // kGreaterEqualInt = kGreaterEqual, + 8, // kGreaterEqualScalar, + 8, // kGreaterEqualString, + kIfElsePrecedence, // kIf, + 13, // kLogicalAnd, + 4, // kLogicalNot, + 14, // kLogicalOr, + 4, // kMinus, + // kMinusInt = kMinus, + 4, // kMinusScalar, + 5, // kModulo, + // kModuloInt = kModulo, + 5, // kModuloScalar, + 5, // kMultiply, + // kMultiplyInt = kMultiply, + 5, // kMultiplyScalar, + kBracketPrecedence, // kParen, + 7, // kShiftLeft, + 7, // kShiftRight, // signed + 6, // kSubtract, + // kSubtractInt = kSubtract, + 6, // kSubtractScalar, + 11, // kXor +}; + +static inline bool is_between(int c, int min, int max) +{ + return (unsigned)(c - min) <= (unsigned)(max - min); +} + +static inline bool is_ws(int c) +{ + return is_between(c, 1, 32); +} + +static int token_length(const char* start) { + char ch = start[0]; + if (! is_between(ch, 'a' , 'z') && ! is_between(ch, 'A', 'Z') && ch != '_' && ch != '$') + return -1; + int length = 0; + do + ch = start[++length]; + while (is_between(ch, 'a' , 'z') || is_between(ch, 'A', 'Z') || is_between(ch, '0', '9') || + ch == '_' || ch == '$'); + return length; +} + +SkScriptEngine::SkScriptEngine(SkOpType returnType) : + fTokenLength(0), fReturnType(returnType), fError(kNoError) +{ + SkSuppress noInitialSuppress; + noInitialSuppress.fOperator = kUnassigned; + noInitialSuppress.fOpStackDepth = 0; + noInitialSuppress.fSuppress = false; + fSuppressStack.push(noInitialSuppress); + *fOpStack.push() = kParen; + fTrackArray.appendClear(); + fTrackString.appendClear(); +} + +SkScriptEngine::~SkScriptEngine() { + for (SkString** stringPtr = fTrackString.begin(); stringPtr < fTrackString.end(); stringPtr++) + delete *stringPtr; + for (SkTypedArray** arrayPtr = fTrackArray.begin(); arrayPtr < fTrackArray.end(); arrayPtr++) + delete *arrayPtr; +} + +int SkScriptEngine::arithmeticOp(char ch, char nextChar, bool lastPush) { + SkOp op = kUnassigned; + bool reverseOperands = false; + bool negateResult = false; + int advance = 1; + switch (ch) { + case '+': + // !!! ignoring unary plus as implemented here has the side effect of + // suppressing errors like +"hi" + if (lastPush == false) // unary plus, don't push an operator + goto returnAdv; + op = kAdd; + break; + case '-': + op = lastPush ? kSubtract : kMinus; + break; + case '*': + op = kMultiply; + break; + case '/': + op = kDivide; + break; + case '>': + if (nextChar == '>') { + op = kShiftRight; + goto twoChar; + } + op = kGreaterEqual; + if (nextChar == '=') + goto twoChar; + reverseOperands = negateResult = true; + break; + case '<': + if (nextChar == '<') { + op = kShiftLeft; + goto twoChar; + } + op = kGreaterEqual; + reverseOperands = nextChar == '='; + negateResult = ! reverseOperands; + advance += reverseOperands; + break; + case '=': + if (nextChar == '=') { + op = kEqual; + goto twoChar; + } + break; + case '!': + if (nextChar == '=') { + op = kEqual; + negateResult = true; +twoChar: + advance++; + break; + } + op = kLogicalNot; + break; + case '?': + op = kIf; + break; + case ':': + op = kElse; + break; + case '^': + op = kXor; + break; + case '(': + *fOpStack.push() = kParen; // push even if eval is suppressed + goto returnAdv; + case '&': + SkASSERT(nextChar != '&'); + op = kBitAnd; + break; + case '|': + SkASSERT(nextChar != '|'); + op = kBitOr; + break; + case '%': + op = kModulo; + break; + case '~': + op = kBitNot; + break; + } + if (op == kUnassigned) + return 0; + if (fSuppressStack.top().fSuppress == false) { + signed char precedence = gPrecedence[op]; + do { + int idx = 0; + SkOp compare; + do { + compare = fOpStack.index(idx); + if ((compare & kArtificialOp) == 0) + break; + idx++; + } while (true); + signed char topPrecedence = gPrecedence[compare]; + SkASSERT(topPrecedence != -1); + if (topPrecedence > precedence || topPrecedence == precedence && + gOpAttributes[op].fLeftType == kNoType) { + break; + } + if (processOp() == false) + return 0; // error + } while (true); + if (negateResult) + *fOpStack.push() = (SkOp) (kLogicalNot | kArtificialOp); + fOpStack.push(op); + if (reverseOperands) + *fOpStack.push() = (SkOp) (kFlipOps | kArtificialOp); + } +returnAdv: + return advance; +} + +void SkScriptEngine::boxCallBack(_boxCallBack func, void* userStorage) { + UserCallBack callBack; + callBack.fBoxCallBack = func; + commonCallBack(kBox, callBack, userStorage); +} + +void SkScriptEngine::commonCallBack(CallBackType type, UserCallBack& callBack, void* userStorage) { + callBack.fCallBackType = type; + callBack.fUserStorage = userStorage; + *fUserCallBacks.prepend() = callBack; +} + +bool SkScriptEngine::convertParams(SkTDArray<SkScriptValue>& params, + const SkFunctionParamType* paramTypes, int paramCount) { + if (params.count() > paramCount) { + fError = kTooManyParameters; + return false; // too many parameters passed + } + for (int index = 0; index < params.count(); index++) { + if (convertTo((SkDisplayTypes) paramTypes[index], ¶ms[index]) == false) + return false; + } + return true; +} + +bool SkScriptEngine::convertTo(SkDisplayTypes toType, SkScriptValue* value ) { + SkDisplayTypes type = value->fType; + if (type == toType) + return true; + if (ToOpType(type) == kObject) { +#if 0 // !!! I want object->string to get string from displaystringtype, not id + if (ToOpType(toType) == kString) { + bool success = handleObjectToString(value->fOperand.fObject); + if (success == false) + return false; + SkOpType type; + fTypeStack.pop(&type); + value->fType = ToDisplayType(type); + fOperandStack.pop(&value->fOperand); + return true; + } +#endif + if (handleUnbox(value) == false) { + fError = kHandleUnboxFailed; + return false; + } + return convertTo(toType, value); + } + return ConvertTo(this, toType, value); +} + +bool SkScriptEngine::evaluateDot(const char*& script, bool suppressed) { + size_t fieldLength = token_length(++script); // skip dot + if (fieldLength == 0) { + fError = kExpectedFieldName; + return false; + } + const char* field = script; + script += fieldLength; + bool success = handleProperty(suppressed); + if (success == false) { + fError = kCouldNotFindReferencedID; // note: never generated by standard animator plugins + return false; + } + return evaluateDotParam(script, suppressed, field, fieldLength); +} + +bool SkScriptEngine::evaluateDotParam(const char*& script, bool suppressed, + const char* field, size_t fieldLength) { + void* object; + if (suppressed) + object = NULL; + else { + if (fTypeStack.top() != kObject) { + fError = kDotOperatorExpectsObject; + return false; + } + object = fOperandStack.top().fObject; + fTypeStack.pop(); + fOperandStack.pop(); + } + char ch; // see if it is a simple member or a function + while (is_ws(ch = script[0])) + script++; + bool success = true; + if (ch != '(') { + if (suppressed == false) { + if ((success = handleMember(field, fieldLength, object)) == false) + fError = kHandleMemberFailed; + } + } else { + SkTDArray<SkScriptValue> params; + *fBraceStack.push() = kFunctionBrace; + success = functionParams(&script, params); + if (success && suppressed == false && + (success = handleMemberFunction(field, fieldLength, object, params)) == false) + fError = kHandleMemberFunctionFailed; + } + return success; +} + +bool SkScriptEngine::evaluateScript(const char** scriptPtr, SkScriptValue* value) { +#ifdef SK_DEBUG + const char** original = scriptPtr; +#endif + bool success; + const char* inner; + if (strncmp(*scriptPtr, "#script:", sizeof("#script:") - 1) == 0) { + *scriptPtr += sizeof("#script:") - 1; + if (fReturnType == kNoType || fReturnType == kString) { + success = innerScript(scriptPtr, value); + if (success == false) + goto end; + inner = value->fOperand.fString->c_str(); + scriptPtr = &inner; + } + } + { + success = innerScript(scriptPtr, value); + if (success == false) + goto end; + const char* script = *scriptPtr; + char ch; + while (is_ws(ch = script[0])) + script++; + if (ch != '\0') { + // error may trigger on scripts like "50,0" that were intended to be written as "[50, 0]" + fError = kPrematureEnd; + success = false; + } + } +end: +#ifdef SK_DEBUG + if (success == false) { + SkDebugf("script failed: %s", *original); + if (fError) + SkDebugf(" %s", errorStrings[fError - 1]); + SkDebugf("\n"); + } +#endif + return success; +} + +void SkScriptEngine::forget(SkTypedArray* array) { + if (array->getType() == SkType_String) { + for (int index = 0; index < array->count(); index++) { + SkString* string = (*array)[index].fString; + int found = fTrackString.find(string); + if (found >= 0) + fTrackString.remove(found); + } + return; + } + if (array->getType() == SkType_Array) { + for (int index = 0; index < array->count(); index++) { + SkTypedArray* child = (*array)[index].fArray; + forget(child); // forgets children of child + int found = fTrackArray.find(child); + if (found >= 0) + fTrackArray.remove(found); + } + } +} + +void SkScriptEngine::functionCallBack(_functionCallBack func, void* userStorage) { + UserCallBack callBack; + callBack.fFunctionCallBack = func; + commonCallBack(kFunction, callBack, userStorage); +} + +bool SkScriptEngine::functionParams(const char** scriptPtr, SkTDArray<SkScriptValue>& params) { + (*scriptPtr)++; // skip open paren + *fOpStack.push() = kParen; + *fBraceStack.push() = kFunctionBrace; + SkBool suppressed = fSuppressStack.top().fSuppress; + do { + SkScriptValue value; + bool success = innerScript(scriptPtr, suppressed ? NULL : &value); + if (success == false) { + fError = kErrorInFunctionParameters; + return false; + } + if (suppressed) + continue; + *params.append() = value; + } while ((*scriptPtr)[-1] == ','); + fBraceStack.pop(); + fOpStack.pop(); // pop paren + (*scriptPtr)++; // advance beyond close paren + return true; +} + +#ifdef SK_DEBUG +bool SkScriptEngine::getErrorString(SkString* str) const { + if (fError) + str->set(errorStrings[fError - 1]); + return fError != 0; +} +#endif + +bool SkScriptEngine::innerScript(const char** scriptPtr, SkScriptValue* value) { + const char* script = *scriptPtr; + char ch; + bool lastPush = false; + bool success = true; + int opBalance = fOpStack.count(); + int baseBrace = fBraceStack.count(); + int suppressBalance = fSuppressStack.count(); + while ((ch = script[0]) != '\0') { + if (is_ws(ch)) { + script++; + continue; + } + SkBool suppressed = fSuppressStack.top().fSuppress; + SkOperand operand; + const char* dotCheck; + if (fBraceStack.count() > baseBrace) { +#if 0 // disable support for struct brace + if (ch == ':') { + SkASSERT(fTokenLength > 0); + SkASSERT(fBraceStack.top() == kStructBrace); + ++script; + SkASSERT(fDisplayable); + SkString token(fToken, fTokenLength); + fTokenLength = 0; + const char* tokenName = token.c_str(); + const SkMemberInfo* tokenInfo SK_INIT_TO_AVOID_WARNING; + if (suppressed == false) { + SkDisplayTypes type = fInfo->getType(); + tokenInfo = SkDisplayType::GetMember(type, &tokenName); + SkASSERT(tokenInfo); + } + SkScriptValue tokenValue; + success = innerScript(&script, &tokenValue); // terminate and return on comma, close brace + SkASSERT(success); + if (suppressed == false) { + if (tokenValue.fType == SkType_Displayable) { + SkASSERT(SkDisplayType::IsDisplayable(tokenInfo->getType())); + fDisplayable->setReference(tokenInfo, tokenValue.fOperand.fDisplayable); + } else { + if (tokenValue.fType != tokenInfo->getType()) { + if (convertTo(tokenInfo->getType(), &tokenValue) == false) + return false; + } + tokenInfo->writeValue(fDisplayable, NULL, 0, 0, + (void*) ((char*) fInfo->memberData(fDisplayable) + tokenInfo->fOffset + fArrayOffset), + tokenInfo->getType(), tokenValue); + } + } + lastPush = false; + continue; + } else +#endif + if (fBraceStack.top() == kArrayBrace) { + SkScriptValue tokenValue; + success = innerScript(&script, &tokenValue); // terminate and return on comma, close brace + if (success == false) { + fError = kErrorInArrrayIndex; + return false; + } + if (suppressed == false) { +#if 0 // no support for structures for now + if (tokenValue.fType == SkType_Structure) { + fArrayOffset += (int) fInfo->getSize(fDisplayable); + } else +#endif + { + SkDisplayTypes type = ToDisplayType(fReturnType); + if (fReturnType == kNoType) { + // !!! short sighted; in the future, allow each returned array component to carry + // its own type, and let caller do any needed conversions + if (value->fOperand.fArray->count() == 0) + value->fOperand.fArray->setType(type = tokenValue.fType); + else + type = value->fOperand.fArray->getType(); + } + if (tokenValue.fType != type) { + if (convertTo(type, &tokenValue) == false) + return false; + } + *value->fOperand.fArray->append() = tokenValue.fOperand; + } + } + lastPush = false; + continue; + } else { + if (token_length(script) == 0) { + fError = kExpectedToken; + return false; + } + } + } + if (lastPush != false && fTokenLength > 0) { + if (ch == '(') { + *fBraceStack.push() = kFunctionBrace; + if (handleFunction(&script, SkToBool(suppressed)) == false) + return false; + lastPush = true; + continue; + } else if (ch == '[') { + if (handleProperty(SkToBool(suppressed)) == false) + return false; // note: never triggered by standard animator plugins + if (handleArrayIndexer(&script, SkToBool(suppressed)) == false) + return false; + lastPush = true; + continue; + } else if (ch != '.') { + if (handleProperty(SkToBool(suppressed)) == false) + return false; // note: never triggered by standard animator plugins + lastPush = true; + continue; + } + } + if (ch == '0' && (script[1] & ~0x20) == 'X') { + if (lastPush != false) { + fError = kExpectedOperator; + return false; + } + script += 2; + script = SkParse::FindHex(script, (uint32_t*)&operand.fS32); + if (script == NULL) { + fError = kExpectedHex; + return false; + } + goto intCommon; + } + if (lastPush == false && ch == '.') + goto scalarCommon; + if (ch >= '0' && ch <= '9') { + if (lastPush != false) { + fError = kExpectedOperator; + return false; + } + dotCheck = SkParse::FindS32(script, &operand.fS32); + if (dotCheck[0] != '.') { + script = dotCheck; +intCommon: + if (suppressed == false) + *fTypeStack.push() = kInt; + } else { +scalarCommon: + script = SkParse::FindScalar(script, &operand.fScalar); + if (suppressed == false) + *fTypeStack.push() = kScalar; + } + if (suppressed == false) + fOperandStack.push(operand); + lastPush = true; + continue; + } + int length = token_length(script); + if (length > 0) { + if (lastPush != false) { + fError = kExpectedOperator; + return false; + } + fToken = script; + fTokenLength = length; + script += length; + lastPush = true; + continue; + } + char startQuote = ch; + if (startQuote == '\'' || startQuote == '\"') { + if (lastPush != false) { + fError = kExpectedOperator; + return false; + } + operand.fString = new SkString(); + track(operand.fString); + ++script; + + // <mrr> this is a lot of calls to append() one char at at time + // how hard to preflight script so we know how much to grow fString by? + do { + if (script[0] == '\\') + ++script; + operand.fString->append(script, 1); + ++script; + if (script[0] == '\0') { + fError = kUnterminatedString; + return false; + } + } while (script[0] != startQuote); + ++script; + if (suppressed == false) { + *fTypeStack.push() = kString; + fOperandStack.push(operand); + } + lastPush = true; + continue; + } + ; + if (ch == '.') { + if (fTokenLength == 0) { + SkScriptValue scriptValue; + SkDEBUGCODE(scriptValue.fOperand.fObject = NULL); + int tokenLength = token_length(++script); + const char* token = script; + script += tokenLength; + if (suppressed == false) { + if (fTypeStack.count() == 0) { + fError = kExpectedTokenBeforeDotOperator; + return false; + } + SkOpType topType; + fTypeStack.pop(&topType); + fOperandStack.pop(&scriptValue.fOperand); + scriptValue.fType = ToDisplayType(topType); + handleBox(&scriptValue); + } + success = evaluateDotParam(script, SkToBool(suppressed), token, tokenLength); + if (success == false) + return false; + lastPush = true; + continue; + } + // get next token, and evaluate immediately + success = evaluateDot(script, SkToBool(suppressed)); + if (success == false) + return false; + lastPush = true; + continue; + } + if (ch == '[') { + if (lastPush == false) { + script++; + *fBraceStack.push() = kArrayBrace; + if (suppressed) + continue; + operand.fArray = value->fOperand.fArray = new SkTypedArray(ToDisplayType(fReturnType)); + track(value->fOperand.fArray); + *fTypeStack.push() = (SkOpType) kArray; + fOperandStack.push(operand); + continue; + } + if (handleArrayIndexer(&script, SkToBool(suppressed)) == false) + return false; + lastPush = true; + continue; + } +#if 0 // structs not supported for now + if (ch == '{') { + if (lastPush == false) { + script++; + *fBraceStack.push() = kStructBrace; + if (suppressed) + continue; + operand.fS32 = 0; + *fTypeStack.push() = (SkOpType) kStruct; + fOperandStack.push(operand); + continue; + } + SkASSERT(0); // braces in other contexts aren't supported yet + } +#endif + if (ch == ')' && fBraceStack.count() > 0) { + SkBraceStyle braceStyle = fBraceStack.top(); + if (braceStyle == kFunctionBrace) { + fBraceStack.pop(); + break; + } + } + if (ch == ',' || ch == ']') { + if (ch != ',') { + SkBraceStyle match; + fBraceStack.pop(&match); + if (match != kArrayBrace) { + fError = kMismatchedArrayBrace; + return false; + } + } + script++; + // !!! see if brace or bracket is correct closer + break; + } + char nextChar = script[1]; + int advance = logicalOp(ch, nextChar); + if (advance < 0) // error + return false; + if (advance == 0) + advance = arithmeticOp(ch, nextChar, lastPush); + if (advance == 0) // unknown token + return false; + if (advance > 0) + script += advance; + lastPush = ch == ']' || ch == ')'; + } + bool suppressed = SkToBool(fSuppressStack.top().fSuppress); + if (fTokenLength > 0) { + success = handleProperty(suppressed); + if (success == false) + return false; // note: never triggered by standard animator plugins + } + while (fOpStack.count() > opBalance) { // leave open paren + if ((fError = opError()) != kNoError) + return false; + if (processOp() == false) + return false; + } + SkOpType topType = fTypeStack.count() > 0 ? fTypeStack.top() : kNoType; + if (suppressed == false && topType != fReturnType && + topType == kString && fReturnType != kNoType) { // if result is a string, give handle property a chance to convert it to the property value + SkString* string = fOperandStack.top().fString; + fToken = string->c_str(); + fTokenLength = string->size(); + fOperandStack.pop(); + fTypeStack.pop(); + success = handleProperty(SkToBool(fSuppressStack.top().fSuppress)); + if (success == false) { // if it couldn't convert, return string (error?) + SkOperand operand; + operand.fS32 = 0; + *fTypeStack.push() = kString; + operand.fString = string; + fOperandStack.push(operand); + } + } + if (value) { + if (fOperandStack.count() == 0) + return false; + SkASSERT(fOperandStack.count() >= 1); + SkASSERT(fTypeStack.count() >= 1); + fOperandStack.pop(&value->fOperand); + SkOpType type; + fTypeStack.pop(&type); + value->fType = ToDisplayType(type); +// SkASSERT(value->fType != SkType_Unknown); + if (topType != fReturnType && topType == kObject && fReturnType != kNoType) { + if (convertTo(ToDisplayType(fReturnType), value) == false) + return false; + } + } + while (fSuppressStack.count() > suppressBalance) + fSuppressStack.pop(); + *scriptPtr = script; + return true; // no error +} + +void SkScriptEngine::memberCallBack(_memberCallBack member , void* userStorage) { + UserCallBack callBack; + callBack.fMemberCallBack = member; + commonCallBack(kMember, callBack, userStorage); +} + +void SkScriptEngine::memberFunctionCallBack(_memberFunctionCallBack func, void* userStorage) { + UserCallBack callBack; + callBack.fMemberFunctionCallBack = func; + commonCallBack(kMemberFunction, callBack, userStorage); +} + +#if 0 +void SkScriptEngine::objectToStringCallBack(_objectToStringCallBack func, void* userStorage) { + UserCallBack callBack; + callBack.fObjectToStringCallBack = func; + commonCallBack(kObjectToString, callBack, userStorage); +} +#endif + +bool SkScriptEngine::handleArrayIndexer(const char** scriptPtr, bool suppressed) { + SkScriptValue scriptValue; + (*scriptPtr)++; + *fOpStack.push() = kParen; + *fBraceStack.push() = kArrayBrace; + SkOpType saveType = fReturnType; + fReturnType = kInt; + bool success = innerScript(scriptPtr, suppressed == false ? &scriptValue : NULL); + if (success == false) + return false; + fReturnType = saveType; + if (suppressed == false) { + if (convertTo(SkType_Int, &scriptValue) == false) + return false; + int index = scriptValue.fOperand.fS32; + SkScriptValue scriptValue; + SkOpType type; + fTypeStack.pop(&type); + fOperandStack.pop(&scriptValue.fOperand); + scriptValue.fType = ToDisplayType(type); + if (type == kObject) { + success = handleUnbox(&scriptValue); + if (success == false) + return false; + if (ToOpType(scriptValue.fType) != kArray) { + fError = kExpectedArray; + return false; + } + } + *fTypeStack.push() = scriptValue.fOperand.fArray->getOpType(); +// SkASSERT(index >= 0); + if ((unsigned) index >= (unsigned) scriptValue.fOperand.fArray->count()) { + fError = kArrayIndexOutOfBounds; + return false; + } + scriptValue.fOperand = scriptValue.fOperand.fArray->begin()[index]; + fOperandStack.push(scriptValue.fOperand); + } + fOpStack.pop(); // pop paren + return success; +} + +bool SkScriptEngine::handleBox(SkScriptValue* scriptValue) { + bool success = true; + for (UserCallBack* callBack = fUserCallBacks.begin(); callBack < fUserCallBacks.end(); callBack++) { + if (callBack->fCallBackType != kBox) + continue; + success = (*callBack->fBoxCallBack)(callBack->fUserStorage, scriptValue); + if (success) { + fOperandStack.push(scriptValue->fOperand); + *fTypeStack.push() = ToOpType(scriptValue->fType); + goto done; + } + } +done: + return success; +} + +bool SkScriptEngine::handleFunction(const char** scriptPtr, bool suppressed) { + SkScriptValue callbackResult; + SkTDArray<SkScriptValue> params; + SkString functionName(fToken, fTokenLength); + fTokenLength = 0; + bool success = functionParams(scriptPtr, params); + if (success == false) + goto done; + if (suppressed == true) + return true; + { + for (UserCallBack* callBack = fUserCallBacks.begin(); callBack < fUserCallBacks.end(); callBack++) { + if (callBack->fCallBackType != kFunction) + continue; + success = (*callBack->fFunctionCallBack)(functionName.c_str(), functionName.size(), params, + callBack->fUserStorage, &callbackResult); + if (success) { + fOperandStack.push(callbackResult.fOperand); + *fTypeStack.push() = ToOpType(callbackResult.fType); + goto done; + } + } + } + fError = kNoFunctionHandlerFound; + return false; +done: + return success; +} + +bool SkScriptEngine::handleMember(const char* field, size_t len, void* object) { + SkScriptValue callbackResult; + bool success = true; + for (UserCallBack* callBack = fUserCallBacks.begin(); callBack < fUserCallBacks.end(); callBack++) { + if (callBack->fCallBackType != kMember) + continue; + success = (*callBack->fMemberCallBack)(field, len, object, callBack->fUserStorage, &callbackResult); + if (success) { + if (callbackResult.fType == SkType_String) + track(callbackResult.fOperand.fString); + fOperandStack.push(callbackResult.fOperand); + *fTypeStack.push() = ToOpType(callbackResult.fType); + goto done; + } + } + return false; +done: + return success; +} + +bool SkScriptEngine::handleMemberFunction(const char* field, size_t len, void* object, SkTDArray<SkScriptValue>& params) { + SkScriptValue callbackResult; + bool success = true; + for (UserCallBack* callBack = fUserCallBacks.begin(); callBack < fUserCallBacks.end(); callBack++) { + if (callBack->fCallBackType != kMemberFunction) + continue; + success = (*callBack->fMemberFunctionCallBack)(field, len, object, params, + callBack->fUserStorage, &callbackResult); + if (success) { + if (callbackResult.fType == SkType_String) + track(callbackResult.fOperand.fString); + fOperandStack.push(callbackResult.fOperand); + *fTypeStack.push() = ToOpType(callbackResult.fType); + goto done; + } + } + return false; +done: + return success; +} + +#if 0 +bool SkScriptEngine::handleObjectToString(void* object) { + SkScriptValue callbackResult; + bool success = true; + for (UserCallBack* callBack = fUserCallBacks.begin(); callBack < fUserCallBacks.end(); callBack++) { + if (callBack->fCallBackType != kObjectToString) + continue; + success = (*callBack->fObjectToStringCallBack)(object, + callBack->fUserStorage, &callbackResult); + if (success) { + if (callbackResult.fType == SkType_String) + track(callbackResult.fOperand.fString); + fOperandStack.push(callbackResult.fOperand); + *fTypeStack.push() = ToOpType(callbackResult.fType); + goto done; + } + } + return false; +done: + return success; +} +#endif + +bool SkScriptEngine::handleProperty(bool suppressed) { + SkScriptValue callbackResult; + bool success = true; + if (suppressed) + goto done; + success = false; // note that with standard animator-script plugins, callback never returns false + { + for (UserCallBack* callBack = fUserCallBacks.begin(); callBack < fUserCallBacks.end(); callBack++) { + if (callBack->fCallBackType != kProperty) + continue; + success = (*callBack->fPropertyCallBack)(fToken, fTokenLength, + callBack->fUserStorage, &callbackResult); + if (success) { + if (callbackResult.fType == SkType_String && callbackResult.fOperand.fString == NULL) { + callbackResult.fOperand.fString = new SkString(fToken, fTokenLength); + track(callbackResult.fOperand.fString); + } + fOperandStack.push(callbackResult.fOperand); + *fTypeStack.push() = ToOpType(callbackResult.fType); + goto done; + } + } + } +done: + fTokenLength = 0; + return success; +} + +bool SkScriptEngine::handleUnbox(SkScriptValue* scriptValue) { + bool success = true; + for (UserCallBack* callBack = fUserCallBacks.begin(); callBack < fUserCallBacks.end(); callBack++) { + if (callBack->fCallBackType != kUnbox) + continue; + success = (*callBack->fUnboxCallBack)(callBack->fUserStorage, scriptValue); + if (success) { + if (scriptValue->fType == SkType_String) + track(scriptValue->fOperand.fString); + goto done; + } + } + return false; +done: + return success; +} + +// note that entire expression is treated as if it were enclosed in parens +// an open paren is always the first thing in the op stack + +int SkScriptEngine::logicalOp(char ch, char nextChar) { + int advance = 1; + SkOp match; + signed char precedence; + switch (ch) { + case ')': + match = kParen; + break; + case ']': + match = kArrayOp; + break; + case '?': + match = kIf; + break; + case ':': + match = kElse; + break; + case '&': + if (nextChar != '&') + goto noMatch; + match = kLogicalAnd; + advance = 2; + break; + case '|': + if (nextChar != '|') + goto noMatch; + match = kLogicalOr; + advance = 2; + break; + default: +noMatch: + return 0; + } + SkSuppress suppress; + precedence = gPrecedence[match]; + if (fSuppressStack.top().fSuppress) { + if (fSuppressStack.top().fOpStackDepth < fOpStack.count()) { + SkOp topOp = fOpStack.top(); + if (gPrecedence[topOp] <= precedence) + fOpStack.pop(); + goto goHome; + } + bool changedPrecedence = gPrecedence[fSuppressStack.top().fOperator] < precedence; + if (changedPrecedence) + fSuppressStack.pop(); + if (precedence == kIfElsePrecedence) { + if (match == kIf) { + if (changedPrecedence) + fOpStack.pop(); + else + *fOpStack.push() = kIf; + } else { + if (fSuppressStack.top().fOpStackDepth == fOpStack.count()) { + goto flipSuppress; + } + fOpStack.pop(); + } + } + if (changedPrecedence == false) + goto goHome; + } + while (gPrecedence[fOpStack.top() & ~kArtificialOp] < precedence) { + if (processOp() == false) + return false; + } + if (fSuppressStack.top().fOpStackDepth > fOpStack.count()) + fSuppressStack.pop(); + switch (match) { + case kParen: + case kArrayOp: + if (fOpStack.count() <= 1 || fOpStack.top() != match) { + fError = kMismatchedBrackets; + return -1; + } + if (match == kParen) + fOpStack.pop(); + else { + SkOpType indexType; + fTypeStack.pop(&indexType); + if (indexType != kInt && indexType != kScalar) { + fError = kExpectedNumberForArrayIndex; // (although, could permit strings eventually) + return -1; + } + SkOperand indexOperand; + fOperandStack.pop(&indexOperand); + int index = indexType == kScalar ? SkScalarFloor(indexOperand.fScalar) : + indexOperand.fS32; + SkOpType arrayType; + fTypeStack.pop(&arrayType); + if ((unsigned)arrayType != (unsigned)kArray) { + fError = kExpectedArray; + return -1; + } + SkOperand arrayOperand; + fOperandStack.pop(&arrayOperand); + SkTypedArray* array = arrayOperand.fArray; + SkOperand operand; + if (array->getIndex(index, &operand) == false) { + fError = kIndexOutOfRange; + return -1; + } + SkOpType resultType = array->getOpType(); + fTypeStack.push(resultType); + fOperandStack.push(operand); + } + break; + case kIf: { + SkScriptValue ifValue; + SkOpType ifType; + fTypeStack.pop(&ifType); + ifValue.fType = ToDisplayType(ifType); + fOperandStack.pop(&ifValue.fOperand); + if (convertTo(SkType_Int, &ifValue) == false) + return -1; + if (ifValue.fType != SkType_Int) { + fError = kExpectedIntForConditionOperator; + return -1; + } + suppress.fSuppress = ifValue.fOperand.fS32 == 0; + suppress.fOperator = kIf; + suppress.fOpStackDepth = fOpStack.count(); + suppress.fElse = false; + fSuppressStack.push(suppress); + // if left is true, do only up to colon + // if left is false, do only after colon + } break; + case kElse: +flipSuppress: + if (fSuppressStack.top().fElse == true) + fSuppressStack.pop(); + fSuppressStack.top().fElse = true; + fSuppressStack.top().fSuppress ^= true; + // flip last do / don't do consideration from last '?' + break; + case kLogicalAnd: + case kLogicalOr: { + if (fTypeStack.top() != kInt) { + fError = kExpectedBooleanExpression; + return -1; + } + int32_t topInt = fOperandStack.top().fS32; + if (fOpStack.top() != kLogicalAnd) + *fOpStack.push() = kLogicalAnd; // really means 'to bool', and is appropriate for 'or' + if (match == kLogicalOr ? topInt != 0 : topInt == 0) { + suppress.fSuppress = true; + suppress.fOperator = match; + suppress.fOpStackDepth = fOpStack.count(); + fSuppressStack.push(suppress); + } else { + fTypeStack.pop(); + fOperandStack.pop(); + } + } break; + default: + SkASSERT(0); + } +goHome: + return advance; +} + +SkScriptEngine::Error SkScriptEngine::opError() { + int opCount = fOpStack.count(); + int operandCount = fOperandStack.count(); + if (opCount == 0) { + if (operandCount != 1) + return kExpectedOperator; + return kNoError; + } + SkOp op = (SkOp) (fOpStack.top() & ~kArtificialOp); + const SkOperatorAttributes* attributes = &gOpAttributes[op]; + if (attributes->fLeftType != kNoType && operandCount < 2) + return kExpectedValue; + if (attributes->fLeftType == kNoType && operandCount < 1) + return kExpectedValue; + return kNoError; +} + +bool SkScriptEngine::processOp() { + SkOp op; + fOpStack.pop(&op); + op = (SkOp) (op & ~kArtificialOp); + const SkOperatorAttributes* attributes = &gOpAttributes[op]; + SkOpType type2; + fTypeStack.pop(&type2); + SkOpType type1 = type2; + SkOperand operand2; + fOperandStack.pop(&operand2); + SkOperand operand1 = operand2; // !!! not really needed, suppresses warning + if (attributes->fLeftType != kNoType) { + fTypeStack.pop(&type1); + fOperandStack.pop(&operand1); + if (op == kFlipOps) { + SkTSwap(type1, type2); + SkTSwap(operand1, operand2); + fOpStack.pop(&op); + op = (SkOp) (op & ~kArtificialOp); + attributes = &gOpAttributes[op]; + } + if (type1 == kObject && (type1 & attributes->fLeftType) == 0) { + SkScriptValue val; + val.fType = ToDisplayType(type1); + val.fOperand = operand1; + bool success = handleUnbox(&val); + if (success == false) + return false; + type1 = ToOpType(val.fType); + operand1 = val.fOperand; + } + } + if (type2 == kObject && (type2 & attributes->fLeftType) == 0) { + SkScriptValue val; + val.fType = ToDisplayType(type2); + val.fOperand = operand2; + bool success = handleUnbox(&val); + if (success == false) + return false; + type2 = ToOpType(val.fType); + operand2 = val.fOperand; + } + if (attributes->fLeftType != kNoType) { + if (type1 != type2) { + if ((attributes->fLeftType & kString) && attributes->fBias & kTowardsString && ((type1 | type2) & kString)) { + if (type1 == kInt || type1 == kScalar) { + convertToString(operand1, type1 == kInt ? SkType_Int : SkType_Float); + type1 = kString; + } + if (type2 == kInt || type2 == kScalar) { + convertToString(operand2, type2 == kInt ? SkType_Int : SkType_Float); + type2 = kString; + } + } else if (attributes->fLeftType & kScalar && ((type1 | type2) & kScalar)) { + if (type1 == kInt) { + operand1.fScalar = IntToScalar(operand1.fS32); + type1 = kScalar; + } + if (type2 == kInt) { + operand2.fScalar = IntToScalar(operand2.fS32); + type2 = kScalar; + } + } + } + if ((type1 & attributes->fLeftType) == 0 || type1 != type2) { + if (type1 == kString) { + const char* result = SkParse::FindScalar(operand1.fString->c_str(), &operand1.fScalar); + if (result == NULL) { + fError = kExpectedNumber; + return false; + } + type1 = kScalar; + } + if (type1 == kScalar && (attributes->fLeftType == kInt || type2 == kInt)) { + operand1.fS32 = SkScalarFloor(operand1.fScalar); + type1 = kInt; + } + } + } + if ((type2 & attributes->fRightType) == 0 || type1 != type2) { + if (type2 == kString) { + const char* result = SkParse::FindScalar(operand2.fString->c_str(), &operand2.fScalar); + if (result == NULL) { + fError = kExpectedNumber; + return false; + } + type2 = kScalar; + } + if (type2 == kScalar && (attributes->fRightType == kInt || type1 == kInt)) { + operand2.fS32 = SkScalarFloor(operand2.fScalar); + type2 = kInt; + } + } + if (type2 == kScalar) + op = (SkOp) (op + 1); + else if (type2 == kString) + op = (SkOp) (op + 2); + switch(op) { + case kAddInt: + operand2.fS32 += operand1.fS32; + break; + case kAddScalar: + operand2.fScalar += operand1.fScalar; + break; + case kAddString: + if (fTrackString.find(operand1.fString) < 0) { + operand1.fString = SkNEW_ARGS(SkString, (*operand1.fString)); + track(operand1.fString); + } + operand1.fString->append(*operand2.fString); + operand2 = operand1; + break; + case kBitAnd: + operand2.fS32 &= operand1.fS32; + break; + case kBitNot: + operand2.fS32 = ~operand2.fS32; + break; + case kBitOr: + operand2.fS32 |= operand1.fS32; + break; + case kDivideInt: + if (operand2.fS32 == 0) { + operand2.fS32 = operand1.fS32 == 0 ? SK_NaN32 : operand1.fS32 > 0 ? SK_MaxS32 : -SK_MaxS32; + break; + } else { + int32_t original = operand2.fS32; + operand2.fS32 = operand1.fS32 / operand2.fS32; + if (original * operand2.fS32 == operand1.fS32) + break; // integer divide was good enough + operand2.fS32 = original; + type2 = kScalar; + } + case kDivideScalar: + if (operand2.fScalar == 0) + operand2.fScalar = operand1.fScalar == 0 ? SK_ScalarNaN : operand1.fScalar > 0 ? SK_ScalarMax : -SK_ScalarMax; + else + operand2.fScalar = SkScalarDiv(operand1.fScalar, operand2.fScalar); + break; + case kEqualInt: + operand2.fS32 = operand1.fS32 == operand2.fS32; + break; + case kEqualScalar: + operand2.fS32 = operand1.fScalar == operand2.fScalar; + type2 = kInt; + break; + case kEqualString: + operand2.fS32 = *operand1.fString == *operand2.fString; + type2 = kInt; + break; + case kGreaterEqualInt: + operand2.fS32 = operand1.fS32 >= operand2.fS32; + break; + case kGreaterEqualScalar: + operand2.fS32 = operand1.fScalar >= operand2.fScalar; + type2 = kInt; + break; + case kGreaterEqualString: + operand2.fS32 = strcmp(operand1.fString->c_str(), operand2.fString->c_str()) >= 0; + type2 = kInt; + break; + case kLogicalAnd: + operand2.fS32 = !! operand2.fS32; // really, ToBool + break; + case kLogicalNot: + operand2.fS32 = ! operand2.fS32; + break; + case kLogicalOr: + SkASSERT(0); // should have already been processed + break; + case kMinusInt: + operand2.fS32 = -operand2.fS32; + break; + case kMinusScalar: + operand2.fScalar = -operand2.fScalar; + break; + case kModuloInt: + operand2.fS32 = operand1.fS32 % operand2.fS32; + break; + case kModuloScalar: + operand2.fScalar = SkScalarMod(operand1.fScalar, operand2.fScalar); + break; + case kMultiplyInt: + operand2.fS32 *= operand1.fS32; + break; + case kMultiplyScalar: + operand2.fScalar = SkScalarMul(operand1.fScalar, operand2.fScalar); + break; + case kShiftLeft: + operand2.fS32 = operand1.fS32 << operand2.fS32; + break; + case kShiftRight: + operand2.fS32 = operand1.fS32 >> operand2.fS32; + break; + case kSubtractInt: + operand2.fS32 = operand1.fS32 - operand2.fS32; + break; + case kSubtractScalar: + operand2.fScalar = operand1.fScalar - operand2.fScalar; + break; + case kXor: + operand2.fS32 ^= operand1.fS32; + break; + default: + SkASSERT(0); + } + fTypeStack.push(type2); + fOperandStack.push(operand2); + return true; +} + +void SkScriptEngine::propertyCallBack(_propertyCallBack prop, void* userStorage) { + UserCallBack callBack; + callBack.fPropertyCallBack = prop; + commonCallBack(kProperty, callBack, userStorage); +} + +void SkScriptEngine::track(SkTypedArray* array) { + SkASSERT(fTrackArray.find(array) < 0); + *(fTrackArray.end() - 1) = array; + fTrackArray.appendClear(); +} + +void SkScriptEngine::track(SkString* string) { + SkASSERT(fTrackString.find(string) < 0); + *(fTrackString.end() - 1) = string; + fTrackString.appendClear(); +} + +void SkScriptEngine::unboxCallBack(_unboxCallBack func, void* userStorage) { + UserCallBack callBack; + callBack.fUnboxCallBack = func; + commonCallBack(kUnbox, callBack, userStorage); +} + +bool SkScriptEngine::ConvertTo(SkScriptEngine* engine, SkDisplayTypes toType, SkScriptValue* value ) { + SkASSERT(value); + if (SkDisplayType::IsEnum(NULL /* fMaker */, toType)) + toType = SkType_Int; + if (toType == SkType_Point || toType == SkType_3D_Point) + toType = SkType_Float; + if (toType == SkType_Drawable) + toType = SkType_Displayable; + SkDisplayTypes type = value->fType; + if (type == toType) + return true; + SkOperand& operand = value->fOperand; + bool success = true; + switch (toType) { + case SkType_Int: + if (type == SkType_Boolean) + break; + if (type == SkType_Float) + operand.fS32 = SkScalarFloor(operand.fScalar); + else { + if (type != SkType_String) { + success = false; + break; // error + } + success = SkParse::FindS32(operand.fString->c_str(), &operand.fS32) != NULL; + } + break; + case SkType_Float: + if (type == SkType_Int) { + if ((uint32_t)operand.fS32 == SK_NaN32) + operand.fScalar = SK_ScalarNaN; + else if (SkAbs32(operand.fS32) == SK_MaxS32) + operand.fScalar = SkSign32(operand.fS32) * SK_ScalarMax; + else + operand.fScalar = SkIntToScalar(operand.fS32); + } else { + if (type != SkType_String) { + success = false; + break; // error + } + success = SkParse::FindScalar(operand.fString->c_str(), &operand.fScalar) != NULL; + } + break; + case SkType_String: { + SkString* strPtr = new SkString(); + SkASSERT(engine); + engine->track(strPtr); + if (type == SkType_Int) + strPtr->appendS32(operand.fS32); + else if (type == SkType_Displayable) + SkASSERT(0); // must call through instance version instead of static version + else { + if (type != SkType_Float) { + success = false; + break; + } + strPtr->appendScalar(operand.fScalar); + } + operand.fString = strPtr; + } break; + case SkType_Array: { + SkTypedArray* array = new SkTypedArray(type); + *array->append() = operand; + engine->track(array); + operand.fArray = array; + } break; + default: + SkASSERT(0); + } + value->fType = toType; + if (success == false) + engine->fError = kTypeConversionFailed; + return success; +} + +SkScalar SkScriptEngine::IntToScalar(int32_t s32) { + SkScalar scalar; + if ((uint32_t)s32 == SK_NaN32) + scalar = SK_ScalarNaN; + else if (SkAbs32(s32) == SK_MaxS32) + scalar = SkSign32(s32) * SK_ScalarMax; + else + scalar = SkIntToScalar(s32); + return scalar; +} + +SkDisplayTypes SkScriptEngine::ToDisplayType(SkOpType type) { + int val = type; + switch (val) { + case kNoType: + return SkType_Unknown; + case kInt: + return SkType_Int; + case kScalar: + return SkType_Float; + case kString: + return SkType_String; + case kArray: + return SkType_Array; + case kObject: + return SkType_Displayable; +// case kStruct: +// return SkType_Structure; + default: + SkASSERT(0); + return SkType_Unknown; + } +} + +SkScriptEngine::SkOpType SkScriptEngine::ToOpType(SkDisplayTypes type) { + if (SkDisplayType::IsDisplayable(NULL /* fMaker */, type)) + return (SkOpType) kObject; + if (SkDisplayType::IsEnum(NULL /* fMaker */, type)) + return kInt; + switch (type) { + case SkType_ARGB: + case SkType_MSec: + case SkType_Int: + return kInt; + case SkType_Float: + case SkType_Point: + case SkType_3D_Point: + return kScalar; + case SkType_Base64: + case SkType_DynamicString: + case SkType_String: + return kString; + case SkType_Array: + return (SkOpType) kArray; + case SkType_Unknown: + return kNoType; + default: + SkASSERT(0); + return kNoType; + } +} + +bool SkScriptEngine::ValueToString(SkScriptValue value, SkString* string) { + switch (value.fType) { + case kInt: + string->reset(); + string->appendS32(value.fOperand.fS32); + break; + case kScalar: + string->reset(); + string->appendScalar(value.fOperand.fScalar); + break; + case kString: + string->set(*value.fOperand.fString); + break; + default: + SkASSERT(0); + return false; + } + return true; // no error +} + +#ifdef SK_SUPPORT_UNITTEST + +#ifdef SK_CAN_USE_FLOAT + #include "SkFloatingPoint.h" +#endif + +#define DEF_SCALAR_ANSWER 0 +#define DEF_STRING_ANSWER NULL + +#define testInt(expression) { #expression, SkType_Int, expression, DEF_SCALAR_ANSWER, DEF_STRING_ANSWER } +#ifdef SK_SCALAR_IS_FLOAT + #define testScalar(expression) { #expression, SkType_Float, 0, (float) expression, DEF_STRING_ANSWER } + #define testRemainder(exp1, exp2) { #exp1 "%" #exp2, SkType_Float, 0, sk_float_mod(exp1, exp2), DEF_STRING_ANSWER } +#else + #ifdef SK_CAN_USE_FLOAT + #define testScalar(expression) { #expression, SkType_Float, 0, (int) ((expression) * 65536.0f), DEF_STRING_ANSWER } + #define testRemainder(exp1, exp2) { #exp1 "%" #exp2, SkType_Float, 0, (int) (sk_float_mod(exp1, exp2) * 65536.0f), DEF_STRING_ANSWER } + #endif +#endif +#define testTrue(expression) { #expression, SkType_Int, 1, DEF_SCALAR_ANSWER, DEF_STRING_ANSWER } +#define testFalse(expression) { #expression, SkType_Int, 0, DEF_SCALAR_ANSWER, DEF_STRING_ANSWER } + +#if !defined(SK_BUILD_FOR_BREW) +static const SkScriptNAnswer scriptTests[] = { + testInt(1>1/2), + testInt((6+7)*8), + testInt(0&&1?2:3), + testInt(3*(4+5)), +#ifdef SK_CAN_USE_FLOAT + testScalar(1.0+2.0), + testScalar(1.0+5), + testScalar(3.0-1.0), + testScalar(6-1.0), + testScalar(- -5.5- -1.5), + testScalar(2.5*6.), + testScalar(0.5*4), + testScalar(4.5/.5), + testScalar(9.5/19), + testRemainder(9.5, 0.5), + testRemainder(9.,2), + testRemainder(9,2.5), + testRemainder(-9,2.5), + testTrue(-9==-9.0), + testTrue(-9.==-4.0-5), + testTrue(-9.*1==-4-5), + testFalse(-9!=-9.0), + testFalse(-9.!=-4.0-5), + testFalse(-9.*1!=-4-5), +#endif + testInt(0x123), + testInt(0XABC), + testInt(0xdeadBEEF), + { "'123'+\"456\"", SkType_String, 0, 0, "123456" }, + { "123+\"456\"", SkType_String, 0, 0, "123456" }, + { "'123'+456", SkType_String, 0, 0, "123456" }, + { "'123'|\"456\"", SkType_Int, 123|456, DEF_SCALAR_ANSWER, DEF_STRING_ANSWER }, + { "123|\"456\"", SkType_Int, 123|456, DEF_SCALAR_ANSWER, DEF_STRING_ANSWER }, + { "'123'|456", SkType_Int, 123|456, DEF_SCALAR_ANSWER, DEF_STRING_ANSWER }, + { "'2'<11", SkType_Int, 1, DEF_SCALAR_ANSWER, DEF_STRING_ANSWER }, + { "2<'11'", SkType_Int, 1, DEF_SCALAR_ANSWER, DEF_STRING_ANSWER }, + { "'2'<'11'", SkType_Int, 0, DEF_SCALAR_ANSWER, DEF_STRING_ANSWER }, + testInt(123), + testInt(-345), + testInt(+678), + testInt(1+2+3), + testInt(3*4+5), + testInt(6+7*8), + testInt(-1-2-8/4), + testInt(-9%4), + testInt(9%-4), + testInt(-9%-4), + testInt(123|978), + testInt(123&978), + testInt(123^978), + testInt(2<<4), + testInt(99>>3), + testInt(~55), + testInt(~~55), + testInt(!55), + testInt(!!55), + // both int + testInt(2<2), + testInt(2<11), + testInt(20<11), + testInt(2<=2), + testInt(2<=11), + testInt(20<=11), + testInt(2>2), + testInt(2>11), + testInt(20>11), + testInt(2>=2), + testInt(2>=11), + testInt(20>=11), + testInt(2==2), + testInt(2==11), + testInt(20==11), + testInt(2!=2), + testInt(2!=11), + testInt(20!=11), +#ifdef SK_CAN_USE_FLOAT + // left int, right scalar + testInt(2<2.), + testInt(2<11.), + testInt(20<11.), + testInt(2<=2.), + testInt(2<=11.), + testInt(20<=11.), + testInt(2>2.), + testInt(2>11.), + testInt(20>11.), + testInt(2>=2.), + testInt(2>=11.), + testInt(20>=11.), + testInt(2==2.), + testInt(2==11.), + testInt(20==11.), + testInt(2!=2.), + testInt(2!=11.), + testInt(20!=11.), + // left scalar, right int + testInt(2.<2), + testInt(2.<11), + testInt(20.<11), + testInt(2.<=2), + testInt(2.<=11), + testInt(20.<=11), + testInt(2.>2), + testInt(2.>11), + testInt(20.>11), + testInt(2.>=2), + testInt(2.>=11), + testInt(20.>=11), + testInt(2.==2), + testInt(2.==11), + testInt(20.==11), + testInt(2.!=2), + testInt(2.!=11), + testInt(20.!=11), + // both scalar + testInt(2.<11.), + testInt(20.<11.), + testInt(2.<=2.), + testInt(2.<=11.), + testInt(20.<=11.), + testInt(2.>2.), + testInt(2.>11.), + testInt(20.>11.), + testInt(2.>=2.), + testInt(2.>=11.), + testInt(20.>=11.), + testInt(2.==2.), + testInt(2.==11.), + testInt(20.==11.), + testInt(2.!=2.), + testInt(2.!=11.), + testInt(20.!=11.), +#endif + // int, string (string is int) + testFalse(2<'2'), + testTrue(2<'11'), + testFalse(20<'11'), + testTrue(2<='2'), + testTrue(2<='11'), + testFalse(20<='11'), + testFalse(2>'2'), + testFalse(2>'11'), + testTrue(20>'11'), + testTrue(2>='2'), + testFalse(2>='11'), + testTrue(20>='11'), + testTrue(2=='2'), + testFalse(2=='11'), + testFalse(2!='2'), + testTrue(2!='11'), + // int, string (string is scalar) + testFalse(2<'2.'), + testTrue(2<'11.'), + testFalse(20<'11.'), + testTrue(2=='2.'), + testFalse(2=='11.'), +#ifdef SK_CAN_USE_FLOAT + // scalar, string + testFalse(2.<'2.'), + testTrue(2.<'11.'), + testFalse(20.<'11.'), + testTrue(2.=='2.'), + testFalse(2.=='11.'), + // string, int + testFalse('2'<2), + testTrue('2'<11), + testFalse('20'<11), + testTrue('2'==2), + testFalse('2'==11), + // string, scalar + testFalse('2'<2.), + testTrue('2'<11.), + testFalse('20'<11.), + testTrue('2'==2.), + testFalse('2'==11.), +#endif + // string, string + testFalse('2'<'2'), + testFalse('2'<'11'), + testFalse('20'<'11'), + testTrue('2'=='2'), + testFalse('2'=='11'), + // logic + testInt(1?2:3), + testInt(0?2:3), + testInt(1&&2||3), + testInt(1&&0||3), + testInt(1&&0||0), + testInt(1||0&&3), + testInt(0||0&&3), + testInt(0||1&&3), + testInt(1?(2?3:4):5), + testInt(0?(2?3:4):5), + testInt(1?(0?3:4):5), + testInt(0?(0?3:4):5), + testInt(1?2?3:4:5), + testInt(0?2?3:4:5), + testInt(1?0?3:4:5), + testInt(0?0?3:4:5), + + testInt(1?2:(3?4:5)), + testInt(0?2:(3?4:5)), + testInt(1?0:(3?4:5)), + testInt(0?0:(3?4:5)), + testInt(1?2:3?4:5), + testInt(0?2:3?4:5), + testInt(1?0:3?4:5), + testInt(0?0:3?4:5) +#ifdef SK_CAN_USE_FLOAT + , { "123.5", SkType_Float, 0, SkIntToScalar(123) + SK_Scalar1/2, DEF_STRING_ANSWER } +#endif +}; +#endif // build for brew + +#define SkScriptNAnswer_testCount SK_ARRAY_COUNT(scriptTests) + +void SkScriptEngine::UnitTest() { +#if !defined(SK_BUILD_FOR_BREW) + for (unsigned index = 0; index < SkScriptNAnswer_testCount; index++) { + SkScriptEngine engine(SkScriptEngine::ToOpType(scriptTests[index].fType)); + SkScriptValue value; + const char* script = scriptTests[index].fScript; + SkASSERT(engine.evaluateScript(&script, &value) == true); + SkASSERT(value.fType == scriptTests[index].fType); + SkScalar error; + switch (value.fType) { + case SkType_Int: + SkASSERT(value.fOperand.fS32 == scriptTests[index].fIntAnswer); + break; + case SkType_Float: + error = SkScalarAbs(value.fOperand.fScalar - scriptTests[index].fScalarAnswer); + SkASSERT(error < SK_Scalar1 / 10000); + break; + case SkType_String: + SkASSERT(strcmp(value.fOperand.fString->c_str(), scriptTests[index].fStringAnswer) == 0); + break; + default: + SkASSERT(0); + } + } +#endif +} +#endif + diff --git a/skia/animator/SkScript.h b/skia/animator/SkScript.h new file mode 100644 index 0000000..ca095ba --- /dev/null +++ b/skia/animator/SkScript.h @@ -0,0 +1,274 @@ +/* libs/graphics/animator/SkScript.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkScript_DEFINED +#define SkScript_DEFINED + +#include "SkOperand.h" +#include "SkIntArray.h" +#include "SkTDict.h" +#include "SkTDStack.h" + +class SkAnimateMaker; + +class SkScriptEngine { +public: + enum Error { + kNoError, + kArrayIndexOutOfBounds, + kCouldNotFindReferencedID, + kDotOperatorExpectsObject, + kErrorInArrrayIndex, + kErrorInFunctionParameters, + kExpectedArray, + kExpectedBooleanExpression, + kExpectedFieldName, + kExpectedHex, + kExpectedIntForConditionOperator, + kExpectedNumber, + kExpectedNumberForArrayIndex, + kExpectedOperator, + kExpectedToken, + kExpectedTokenBeforeDotOperator, + kExpectedValue, + kHandleMemberFailed, + kHandleMemberFunctionFailed, + kHandleUnboxFailed, + kIndexOutOfRange, + kMismatchedArrayBrace, + kMismatchedBrackets, + kNoFunctionHandlerFound, + kPrematureEnd, + kTooManyParameters, + kTypeConversionFailed, + kUnterminatedString + }; + + enum SkOpType { + kNoType, + kInt = 1, + kScalar = 2, + kString = 4, + kArray = 8, + kObject = 16 +// kStruct = 32 + }; + + typedef bool (*_boxCallBack)(void* userStorage, SkScriptValue* result); + typedef bool (*_functionCallBack)(const char* func, size_t len, SkTDArray<SkScriptValue>& params, + void* userStorage, SkScriptValue* result); + typedef bool (*_memberCallBack)(const char* member, size_t len, void* object, + void* userStorage, SkScriptValue* result); + typedef bool (*_memberFunctionCallBack)(const char* member, size_t len, void* object, + SkTDArray<SkScriptValue>& params, void* userStorage, SkScriptValue* result); +// typedef bool (*_objectToStringCallBack)(void* object, void* userStorage, SkScriptValue* result); + typedef bool (*_propertyCallBack)(const char* prop, size_t len, void* userStorage, SkScriptValue* result); + typedef bool (*_unboxCallBack)(void* userStorage, SkScriptValue* result); + SkScriptEngine(SkOpType returnType); + ~SkScriptEngine(); + void boxCallBack(_boxCallBack func, void* userStorage); + bool convertTo(SkDisplayTypes , SkScriptValue* ); + bool evaluateScript(const char** script, SkScriptValue* value); + void forget(SkTypedArray* array); + void functionCallBack(_functionCallBack func, void* userStorage); + Error getError() const { return fError; } +#ifdef SK_DEBUG + bool getErrorString(SkString* err) const; +#endif + void memberCallBack(_memberCallBack , void* userStorage); + void memberFunctionCallBack(_memberFunctionCallBack , void* userStorage); +// void objectToStringCallBack(_objectToStringCallBack , void* userStorage); + void propertyCallBack(_propertyCallBack prop, void* userStorage); + void track(SkTypedArray* array); + void track(SkString* string); + void unboxCallBack(_unboxCallBack func, void* userStorage); + static bool ConvertTo(SkScriptEngine* , SkDisplayTypes toType, SkScriptValue* value); + static SkScalar IntToScalar(int32_t ); + static SkDisplayTypes ToDisplayType(SkOpType type); + static SkOpType ToOpType(SkDisplayTypes type); + static bool ValueToString(SkScriptValue value, SkString* string); + + enum CallBackType { + kBox, + kFunction, + kMember, + kMemberFunction, + // kObjectToString, + kProperty, + kUnbox + }; + + struct UserCallBack { + CallBackType fCallBackType; + void* fUserStorage; + union { + _boxCallBack fBoxCallBack; + _functionCallBack fFunctionCallBack; + _memberCallBack fMemberCallBack; + _memberFunctionCallBack fMemberFunctionCallBack; + // _objectToStringCallBack fObjectToStringCallBack; + _propertyCallBack fPropertyCallBack; + _unboxCallBack fUnboxCallBack; + }; + }; + + enum SkOp { + kUnassigned, + kAdd, + kAddInt = kAdd, + kAddScalar, + kAddString, // string concat + kArrayOp, + kBitAnd, + kBitNot, + kBitOr, + kDivide, + kDivideInt = kDivide, + kDivideScalar, + kElse, + kEqual, + kEqualInt = kEqual, + kEqualScalar, + kEqualString, + kFlipOps, + kGreaterEqual, + kGreaterEqualInt = kGreaterEqual, + kGreaterEqualScalar, + kGreaterEqualString, + kIf, + kLogicalAnd, + kLogicalNot, + kLogicalOr, + kMinus, + kMinusInt = kMinus, + kMinusScalar, + kModulo, + kModuloInt = kModulo, + kModuloScalar, + kMultiply, + kMultiplyInt = kMultiply, + kMultiplyScalar, + kParen, + kShiftLeft, + kShiftRight, // signed + kSubtract, + kSubtractInt = kSubtract, + kSubtractScalar, + kXor, + kArtificialOp = 0x40 + }; + + enum SkOpBias { + kNoBias, + kTowardsNumber = 0, + kTowardsString + }; + +protected: + + struct SkOperatorAttributes { + unsigned int fLeftType : 3; // SkOpType, but only lower values + unsigned int fRightType : 3; // SkOpType, but only lower values + SkOpBias fBias : 1; + }; + + struct SkSuppress { // !!! could be compressed to a long + SkOp fOperator; // operand which enabled suppression + int fOpStackDepth; // depth when suppression operator was found + SkBool8 fSuppress; // set if suppression happens now, as opposed to later + SkBool8 fElse; // set on the : half of ? : + }; + + static const SkOperatorAttributes gOpAttributes[]; + static const signed char gPrecedence[]; + int arithmeticOp(char ch, char nextChar, bool lastPush); + void commonCallBack(CallBackType type, UserCallBack& callBack, void* userStorage); + bool convertParams(SkTDArray<SkScriptValue>&, const SkFunctionParamType* , + int paramTypeCount); + void convertToString(SkOperand& operand, SkDisplayTypes type) { + SkScriptValue scriptValue; + scriptValue.fOperand = operand; + scriptValue.fType = type; + convertTo(SkType_String, &scriptValue); + operand = scriptValue.fOperand; + } + bool evaluateDot(const char*& script, bool suppressed); + bool evaluateDotParam(const char*& script, bool suppressed, const char* field, size_t fieldLength); + bool functionParams(const char** scriptPtr, SkTDArray<SkScriptValue>& params); + bool handleArrayIndexer(const char** scriptPtr, bool suppressed); + bool handleBox(SkScriptValue* value); + bool handleFunction(const char** scriptPtr, bool suppressed); + bool handleMember(const char* field, size_t len, void* object); + bool handleMemberFunction(const char* field, size_t len, void* object, SkTDArray<SkScriptValue>& params); +// bool handleObjectToString(void* object); + bool handleProperty(bool suppressed); + bool handleUnbox(SkScriptValue* scriptValue); + bool innerScript(const char** scriptPtr, SkScriptValue* value); + int logicalOp(char ch, char nextChar); + Error opError(); + bool processOp(); + void setAnimateMaker(SkAnimateMaker* maker) { fMaker = maker; } + bool setError(Error , const char* pos); + enum SkBraceStyle { + // kStructBrace, + kArrayBrace, + kFunctionBrace + }; + +#if 0 + SkIntArray(SkBraceStyle) fBraceStack; // curly, square, function paren + SkIntArray(SkOp) fOpStack; + SkIntArray(SkOpType) fTypeStack; + SkTDOperandArray fOperandStack; + SkTDArray<SkSuppress> fSuppressStack; +#else + SkTDStack<SkBraceStyle> fBraceStack; // curly, square, function paren + SkTDStack<SkOp> fOpStack; + SkTDStack<SkOpType> fTypeStack; + SkTDStack<SkOperand> fOperandStack; + SkTDStack<SkSuppress> fSuppressStack; +#endif + SkAnimateMaker* fMaker; + SkTDTypedArrayArray fTrackArray; + SkTDStringArray fTrackString; + const char* fToken; // one-deep stack + size_t fTokenLength; + SkTDArray<UserCallBack> fUserCallBacks; + SkOpType fReturnType; + Error fError; + int fErrorPosition; +private: + friend class SkTypedArray; +#ifdef SK_SUPPORT_UNITTEST +public: + static void UnitTest(); +#endif +}; + +#ifdef SK_SUPPORT_UNITTEST + +struct SkScriptNAnswer { + const char* fScript; + SkDisplayTypes fType; + int32_t fIntAnswer; + SkScalar fScalarAnswer; + const char* fStringAnswer; +}; + +#endif + +#endif // SkScript_DEFINED diff --git a/skia/animator/SkScript2.h b/skia/animator/SkScript2.h new file mode 100644 index 0000000..4d8bd8c --- /dev/null +++ b/skia/animator/SkScript2.h @@ -0,0 +1,285 @@ +#ifndef SkScript2_DEFINED +#define SkScript2_DEFINED + +#include "SkOperand2.h" +#include "SkStream.h" +#include "SkTDArray.h" +#include "SkTDArray_Experimental.h" +#include "SkTDict.h" +#include "SkTDStack.h" + +typedef SkLongArray(SkString*) SkTDStringArray; + +class SkAnimateMaker; +class SkScriptCallBack; + +class SkScriptEngine2 { +public: + enum Error { + kNoError, + kArrayIndexOutOfBounds, + kCouldNotFindReferencedID, + kFunctionCallFailed, + kMemberOpFailed, + kPropertyOpFailed + }; + + enum Attrs { + kConstant, + kVariable + }; + + SkScriptEngine2(SkOperand2::OpType returnType); + ~SkScriptEngine2(); + bool convertTo(SkOperand2::OpType , SkScriptValue2* ); + bool evaluateScript(const char** script, SkScriptValue2* value); + void forget(SkOpArray* array); + Error getError() { return fError; } + SkOperand2::OpType getReturnType() { return fReturnType; } + void track(SkOpArray* array) { + SkASSERT(fTrackArray.find(array) < 0); + *fTrackArray.append() = array; } + void track(SkString* string) { + SkASSERT(fTrackString.find(string) < 0); + *fTrackString.append() = string; + } + static bool ConvertTo(SkScriptEngine2* , SkOperand2::OpType toType, SkScriptValue2* value); + static SkScalar IntToScalar(int32_t ); + static bool ValueToString(const SkScriptValue2& value, SkString* string); + + enum Op { // used by tokenizer attribute table + kUnassigned, + kAdd, + kBitAnd, + kBitNot, + kBitOr, + kDivide, + kEqual, + kFlipOps, + kGreaterEqual, + kLogicalAnd, + kLogicalNot, + kLogicalOr, + kMinus, + kModulo, + kMultiply, + kShiftLeft, + kShiftRight, // signed + kSubtract, + kXor, +// following not in attribute table + kArrayOp, + kElse, + kIf, + kParen, + kLastLogicalOp, + kArtificialOp = 0x20 + }; + + enum TypeOp { // generated by tokenizer + kNop, // should never get generated + kAccumulatorPop, + kAccumulatorPush, + kAddInt, + kAddScalar, + kAddString, // string concat + kArrayIndex, + kArrayParam, + kArrayToken, + kBitAndInt, + kBitNotInt, + kBitOrInt, + kBoxToken, + kCallback, + kDivideInt, + kDivideScalar, + kDotOperator, + kElseOp, + kEnd, + kEqualInt, + kEqualScalar, + kEqualString, + kFunctionCall, + kFlipOpsOp, + kFunctionToken, + kGreaterEqualInt, + kGreaterEqualScalar, + kGreaterEqualString, + kIfOp, + kIntToScalar, + kIntToScalar2, + kIntToString, + kIntToString2, + kIntegerAccumulator, + kIntegerOperand, + kLogicalAndInt, + kLogicalNotInt, + kLogicalOrInt, + kMemberOp, + kMinusInt, + kMinusScalar, + kModuloInt, + kModuloScalar, + kMultiplyInt, + kMultiplyScalar, + kPropertyOp, + kScalarAccumulator, + kScalarOperand, + kScalarToInt, + kScalarToInt2, + kScalarToString, + kScalarToString2, + kShiftLeftInt, + kShiftRightInt, // signed + kStringAccumulator, + kStringOperand, + kStringToInt, + kStringToScalar, + kStringToScalar2, + kStringTrack, + kSubtractInt, + kSubtractScalar, + kToBool, + kUnboxToken, + kUnboxToken2, + kXorInt, + kLastTypeOp + }; + + enum OpBias { + kNoBias, + kTowardsNumber = 0, + kTowardsString + }; + +protected: + + enum BraceStyle { + // kStructBrace, + kArrayBrace, + kFunctionBrace + }; + + enum AddTokenRegister { + kAccumulator, + kOperand + }; + + enum ResultIsBoolean { + kResultIsNotBoolean, + kResultIsBoolean + }; + + struct OperatorAttributes { + unsigned int fLeftType : 3; // SkOpType union, but only lower values + unsigned int fRightType : 3; // SkOpType union, but only lower values + OpBias fBias : 1; + ResultIsBoolean fResultIsBoolean : 1; + }; + + struct Branch { + Branch() { + } + + Branch(Op op, int depth, unsigned offset) : fOffset(offset), fOpStackDepth(depth), fOperator(op), + fPrimed(kIsNotPrimed), fDone(kIsNotDone) { + } + + enum Primed { + kIsNotPrimed, + kIsPrimed + }; + + enum Done { + kIsNotDone, + kIsDone, + }; + + unsigned fOffset : 16; // offset in generated stream where branch needs to go + int fOpStackDepth : 7; // depth when operator was found + Op fOperator : 6; // operand which generated branch + mutable Primed fPrimed : 1; // mark when next instruction generates branch + Done fDone : 1; // mark when branch is complete + void prime() { fPrimed = kIsPrimed; } + void resolve(SkDynamicMemoryWStream* , size_t offset); + }; + + static const OperatorAttributes gOpAttributes[]; + static const signed char gPrecedence[]; + static const TypeOp gTokens[]; + void addToken(TypeOp ); + void addTokenConst(SkScriptValue2* , AddTokenRegister , SkOperand2::OpType , TypeOp ); + void addTokenInt(int ); + void addTokenScalar(SkScalar ); + void addTokenString(const SkString& ); + void addTokenValue(const SkScriptValue2& , AddTokenRegister ); + int arithmeticOp(char ch, char nextChar, bool lastPush); + bool convertParams(SkTDArray<SkScriptValue2>* , + const SkOperand2::OpType* paramTypes, int paramTypeCount); + void convertToString(SkOperand2* operand, SkOperand2::OpType type) { + SkScriptValue2 scriptValue; + scriptValue.fOperand = *operand; + scriptValue.fType = type; + convertTo(SkOperand2::kString, &scriptValue); + *operand = scriptValue.fOperand; + } + bool evaluateDot(const char*& script); + bool evaluateDotParam(const char*& script, const char* field, size_t fieldLength); + bool functionParams(const char** scriptPtr, SkTDArray<SkScriptValue2>* params); + size_t getTokenOffset(); + SkOperand2::OpType getUnboxType(SkOperand2 scriptValue); + bool handleArrayIndexer(const char** scriptPtr); + bool handleFunction(const char** scriptPtr); + bool handleMember(const char* field, size_t len, void* object); + bool handleMemberFunction(const char* field, size_t len, void* object, + SkTDArray<SkScriptValue2>* params); + bool handleProperty(); + bool handleUnbox(SkScriptValue2* scriptValue); + bool innerScript(const char** scriptPtr, SkScriptValue2* value); + int logicalOp(char ch, char nextChar); + void processLogicalOp(Op op); + bool processOp(); + void resolveBranch(Branch& ); +// void setAnimateMaker(SkAnimateMaker* maker) { fMaker = maker; } + SkDynamicMemoryWStream fStream; + SkDynamicMemoryWStream* fActiveStream; + SkTDStack<BraceStyle> fBraceStack; // curly, square, function paren + SkTDStack<Branch> fBranchStack; // logical operators, slot to store forward branch + SkLongArray(SkScriptCallBack*) fCallBackArray; + SkTDStack<Op> fOpStack; + SkTDStack<SkScriptValue2> fValueStack; +// SkAnimateMaker* fMaker; + SkLongArray(SkOpArray*) fTrackArray; + SkTDStringArray fTrackString; + const char* fToken; // one-deep stack + size_t fTokenLength; + SkOperand2::OpType fReturnType; + Error fError; + SkOperand2::OpType fAccumulatorType; // tracking for code generation + SkBool fBranchPopAllowed; + SkBool fConstExpression; + SkBool fOperandInUse; +private: +#ifdef SK_DEBUG +public: + void decompile(const unsigned char* , size_t ); + static void UnitTest(); + static void ValidateDecompileTable(); +#endif +}; + +#ifdef SK_DEBUG + +struct SkScriptNAnswer2 { + const char* fScript; + SkOperand2::OpType fType; + int32_t fIntAnswer; + SkScalar fScalarAnswer; + const char* fStringAnswer; +}; + +#endif + + +#endif // SkScript2_DEFINED + diff --git a/skia/animator/SkScriptCallBack.h b/skia/animator/SkScriptCallBack.h new file mode 100644 index 0000000..725e493 --- /dev/null +++ b/skia/animator/SkScriptCallBack.h @@ -0,0 +1,58 @@ +#ifndef SkScriptCallBack_DEFINED +#define SkScriptCallBack_DEFINED + +#include "SkOperand2.h" +#include "SkTDArray_Experimental.h" + +class SkScriptCallBack { +public: + enum Type { + kBox, + kFunction, + kMember, + kMemberFunction, + kProperty, + kUnbox + }; + + virtual bool getReference(const char* , size_t len, SkScriptValue2* result) { return false; } + virtual SkOperand2::OpType getReturnType(size_t ref, SkOperand2*) { + return SkOperand2::kS32; } + virtual Type getType() const = 0; +}; + +class SkScriptCallBackConvert : public SkScriptCallBack { +public: + virtual bool convert(SkOperand2::OpType type, SkOperand2* operand) = 0; +}; + +class SkScriptCallBackFunction : public SkScriptCallBack { +public: + virtual void getParamTypes(SkIntArray(SkOperand2::OpType)* types) = 0; + virtual Type getType() const { return kFunction; } + virtual bool invoke(size_t ref, SkOpArray* params, SkOperand2* value) = 0; +}; + +class SkScriptCallBackMember: public SkScriptCallBack { +public: + bool getMemberReference(const char* , size_t len, void* object, SkScriptValue2* ref); + virtual Type getType() const { return kMember; } + virtual bool invoke(size_t ref, void* object, SkOperand2* value) = 0; +}; + +class SkScriptCallBackMemberFunction : public SkScriptCallBack { +public: + bool getMemberReference(const char* , size_t len, void* object, SkScriptValue2* ref); + virtual void getParamTypes(SkIntArray(SkOperand2::OpType)* types) = 0; + virtual Type getType() const { return kMemberFunction; } + virtual bool invoke(size_t ref, void* object, SkOpArray* params, SkOperand2* value) = 0; +}; + +class SkScriptCallBackProperty : public SkScriptCallBack { +public: + virtual bool getConstValue(const char* name, size_t len, SkOperand2* value) { return false; } + virtual bool getResult(size_t ref, SkOperand2* answer) { return false; } + virtual Type getType() const { return kProperty; } +}; + +#endif // SkScriptCallBack_DEFINED diff --git a/skia/animator/SkScriptDecompile.cpp b/skia/animator/SkScriptDecompile.cpp new file mode 100644 index 0000000..22bd7b4 --- /dev/null +++ b/skia/animator/SkScriptDecompile.cpp @@ -0,0 +1,221 @@ +/* libs/graphics/animator/SkScriptDecompile.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkScript2.h" + +#ifdef SK_DEBUG + +#define TypeOpName(op) {SkScriptEngine2::op, #op } + +static const struct OpName { + SkScriptEngine2::TypeOp fOp; + const char* fName; +} gOpNames[] = { + TypeOpName(kNop), // should never get generated + TypeOpName(kAccumulatorPop), + TypeOpName(kAccumulatorPush), + TypeOpName(kAddInt), + TypeOpName(kAddScalar), + TypeOpName(kAddString), // string concat + TypeOpName(kArrayIndex), + TypeOpName(kArrayParam), + TypeOpName(kArrayToken), + TypeOpName(kBitAndInt), + TypeOpName(kBitNotInt), + TypeOpName(kBitOrInt), + TypeOpName(kBoxToken), + TypeOpName(kCallback), + TypeOpName(kDivideInt), + TypeOpName(kDivideScalar), + TypeOpName(kDotOperator), + TypeOpName(kElseOp), + TypeOpName(kEnd), + TypeOpName(kEqualInt), + TypeOpName(kEqualScalar), + TypeOpName(kEqualString), + TypeOpName(kFunctionCall), + TypeOpName(kFlipOpsOp), + TypeOpName(kFunctionToken), + TypeOpName(kGreaterEqualInt), + TypeOpName(kGreaterEqualScalar), + TypeOpName(kGreaterEqualString), + TypeOpName(kIfOp), + TypeOpName(kIntToScalar), + TypeOpName(kIntToScalar2), + TypeOpName(kIntToString), + TypeOpName(kIntToString2), + TypeOpName(kIntegerAccumulator), + TypeOpName(kIntegerOperand), + TypeOpName(kLogicalAndInt), + TypeOpName(kLogicalNotInt), + TypeOpName(kLogicalOrInt), + TypeOpName(kMemberOp), + TypeOpName(kMinusInt), + TypeOpName(kMinusScalar), + TypeOpName(kModuloInt), + TypeOpName(kModuloScalar), + TypeOpName(kMultiplyInt), + TypeOpName(kMultiplyScalar), + TypeOpName(kPropertyOp), + TypeOpName(kScalarAccumulator), + TypeOpName(kScalarOperand), + TypeOpName(kScalarToInt), + TypeOpName(kScalarToInt2), + TypeOpName(kScalarToString), + TypeOpName(kScalarToString2), + TypeOpName(kShiftLeftInt), + TypeOpName(kShiftRightInt), // signed + TypeOpName(kStringAccumulator), + TypeOpName(kStringOperand), + TypeOpName(kStringToInt), + TypeOpName(kStringToScalar), + TypeOpName(kStringToScalar2), + TypeOpName(kStringTrack), + TypeOpName(kSubtractInt), + TypeOpName(kSubtractScalar), + TypeOpName(kToBool), + TypeOpName(kUnboxToken), + TypeOpName(kUnboxToken2), + TypeOpName(kXorInt) +}; + +static size_t gOpNamesSize = sizeof(gOpNames) / sizeof(gOpNames[0]); + +#define OperandName(op) {SkOperand2::op, #op } + +static const struct OperName { + SkOperand2::OpType fType; + const char* fName; +} gOperandNames[] = { + OperandName(kNoType), + OperandName(kS32), + OperandName(kScalar), + OperandName(kString), + OperandName(kArray), + OperandName(kObject) +}; + +static size_t gOperandNamesSize = sizeof(gOperandNames) / sizeof(gOperandNames[0]); + +// check to see that there are no missing or duplicate entries +void SkScriptEngine2::ValidateDecompileTable() { + SkScriptEngine2::TypeOp op = SkScriptEngine2::kNop; + int index; + for (index = 0; index < gOpNamesSize; index++) { + SkASSERT(gOpNames[index].fOp == op); + op = (SkScriptEngine2::TypeOp) (op + 1); + } + index = 0; + SkOperand2::OpType type = SkOperand2::kNoType; + SkASSERT(gOperandNames[index].fType == type); + for (; index < gOperandNamesSize - 1; ) { + type = (SkOperand2::OpType) (1 << index); + SkASSERT(gOperandNames[++index].fType == type); + } +} + +void SkScriptEngine2::decompile(const unsigned char* start, size_t length) { + SkASSERT(length > 0); + const unsigned char* opCode = start; + do { + SkASSERT(opCode - start < length); + SkScriptEngine2::TypeOp op = (SkScriptEngine2::TypeOp) *opCode++; + SkASSERT(op < gOpNamesSize); + SkDebugf("%d: %s", opCode - start - 1, gOpNames[op].fName); + switch (op) { + case SkScriptEngine2::kCallback: { + int index; + memcpy(&index, opCode, sizeof(index)); + opCode += sizeof(index); + SkDebugf(" index: %d", index); + } break; + case SkScriptEngine2::kFunctionCall: + case SkScriptEngine2::kMemberOp: + case SkScriptEngine2::kPropertyOp: { + size_t ref; + memcpy(&ref, opCode, sizeof(ref)); + opCode += sizeof(ref); + SkDebugf(" ref: %d", ref); + } break; + case SkScriptEngine2::kIntegerAccumulator: + case SkScriptEngine2::kIntegerOperand: { + int32_t integer; + memcpy(&integer, opCode, sizeof(integer)); + opCode += sizeof(int32_t); + SkDebugf(" integer: %d", integer); + } break; + case SkScriptEngine2::kScalarAccumulator: + case SkScriptEngine2::kScalarOperand: { + SkScalar scalar; + memcpy(&scalar, opCode, sizeof(scalar)); + opCode += sizeof(SkScalar); +#ifdef SK_CAN_USE_FLOAT + SkDebugf(" scalar: %g", SkScalarToFloat(scalar)); +#else + SkDebugf(" scalar: %x", scalar); +#endif + } break; + case SkScriptEngine2::kStringAccumulator: + case SkScriptEngine2::kStringOperand: { + int size; + SkString* strPtr = new SkString(); + memcpy(&size, opCode, sizeof(size)); + opCode += sizeof(size); + strPtr->set((char*) opCode, size); + opCode += size; + SkDebugf(" string: %s", strPtr->c_str()); + delete strPtr; + } break; + case SkScriptEngine2::kBoxToken: { + SkOperand2::OpType type; + memcpy(&type, opCode, sizeof(type)); + opCode += sizeof(type); + int index = 0; + if (type == 0) + SkDebugf(" type: %s", gOperandNames[index].fName); + else { + while (type != 0) { + SkASSERT(index + 1 < gOperandNamesSize); + if (type & (1 << index)) { + type = (SkOperand2::OpType) (type & ~(1 << index)); + SkDebugf(" type: %s", gOperandNames[index + 1].fName); + } + index++; + } + } + } break; + case SkScriptEngine2::kIfOp: + case SkScriptEngine2::kLogicalAndInt: + case SkScriptEngine2::kElseOp: + case SkScriptEngine2::kLogicalOrInt: { + int size; + memcpy(&size, opCode, sizeof(size)); + opCode += sizeof(size); + SkDebugf(" offset (address): %d (%d)", size, opCode - start + size); + } break; + case SkScriptEngine2::kEnd: + goto done; + case SkScriptEngine2::kNop: + SkASSERT(0); + } + SkDebugf("\n"); + } while (true); +done: + SkDebugf("\n"); +} + +#endif diff --git a/skia/animator/SkScriptRuntime.cpp b/skia/animator/SkScriptRuntime.cpp new file mode 100644 index 0000000..6d8c208 --- /dev/null +++ b/skia/animator/SkScriptRuntime.cpp @@ -0,0 +1,342 @@ +#include "SkScriptRuntime.h" +#include "SkScript2.h" +#include "SkParse.h" +#include "SkScriptCallBack.h" +#include "SkString.h" +#include "SkOpArray.h" + +// script tokenizer + +// turn text into token string +// turn number literals into inline UTF8-style values +// process operators to turn standard notation into stack notation + +// defer processing until the tokens can all be resolved +// then, turn token strings into indices into the appropriate tables / dictionaries + +// consider: const evaluation? + +// replace script string with script tokens preceeded by special value + +// need second version of script plugins that return private index of found value? + // then would need in script index of plugin, private index + +// encode brace stack push/pop as opcodes + +// should token script enocde type where possible? + +// current flow: + // strip whitespace + // if in array brace [ recurse, continue + // if token, handle function, or array, or property (continue) + // parse number, continue + // parse token, continue + // parse string literal, continue + // if dot operator, handle dot, continue + // if [ , handle array literal or accessor, continue + // if ), pop (if function, break) + // if ], pop ; if ',' break + // handle logical ops + // or, handle arithmetic ops + // loop + +// !!! things to do + // add separate processing loop to advance while suppressed + // or, include jump offset to skip suppressed code? + +SkScriptRuntime::~SkScriptRuntime() { + for (SkString** stringPtr = fTrackString.begin(); stringPtr < fTrackString.end(); stringPtr++) + delete *stringPtr; + for (SkOpArray** arrayPtr = fTrackArray.begin(); arrayPtr < fTrackArray.end(); arrayPtr++) + delete *arrayPtr; +} + +bool SkScriptRuntime::executeTokens(unsigned char* opCode) { + SkOperand2 operand[2]; // 1=accumulator and 2=operand + SkScriptEngine2::TypeOp op; + size_t ref; + int index, size; + int registerLoad; + SkScriptCallBack* callBack SK_INIT_TO_AVOID_WARNING; + do { + switch ((op = (SkScriptEngine2::TypeOp) *opCode++)) { + case SkScriptEngine2::kArrayToken: // create an array + operand[0].fArray = new SkOpArray(SkOperand2::kNoType /*fReturnType*/); + break; + case SkScriptEngine2::kArrayIndex: // array accessor + index = operand[1].fS32; + if (index >= operand[0].fArray->count()) { + fError = kArrayIndexOutOfBounds; + return false; + } + operand[0] = operand[0].fArray->begin()[index]; + break; + case SkScriptEngine2::kArrayParam: // array initializer, or function param + *operand[0].fArray->append() = operand[1]; + break; + case SkScriptEngine2::kCallback: + memcpy(&index, opCode, sizeof(index)); + opCode += sizeof(index); + callBack = fCallBackArray[index]; + break; + case SkScriptEngine2::kFunctionCall: { + memcpy(&ref, opCode, sizeof(ref)); + opCode += sizeof(ref); + SkScriptCallBackFunction* callBackFunction = (SkScriptCallBackFunction*) callBack; + if (callBackFunction->invoke(ref, operand[0].fArray, /* params */ + &operand[0] /* result */) == false) { + fError = kFunctionCallFailed; + return false; + } + } break; + case SkScriptEngine2::kMemberOp: { + memcpy(&ref, opCode, sizeof(ref)); + opCode += sizeof(ref); + SkScriptCallBackMember* callBackMember = (SkScriptCallBackMember*) callBack; + if (callBackMember->invoke(ref, operand[0].fObject, &operand[0]) == false) { + fError = kMemberOpFailed; + return false; + } + } break; + case SkScriptEngine2::kPropertyOp: { + memcpy(&ref, opCode, sizeof(ref)); + opCode += sizeof(ref); + SkScriptCallBackProperty* callBackProperty = (SkScriptCallBackProperty*) callBack; + if (callBackProperty->getResult(ref, &operand[0])== false) { + fError = kPropertyOpFailed; + return false; + } + } break; + case SkScriptEngine2::kAccumulatorPop: + fRunStack.pop(&operand[0]); + break; + case SkScriptEngine2::kAccumulatorPush: + *fRunStack.push() = operand[0]; + break; + case SkScriptEngine2::kIntegerAccumulator: + case SkScriptEngine2::kIntegerOperand: + registerLoad = op - SkScriptEngine2::kIntegerAccumulator; + memcpy(&operand[registerLoad].fS32, opCode, sizeof(int32_t)); + opCode += sizeof(int32_t); + break; + case SkScriptEngine2::kScalarAccumulator: + case SkScriptEngine2::kScalarOperand: + registerLoad = op - SkScriptEngine2::kScalarAccumulator; + memcpy(&operand[registerLoad].fScalar, opCode, sizeof(SkScalar)); + opCode += sizeof(SkScalar); + break; + case SkScriptEngine2::kStringAccumulator: + case SkScriptEngine2::kStringOperand: { + SkString* strPtr = new SkString(); + track(strPtr); + registerLoad = op - SkScriptEngine2::kStringAccumulator; + memcpy(&size, opCode, sizeof(size)); + opCode += sizeof(size); + strPtr->set((char*) opCode, size); + opCode += size; + operand[registerLoad].fString = strPtr; + } break; + case SkScriptEngine2::kStringTrack: // call after kObjectToValue + track(operand[0].fString); + break; + case SkScriptEngine2::kBoxToken: { + SkOperand2::OpType type; + memcpy(&type, opCode, sizeof(type)); + opCode += sizeof(type); + SkScriptCallBackConvert* callBackBox = (SkScriptCallBackConvert*) callBack; + if (callBackBox->convert(type, &operand[0]) == false) + return false; + } break; + case SkScriptEngine2::kUnboxToken: + case SkScriptEngine2::kUnboxToken2: { + SkScriptCallBackConvert* callBackUnbox = (SkScriptCallBackConvert*) callBack; + if (callBackUnbox->convert(SkOperand2::kObject, &operand[0]) == false) + return false; + } break; + case SkScriptEngine2::kIfOp: + case SkScriptEngine2::kLogicalAndInt: + memcpy(&size, opCode, sizeof(size)); + opCode += sizeof(size); + if (operand[0].fS32 == 0) + opCode += size; // skip to else (or end of if predicate) + break; + case SkScriptEngine2::kElseOp: + memcpy(&size, opCode, sizeof(size)); + opCode += sizeof(size); + opCode += size; // if true: after predicate, always skip to end of else + break; + case SkScriptEngine2::kLogicalOrInt: + memcpy(&size, opCode, sizeof(size)); + opCode += sizeof(size); + if (operand[0].fS32 != 0) + opCode += size; // skip to kToBool opcode after || predicate + break; + // arithmetic conversion ops + case SkScriptEngine2::kFlipOpsOp: + SkTSwap(operand[0], operand[1]); + break; + case SkScriptEngine2::kIntToString: + case SkScriptEngine2::kIntToString2: + case SkScriptEngine2::kScalarToString: + case SkScriptEngine2::kScalarToString2:{ + SkString* strPtr = new SkString(); + track(strPtr); + if (op == SkScriptEngine2::kIntToString || op == SkScriptEngine2::kIntToString2) + strPtr->appendS32(operand[op - SkScriptEngine2::kIntToString].fS32); + else + strPtr->appendScalar(operand[op - SkScriptEngine2::kScalarToString].fScalar); + operand[0].fString = strPtr; + } break; + case SkScriptEngine2::kIntToScalar: + case SkScriptEngine2::kIntToScalar2: + operand[0].fScalar = SkScriptEngine2::IntToScalar(operand[op - SkScriptEngine2::kIntToScalar].fS32); + break; + case SkScriptEngine2::kStringToInt: + if (SkParse::FindS32(operand[0].fString->c_str(), &operand[0].fS32) == false) + return false; + break; + case SkScriptEngine2::kStringToScalar: + case SkScriptEngine2::kStringToScalar2: + if (SkParse::FindScalar(operand[0].fString->c_str(), + &operand[op - SkScriptEngine2::kStringToScalar].fScalar) == false) + return false; + break; + case SkScriptEngine2::kScalarToInt: + operand[0].fS32 = SkScalarFloor(operand[0].fScalar); + break; + // arithmetic ops + case SkScriptEngine2::kAddInt: + operand[0].fS32 += operand[1].fS32; + break; + case SkScriptEngine2::kAddScalar: + operand[0].fScalar += operand[1].fScalar; + break; + case SkScriptEngine2::kAddString: +// if (fTrackString.find(operand[1].fString) < 0) { +// operand[1].fString = SkNEW_ARGS(SkString, (*operand[1].fString)); +// track(operand[1].fString); +// } + operand[0].fString->append(*operand[1].fString); + break; + case SkScriptEngine2::kBitAndInt: + operand[0].fS32 &= operand[1].fS32; + break; + case SkScriptEngine2::kBitNotInt: + operand[0].fS32 = ~operand[0].fS32; + break; + case SkScriptEngine2::kBitOrInt: + operand[0].fS32 |= operand[1].fS32; + break; + case SkScriptEngine2::kDivideInt: + SkASSERT(operand[1].fS32 != 0); + if (operand[1].fS32 == 0) + operand[0].fS32 = operand[0].fS32 == 0 ? SK_NaN32 : + operand[0].fS32 > 0 ? SK_MaxS32 : -SK_MaxS32; + else + if (operand[1].fS32 != 0) // throw error on divide by zero? + operand[0].fS32 /= operand[1].fS32; + break; + case SkScriptEngine2::kDivideScalar: + if (operand[1].fScalar == 0) + operand[0].fScalar = operand[0].fScalar == 0 ? SK_ScalarNaN : + operand[0].fScalar > 0 ? SK_ScalarMax : -SK_ScalarMax; + else + operand[0].fScalar = SkScalarDiv(operand[0].fScalar, operand[1].fScalar); + break; + case SkScriptEngine2::kEqualInt: + operand[0].fS32 = operand[0].fS32 == operand[1].fS32; + break; + case SkScriptEngine2::kEqualScalar: + operand[0].fS32 = operand[0].fScalar == operand[1].fScalar; + break; + case SkScriptEngine2::kEqualString: + operand[0].fS32 = *operand[0].fString == *operand[1].fString; + break; + case SkScriptEngine2::kGreaterEqualInt: + operand[0].fS32 = operand[0].fS32 >= operand[1].fS32; + break; + case SkScriptEngine2::kGreaterEqualScalar: + operand[0].fS32 = operand[0].fScalar >= operand[1].fScalar; + break; + case SkScriptEngine2::kGreaterEqualString: + operand[0].fS32 = strcmp(operand[0].fString->c_str(), operand[1].fString->c_str()) >= 0; + break; + case SkScriptEngine2::kToBool: + operand[0].fS32 = !! operand[0].fS32; + break; + case SkScriptEngine2::kLogicalNotInt: + operand[0].fS32 = ! operand[0].fS32; + break; + case SkScriptEngine2::kMinusInt: + operand[0].fS32 = -operand[0].fS32; + break; + case SkScriptEngine2::kMinusScalar: + operand[0].fScalar = -operand[0].fScalar; + break; + case SkScriptEngine2::kModuloInt: + operand[0].fS32 %= operand[1].fS32; + break; + case SkScriptEngine2::kModuloScalar: + operand[0].fScalar = SkScalarMod(operand[0].fScalar, operand[1].fScalar); + break; + case SkScriptEngine2::kMultiplyInt: + operand[0].fS32 *= operand[1].fS32; + break; + case SkScriptEngine2::kMultiplyScalar: + operand[0].fScalar = SkScalarMul(operand[0].fScalar, operand[1].fScalar); + break; + case SkScriptEngine2::kShiftLeftInt: + operand[0].fS32 <<= operand[1].fS32; + break; + case SkScriptEngine2::kShiftRightInt: + operand[0].fS32 >>= operand[1].fS32; + break; + case SkScriptEngine2::kSubtractInt: + operand[0].fS32 -= operand[1].fS32; + break; + case SkScriptEngine2::kSubtractScalar: + operand[0].fScalar -= operand[1].fScalar; + break; + case SkScriptEngine2::kXorInt: + operand[0].fS32 ^= operand[1].fS32; + break; + case SkScriptEngine2::kEnd: + goto done; + case SkScriptEngine2::kNop: + SkASSERT(0); + } + } while (true); +done: + fRunStack.push(operand[0]); + return true; +} + +bool SkScriptRuntime::getResult(SkOperand2* result) { + if (fRunStack.count() == 0) + return false; + fRunStack.pop(result); + return true; +} + +void SkScriptRuntime::track(SkOpArray* array) { + SkASSERT(fTrackArray.find(array) < 0); + *fTrackArray.append() = array; +} + +void SkScriptRuntime::track(SkString* string) { + SkASSERT(fTrackString.find(string) < 0); + *fTrackString.append() = string; +} + +void SkScriptRuntime::untrack(SkOpArray* array) { + int index = fTrackArray.find(array); + SkASSERT(index >= 0); + fTrackArray.begin()[index] = NULL; +} + +void SkScriptRuntime::untrack(SkString* string) { + int index = fTrackString.find(string); + SkASSERT(index >= 0); + fTrackString.begin()[index] = NULL; +} + diff --git a/skia/animator/SkScriptRuntime.h b/skia/animator/SkScriptRuntime.h new file mode 100644 index 0000000..c864fe4 --- /dev/null +++ b/skia/animator/SkScriptRuntime.h @@ -0,0 +1,43 @@ +#ifndef SkScriptRuntime_DEFINED +#define SkScriptRuntime_DEFINED + +#include "SkOperand2.h" +#include "SkTDArray_Experimental.h" +#include "SkTDStack.h" + +class SkScriptCallBack; + +typedef SkLongArray(SkString*) SkTDStringArray; +typedef SkLongArray(SkScriptCallBack*) SkTDScriptCallBackArray; + +class SkScriptRuntime { +public: + enum SkError { + kNoError, + kArrayIndexOutOfBounds, + kCouldNotFindReferencedID, + kFunctionCallFailed, + kMemberOpFailed, + kPropertyOpFailed + }; + + SkScriptRuntime(SkTDScriptCallBackArray& callBackArray) : fCallBackArray(callBackArray) + { } + ~SkScriptRuntime(); + bool executeTokens(unsigned char* opCode); + bool getResult(SkOperand2* result); + void untrack(SkOpArray* array); + void untrack(SkString* string); +private: + void track(SkOpArray* array); + void track(SkString* string); + SkTDScriptCallBackArray& fCallBackArray; + SkError fError; + SkTDStack<SkOperand2> fRunStack; + SkLongArray(SkOpArray*) fTrackArray; + SkTDStringArray fTrackString; + // illegal + SkScriptRuntime& operator=(const SkScriptRuntime&); +}; + +#endif // SkScriptRuntime_DEFINED
\ No newline at end of file diff --git a/skia/animator/SkScriptTokenizer.cpp b/skia/animator/SkScriptTokenizer.cpp new file mode 100644 index 0000000..d75e68e --- /dev/null +++ b/skia/animator/SkScriptTokenizer.cpp @@ -0,0 +1,1514 @@ +#include "SkScript2.h" +#include "SkFloatingPoint.h" +#include "SkMath.h" +#include "SkParse.h" +#include "SkScriptCallBack.h" +#include "SkScriptRuntime.h" +#include "SkString.h" +#include "SkOpArray.h" + +const SkScriptEngine2::OperatorAttributes SkScriptEngine2::gOpAttributes[] = { +{ SkOperand2::kNoType }, +{ SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar | SkOperand2::kString), + SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar | SkOperand2::kString), kTowardsString }, // kAdd +{ SkOperand2::kS32, SkOperand2::kS32, kNoBias }, // kBitAnd +{ SkOperand2::kNoType, SkOperand2::kS32, kNoBias }, // kBitNot +{ SkOperand2::kS32, SkOperand2::kS32, kNoBias }, // kBitOr +{ SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar), + SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar), kNoBias }, // kDivide +{ SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar | SkOperand2::kString), + SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar |SkOperand2:: kString), kTowardsNumber, + kResultIsBoolean }, // kEqual +{ SkOperand2::kS32 }, // kFlipOps +{ SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar | SkOperand2::kString), + SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar | SkOperand2::kString), kTowardsNumber, + kResultIsBoolean }, // kGreaterEqual +{ SkOperand2::kNoType, SkOperand2::kS32, kNoBias }, // kLogicalAnd (really, ToBool) +{ SkOperand2::kNoType, SkOperand2::kS32, kNoBias }, // kLogicalNot +{ SkOperand2::kS32, SkOperand2::kS32, kNoBias }, // kLogicalOr +{ SkOperand2::kNoType, SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar), kNoBias }, // kMinus +{ SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar), + SkOperand2::OpType(SkOperand2::kS32 |SkOperand2:: kScalar), kNoBias }, // kModulo +{ SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar), + SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar), kNoBias }, // kMultiply +{ SkOperand2::kS32, SkOperand2::kS32, kNoBias }, // kShiftLeft +{ SkOperand2::kS32, SkOperand2::kS32, kNoBias }, // kShiftRight +{ SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar), + SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar), kNoBias }, // kSubtract +{ SkOperand2::kS32, SkOperand2::kS32, kNoBias } // kXor +}; + +#define kBracketPrecedence 16 +#define kIfElsePrecedence 15 + +const signed char SkScriptEngine2::gPrecedence[] = { + 17, // kUnassigned, + 6, // kAdd, + 10, // kBitAnd, + 4, // kBitNot, + 12, // kBitOr, + 5, // kDivide, + 9, // kEqual, + -1, // kFlipOps, + 8, // kGreaterEqual, + 13, // kLogicalAnd, + 4, // kLogicalNot, + 14, // kLogicalOr, + 4, // kMinus, + 5, // kModulo, + 5, // kMultiply, + 7, // kShiftLeft, + 7, // kShiftRight, // signed + 6, // kSubtract, + 11, // kXor + kBracketPrecedence, // kArrayOp + kIfElsePrecedence, // kElse + kIfElsePrecedence, // kIf + kBracketPrecedence, // kParen +}; + +const SkScriptEngine2::TypeOp SkScriptEngine2::gTokens[] = { + kNop, // unassigned + kAddInt, // kAdd, + kBitAndInt, // kBitAnd, + kBitNotInt, // kBitNot, + kBitOrInt, // kBitOr, + kDivideInt, // kDivide, + kEqualInt, // kEqual, + kFlipOpsOp, // kFlipOps, + kGreaterEqualInt, // kGreaterEqual, + kLogicalAndInt, // kLogicalAnd, + kLogicalNotInt, // kLogicalNot, + kLogicalOrInt, // kLogicalOr, + kMinusInt, // kMinus, + kModuloInt, // kModulo, + kMultiplyInt, // kMultiply, + kShiftLeftInt, // kShiftLeft, + kShiftRightInt, // kShiftRight, // signed + kSubtractInt, // kSubtract, + kXorInt // kXor +}; + +static inline bool is_between(int c, int min, int max) +{ + return (unsigned)(c - min) <= (unsigned)(max - min); +} + +static inline bool is_ws(int c) +{ + return is_between(c, 1, 32); +} + +static int token_length(const char* start) { + char ch = start[0]; + if (! is_between(ch, 'a' , 'z') && ! is_between(ch, 'A', 'Z') && ch != '_' && ch != '$') + return -1; + int length = 0; + do + ch = start[++length]; + while (is_between(ch, 'a' , 'z') || is_between(ch, 'A', 'Z') || is_between(ch, '0', '9') || + ch == '_' || ch == '$'); + return length; +} + +SkScriptEngine2::SkScriptEngine2(SkOperand2::OpType returnType) : fActiveStream(&fStream), +fTokenLength(0), fReturnType(returnType), fError(kNoError), +fAccumulatorType(SkOperand2::kNoType), +fBranchPopAllowed(true), fConstExpression(true), fOperandInUse(false) +{ + Branch branch(kUnassigned, 0, 0); + fBranchStack.push(branch); + *fOpStack.push() = (Op) kParen; +} + +SkScriptEngine2::~SkScriptEngine2() { + for (SkString** stringPtr = fTrackString.begin(); stringPtr < fTrackString.end(); stringPtr++) + delete *stringPtr; + for (SkOpArray** arrayPtr = fTrackArray.begin(); arrayPtr < fTrackArray.end(); arrayPtr++) + delete *arrayPtr; +} + +void SkScriptEngine2::addToken(SkScriptEngine2::TypeOp op) { + int limit = fBranchStack.count() - 1; + for (int index = 0; index < limit; index++) { + Branch& branch = fBranchStack.index(index); + if (branch.fPrimed == Branch::kIsPrimed) + resolveBranch(branch); + } + if (fBranchPopAllowed) { + while (fBranchStack.top().fDone == Branch::kIsDone) + fBranchStack.pop(); + } + unsigned char charOp = (unsigned char) op; + fActiveStream->write(&charOp, sizeof(charOp)); +} + +void SkScriptEngine2::addTokenConst(SkScriptValue2* value, AddTokenRegister reg, + SkOperand2::OpType toType, SkScriptEngine2::TypeOp op) { + if (value->fIsConstant == SkScriptValue2::kConstant && convertTo(toType, value)) + return; + addTokenValue(*value, reg); + addToken(op); + value->fIsWritten = SkScriptValue2::kWritten; + value->fType = toType; +} + +void SkScriptEngine2::addTokenInt(int integer) { + fActiveStream->write(&integer, sizeof(integer)); +} + +void SkScriptEngine2::addTokenScalar(SkScalar scalar) { + fActiveStream->write(&scalar, sizeof(scalar)); +} + +void SkScriptEngine2::addTokenString(const SkString& string) { + int size = string.size(); + addTokenInt(size); + fActiveStream->write(string.c_str(), size); +} + +void SkScriptEngine2::addTokenValue(const SkScriptValue2& value, AddTokenRegister reg) { + if (value.isConstant() == false) { + if (reg == kAccumulator) { + if (fAccumulatorType == SkOperand2::kNoType) + addToken(kAccumulatorPop); + } else { + ; // !!! incomplete? + } + return; + } + if (reg == kAccumulator && fAccumulatorType != SkOperand2::kNoType) + addToken(kAccumulatorPush); + switch (value.fType) { + case SkOperand2::kS32: + addToken(reg == kAccumulator ? kIntegerAccumulator : kIntegerOperand); + addTokenInt(value.fOperand.fS32); + if (reg == kAccumulator) + fAccumulatorType = SkOperand2::kS32; + else + fOperandInUse = true; + break; + case SkOperand2::kScalar: + addToken(reg == kAccumulator ? kScalarAccumulator : kScalarOperand); + addTokenScalar(value.fOperand.fScalar); + if (reg == kAccumulator) + fAccumulatorType = SkOperand2::kScalar; + else + fOperandInUse = true; + break; + case SkOperand2::kString: + addToken(reg == kAccumulator ? kStringAccumulator : kStringOperand); + addTokenString(*value.fOperand.fString); + if (reg == kAccumulator) + fAccumulatorType = SkOperand2::kString; + else + fOperandInUse = true; + break; + default: + SkASSERT(0); //!!! not implemented yet + } +} + +int SkScriptEngine2::arithmeticOp(char ch, char nextChar, bool lastPush) { + Op op = kUnassigned; + bool reverseOperands = false; + bool negateResult = false; + int advance = 1; + switch (ch) { + case '+': + // !!! ignoring unary plus as implemented here has the side effect of + // suppressing errors like +"hi" + if (lastPush == false) // unary plus, don't push an operator + goto returnAdv; + op = kAdd; + break; + case '-': + op = lastPush ? kSubtract : kMinus; + break; + case '*': + op = kMultiply; + break; + case '/': + op = kDivide; + break; + case '>': + if (nextChar == '>') { + op = kShiftRight; + goto twoChar; + } + op = kGreaterEqual; + if (nextChar == '=') + goto twoChar; + reverseOperands = negateResult = true; + break; + case '<': + if (nextChar == '<') { + op = kShiftLeft; + goto twoChar; + } + op = kGreaterEqual; + reverseOperands = nextChar == '='; + negateResult = ! reverseOperands; + advance += reverseOperands; + break; + case '=': + if (nextChar == '=') { + op = kEqual; + goto twoChar; + } + break; + case '!': + if (nextChar == '=') { + op = kEqual; + negateResult = true; +twoChar: + advance++; + break; + } + op = kLogicalNot; + break; + case '?': + op =(Op) kIf; + break; + case ':': + op = (Op) kElse; + break; + case '^': + op = kXor; + break; + case '(': + *fOpStack.push() = (Op) kParen; + goto returnAdv; + case '&': + SkASSERT(nextChar != '&'); + op = kBitAnd; + break; + case '|': + SkASSERT(nextChar != '|'); + op = kBitOr; + break; + case '%': + op = kModulo; + break; + case '~': + op = kBitNot; + break; + } + if (op == kUnassigned) + return 0; + signed char precedence = gPrecedence[op]; + do { + int idx = 0; + Op compare; + do { + compare = fOpStack.index(idx); + if ((compare & kArtificialOp) == 0) + break; + idx++; + } while (true); + signed char topPrecedence = gPrecedence[compare]; + SkASSERT(topPrecedence != -1); + if (topPrecedence > precedence || topPrecedence == precedence && + gOpAttributes[op].fLeftType == SkOperand2::kNoType) { + break; + } + processOp(); + } while (true); + if (negateResult) + *fOpStack.push() = (Op) (kLogicalNot | kArtificialOp); + fOpStack.push(op); + if (reverseOperands) + *fOpStack.push() = (Op) (kFlipOps | kArtificialOp); +returnAdv: + return advance; +} + +bool SkScriptEngine2::convertParams(SkTDArray<SkScriptValue2>* params, + const SkOperand2::OpType* paramTypes, int paramCount) { + int count = params->count(); + if (count > paramCount) { + SkASSERT(0); + return false; // too many parameters passed + } + for (int index = 0; index < count; index++) + convertTo(paramTypes[index], &(*params)[index]); + return true; +} + +bool SkScriptEngine2::convertTo(SkOperand2::OpType toType, SkScriptValue2* value ) { + SkOperand2::OpType type = value->fType; + if (type == toType) + return true; + if (type == SkOperand2::kObject) { + if (handleUnbox(value) == false) + return false; + return convertTo(toType, value); + } + return ConvertTo(this, toType, value); +} + +bool SkScriptEngine2::evaluateDot(const char*& script) { + size_t fieldLength = token_length(++script); // skip dot + SkASSERT(fieldLength > 0); // !!! add error handling + const char* field = script; + script += fieldLength; + bool success = handleProperty(); + if (success == false) { + fError = kCouldNotFindReferencedID; + goto error; + } + return evaluateDotParam(script, field, fieldLength); +error: + return false; +} + +bool SkScriptEngine2::evaluateDotParam(const char*& script, const char* field, size_t fieldLength) { + SkScriptValue2& top = fValueStack.top(); + if (top.fType != SkOperand2::kObject) + return false; + void* object = top.fOperand.fObject; + fValueStack.pop(); + char ch; // see if it is a simple member or a function + while (is_ws(ch = script[0])) + script++; + bool success = true; + if (ch != '(') + success = handleMember(field, fieldLength, object); + else { + SkTDArray<SkScriptValue2> params; + *fBraceStack.push() = kFunctionBrace; + success = functionParams(&script, ¶ms); + if (success) + success = handleMemberFunction(field, fieldLength, object, ¶ms); + } + return success; +} + +bool SkScriptEngine2::evaluateScript(const char** scriptPtr, SkScriptValue2* value) { + // fArrayOffset = 0; // no support for structures for now + bool success; + const char* inner; + if (strncmp(*scriptPtr, "#script:", sizeof("#script:") - 1) == 0) { + *scriptPtr += sizeof("#script:") - 1; + if (fReturnType == SkOperand2::kNoType || fReturnType == SkOperand2::kString) { + success = innerScript(scriptPtr, value); + SkASSERT(success); + inner = value->fOperand.fString->c_str(); + scriptPtr = &inner; + } + } + success = innerScript(scriptPtr, value); + const char* script = *scriptPtr; + char ch; + while (is_ws(ch = script[0])) + script++; + if (ch != '\0') { + // error may trigger on scripts like "50,0" that were intended to be written as "[50, 0]" + return false; + } + return success; +} + +void SkScriptEngine2::forget(SkOpArray* array) { + if (array->getType() == SkOperand2::kString) { + for (int index = 0; index < array->count(); index++) { + SkString* string = (*array)[index].fString; + int found = fTrackString.find(string); + if (found >= 0) + fTrackString.remove(found); + } + return; + } + if (array->getType() == SkOperand2::kArray) { + for (int index = 0; index < array->count(); index++) { + SkOpArray* child = (*array)[index].fArray; + forget(child); // forgets children of child + int found = fTrackArray.find(child); + if (found >= 0) + fTrackArray.remove(found); + } + } +} + +bool SkScriptEngine2::functionParams(const char** scriptPtr, SkTDArray<SkScriptValue2>* params) { + (*scriptPtr)++; // skip open paren + *fOpStack.push() = (Op) kParen; + *fBraceStack.push() = kFunctionBrace; + do { + SkScriptValue2 value; + bool success = innerScript(scriptPtr, &value); + SkASSERT(success); + if (success == false) + return false; + *params->append() = value; + } while ((*scriptPtr)[-1] == ','); + fBraceStack.pop(); + fOpStack.pop(); // pop paren + (*scriptPtr)++; // advance beyond close paren + return true; +} + +size_t SkScriptEngine2::getTokenOffset() { + return fActiveStream->getOffset(); +} + +SkOperand2::OpType SkScriptEngine2::getUnboxType(SkOperand2 scriptValue) { + for (SkScriptCallBack** callBack = fCallBackArray.begin(); callBack < fCallBackArray.end(); callBack++) { + if ((*callBack)->getType() != SkScriptCallBack::kUnbox) + continue; + return (*callBack)->getReturnType(0, &scriptValue); + } + return SkOperand2::kObject; +} + +bool SkScriptEngine2::innerScript(const char** scriptPtr, SkScriptValue2* value) { + const char* script = *scriptPtr; + char ch; + bool lastPush = false; + bool success = true; + int opBalance = fOpStack.count(); + int baseBrace = fBraceStack.count(); + int branchBalance = fBranchStack.count(); + while ((ch = script[0]) != '\0') { + if (is_ws(ch)) { + script++; + continue; + } + SkScriptValue2 operand; + const char* dotCheck; + if (fBraceStack.count() > baseBrace) { + if (fBraceStack.top() == kArrayBrace) { + SkScriptValue2 tokenValue; + success = innerScript(&script, &tokenValue); // terminate and return on comma, close brace + SkASSERT(success); + { + SkOperand2::OpType type = fReturnType; + if (fReturnType == SkOperand2::kNoType) { + // !!! short sighted; in the future, allow each returned array component to carry + // its own type, and let caller do any needed conversions + if (value->fOperand.fArray->count() == 0) + value->fOperand.fArray->setType(type = tokenValue.fType); + else + type = value->fOperand.fArray->getType(); + } + if (tokenValue.fType != type) + convertTo(type, &tokenValue); + *value->fOperand.fArray->append() = tokenValue.fOperand; + } + lastPush = false; + continue; + } else + SkASSERT(token_length(script) > 0); + } + if (lastPush != false && fTokenLength > 0) { + if (ch == '(') { + *fBraceStack.push() = kFunctionBrace; + SkString functionName(fToken, fTokenLength); + + if (handleFunction(&script) == false) + return false; + lastPush = true; + continue; + } else if (ch == '[') { + if (handleProperty() == false) { + SkASSERT(0); + return false; + } + if (handleArrayIndexer(&script) == false) + return false; + lastPush = true; + continue; + } else if (ch != '.') { + if (handleProperty() == false) { + SkASSERT(0); + return false; + } + lastPush = true; + continue; + } + } + if (ch == '0' && (script[1] & ~0x20) == 'X') { + SkASSERT(lastPush == false); + script += 2; + script = SkParse::FindHex(script, (uint32_t*) &operand.fOperand.fS32); + SkASSERT(script); + goto intCommon; + } + if (lastPush == false && ch == '.') + goto scalarCommon; + if (ch >= '0' && ch <= '9') { + SkASSERT(lastPush == false); + dotCheck = SkParse::FindS32(script, &operand.fOperand.fS32); + if (dotCheck[0] != '.') { + script = dotCheck; +intCommon: + operand.fType = SkOperand2::kS32; + } else { +scalarCommon: + script = SkParse::FindScalar(script, &operand.fOperand.fScalar); + operand.fType = SkOperand2::kScalar; + } + operand.fIsConstant = SkScriptValue2::kConstant; + fValueStack.push(operand); + lastPush = true; + continue; + } + int length = token_length(script); + if (length > 0) { + SkASSERT(lastPush == false); + fToken = script; + fTokenLength = length; + script += length; + lastPush = true; + continue; + } + char startQuote = ch; + if (startQuote == '\'' || startQuote == '\"') { + SkASSERT(lastPush == false); + operand.fOperand.fString = new SkString(); + ++script; + const char* stringStart = script; + do { // measure string + if (script[0] == '\\') + ++script; + ++script; + SkASSERT(script[0]); // !!! throw an error + } while (script[0] != startQuote); + operand.fOperand.fString->set(stringStart, script - stringStart); + script = stringStart; + char* stringWrite = operand.fOperand.fString->writable_str(); + do { // copy string + if (script[0] == '\\') + ++script; + *stringWrite++ = script[0]; + ++script; + SkASSERT(script[0]); // !!! throw an error + } while (script[0] != startQuote); + ++script; + track(operand.fOperand.fString); + operand.fType = SkOperand2::kString; + operand.fIsConstant = SkScriptValue2::kConstant; + fValueStack.push(operand); + lastPush = true; + continue; + } + if (ch == '.') { + if (fTokenLength == 0) { + SkScriptValue2 scriptValue; + SkDEBUGCODE(scriptValue.fOperand.fObject = NULL); + int tokenLength = token_length(++script); + const char* token = script; + script += tokenLength; + SkASSERT(fValueStack.count() > 0); // !!! add error handling + SkScriptValue2 top; + fValueStack.pop(&top); + + addTokenInt(top.fType); + addToken(kBoxToken); + top.fType = SkOperand2::kObject; + top.fIsConstant = SkScriptValue2::kVariable; + fConstExpression = false; + fValueStack.push(top); + success = evaluateDotParam(script, token, tokenLength); + SkASSERT(success); + lastPush = true; + continue; + } + // get next token, and evaluate immediately + success = evaluateDot(script); + if (success == false) { + // SkASSERT(0); + return false; + } + lastPush = true; + continue; + } + if (ch == '[') { + if (lastPush == false) { + script++; + *fBraceStack.push() = kArrayBrace; + operand.fOperand.fArray = value->fOperand.fArray = new SkOpArray(fReturnType); + track(value->fOperand.fArray); + + operand.fType = SkOperand2::kArray; + operand.fIsConstant = SkScriptValue2::kVariable; + fValueStack.push(operand); + continue; + } + if (handleArrayIndexer(&script) == false) + return false; + lastPush = true; + continue; + } +#if 0 // structs not supported for now + if (ch == '{') { + if (lastPush == false) { + script++; + *fBraceStack.push() = kStructBrace; + operand.fS32 = 0; + *fTypeStack.push() = (SkOpType) kStruct; + fOperandStack.push(operand); + continue; + } + SkASSERT(0); // braces in other contexts aren't supported yet + } +#endif + if (ch == ')' && fBraceStack.count() > 0) { + BraceStyle braceStyle = fBraceStack.top(); + if (braceStyle == kFunctionBrace) { + fBraceStack.pop(); + break; + } + } + if (ch == ',' || ch == ']') { + if (ch != ',') { + BraceStyle match; + fBraceStack.pop(&match); + SkASSERT(match == kArrayBrace); + } + script++; + // !!! see if brace or bracket is correct closer + break; + } + char nextChar = script[1]; + int advance = logicalOp(ch, nextChar); + if (advance == 0) + advance = arithmeticOp(ch, nextChar, lastPush); + if (advance == 0) // unknown token + return false; + if (advance > 0) + script += advance; + lastPush = ch == ']' || ch == ')'; + } + if (fTokenLength > 0) { + success = handleProperty(); + SkASSERT(success); + } + int branchIndex = 0; + branchBalance = fBranchStack.count() - branchBalance; + fBranchPopAllowed = false; + while (branchIndex < branchBalance) { + Branch& branch = fBranchStack.index(branchIndex++); + if (branch.fPrimed == Branch::kIsPrimed) + break; + Op branchOp = branch.fOperator; + SkOperand2::OpType lastType = fValueStack.top().fType; + addTokenValue(fValueStack.top(), kAccumulator); + fValueStack.pop(); + if (branchOp == kLogicalAnd || branchOp == kLogicalOr) { + if (branch.fOperator == kLogicalAnd) + branch.prime(); + addToken(kToBool); + } else { + resolveBranch(branch); + SkScriptValue2 operand; + operand.fType = lastType; + // !!! note that many branching expressions could be constant + // today, we always evaluate branches as returning variables + operand.fIsConstant = SkScriptValue2::kVariable; + fValueStack.push(operand); + } + if (branch.fDone == Branch::kIsNotDone) + branch.prime(); + } + fBranchPopAllowed = true; + while (fBranchStack.top().fDone == Branch::kIsDone) + fBranchStack.pop(); + while (fOpStack.count() > opBalance) { // leave open paren + if (processOp() == false) + return false; + } + SkOperand2::OpType topType = fValueStack.count() > 0 ? fValueStack.top().fType : SkOperand2::kNoType; + if (topType != fReturnType && + topType == SkOperand2::kString && fReturnType != SkOperand2::kNoType) { // if result is a string, give handle property a chance to convert it to the property value + SkString* string = fValueStack.top().fOperand.fString; + fToken = string->c_str(); + fTokenLength = string->size(); + fValueStack.pop(); + success = handleProperty(); + if (success == false) { // if it couldn't convert, return string (error?) + SkScriptValue2 operand; + operand.fType = SkOperand2::kString; + operand.fOperand.fString = string; + operand.fIsConstant = SkScriptValue2::kVariable; // !!! ? + fValueStack.push(operand); + } + } + if (fStream.getOffset() > 0) { + addToken(kEnd); +#ifdef SK_DEBUG + decompile((const unsigned char*)fStream.getStream(), fStream.getOffset()); +#endif + SkScriptRuntime runtime(fCallBackArray); + runtime.executeTokens((unsigned char*) fStream.getStream()); + SkScriptValue2 value1; + runtime.getResult(&value1.fOperand); + value1.fType = fReturnType; + fValueStack.push(value1); + } + if (value) { + if (fValueStack.count() == 0) + return false; + fValueStack.pop(value); + if (value->fType != fReturnType && value->fType == SkOperand2::kObject && + fReturnType != SkOperand2::kNoType) + convertTo(fReturnType, value); + } + // if (fBranchStack.top().fOpStackDepth > fOpStack.count()) + // resolveBranch(); + *scriptPtr = script; + return true; // no error +} + +bool SkScriptEngine2::handleArrayIndexer(const char** scriptPtr) { + SkScriptValue2 scriptValue; + (*scriptPtr)++; + *fOpStack.push() = (Op) kParen; + *fBraceStack.push() = kArrayBrace; + SkOperand2::OpType saveType = fReturnType; + fReturnType = SkOperand2::kS32; + bool success = innerScript(scriptPtr, &scriptValue); + fReturnType = saveType; + SkASSERT(success); + success = convertTo(SkOperand2::kS32, &scriptValue); + SkASSERT(success); + int index = scriptValue.fOperand.fS32; + fValueStack.pop(&scriptValue); + if (scriptValue.fType == SkOperand2::kObject) { + success = handleUnbox(&scriptValue); + SkASSERT(success); + SkASSERT(scriptValue.fType == SkOperand2::kArray); + } + scriptValue.fType = scriptValue.fOperand.fArray->getType(); + // SkASSERT(index >= 0); + if ((unsigned) index >= (unsigned) scriptValue.fOperand.fArray->count()) { + fError = kArrayIndexOutOfBounds; + return false; + } + scriptValue.fOperand = scriptValue.fOperand.fArray->begin()[index]; + scriptValue.fIsConstant = SkScriptValue2::kVariable; + fValueStack.push(scriptValue); + fOpStack.pop(); // pop paren + return success; +} + +bool SkScriptEngine2::handleFunction(const char** scriptPtr) { + const char* functionName = fToken; + size_t functionNameLen = fTokenLength; + fTokenLength = 0; + SkTDArray<SkScriptValue2> params; + bool success = functionParams(scriptPtr, ¶ms); + if (success == false) + goto done; + { + for (SkScriptCallBack** callBack = fCallBackArray.begin(); callBack < fCallBackArray.end(); callBack++) { + if ((*callBack)->getType() != SkScriptCallBack::kFunction) + continue; + SkScriptValue2 callbackResult; + success = (*callBack)->getReference(functionName, functionNameLen, &callbackResult); + if (success) { + callbackResult.fType = (*callBack)->getReturnType(callbackResult.fOperand.fReference, NULL); + callbackResult.fIsConstant = SkScriptValue2::kVariable; + fValueStack.push(callbackResult); + goto done; + } + } + } + return false; +done: + fOpStack.pop(); + return success; +} + +bool SkScriptEngine2::handleMember(const char* field, size_t len, void* object) { + bool success = true; + for (SkScriptCallBack** callBack = fCallBackArray.begin(); callBack < fCallBackArray.end(); callBack++) { + if ((*callBack)->getType() != SkScriptCallBack::kMember) + continue; + SkScriptValue2 callbackResult; + success = (*callBack)->getReference(field, len, &callbackResult); + if (success) { + if (callbackResult.fType == SkOperand2::kString) + track(callbackResult.fOperand.fString); + callbackResult.fIsConstant = SkScriptValue2::kVariable; + fValueStack.push(callbackResult); + goto done; + } + } + return false; +done: + return success; +} + +bool SkScriptEngine2::handleMemberFunction(const char* field, size_t len, void* object, + SkTDArray<SkScriptValue2>* params) { + bool success = true; + for (SkScriptCallBack** callBack = fCallBackArray.begin(); callBack < fCallBackArray.end(); callBack++) { + if ((*callBack)->getType() != SkScriptCallBack::kMemberFunction) + continue; + SkScriptValue2 callbackResult; + success = (*callBack)->getReference(field, len, &callbackResult); + if (success) { + if (callbackResult.fType == SkOperand2::kString) + track(callbackResult.fOperand.fString); + callbackResult.fIsConstant = SkScriptValue2::kVariable; + fValueStack.push(callbackResult); + goto done; + } + } + return false; +done: + return success; +} + +bool SkScriptEngine2::handleProperty() { + bool success = true; + for (SkScriptCallBack** callBack = fCallBackArray.begin(); callBack < fCallBackArray.end(); callBack++) { + if ((*callBack)->getType() != SkScriptCallBack::kProperty) + continue; + SkScriptValue2 callbackResult; + success = (*callBack)->getReference(fToken, fTokenLength, &callbackResult); + if (success) { + if (callbackResult.fType == SkOperand2::kString && callbackResult.fOperand.fString == NULL) { + callbackResult.fOperand.fString = new SkString(fToken, fTokenLength); + track(callbackResult.fOperand.fString); + } + callbackResult.fIsConstant = SkScriptValue2::kVariable; + fValueStack.push(callbackResult); + goto done; + } + } +done: + fTokenLength = 0; + return success; +} + +bool SkScriptEngine2::handleUnbox(SkScriptValue2* scriptValue) { + bool success = true; + for (SkScriptCallBack** callBack = fCallBackArray.begin(); callBack < fCallBackArray.end(); callBack++) { + if ((*callBack)->getType() != SkScriptCallBack::kUnbox) + continue; + SkScriptCallBackConvert* callBackConvert = (SkScriptCallBackConvert*) *callBack; + success = callBackConvert->convert(scriptValue->fType, &scriptValue->fOperand); + if (success) { + if (scriptValue->fType == SkOperand2::kString) + track(scriptValue->fOperand.fString); + goto done; + } + } + return false; +done: + return success; +} + +// note that entire expression is treated as if it were enclosed in parens +// an open paren is always the first thing in the op stack + +int SkScriptEngine2::logicalOp(char ch, char nextChar) { + int advance = 1; + Op op; + signed char precedence; + switch (ch) { + case ')': + op = (Op) kParen; + break; + case ']': + op = (Op) kArrayOp; + break; + case '?': + op = (Op) kIf; + break; + case ':': + op = (Op) kElse; + break; + case '&': + if (nextChar != '&') + goto noMatch; + op = kLogicalAnd; + advance = 2; + break; + case '|': + if (nextChar != '|') + goto noMatch; + op = kLogicalOr; + advance = 2; + break; + default: + noMatch: + return 0; + } + precedence = gPrecedence[op]; + int branchIndex = 0; + fBranchPopAllowed = false; + do { + while (gPrecedence[fOpStack.top() & ~kArtificialOp] < precedence) + processOp(); + Branch& branch = fBranchStack.index(branchIndex++); + Op branchOp = branch.fOperator; + if (gPrecedence[branchOp] >= precedence) + break; + addTokenValue(fValueStack.top(), kAccumulator); + fValueStack.pop(); + if (branchOp == kLogicalAnd || branchOp == kLogicalOr) { + if (branch.fOperator == kLogicalAnd) + branch.prime(); + addToken(kToBool); + } else + resolveBranch(branch); + if (branch.fDone == Branch::kIsNotDone) + branch.prime(); + } while (true); + fBranchPopAllowed = true; + while (fBranchStack.top().fDone == Branch::kIsDone) + fBranchStack.pop(); + processLogicalOp(op); + return advance; +} + +void SkScriptEngine2::processLogicalOp(Op op) { + switch (op) { + case kParen: + case kArrayOp: + SkASSERT(fOpStack.count() > 1 && fOpStack.top() == op); // !!! add error handling + if (op == kParen) + fOpStack.pop(); + else { + SkScriptValue2 value; + fValueStack.pop(&value); + SkASSERT(value.fType == SkOperand2::kS32 || value.fType == SkOperand2::kScalar); // !!! add error handling (although, could permit strings eventually) + int index = value.fType == SkOperand2::kScalar ? SkScalarFloor(value.fOperand.fScalar) : + value.fOperand.fS32; + SkScriptValue2 arrayValue; + fValueStack.pop(&arrayValue); + SkASSERT(arrayValue.fType == SkOperand2::kArray); // !!! add error handling + SkOpArray* array = arrayValue.fOperand.fArray; + SkOperand2 operand; + bool success = array->getIndex(index, &operand); + SkASSERT(success); // !!! add error handling + SkScriptValue2 resultValue; + resultValue.fType = array->getType(); + resultValue.fOperand = operand; + resultValue.fIsConstant = SkScriptValue2::kVariable; + fValueStack.push(resultValue); + } + break; + case kIf: { + if (fAccumulatorType == SkOperand2::kNoType) { + addTokenValue(fValueStack.top(), kAccumulator); + fValueStack.pop(); + } + SkASSERT(fAccumulatorType != SkOperand2::kString); // !!! add error handling + addToken(kIfOp); + Branch branch(op, fOpStack.count(), getTokenOffset()); + *fBranchStack.push() = branch; + addTokenInt(0); // placeholder for future branch + fAccumulatorType = SkOperand2::kNoType; + } break; + case kElse: { + addTokenValue(fValueStack.top(), kAccumulator); + fValueStack.pop(); + addToken(kElseOp); + size_t newOffset = getTokenOffset(); + addTokenInt(0); // placeholder for future branch + Branch& branch = fBranchStack.top(); + resolveBranch(branch); + branch.fOperator = op; + branch.fDone = Branch::kIsNotDone; + SkASSERT(branch.fOpStackDepth == fOpStack.count()); + branch.fOffset = newOffset; + fAccumulatorType = SkOperand2::kNoType; + } break; + case kLogicalAnd: + case kLogicalOr: { + Branch& oldTop = fBranchStack.top(); + Branch::Primed wasPrime = oldTop.fPrimed; + Branch::Done wasDone = oldTop.fDone; + oldTop.fPrimed = Branch::kIsNotPrimed; + oldTop.fDone = Branch::kIsNotDone; + if (fAccumulatorType == SkOperand2::kNoType) { + SkASSERT(fValueStack.top().fType == SkOperand2::kS32); // !!! add error handling, and conversion to int? + addTokenValue(fValueStack.top(), kAccumulator); + fValueStack.pop(); + } else + SkASSERT(fAccumulatorType == SkOperand2::kS32); + // if 'and', write beq goto opcode after end of predicate (after to bool) + // if 'or', write bne goto to bool + addToken(op == kLogicalAnd ? kLogicalAndInt : kLogicalOrInt); + Branch branch(op, fOpStack.count(), getTokenOffset()); + addTokenInt(0); // placeholder for future branch + oldTop.fPrimed = wasPrime; + oldTop.fDone = wasDone; + *fBranchStack.push() = branch; + fAccumulatorType = SkOperand2::kNoType; + } break; + default: + SkASSERT(0); + } +} + +bool SkScriptEngine2::processOp() { + Op op; + fOpStack.pop(&op); + op = (Op) (op & ~kArtificialOp); + const OperatorAttributes* attributes = &gOpAttributes[op]; + SkScriptValue2 value1 = { 0 }; + SkScriptValue2 value2; + fValueStack.pop(&value2); + value2.fIsWritten = SkScriptValue2::kUnwritten; + // SkScriptEngine2::SkTypeOp convert1[3]; + // SkScriptEngine2::SkTypeOp convert2[3]; + // SkScriptEngine2::SkTypeOp* convert2Ptr = convert2; + bool constantOperands = value2.fIsConstant == SkScriptValue2::kConstant; + if (attributes->fLeftType != SkOperand2::kNoType) { + fValueStack.pop(&value1); + constantOperands &= value1.fIsConstant == SkScriptValue2::kConstant; + value1.fIsWritten = SkScriptValue2::kUnwritten; + if (op == kFlipOps) { + SkTSwap(value1, value2); + fOpStack.pop(&op); + op = (Op) (op & ~kArtificialOp); + attributes = &gOpAttributes[op]; + if (constantOperands == false) + addToken(kFlipOpsOp); + } + if (value1.fType == SkOperand2::kObject && (value1.fType & attributes->fLeftType) == 0) { + value1.fType = getUnboxType(value1.fOperand); + addToken(kUnboxToken); + } + } + if (value2.fType == SkOperand2::kObject && (value2.fType & attributes->fLeftType) == 0) { + value1.fType = getUnboxType(value2.fOperand); + addToken(kUnboxToken2); + } + if (attributes->fLeftType != SkOperand2::kNoType) { + if (value1.fType != value2.fType) { + if ((attributes->fLeftType & SkOperand2::kString) && attributes->fBias & kTowardsString && + ((value1.fType | value2.fType) & SkOperand2::kString)) { + if (value1.fType == SkOperand2::kS32 || value1.fType == SkOperand2::kScalar) { + addTokenConst(&value1, kAccumulator, SkOperand2::kString, + value1.fType == SkOperand2::kS32 ? kIntToString : kScalarToString); + } + if (value2.fType == SkOperand2::kS32 || value2.fType == SkOperand2::kScalar) { + addTokenConst(&value2, kOperand, SkOperand2::kString, + value2.fType == SkOperand2::kS32 ? kIntToString2 : kScalarToString2); + } + } else if (attributes->fLeftType & SkOperand2::kScalar && ((value1.fType | value2.fType) & + SkOperand2::kScalar)) { + if (value1.fType == SkOperand2::kS32) + addTokenConst(&value1, kAccumulator, SkOperand2::kScalar, kIntToScalar); + if (value2.fType == SkOperand2::kS32) + addTokenConst(&value2, kOperand, SkOperand2::kScalar, kIntToScalar2); + } + } + if ((value1.fType & attributes->fLeftType) == 0 || value1.fType != value2.fType) { + if (value1.fType == SkOperand2::kString) + addTokenConst(&value1, kAccumulator, SkOperand2::kScalar, kStringToScalar); + if (value1.fType == SkOperand2::kScalar && (attributes->fLeftType == SkOperand2::kS32 || + value2.fType == SkOperand2::kS32)) + addTokenConst(&value1, kAccumulator, SkOperand2::kS32, kScalarToInt); + } + } + AddTokenRegister rhRegister = attributes->fLeftType != SkOperand2::kNoType ? + kOperand : kAccumulator; + if ((value2.fType & attributes->fRightType) == 0 || value1.fType != value2.fType) { + if (value2.fType == SkOperand2::kString) + addTokenConst(&value2, rhRegister, SkOperand2::kScalar, kStringToScalar2); + if (value2.fType == SkOperand2::kScalar && (attributes->fRightType == SkOperand2::kS32 || + value1.fType == SkOperand2::kS32)) + addTokenConst(&value2, rhRegister, SkOperand2::kS32, kScalarToInt2); + } + TypeOp typeOp = gTokens[op]; + if (value2.fType == SkOperand2::kScalar) + typeOp = (TypeOp) (typeOp + 1); + else if (value2.fType == SkOperand2::kString) + typeOp = (TypeOp) (typeOp + 2); + SkDynamicMemoryWStream stream; + SkOperand2::OpType saveType; + SkBool saveOperand; + if (constantOperands) { + fActiveStream = &stream; + saveType = fAccumulatorType; + saveOperand = fOperandInUse; + fAccumulatorType = SkOperand2::kNoType; + fOperandInUse = false; + } + if (attributes->fLeftType != SkOperand2::kNoType) { // two operands + if (value1.fIsWritten == SkScriptValue2::kUnwritten) + addTokenValue(value1, kAccumulator); + } + if (value2.fIsWritten == SkScriptValue2::kUnwritten) + addTokenValue(value2, rhRegister); + addToken(typeOp); + if (constantOperands) { + addToken(kEnd); +#ifdef SK_DEBUG + decompile((const unsigned char*) stream.getStream(), stream.getOffset()); +#endif + SkScriptRuntime runtime(fCallBackArray); + runtime.executeTokens((unsigned char*) stream.getStream()); + runtime.getResult(&value1.fOperand); + if (attributes->fResultIsBoolean == kResultIsBoolean) + value1.fType = SkOperand2::kS32; + else if (attributes->fLeftType == SkOperand2::kNoType) // unary operand + value1.fType = value2.fType; + fValueStack.push(value1); + if (value1.fType == SkOperand2::kString) + runtime.untrack(value1.fOperand.fString); + else if (value1.fType == SkOperand2::kArray) + runtime.untrack(value1.fOperand.fArray); + fActiveStream = &fStream; + fAccumulatorType = saveType; + fOperandInUse = saveOperand; + return true; + } + value2.fIsConstant = SkScriptValue2::kVariable; + fValueStack.push(value2); + return true; +} + +void SkScriptEngine2::Branch::resolve(SkDynamicMemoryWStream* stream, size_t off) { + SkASSERT(fDone == kIsNotDone); + fPrimed = kIsNotPrimed; + fDone = kIsDone; + SkASSERT(off > fOffset + sizeof(size_t)); + size_t offset = off - fOffset - sizeof(offset); + stream->write(&offset, fOffset, sizeof(offset)); +} + +void SkScriptEngine2::resolveBranch(SkScriptEngine2::Branch& branch) { + branch.resolve(fActiveStream, getTokenOffset()); +} + +bool SkScriptEngine2::ConvertTo(SkScriptEngine2* engine, SkOperand2::OpType toType, SkScriptValue2* value ) { + SkASSERT(value); + SkOperand2::OpType type = value->fType; + if (type == toType) + return true; + SkOperand2& operand = value->fOperand; + bool success = true; + switch (toType) { + case SkOperand2::kS32: + if (type == SkOperand2::kScalar) + operand.fS32 = SkScalarFloor(operand.fScalar); + else { + SkASSERT(type == SkOperand2::kString); + success = SkParse::FindS32(operand.fString->c_str(), &operand.fS32) != NULL; + } + break; + case SkOperand2::kScalar: + if (type == SkOperand2::kS32) + operand.fScalar = IntToScalar(operand.fS32); + else { + SkASSERT(type == SkOperand2::kString); + success = SkParse::FindScalar(operand.fString->c_str(), &operand.fScalar) != NULL; + } + break; + case SkOperand2::kString: { + SkString* strPtr = new SkString(); + SkASSERT(engine); + engine->track(strPtr); + if (type == SkOperand2::kS32) + strPtr->appendS32(operand.fS32); + else { + SkASSERT(type == SkOperand2::kScalar); + strPtr->appendScalar(operand.fScalar); + } + operand.fString = strPtr; + } break; + case SkOperand2::kArray: { + SkOpArray* array = new SkOpArray(type); + *array->append() = operand; + engine->track(array); + operand.fArray = array; + } break; + default: + SkASSERT(0); + } + value->fType = toType; + return success; +} + +SkScalar SkScriptEngine2::IntToScalar(int32_t s32) { + SkScalar scalar; + if (s32 == SK_NaN32) + scalar = SK_ScalarNaN; + else if (SkAbs32(s32) == SK_MaxS32) + scalar = SkSign32(s32) * SK_ScalarMax; + else + scalar = SkIntToScalar(s32); + return scalar; +} + +bool SkScriptEngine2::ValueToString(const SkScriptValue2& value, SkString* string) { + switch (value.fType) { + case SkOperand2::kS32: + string->reset(); + string->appendS32(value.fOperand.fS32); + break; + case SkOperand2::kScalar: + string->reset(); + string->appendScalar(value.fOperand.fScalar); + break; + case SkOperand2::kString: + string->set(*value.fOperand.fString); + break; + default: + SkASSERT(0); + return false; + } + return true; // no error +} + +#ifdef SK_DEBUG + +#define testInt(expression) { #expression, SkOperand2::kS32, expression } +#ifdef SK_SCALAR_IS_FLOAT +#define testScalar(expression) { #expression, SkOperand2::kScalar, 0, (float) expression } +#define testRemainder(exp1, exp2) { #exp1 "%" #exp2, SkOperand2::kScalar, 0, fmodf(exp1, exp2) } +#else +#ifdef SK_CAN_USE_FLOAT +#define testScalar(expression) { #expression, SkOperand2::kScalar, 0, (int) ((expression) * 65536.0f) } +#define testRemainder(exp1, exp2) { #exp1 "%" #exp2, SkOperand2::kScalar, 0, (int) (fmod(exp1, exp2) * 65536.0f) } +#endif +#endif +#define testTrue(expression) { #expression, SkOperand2::kS32, 1 } +#define testFalse(expression) { #expression, SkOperand2::kS32, 0 } + +#if !defined(SK_BUILD_FOR_BREW) +static const SkScriptNAnswer2 scriptTests[] = { + testInt(1||0&&3), +#ifdef SK_CAN_USE_FLOAT + testScalar(- -5.5- -1.5), + testScalar(1.0+5), +#endif + testInt((6+7)*8), + testInt(3*(4+5)), +#ifdef SK_CAN_USE_FLOAT + testScalar(1.0+2.0), + testScalar(3.0-1.0), + testScalar(6-1.0), + testScalar(2.5*6.), + testScalar(0.5*4), + testScalar(4.5/.5), + testScalar(9.5/19), + testRemainder(9.5, 0.5), + testRemainder(9.,2), + testRemainder(9,2.5), + testRemainder(-9,2.5), + testTrue(-9==-9.0), + testTrue(-9.==-4.0-5), + testTrue(-9.*1==-4-5), + testFalse(-9!=-9.0), + testFalse(-9.!=-4.0-5), + testFalse(-9.*1!=-4-5), +#endif + testInt(0x123), + testInt(0XABC), + testInt(0xdeadBEEF), + { "'123'+\"456\"", SkOperand2::kString, 0, 0, "123456" }, + { "123+\"456\"", SkOperand2::kString, 0, 0, "123456" }, + { "'123'+456", SkOperand2::kString, 0, 0, "123456" }, + { "'123'|\"456\"", SkOperand2::kS32, 123|456 }, + { "123|\"456\"", SkOperand2::kS32, 123|456 }, + { "'123'|456", SkOperand2::kS32, 123|456 }, + { "'2'<11", SkOperand2::kS32, 1 }, + { "2<'11'", SkOperand2::kS32, 1 }, + { "'2'<'11'", SkOperand2::kS32, 0 }, + testInt(123), + testInt(-345), + testInt(+678), + testInt(1+2+3), + testInt(3*4+5), + testInt(6+7*8), + testInt(-1-2-8/4), + testInt(-9%4), + testInt(9%-4), + testInt(-9%-4), + testInt(123|978), + testInt(123&978), + testInt(123^978), + testInt(2<<4), + testInt(99>>3), + testInt(~55), + testInt(~~55), + testInt(!55), + testInt(!!55), + // both int + testInt(2<2), + testInt(2<11), + testInt(20<11), + testInt(2<=2), + testInt(2<=11), + testInt(20<=11), + testInt(2>2), + testInt(2>11), + testInt(20>11), + testInt(2>=2), + testInt(2>=11), + testInt(20>=11), + testInt(2==2), + testInt(2==11), + testInt(20==11), + testInt(2!=2), + testInt(2!=11), + testInt(20!=11), +#ifdef SK_CAN_USE_FLOAT + // left int, right scalar + testInt(2<2.), + testInt(2<11.), + testInt(20<11.), + testInt(2<=2.), + testInt(2<=11.), + testInt(20<=11.), + testInt(2>2.), + testInt(2>11.), + testInt(20>11.), + testInt(2>=2.), + testInt(2>=11.), + testInt(20>=11.), + testInt(2==2.), + testInt(2==11.), + testInt(20==11.), + testInt(2!=2.), + testInt(2!=11.), + testInt(20!=11.), + // left scalar, right int + testInt(2.<2), + testInt(2.<11), + testInt(20.<11), + testInt(2.<=2), + testInt(2.<=11), + testInt(20.<=11), + testInt(2.>2), + testInt(2.>11), + testInt(20.>11), + testInt(2.>=2), + testInt(2.>=11), + testInt(20.>=11), + testInt(2.==2), + testInt(2.==11), + testInt(20.==11), + testInt(2.!=2), + testInt(2.!=11), + testInt(20.!=11), + // both scalar + testInt(2.<11.), + testInt(20.<11.), + testInt(2.<=2.), + testInt(2.<=11.), + testInt(20.<=11.), + testInt(2.>2.), + testInt(2.>11.), + testInt(20.>11.), + testInt(2.>=2.), + testInt(2.>=11.), + testInt(20.>=11.), + testInt(2.==2.), + testInt(2.==11.), + testInt(20.==11.), + testInt(2.!=2.), + testInt(2.!=11.), + testInt(20.!=11.), +#endif + // int, string (string is int) + testFalse(2<'2'), + testTrue(2<'11'), + testFalse(20<'11'), + testTrue(2<='2'), + testTrue(2<='11'), + testFalse(20<='11'), + testFalse(2>'2'), + testFalse(2>'11'), + testTrue(20>'11'), + testTrue(2>='2'), + testFalse(2>='11'), + testTrue(20>='11'), + testTrue(2=='2'), + testFalse(2=='11'), + testFalse(2!='2'), + testTrue(2!='11'), + // int, string (string is scalar) + testFalse(2<'2.'), + testTrue(2<'11.'), + testFalse(20<'11.'), + testTrue(2=='2.'), + testFalse(2=='11.'), +#ifdef SK_CAN_USE_FLOAT + // scalar, string + testFalse(2.<'2.'), + testTrue(2.<'11.'), + testFalse(20.<'11.'), + testTrue(2.=='2.'), + testFalse(2.=='11.'), + // string, int + testFalse('2'<2), + testTrue('2'<11), + testFalse('20'<11), + testTrue('2'==2), + testFalse('2'==11), + // string, scalar + testFalse('2'<2.), + testTrue('2'<11.), + testFalse('20'<11.), + testTrue('2'==2.), + testFalse('2'==11.), +#endif + // string, string + testFalse('2'<'2'), + testFalse('2'<'11'), + testFalse('20'<'11'), + testTrue('2'=='2'), + testFalse('2'=='11'), + // logic + testInt(1?2:3), + testInt(0?2:3), + testInt(1&&2||3), + testInt(1&&0||3), + testInt(1&&0||0), + testInt(1||0&&3), + testInt(0||0&&3), + testInt(0||1&&3), + testInt(0&&1?2:3) +#ifdef SK_CAN_USE_FLOAT + , { "123.5", SkOperand2::kScalar, 0, SkIntToScalar(123) + SK_Scalar1/2 } +#endif +}; +#endif // build for brew + +#define SkScriptNAnswer_testCount SK_ARRAY_COUNT(scriptTests) + +void SkScriptEngine2::UnitTest() { +#if !defined(SK_BUILD_FOR_BREW) && defined(SK_SUPPORT_UNITTEST) + ValidateDecompileTable(); + for (int index = 0; index < SkScriptNAnswer_testCount; index++) { + SkScriptEngine2 engine(scriptTests[index].fType); + SkScriptValue2 value; + const char* script = scriptTests[index].fScript; + const char* scriptPtr = script; + SkASSERT(engine.evaluateScript(&scriptPtr, &value) == true); + SkASSERT(value.fType == scriptTests[index].fType); + SkScalar error; + switch (value.fType) { + case SkOperand2::kS32: + if (value.fOperand.fS32 != scriptTests[index].fIntAnswer) + SkDEBUGF(("script '%s' == value %d != expected answer %d\n", script, value.fOperand.fS32, scriptTests[index].fIntAnswer)); + SkASSERT(value.fOperand.fS32 == scriptTests[index].fIntAnswer); + break; + case SkOperand2::kScalar: + error = SkScalarAbs(value.fOperand.fScalar - scriptTests[index].fScalarAnswer); +#ifdef SK_CAN_USE_FLOAT + if (error >= SK_Scalar1 / 10000) + SkDEBUGF(("script '%s' == value %g != expected answer %g\n", script, value.fOperand.fScalar / (1.0f * SK_Scalar1), scriptTests[index].fScalarAnswer / (1.0f * SK_Scalar1))); +#endif + SkASSERT(error < SK_Scalar1 / 10000); + break; + case SkOperand2::kString: + SkASSERT(value.fOperand.fString->equals(scriptTests[index].fStringAnswer)); + break; + default: + SkASSERT(0); + } + } +#endif +} +#endif diff --git a/skia/animator/SkSnapshot.cpp b/skia/animator/SkSnapshot.cpp new file mode 100644 index 0000000..0b331f5 --- /dev/null +++ b/skia/animator/SkSnapshot.cpp @@ -0,0 +1,74 @@ +/* libs/graphics/animator/SkSnapshot.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkTypes.h" + +#ifdef SK_SUPPORT_IMAGE_ENCODE + +#include "SkSnapshot.h" +#include "SkAnimateMaker.h" +#include "SkCanvas.h" + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkSnapshot::fInfo[] = { + SK_MEMBER(filename, String), + SK_MEMBER(quality, Float), + SK_MEMBER(sequence, Boolean), + SK_MEMBER(type, BitmapEncoding) +}; + +#endif + +DEFINE_GET_MEMBER(SkSnapshot); + +SkSnapshot::SkSnapshot() +{ + quality = 100 * SK_Scalar1; + type = (SkImageEncoder::Type) -1; + sequence = false; + fSeqVal = 0; +} + +#include "SkDevice.h" + +bool SkSnapshot::draw(SkAnimateMaker& maker) { + SkASSERT(type >= 0); + SkASSERT(filename.size() > 0); + SkImageEncoder* encoder = SkImageEncoder::Create((SkImageEncoder::Type) type); + + SkString name(filename); + if (sequence) { + char num[4] = "000"; + num[0] = (char) (num[0] + fSeqVal / 100); + num[1] = (char) (num[1] + fSeqVal / 10 % 10); + num[2] = (char) (num[2] + fSeqVal % 10); + name.append(num); + if (++fSeqVal > 999) + sequence = false; + } + if (type == SkImageEncoder::kJPEG_Type) + name.append(".jpg"); + else if (type == SkImageEncoder::kPNG_Type) + name.append(".png"); + encoder->encodeFile(name.c_str(), + maker.fCanvas->getDevice()->accessBitmap(false), + SkScalarFloor(quality)); + return false; +} + +#endif diff --git a/skia/animator/SkSnapshot.h b/skia/animator/SkSnapshot.h new file mode 100644 index 0000000..b63145d --- /dev/null +++ b/skia/animator/SkSnapshot.h @@ -0,0 +1,42 @@ +/* libs/graphics/animator/SkSnapshot.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkSnapShot_DEFINED +#define SkSnapShot_DEFINED + +#ifdef SK_SUPPORT_IMAGE_ENCODE + +#include "SkDrawable.h" +#include "SkImageDecoder.h" +#include "SkMemberInfo.h" +#include "SkString.h" + +class SkSnapshot: public SkDrawable { + DECLARE_MEMBER_INFO(Snapshot); + SkSnapshot(); + virtual bool draw(SkAnimateMaker& ); + private: + SkString filename; + SkScalar quality; + SkBool sequence; + int /*SkImageEncoder::Type*/ type; + int fSeqVal; +}; + +#endif // SK_SUPPORT_IMAGE_ENCODE +#endif // SkSnapShot_DEFINED + diff --git a/skia/animator/SkTDArray_Experimental.h b/skia/animator/SkTDArray_Experimental.h new file mode 100644 index 0000000..c59f670 --- /dev/null +++ b/skia/animator/SkTDArray_Experimental.h @@ -0,0 +1,150 @@ +/* libs/graphics/animator/SkTDArray_Experimental.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkTDArray_Experimental_DEFINED +#define SkTDArray_Experimental_DEFINED + +#include "SkTypes.h" + +#ifdef SK_BUILD_FOR_UNIX +#define SK_BUILD_FOR_ADS_12 +#endif + +#ifndef SK_BUILD_FOR_ADS_12 +#define SK_SMALLER_ARRAY_TEMPLATE_EXPERIMENT 1 +#else +#define SK_SMALLER_ARRAY_TEMPLATE_EXPERIMENT 0 +#endif + +#if SK_SMALLER_ARRAY_TEMPLATE_EXPERIMENT == 0 +#include "SkTDArray.h" +#define SkIntArray(type) SkTDArray<type> +#define SkLongArray(type) SkTDArray<type> +#else + +class SkDS32Array { +protected: + SkDS32Array(); + SkDS32Array(const SkDS32Array& src); + SkDS32Array(const int32_t src[], U16CPU count); + SkDS32Array& operator=(const SkDS32Array& src); + friend int operator==(const SkDS32Array& a, const SkDS32Array& b); + int32_t* append() { return this->append(1, NULL); } + int32_t* append(U16CPU count, const int32_t* src = NULL); + + int32_t* appendClear() + { + int32_t* result = this->append(); + *result = 0; + return result; + } + + int find(const int32_t& elem) const; + int32_t* insert(U16CPU index, U16CPU count, const int32_t* src); + int rfind(const int32_t& elem) const; + void swap(SkDS32Array& other); +public: + bool isEmpty() const { return fCount == 0; } + int count() const { return fCount; } + + void remove(U16CPU index, U16CPU count = 1) + { + SkASSERT(index + count <= fCount); + fCount = SkToU16(fCount - count); + memmove(fArray + index, fArray + index + count, sizeof(int32_t) * (fCount - index)); + } + + void reset() + { + if (fArray) + { + sk_free(fArray); + fArray = NULL; +#ifdef SK_DEBUG + fData = NULL; +#endif + fReserve = fCount = 0; + } + else + { + SkASSERT(fReserve == 0 && fCount == 0); + } + } + + void setCount(U16CPU count) + { + if (count > fReserve) + this->growBy(count - fCount); + else + fCount = SkToU16(count); + } +protected: +#ifdef SK_DEBUG + enum { + kDebugArraySize = 24 + }; + int32_t(* fData)[kDebugArraySize]; +#endif + int32_t* fArray; + uint16_t fReserve, fCount; + void growBy(U16CPU extra); +}; + +#ifdef SK_DEBUG + #define SYNC() fTData = (T (*)[kDebugArraySize]) fArray +#else + #define SYNC() +#endif + +template <typename T> class SkTDS32Array : public SkDS32Array { +public: + SkTDS32Array() { SkDEBUGCODE(fTData=NULL); SkASSERT(sizeof(T) == sizeof(int32_t)); } + SkTDS32Array(const SkTDS32Array<T>& src) : SkDS32Array(src) {} + ~SkTDS32Array() { sk_free(fArray); } + T& operator[](int index) const { SYNC(); SkASSERT((unsigned)index < fCount); return ((T*) fArray)[index]; } + SkTDS32Array<T>& operator=(const SkTDS32Array<T>& src) { + return (SkTDS32Array<T>&) SkDS32Array::operator=(src); } + friend int operator==(const SkTDS32Array<T>& a, const SkTDS32Array<T>& b) { + return operator==((const SkDS32Array&) a, (const SkDS32Array&) b); } + T* append() { return (T*) SkDS32Array::append(); } + T* appendClear() { return (T*) SkDS32Array::appendClear(); } + T* append(U16CPU count, const T* src = NULL) { return (T*) SkDS32Array::append(count, (const int32_t*) src); } + T* begin() const { SYNC(); return (T*) fArray; } + T* end() const { return (T*) (fArray ? fArray + fCount : NULL); } + int find(const T& elem) const { return SkDS32Array::find((const int32_t&) elem); } + T* insert(U16CPU index) { return this->insert(index, 1, NULL); } + T* insert(U16CPU index, U16CPU count, const T* src = NULL) { + return (T*) SkDS32Array::insert(index, count, (const int32_t*) src); } + int rfind(const T& elem) const { return SkDS32Array::rfind((const int32_t&) elem); } + T* push() { return this->append(); } + void push(T& elem) { *this->append() = elem; } + const T& top() const { return (*this)[fCount - 1]; } + T& top() { return (*this)[fCount - 1]; } + void pop(T* elem) { if (elem) *elem = (*this)[fCount - 1]; --fCount; } + void pop() { --fCount; } +private: +#ifdef SK_DEBUG + mutable T(* fTData)[kDebugArraySize]; +#endif +}; + +#define SkIntArray(type) SkTDS32Array<type> // holds 32 bit data types +#define SkLongArray(type) SkTDS32Array<type> // holds 32/64 bit data types depending on pointer size + +#endif // SK_SMALLER_ARRAY_TEMPLATE_EXPERIMENT + +#endif // SkTDArray_Experimental_DEFINED diff --git a/skia/animator/SkTextOnPath.cpp b/skia/animator/SkTextOnPath.cpp new file mode 100644 index 0000000..af0eb4c --- /dev/null +++ b/skia/animator/SkTextOnPath.cpp @@ -0,0 +1,47 @@ +/* libs/graphics/animator/SkTextOnPath.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkTextOnPath.h" +#include "SkAnimateMaker.h" +#include "SkCanvas.h" +#include "SkDrawPath.h" +#include "SkDrawText.h" +#include "SkPaint.h" + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkTextOnPath::fInfo[] = { + SK_MEMBER(offset, Float), + SK_MEMBER(path, Path), + SK_MEMBER(text, Text) +}; + +#endif + +DEFINE_GET_MEMBER(SkTextOnPath); + +SkTextOnPath::SkTextOnPath() : offset(0), path(NULL), text(NULL) { +} + +bool SkTextOnPath::draw(SkAnimateMaker& maker) { + SkASSERT(text); + SkASSERT(path); + SkBoundableAuto boundable(this, maker); + maker.fCanvas->drawTextOnPathHV(text->getText(), text->getSize(), + path->getPath(), offset, 0, *maker.fPaint); + return false; +} diff --git a/skia/animator/SkTextOnPath.h b/skia/animator/SkTextOnPath.h new file mode 100644 index 0000000..f01e118 --- /dev/null +++ b/skia/animator/SkTextOnPath.h @@ -0,0 +1,38 @@ +/* libs/graphics/animator/SkTextOnPath.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkTextOnPath_DEFINED +#define SkTextOnPath_DEFINED + +#include "SkBoundable.h" +#include "SkMemberInfo.h" + +class SkDrawPath; +class SkText; + +class SkTextOnPath : public SkBoundable { + DECLARE_MEMBER_INFO(TextOnPath); + SkTextOnPath(); + virtual bool draw(SkAnimateMaker& ); +private: + SkScalar offset; + SkDrawPath* path; + SkText* text; + typedef SkBoundable INHERITED; +}; + +#endif // SkTextOnPath_DEFINED diff --git a/skia/animator/SkTextToPath.cpp b/skia/animator/SkTextToPath.cpp new file mode 100644 index 0000000..2a3fa8d --- /dev/null +++ b/skia/animator/SkTextToPath.cpp @@ -0,0 +1,56 @@ +/* libs/graphics/animator/SkTextToPath.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkTextToPath.h" +#include "SkAnimateMaker.h" +#include "SkDrawPaint.h" +#include "SkDrawPath.h" +#include "SkDrawText.h" +#include "SkPaint.h" + +#if SK_USE_CONDENSED_INFO == 0 + +const SkMemberInfo SkTextToPath::fInfo[] = { + SK_MEMBER(paint, Paint), + SK_MEMBER(path, Path), + SK_MEMBER(text, Text) +}; + +#endif + +DEFINE_GET_MEMBER(SkTextToPath); + +SkTextToPath::SkTextToPath() : paint(NULL), path(NULL), text(NULL) { +} + +bool SkTextToPath::draw(SkAnimateMaker& maker) { + path->draw(maker); + return false; +} + +void SkTextToPath::onEndElement(SkAnimateMaker& maker) { + if (paint == NULL || path == NULL || text == NULL) { + // !!! add error message here + maker.setErrorCode(SkDisplayXMLParserError::kErrorInAttributeValue); + return; + } + SkPaint realPaint; + paint->setupPaint(&realPaint); + realPaint.getTextPath(text->getText(), text->getSize(), text->x, + text->y, &path->getPath()); +} + diff --git a/skia/animator/SkTextToPath.h b/skia/animator/SkTextToPath.h new file mode 100644 index 0000000..2ba41cf --- /dev/null +++ b/skia/animator/SkTextToPath.h @@ -0,0 +1,40 @@ +/* libs/graphics/animator/SkTextToPath.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkTextToPath_DEFINED +#define SkTextToPath_DEFINED + +#include "SkDrawPath.h" +#include "SkMemberInfo.h" + +class SkDrawPaint; +class SkDrawPath; +class SkText; + +class SkTextToPath : public SkDrawable { + DECLARE_MEMBER_INFO(TextToPath); + SkTextToPath(); + virtual bool draw(SkAnimateMaker& ); + virtual void onEndElement(SkAnimateMaker& ); +private: + SkDrawPaint* paint; + SkDrawPath* path; + SkText* text; +}; + +#endif // SkTextToPath_DEFINED + diff --git a/skia/animator/SkTime.cpp b/skia/animator/SkTime.cpp new file mode 100644 index 0000000..de091fe --- /dev/null +++ b/skia/animator/SkTime.cpp @@ -0,0 +1,89 @@ +/* libs/graphics/animator/SkTime.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkTime.h" + +#ifdef SK_BUILD_FOR_WIN + +#ifdef SK_DEBUG +SkMSec gForceTickCount = (SkMSec) -1; +#endif + +void SkTime::GetDateTime(DateTime* t) +{ + if (t) + { + SYSTEMTIME syst; + + ::GetLocalTime(&syst); + t->fYear = SkToU16(syst.wYear); + t->fMonth = SkToU8(syst.wMonth); + t->fDayOfWeek = SkToU8(syst.wDayOfWeek); + t->fDay = SkToU8(syst.wDay); + t->fHour = SkToU8(syst.wHour); + t->fMinute = SkToU8(syst.wMinute); + t->fSecond = SkToU8(syst.wSecond); + } +} + +SkMSec SkTime::GetMSecs() +{ +#ifdef SK_DEBUG + if (gForceTickCount != (SkMSec) -1) + return gForceTickCount; +#endif + return ::GetTickCount(); +} + +#elif defined(xSK_BUILD_FOR_MAC) + +#include <time.h> + +void SkTime::GetDateTime(DateTime* t) +{ + if (t) + { + tm syst; + time_t tm; + + time(&tm); + localtime_r(&tm, &syst); + t->fYear = SkToU16(syst.tm_year); + t->fMonth = SkToU8(syst.tm_mon + 1); + t->fDayOfWeek = SkToU8(syst.tm_wday); + t->fDay = SkToU8(syst.tm_mday); + t->fHour = SkToU8(syst.tm_hour); + t->fMinute = SkToU8(syst.tm_min); + t->fSecond = SkToU8(syst.tm_sec); + } +} + +#include "Sk64.h" + +SkMSec SkTime::GetMSecs() +{ + UnsignedWide wide; + Sk64 s; + + ::Microseconds(&wide); + s.set(wide.hi, wide.lo); + s.div(1000, Sk64::kRound_DivOption); + return s.get32(); +} + +#endif + diff --git a/skia/animator/SkTypedArray.cpp b/skia/animator/SkTypedArray.cpp new file mode 100644 index 0000000..fb5ba2c --- /dev/null +++ b/skia/animator/SkTypedArray.cpp @@ -0,0 +1,187 @@ +/* libs/graphics/animator/SkTypedArray.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkTypedArray.h" + +SkTypedArray::SkTypedArray() : fType(SkType_Unknown) { +} + +SkTypedArray::SkTypedArray(SkDisplayTypes type) : fType(type) { +} + +bool SkTypedArray::getIndex(int index, SkOperand* operand) { + if (index >= count()) { + SkASSERT(0); + return false; + } + *operand = begin()[index]; + return true; +} + + +#if SK_SMALLER_ARRAY_TEMPLATE_EXPERIMENT == 1 +SkDS32Array::SkDS32Array() +{ + fReserve = fCount = 0; + fArray = NULL; +#ifdef SK_DEBUG + fData = NULL; +#endif +} + +SkDS32Array::SkDS32Array(const SkDS32Array& src) +{ + fReserve = fCount = 0; + fArray = NULL; +#ifdef SK_DEBUG + fData = NULL; +#endif + SkDS32Array tmp(src.fArray, src.fCount); + this->swap(tmp); +} + +SkDS32Array::SkDS32Array(const int32_t src[], U16CPU count) +{ + SkASSERT(src || count == 0); + + fReserve = fCount = 0; + fArray = NULL; +#ifdef SK_DEBUG + fData = NULL; +#endif + if (count) + { + fArray = (int32_t*)sk_malloc_throw(count * sizeof(int32_t)); +#ifdef SK_DEBUG + fData = (int32_t (*)[kDebugArraySize]) fArray; +#endif + memcpy(fArray, src, sizeof(int32_t) * count); + fReserve = fCount = SkToU16(count); + } +} + +SkDS32Array& SkDS32Array::operator=(const SkDS32Array& src) +{ + if (this != &src) + { + if (src.fCount > fReserve) + { + SkDS32Array tmp(src.fArray, src.fCount); + this->swap(tmp); + } + else + { + memcpy(fArray, src.fArray, sizeof(int32_t) * src.fCount); + fCount = src.fCount; + } + } + return *this; +} + +int operator==(const SkDS32Array& a, const SkDS32Array& b) +{ + return a.fCount == b.fCount && + (a.fCount == 0 || !memcmp(a.fArray, b.fArray, a.fCount * sizeof(int32_t))); +} + +void SkDS32Array::swap(SkDS32Array& other) +{ + SkTSwap(fArray, other.fArray); +#ifdef SK_DEBUG + SkTSwap(fData, other.fData); +#endif + SkTSwap(fReserve, other.fReserve); + SkTSwap(fCount, other.fCount); +} + +int32_t* SkDS32Array::append(U16CPU count, const int32_t* src) +{ + unsigned oldCount = fCount; + if (count) + { + SkASSERT(src == NULL || fArray == NULL || + src + count <= fArray || fArray + count <= src); + + this->growBy(count); + if (src) + memcpy(fArray + oldCount, src, sizeof(int32_t) * count); + } + return fArray + oldCount; +} + +int SkDS32Array::find(const int32_t& elem) const +{ + const int32_t* iter = fArray; + const int32_t* stop = fArray + fCount; + + for (; iter < stop; iter++) + { + if (*iter == elem) + return (int) (iter - fArray); + } + return -1; +} + +void SkDS32Array::growBy(U16CPU extra) +{ + SkASSERT(extra); + SkASSERT(fCount + extra <= 0xFFFF); + + if (fCount + extra > fReserve) + { + size_t size = fCount + extra + 4; + size += size >> 2; + int32_t* array = (int32_t*)sk_malloc_throw(size * sizeof(int32_t)); + memcpy(array, fArray, fCount * sizeof(int32_t)); + + sk_free(fArray); + fArray = array; +#ifdef SK_DEBUG + fData = (int32_t (*)[kDebugArraySize]) fArray; +#endif + fReserve = SkToU16((U16CPU)size); + } + fCount = SkToU16(fCount + extra); +} + +int32_t* SkDS32Array::insert(U16CPU index, U16CPU count, const int32_t* src) +{ + SkASSERT(count); + int oldCount = fCount; + this->growBy(count); + int32_t* dst = fArray + index; + memmove(dst + count, dst, sizeof(int32_t) * (oldCount - index)); + if (src) + memcpy(dst, src, sizeof(int32_t) * count); + return dst; +} + + + int SkDS32Array::rfind(const int32_t& elem) const + { + const int32_t* iter = fArray + fCount; + const int32_t* stop = fArray; + + while (iter > stop) + { + if (*--iter == elem) + return (int) (iter - stop); + } + return -1; + } + +#endif diff --git a/skia/animator/SkTypedArray.h b/skia/animator/SkTypedArray.h new file mode 100644 index 0000000..0535426 --- /dev/null +++ b/skia/animator/SkTypedArray.h @@ -0,0 +1,39 @@ +/* libs/graphics/animator/SkTypedArray.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkTypedArray_DEFINED +#define SkTypedArray_DEFINED + +#include "SkScript.h" +#include "SkTDArray_Experimental.h" + +class SkTypedArray : public SkTDOperandArray { +public: + SkTypedArray(); + SkTypedArray(SkDisplayTypes type); + bool getIndex(int index, SkOperand* operand); + SkDisplayTypes getType() { return fType; } + SkScriptEngine::SkOpType getOpType() { return SkScriptEngine::ToOpType(fType); } + void setType(SkDisplayTypes type) { + // SkASSERT(count() == 0); + fType = type; + } +protected: + SkDisplayTypes fType; +}; + +#endif // SkTypedArray_DEFINED diff --git a/skia/animator/SkXMLAnimatorWriter.cpp b/skia/animator/SkXMLAnimatorWriter.cpp new file mode 100644 index 0000000..86a43a1 --- /dev/null +++ b/skia/animator/SkXMLAnimatorWriter.cpp @@ -0,0 +1,91 @@ +/* libs/graphics/animator/SkXMLAnimatorWriter.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkXMLAnimatorWriter.h" +#include "SkAnimator.h" +#include "SkAnimateMaker.h" +#include "SkDisplayXMLParser.h" + +SkXMLAnimatorWriter::SkXMLAnimatorWriter(SkAnimator* animator) : fAnimator(animator) +{ + fParser = new SkDisplayXMLParser(*fAnimator->fMaker); +} + +SkXMLAnimatorWriter::~SkXMLAnimatorWriter() { + delete fParser; +} + +void SkXMLAnimatorWriter::onAddAttributeLen(const char name[], const char value[], size_t length) +{ + fParser->onAddAttributeLen(name, value, length); +} + +void SkXMLAnimatorWriter::onEndElement() +{ + Elem* elem = getEnd(); + fParser->onEndElement(elem->fName.c_str()); + doEnd(elem); +} + +void SkXMLAnimatorWriter::onStartElementLen(const char name[], size_t length) +{ + doStart(name, length); + fParser->onStartElementLen(name, length); +} + +void SkXMLAnimatorWriter::writeHeader() +{ +} + +#ifdef SK_DEBUG +#include "SkCanvas.h" +#include "SkPaint.h" + +void SkXMLAnimatorWriter::UnitTest(SkCanvas* canvas) +{ + SkAnimator s; + SkXMLAnimatorWriter w(&s); + w.startElement("screenplay"); + w.startElement("animateField"); + w.addAttribute("field", "x1"); + w.addAttribute("id", "to100"); + w.addAttribute("from", "0"); + w.addAttribute("to", "100"); + w.addAttribute("dur", "1"); + w.endElement(); + w.startElement("event"); + w.addAttribute("kind", "onLoad"); + w.startElement("line"); + w.addAttribute("id", "line"); + w.addAttribute("x1", "-1"); + w.addAttribute("y1", "20"); + w.addAttribute("x2", "150"); + w.addAttribute("y2", "40"); + w.endElement(); + w.startElement("apply"); + w.addAttribute("animator", "to100"); + w.addAttribute("scope", "line"); + w.endElement(); + w.endElement(); + w.endElement(); + SkPaint paint; + canvas->drawColor(SK_ColorWHITE); + s.draw(canvas, &paint, 0); +} + +#endif + diff --git a/skia/animator/SkXMLAnimatorWriter.h b/skia/animator/SkXMLAnimatorWriter.h new file mode 100644 index 0000000..cc02b1c --- /dev/null +++ b/skia/animator/SkXMLAnimatorWriter.h @@ -0,0 +1,42 @@ +/* libs/graphics/animator/SkXMLAnimatorWriter.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkXMLAnimatorWriter_DEFINED +#define SkXMLAnimatorWriter_DEFINED + +#include "SkXMLWriter.h" + +class SkAnimator; +class SkDisplayXMLParser; + +class SkXMLAnimatorWriter : public SkXMLWriter { +public: + SkXMLAnimatorWriter(SkAnimator*); + virtual ~SkXMLAnimatorWriter(); + virtual void writeHeader(); + SkDEBUGCODE(static void UnitTest(class SkCanvas* canvas);) +protected: + virtual void onAddAttributeLen(const char name[], const char value[], size_t length); + virtual void onEndElement(); + virtual void onStartElementLen(const char elem[], size_t length); +private: + SkAnimator* fAnimator; + SkDisplayXMLParser* fParser; +}; + +#endif // SkXMLAnimatorWriter_DEFINED + diff --git a/skia/animator/thingstodo.txt b/skia/animator/thingstodo.txt new file mode 100644 index 0000000..8d0d47a --- /dev/null +++ b/skia/animator/thingstodo.txt @@ -0,0 +1,21 @@ +things to do: + figure out where endless or very deep recursion is possible + at these points, generate an error if actual physical stack gets too large + candidates are scripts + eval(eval(eval... user callouts + ((((( operator precedence or similar making stack deep + groups within groups + very large apply create or apply immediate steps + + write tests for math functions + looks like random takes a parameter when it should take zero parameters + + add Math, Number files to perforce for docs + alphabetize attributes in docs + + manually modified tools/screenplayDocs/xmlToJPEG.cpp + + fix docs where lines are stitched together (insert space) + + naked <data> outside of <post> asserts on name + handle errors for all element not contained by correct parents
\ No newline at end of file diff --git a/skia/corecg/Android.mk b/skia/corecg/Android.mk new file mode 100644 index 0000000..ead28bd --- /dev/null +++ b/skia/corecg/Android.mk @@ -0,0 +1,40 @@ +LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_ARM_MODE := arm
+
+LOCAL_SRC_FILES:= \
+ Sk64.cpp \
+ SkBuffer.cpp \
+ SkChunkAlloc.cpp \
+ SkCordic.cpp \
+ SkDebug.cpp \
+ SkDebug_stdio.cpp \
+ SkFloat.cpp \
+ SkInterpolator.cpp \
+ SkMath.cpp \
+ SkMatrix.cpp \
+ SkMemory_stdlib.cpp \
+ SkPoint.cpp \
+ SkRect.cpp \
+ SkRegion.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils \
+ libutils
+
+LOCAL_C_INCLUDES += \
+ include/corecg
+
+#LOCAL_CFLAGS+=
+#LOCAL_LDFLAGS:=
+
+LOCAL_MODULE:= libcorecg
+
+LOCAL_CFLAGS += -fstrict-aliasing
+
+ifeq ($(TARGET_ARCH),arm)
+ LOCAL_CFLAGS += -fomit-frame-pointer
+endif
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/skia/corecg/MODULE_LICENSE_APACHE2 b/skia/corecg/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/skia/corecg/MODULE_LICENSE_APACHE2 diff --git a/skia/corecg/Makefile b/skia/corecg/Makefile new file mode 100644 index 0000000..72f2f2a --- /dev/null +++ b/skia/corecg/Makefile @@ -0,0 +1,40 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_ARM_MODE := arm + +LOCAL_SRC_FILES:= \ + Sk64.cpp \ + SkBuffer.cpp \ + SkChunkAlloc.cpp \ + SkCordic.cpp \ + SkDebug.cpp \ + SkDebug_stdio.cpp \ + SkFloat.cpp \ + SkInterpolator.cpp \ + SkMath.cpp \ + SkMatrix.cpp \ + SkMemory_stdlib.cpp \ + SkPoint.cpp \ + SkRect.cpp \ + SkRegion.cpp + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libutils + +LOCAL_C_INCLUDES += \ + include/corecg + +#LOCAL_CFLAGS+= +#LOCAL_LDFLAGS:= + +LOCAL_MODULE:= libcorecg + +LOCAL_CFLAGS += -fstrict-aliasing + +ifeq ($(TARGET_ARCH),arm) + LOCAL_CFLAGS += -fomit-frame-pointer +endif + +include $(BUILD_SHARED_LIBRARY) diff --git a/skia/corecg/NOTICE b/skia/corecg/NOTICE new file mode 100644 index 0000000..29f81d8 --- /dev/null +++ b/skia/corecg/NOTICE @@ -0,0 +1,201 @@ + Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/skia/corecg/Sk64.cpp b/skia/corecg/Sk64.cpp new file mode 100644 index 0000000..028a4ae --- /dev/null +++ b/skia/corecg/Sk64.cpp @@ -0,0 +1,575 @@ +/* libs/corecg/Sk64.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "Sk64.h" + +#define shift_left(hi, lo) \ + hi = (hi << 1) | (lo >> 31); \ + lo <<= 1 + +#define shift_left_bits(hi, lo, bits) \ + SkASSERT((unsigned)(bits) < 31); \ + hi = (hi << (bits)) | (lo >> (32 - (bits))); \ + lo <<= (bits) + +////////////////////////////////////////////////////////////////////// + +int Sk64::getClzAbs() const +{ + int32_t hi = fHi; + uint32_t lo = fLo; + + // get abs + if (hi < 0) + { + hi = -hi - Sk32ToBool(lo); + lo = 0 - lo; + } + return hi ? SkCLZ(hi) : SkCLZ(lo) + 32; +} + +void Sk64::shiftLeft(unsigned bits) +{ + SkASSERT(bits <= 63); + if (bits == 0) + return; + + if (bits >= 32) + { + fHi = fLo << (bits - 32); + fLo = 0; + } + else + { + fHi = (fHi << bits) | (fLo >> (32 - bits)); + fLo <<= bits; + } +} + +int32_t Sk64::getShiftRight(unsigned bits) const +{ + SkASSERT(bits <= 63); + + if (bits == 0) + return fLo; + + if (bits >= 32) + return fHi >> (bits - 32); + else + { +#ifdef SK_DEBUG + int32_t tmp = fHi >> bits; + SkASSERT(tmp == 0 || tmp == -1); +#endif + return (fHi << (32 - bits)) | (fLo >> bits); + } +} + +void Sk64::shiftRight(unsigned bits) +{ + SkASSERT(bits <= 63); + if (bits == 0) + return; + + if (bits >= 32) + { + fLo = fHi >> (bits - 32); + fHi >>= 31; + } + else + { + fLo = (fHi << (32 - bits)) | (fLo >> bits); + fHi >>= bits; + } +} + +void Sk64::roundRight(unsigned bits) +{ + SkASSERT(bits <= 63); + if (bits) + { + Sk64 one; + one.set(1); + one.shiftLeft(bits - 1); + this->add(one); + this->shiftRight(bits); + } +} + +int Sk64::shiftToMake32() const +{ + int32_t hi = fHi; + uint32_t lo = fLo; + + if (hi < 0) // make it positive + { + hi = -hi - Sk32ToBool(lo); + lo = 0 - lo; + } + + if (hi == 0) + return lo >> 31; + else + return 33 - SkCLZ(hi); +} + +void Sk64::negate() +{ + fHi = -fHi - Sk32ToBool(fLo); + fLo = 0 - fLo; +} + +void Sk64::abs() +{ + if (fHi < 0) + { + fHi = -fHi - Sk32ToBool(fLo); + fLo = 0 - fLo; + } +} + +//////////////////////////////////////////////////////////////// + +static inline int32_t round_right_16(int32_t hi, uint32_t lo) +{ + uint32_t sum = lo + (1 << 15); + hi += (sum < lo); + return (hi << 16) | (sum >> 16); +} + +SkBool Sk64::isFixed() const +{ + Sk64 tmp = *this; + tmp.roundRight(16); + return tmp.is32(); +} + +SkFract Sk64::getFract() const +{ + Sk64 tmp = *this; + tmp.roundRight(30); + return tmp.get32(); +} + +void Sk64::sub(const Sk64& a) +{ + fHi = fHi - a.fHi - (fLo < a.fLo); + fLo = fLo - a.fLo; +} + +void Sk64::rsub(const Sk64& a) +{ + fHi = a.fHi - fHi - (a.fLo < fLo); + fLo = a.fLo - fLo; +} + +void Sk64::setMul(int32_t a, int32_t b) +{ + int sa = a >> 31; + int sb = b >> 31; + // now make them positive + a = (a ^ sa) - sa; + b = (b ^ sb) - sb; + + uint32_t ah = a >> 16; + uint32_t al = a & 0xFFFF; + uint32_t bh = b >> 16; + uint32_t bl = b & 0xFFFF; + + uint32_t A = ah * bh; + uint32_t B = ah * bl + al * bh; + uint32_t C = al * bl; + + /* [ A ] + [ B ] + [ C ] + */ + fLo = C + (B << 16); + fHi = A + (B >>16) + (fLo < C); + + if (sa != sb) + this->negate(); +} + +void Sk64::div(int32_t denom, DivOptions option) +{ + SkASSERT(denom); + + int32_t hi = fHi; + uint32_t lo = fLo; + int sign = denom ^ hi; + + denom = SkAbs32(denom); + if (hi < 0) + { + hi = -hi - Sk32ToBool(lo); + lo = 0 - lo; + } + + if (option == kRound_DivOption) // add denom/2 + { + uint32_t newLo = lo + (denom >> 1); + hi += (newLo < lo); + lo = newLo; + } + + if (hi == 0) // fast-case + { + if (lo < (uint32_t)denom) + this->set(0, 0); + else + { + this->set(0, lo / denom); + if (sign < 0) + this->negate(); + } + return; + } + + int bits; + + { + int dbits = SkCLZ(denom); + int nbits = SkCLZ(hi); + + bits = 32 + dbits - nbits; + SkASSERT(bits <= 63); + if (bits <= 0) + { + this->set(0, 0); + return; + } + denom <<= (dbits - 1); + shift_left_bits(hi, lo, nbits - 1); + } + + int32_t rhi = 0; + uint32_t rlo = 0; + + do { + shift_left(rhi, rlo); +#ifdef SK_CPU_HAS_CONDITIONAL_INSTR + if ((uint32_t)denom <= (uint32_t)hi) + { + hi -= denom; + rlo |= 1; + } +#else + int32_t diff = (denom - hi - 1) >> 31; + hi -= denom & diff; + rlo -= diff; +#endif + shift_left(hi, lo); + } while (--bits >= 0); + SkASSERT(rhi >= 0); + + fHi = rhi; + fLo = rlo; + if (sign < 0) + this->negate(); +} + +#define shift_left_2(a, b, c) \ + a = (a << 2) | (b >> 30); \ + b = (b << 2) | (c >> 30); \ + c <<= 2 + +int32_t Sk64::getSqrt() const +{ + SkASSERT(!this->isNeg()); + + uint32_t hi = fHi; + uint32_t lo = fLo; + uint32_t sqr = 0; + uint32_t root = 0; + int count = 31; + + do { + root <<= 1; + shift_left_2(sqr, hi, lo); + + uint32_t testDiv = (root << 1) + 1; + if (sqr >= testDiv) + { + sqr -= testDiv; + root++; + } + } while (--count >= 0); + SkASSERT((int32_t)root >= 0); + + return root; +} + +#ifdef SkLONGLONG + SkLONGLONG Sk64::getLongLong() const + { + SkLONGLONG value = fHi; + value <<= 32; + return value | fLo; + } +#endif + +SkFixed Sk64::getFixedDiv(const Sk64& denom) const +{ + Sk64 N = *this; + Sk64 D = denom; + int32_t sign = SkExtractSign(N.fHi ^ D.fHi); + SkFixed result; + + N.abs(); + D.abs(); + + // need to knock D down to just 31 bits + // either by rounding it to the right, or shifting N to the left + // then we can just call 64/32 div + + int nclz = N.fHi ? SkCLZ(N.fHi) : 32; + int dclz = D.fHi ? SkCLZ(D.fHi) : (33 - (D.fLo >> 31)); + + int shiftN = nclz - 1; + SkASSERT(shiftN >= 0); + int shiftD = 33 - dclz; + SkASSERT(shiftD >= 0); + + if (shiftD + shiftN < 16) + shiftD = 16 - shiftN; + else + shiftN = 16 - shiftD; + + D.roundRight(shiftD); + if (D.isZero()) + result = SK_MaxS32; + else + { + if (shiftN >= 0) + N.shiftLeft(shiftN); + else + N.roundRight(-shiftN); + N.div(D.get32(), Sk64::kTrunc_DivOption); + if (N.is32()) + result = N.get32(); + else + result = SK_MaxS32; + } + return SkApplySign(result, sign); +} + +/////////////////////////////////////////////////////////////////////// + +#ifdef SK_DEBUG + +#include "SkRandom.h" +#include <math.h> + +#ifdef SK_SUPPORT_UNITTEST +struct BoolTable { + int8_t zero, pos, neg, toBool, sign; +}; + +static void bool_table_test(const Sk64& a, const BoolTable& table) +{ + SkASSERT(a.isZero() != a.nonZero()); + + SkASSERT(!a.isZero() == !table.zero); + SkASSERT(!a.isPos() == !table.pos); + SkASSERT(!a.isNeg() == !table.neg); + SkASSERT(a.sign() == table.sign); +} + +#ifdef SkLONGLONG + static SkLONGLONG asLL(const Sk64& a) + { + return ((SkLONGLONG)a.fHi << 32) | a.fLo; + } +#endif +#endif + +void Sk64::UnitTest() +{ +#ifdef SK_SUPPORT_UNITTEST + enum BoolTests { + kZero_BoolTest, + kPos_BoolTest, + kNeg_BoolTest + }; + static const BoolTable gBoolTable[] = { + { 1, 0, 0, 0, 0 }, + { 0, 1, 0, 1, 1 }, + { 0, 0, 1, 1, -1 } + }; + + Sk64 a, b, c; + + a.fHi = a.fLo = 0; + b.set(0); + c.setZero(); + SkASSERT(a == b); + SkASSERT(a == c); + bool_table_test(a, gBoolTable[kZero_BoolTest]); + + a.fHi = 0; a.fLo = 5; + b.set(5); + SkASSERT(a == b); + SkASSERT(a.is32() && a.get32() == 5 && !a.is64()); + bool_table_test(a, gBoolTable[kPos_BoolTest]); + + a.fHi = -1; a.fLo = (uint32_t)-5; + b.set(-5); + SkASSERT(a == b); + SkASSERT(a.is32() && a.get32() == -5 && !a.is64()); + bool_table_test(a, gBoolTable[kNeg_BoolTest]); + + a.setZero(); + b.set(6); + c.set(-6); + SkASSERT(a != b && b != c && a != c); + SkASSERT(!(a == b) && !(a == b) && !(a == b)); + SkASSERT(a < b && b > a && a <= b && b >= a); + SkASSERT(c < a && a > c && c <= a && a >= c); + SkASSERT(c < b && b > c && c <= b && b >= c); + + // Now test add/sub + + SkRandom rand; + int i; + + for (i = 0; i < 1000; i++) + { + int aa = rand.nextS() >> 1; + int bb = rand.nextS() >> 1; + a.set(aa); + b.set(bb); + SkASSERT(a.get32() == aa && b.get32() == bb); + c = a; c.add(bb); + SkASSERT(c.get32() == aa + bb); + c = a; c.add(-bb); + SkASSERT(c.get32() == aa - bb); + c = a; c.add(b); + SkASSERT(c.get32() == aa + bb); + c = a; c.sub(b); + SkASSERT(c.get32() == aa - bb); + } + +#ifdef SkLONGLONG + for (i = 0; i < 1000; i++) + { + rand.next64(&a); //a.fHi >>= 1; // avoid overflow + rand.next64(&b); //b.fHi >>= 1; // avoid overflow + + if (!(i & 3)) // want to explicitly test these cases + { + a.fLo = 0; + b.fLo = 0; + } + else if (!(i & 7)) // want to explicitly test these cases + { + a.fHi = 0; + b.fHi = 0; + } + + SkLONGLONG aa = asLL(a); + SkLONGLONG bb = asLL(b); + + SkASSERT((a < b) == (aa < bb)); + SkASSERT((a <= b) == (aa <= bb)); + SkASSERT((a > b) == (aa > bb)); + SkASSERT((a >= b) == (aa >= bb)); + SkASSERT((a == b) == (aa == bb)); + SkASSERT((a != b) == (aa != bb)); + + c = a; c.add(b); + SkASSERT(asLL(c) == aa + bb); + c = a; c.sub(b); + SkASSERT(asLL(c) == aa - bb); + c = a; c.rsub(b); + SkASSERT(asLL(c) == bb - aa); + c = a; c.negate(); + SkASSERT(asLL(c) == -aa); + + int bits = rand.nextU() & 63; + c = a; c.shiftLeft(bits); + SkASSERT(asLL(c) == (aa << bits)); + c = a; c.shiftRight(bits); + SkASSERT(asLL(c) == (aa >> bits)); + c = a; c.roundRight(bits); + + SkLONGLONG tmp; + + tmp = aa; + if (bits > 0) + tmp += (SkLONGLONG)1 << (bits - 1); + SkASSERT(asLL(c) == (tmp >> bits)); + + c.setMul(a.fHi, b.fHi); + tmp = (SkLONGLONG)a.fHi * b.fHi; + SkASSERT(asLL(c) == tmp); + } + + + for (i = 0; i < 100000; i++) + { + Sk64 wide; + int32_t denom = rand.nextS(); + + while (denom == 0) + denom = rand.nextS(); + wide.setMul(rand.nextS(), rand.nextS()); + SkLONGLONG check = wide.getLongLong(); + + wide.div(denom, Sk64::kTrunc_DivOption); + check /= denom; + SkLONGLONG w = wide.getLongLong(); + + SkASSERT(check == w); + +#ifdef SK_CAN_USE_FLOATx + wide.setMul(rand.nextS(), rand.nextS()); + wide.abs(); + denom = wide.getSqrt(); + int32_t ck = (int32_t)sqrt((double)wide.getLongLong()); + int diff = denom - ck; + SkASSERT(SkAbs32(diff) <= 1); + + wide.setMul(rand.nextS(), rand.nextS()); + Sk64 dwide; + dwide.setMul(rand.nextS(), rand.nextS()); + SkFixed fixdiv = wide.getFixedDiv(dwide); + double dnumer = (double)wide.getLongLong(); + double ddenom = (double)dwide.getLongLong(); + double ddiv = dnumer / ddenom; + SkFixed dfixdiv; + if (ddiv >= (double)SK_MaxS32 / (double)SK_Fixed1) + dfixdiv = SK_MaxS32; + else if (ddiv <= -(double)SK_MaxS32 / (double)SK_Fixed1) + dfixdiv = SK_MinS32; + else + dfixdiv = SkFloatToFixed(dnumer / ddenom); + diff = fixdiv - dfixdiv; + + if (SkAbs32(diff) > 1) { + SkDebugf(" %d === numer %g denom %g div %g xdiv %x fxdiv %x\n", + i, dnumer, ddenom, ddiv, dfixdiv, fixdiv); + } +// SkASSERT(SkAbs32(diff) <= 1); +#endif + } +#endif +#endif +} + +#endif + diff --git a/skia/corecg/SkBuffer.cpp b/skia/corecg/SkBuffer.cpp new file mode 100644 index 0000000..1a8f069 --- /dev/null +++ b/skia/corecg/SkBuffer.cpp @@ -0,0 +1,137 @@ +/* libs/corecg/SkBuffer.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkBuffer.h" + +//////////////////////////////////////////////////////////////////////////////////////// + +void SkRBuffer::readNoSizeCheck(void* buffer, size_t size) +{ + SkASSERT((fData != 0 && fStop == 0) || fPos + size <= fStop); + if (buffer) + memcpy(buffer, fPos, size); + fPos += size; +} + +const void* SkRBuffer::skip(size_t size) +{ + const void* result = fPos; + readNoSizeCheck(NULL, size); + return result; +} + +size_t SkRBuffer::skipToAlign4() +{ + size_t pos = this->pos(); + size_t n = SkAlign4(pos) - pos; + fPos += n; + return n; +} + +void* SkWBuffer::skip(size_t size) +{ + void* result = fPos; + writeNoSizeCheck(NULL, size); + return fData == NULL ? NULL : result; +} + +void SkWBuffer::writeNoSizeCheck(const void* buffer, size_t size) +{ + SkASSERT(fData == 0 || fStop == 0 || fPos + size <= fStop); + if (fData && buffer) + memcpy(fPos, buffer, size); + fPos += size; +} + +size_t SkWBuffer::padToAlign4() +{ + size_t pos = this->pos(); + size_t n = SkAlign4(pos) - pos; + + if (n && fData) + { + char* p = fPos; + char* stop = p + n; + do { + *p++ = 0; + } while (p < stop); + } + fPos += n; + return n; +} + +#if 0 +#ifdef SK_DEBUG + static void AssertBuffer32(const void* buffer) + { + SkASSERT(buffer); + SkASSERT(((size_t)buffer & 3) == 0); + } +#else + #define AssertBuffer32(buffer) +#endif + +void* sk_buffer_write_int32(void* buffer, int32_t value) +{ + AssertBuffer32(buffer); + *(int32_t*)buffer = value; + return (char*)buffer + sizeof(int32_t); +} + +void* sk_buffer_write_int32(void* buffer, const int32_t values[], int count) +{ + AssertBuffer32(buffer); + SkASSERT(count >= 0); + + memcpy((int32_t*)buffer, values, count * sizeof(int32_t)); + return (char*)buffer + count * sizeof(int32_t); +} + +const void* sk_buffer_read_int32(const void* buffer, int32_t* value) +{ + AssertBuffer32(buffer); + if (value) + *value = *(const int32_t*)buffer; + return (const char*)buffer + sizeof(int32_t); +} + +const void* sk_buffer_read_int32(const void* buffer, int32_t values[], int count) +{ + AssertBuffer32(buffer); + SkASSERT(count >= 0); + + if (values) + memcpy(values, (const int32_t*)buffer, count * sizeof(int32_t)); + return (const char*)buffer + count * sizeof(int32_t); +} + +void* sk_buffer_write_ptr(void* buffer, void* ptr) +{ + AssertBuffer32(buffer); + *(void**)buffer = ptr; + return (char*)buffer + sizeof(void*); +} + +const void* sk_buffer_read_ptr(const void* buffer, void** ptr) +{ + AssertBuffer32(buffer); + if (ptr) + *ptr = *(void**)buffer; + return (const char*)buffer + sizeof(void*); +} + +#endif diff --git a/skia/corecg/SkChunkAlloc.cpp b/skia/corecg/SkChunkAlloc.cpp new file mode 100644 index 0000000..b9cfd90 --- /dev/null +++ b/skia/corecg/SkChunkAlloc.cpp @@ -0,0 +1,120 @@ +/* libs/corecg/SkChunkAlloc.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkChunkAlloc.h" + +struct SkChunkAlloc::Block { + Block* fNext; + size_t fFreeSize; + char* fFreePtr; + // data[] follows + + void freeChain() { // this can be null + Block* block = this; + while (block) { + Block* next = block->fNext; + sk_free(block); + block = next; + } + }; + + Block* tail() { + Block* block = this; + if (block) { + for (;;) { + Block* next = block->fNext; + if (NULL == next) { + break; + } + block = next; + } + } + return block; + } +}; + +SkChunkAlloc::SkChunkAlloc(size_t minSize) + : fBlock(NULL), fMinSize(SkAlign4(minSize)), fPool(NULL), fTotalCapacity(0) +{ +} + +SkChunkAlloc::~SkChunkAlloc() { + this->reset(); +} + +void SkChunkAlloc::reset() { + fBlock->freeChain(); + fBlock = NULL; + fPool->freeChain(); + fPool = NULL; + fTotalCapacity = 0; +} + +void SkChunkAlloc::reuse() { + if (fPool && fBlock) { + fPool->tail()->fNext = fBlock; + } + fPool = fBlock; + fBlock = NULL; + fTotalCapacity = 0; +} + +SkChunkAlloc::Block* SkChunkAlloc::newBlock(size_t bytes, AllocFailType ftype) { + Block* block = fPool; + + if (block && bytes <= block->fFreeSize) { + fPool = block->fNext; + return block; + } + + size_t size = SkMax32((int32_t)bytes, (int32_t)fMinSize); + + block = (Block*)sk_malloc_flags(sizeof(Block) + size, + ftype == kThrow_AllocFailType ? SK_MALLOC_THROW : 0); + + if (block) { + // block->fNext = fBlock; + block->fFreeSize = size; + block->fFreePtr = (char*)block + sizeof(Block); + + fTotalCapacity += size; + } + return block; +} + +void* SkChunkAlloc::alloc(size_t bytes, AllocFailType ftype) { + bytes = SkAlign4(bytes); + + Block* block = fBlock; + + if (block == NULL || bytes > block->fFreeSize) { + block = this->newBlock(bytes, ftype); + if (NULL == block) { + return NULL; + } + block->fNext = fBlock; + fBlock = block; + } + + SkASSERT(block && bytes <= block->fFreeSize); + void* ptr = block->fFreePtr; + + block->fFreeSize -= bytes; + block->fFreePtr += bytes; + return ptr; +} + diff --git a/skia/corecg/SkCordic.cpp b/skia/corecg/SkCordic.cpp new file mode 100644 index 0000000..accbd95 --- /dev/null +++ b/skia/corecg/SkCordic.cpp @@ -0,0 +1,301 @@ +/* libs/corecg/SkCordic.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkCordic.h" +#include "SkMath.h" +#include "Sk64.h" + +// 0x20000000 equals pi / 4 +const int32_t kATanDegrees[] = { 0x20000000, + 0x12E4051D, 0x9FB385B, 0x51111D4, 0x28B0D43, 0x145D7E1, 0xA2F61E, 0x517C55, + 0x28BE53, 0x145F2E, 0xA2F98, 0x517CC, 0x28BE6, 0x145F3, 0xA2F9, 0x517C, + 0x28BE, 0x145F, 0xA2F, 0x517, 0x28B, 0x145, 0xA2, 0x51, 0x28, 0x14, + 0xA, 0x5, 0x2, 0x1 }; + +const int32_t kFixedInvGain1 = 0x18bde0bb; // 0.607252935 + +static void SkCircularRotation(int32_t* x0, int32_t* y0, int32_t* z0) +{ + int32_t t = 0; + int32_t x = *x0; + int32_t y = *y0; + int32_t z = *z0; + const int32_t* tanPtr = kATanDegrees; + do { + int32_t x1 = y >> t; + int32_t y1 = x >> t; + int32_t tan = *tanPtr++; + if (z >= 0) { + x -= x1; + y += y1; + z -= tan; + } else { + x += x1; + y -= y1; + z += tan; + } + } while (++t < 16); // 30); + *x0 = x; + *y0 = y; + *z0 = z; +} + +SkFixed SkCordicSinCos(SkFixed radians, SkFixed* cosp) +{ + int32_t scaledRadians = radians * 0x28be; // scale radians to 65536 / PI() + int quadrant = scaledRadians >> 30; + quadrant += 1; + if (quadrant & 2) + scaledRadians = -scaledRadians + 0x80000000; + /* |a| <= 90 degrees as a 1.31 number */ + SkFixed sin = 0; + SkFixed cos = kFixedInvGain1; + SkCircularRotation(&cos, &sin, &scaledRadians); + Sk64 scaled; + scaled.setMul(sin, 0x6488d); + sin = scaled.fHi; + scaled.setMul(cos, 0x6488d); + if (quadrant & 2) + scaled.fHi = - scaled.fHi; + *cosp = scaled.fHi; + return sin; +} + +SkFixed SkCordicTan(SkFixed a) +{ + int32_t cos; + int32_t sin = SkCordicSinCos(a, &cos); + return SkFixedDiv(sin, cos); +} + +static int32_t SkCircularVector(int32_t* y0, int32_t* x0, int32_t vecMode) +{ + int32_t x = *x0; + int32_t y = *y0; + int32_t z = 0; + int32_t t = 0; + const int32_t* tanPtr = kATanDegrees; + do { + int32_t x1 = y >> t; + int32_t y1 = x >> t; + int32_t tan = *tanPtr++; + if (y < vecMode) { + x -= x1; + y += y1; + z -= tan; + } else { + x += x1; + y -= y1; + z += tan; + } + } while (++t < 16); // 30 + Sk64 scaled; + scaled.setMul(z, 0x6488d); // scale back into the SkScalar space (0x100000000/0x28be) + return scaled.fHi; +} + +SkFixed SkCordicASin(SkFixed a) { + int32_t sign = SkExtractSign(a); + int32_t z = SkFixedAbs(a); + if (z >= SK_Fixed1) + return SkApplySign(SK_FixedPI>>1, sign); + int32_t x = kFixedInvGain1; + int32_t y = 0; + z *= 0x28be; + z = SkCircularVector(&y, &x, z); + z = SkApplySign(z, ~sign); + return z; +} + +SkFixed SkCordicACos(SkFixed a) { + int32_t z = SkCordicASin(a); + z = (SK_FixedPI>>1) - z; + return z; +} + +SkFixed SkCordicATan2(SkFixed y, SkFixed x) { + if ((x | y) == 0) + return 0; + int32_t xsign = SkExtractSign(x); + x = SkFixedAbs(x); + int32_t result = SkCircularVector(&y, &x, 0); + if (xsign) { + int32_t rsign = SkExtractSign(result); + if (y == 0) + rsign = 0; + SkFixed pi = SkApplySign(SK_FixedPI, rsign); + result = pi - result; + } + return result; +} + +const int32_t kATanHDegrees[] = { + 0x1661788D, 0xA680D61, 0x51EA6FC, 0x28CBFDD, 0x1460E34, + 0xA2FCE8, 0x517D2E, 0x28BE6E, 0x145F32, + 0xA2F98, 0x517CC, 0x28BE6, 0x145F3, 0xA2F9, 0x517C, + 0x28BE, 0x145F, 0xA2F, 0x517, 0x28B, 0x145, 0xA2, 0x51, 0x28, 0x14, + 0xA, 0x5, 0x2, 0x1 }; + +const int32_t kFixedInvGain2 = 0x31330AAA; // 1.207534495 + +static void SkHyperbolic(int32_t* x0, int32_t* y0, int32_t* z0, int mode) +{ + int32_t t = 1; + int32_t x = *x0; + int32_t y = *y0; + int32_t z = *z0; + const int32_t* tanPtr = kATanHDegrees; + int k = -3; + do { + int32_t x1 = y >> t; + int32_t y1 = x >> t; + int32_t tan = *tanPtr++; + int count = 2 + (k >> 31); + if (++k == 1) + k = -2; + do { + if (((y >> 31) & mode) | ~((z >> 31) | mode)) { + x += x1; + y += y1; + z -= tan; + } else { + x -= x1; + y -= y1; + z += tan; + } + } while (--count); + } while (++t < 30); + *x0 = x; + *y0 = y; + *z0 = z; +} + +SkFixed SkCordicLog(SkFixed a) { + a *= 0x28be; + int32_t x = a + 0x28BE60DB; // 1.0 + int32_t y = a - 0x28BE60DB; + int32_t z = 0; + SkHyperbolic(&x, &y, &z, -1); + Sk64 scaled; + scaled.setMul(z, 0x6488d); + z = scaled.fHi; + return z << 1; +} + +SkFixed SkCordicExp(SkFixed a) { + int32_t cosh = kFixedInvGain2; + int32_t sinh = 0; + SkHyperbolic(&cosh, &sinh, &a, 0); + return cosh + sinh; +} + +#ifdef SK_DEBUG + +#ifdef SK_CAN_USE_FLOAT + #include "SkFloatingPoint.h" +#endif + +void SkCordic_UnitTest() +{ +#if defined(SK_SUPPORT_UNITTEST) && defined(SK_CAN_USE_FLOAT) + float val; + for (float angle = -720; angle < 720; angle += 30) { + float radian = angle * 3.1415925358f / 180.0f; + SkFixed f_angle = (int) (radian * 65536.0f); + // sincos + float sine = sinf(radian); + float cosine = cosf(radian); + SkFixed f_cosine; + SkFixed f_sine = SkCordicSinCos(f_angle, &f_cosine); + float sine2 = (float) f_sine / 65536.0f; + float cosine2 = (float) f_cosine / 65536.0f; + float error = fabsf(sine - sine2); + if (error > 0.001) + SkDebugf("sin error : angle = %g ; sin = %g ; cordic = %g\n", angle, sine, sine2); + error = fabsf(cosine - cosine2); + if (error > 0.001) + SkDebugf("cos error : angle = %g ; cos = %g ; cordic = %g\n", angle, cosine, cosine2); + // tan + float _tan = tanf(radian); + SkFixed f_tan = SkCordicTan(f_angle); + float tan2 = (float) f_tan / 65536.0f; + error = fabsf(_tan - tan2); + if (error > 0.05 && fabsf(_tan) < 1e6) + SkDebugf("tan error : angle = %g ; tan = %g ; cordic = %g\n", angle, _tan, tan2); + } + for (val = -1; val <= 1; val += .1f) { + SkFixed f_val = (int) (val * 65536.0f); + // asin + float arcsine = asinf(val); + SkFixed f_arcsine = SkCordicASin(f_val); + float arcsine2 = (float) f_arcsine / 65536.0f; + float error = fabsf(arcsine - arcsine2); + if (error > 0.001) + SkDebugf("asin error : val = %g ; asin = %g ; cordic = %g\n", val, arcsine, arcsine2); + } +#if 1 + for (val = -1; val <= 1; val += .1f) { +#else + val = .5; { +#endif + SkFixed f_val = (int) (val * 65536.0f); + // acos + float arccos = acosf(val); + SkFixed f_arccos = SkCordicACos(f_val); + float arccos2 = (float) f_arccos / 65536.0f; + float error = fabsf(arccos - arccos2); + if (error > 0.001) + SkDebugf("acos error : val = %g ; acos = %g ; cordic = %g\n", val, arccos, arccos2); + } + // atan2 +#if 1 + for (val = -1000; val <= 1000; val += 500.f) { + for (float val2 = -1000; val2 <= 1000; val2 += 500.f) { +#else + val = 0; { + float val2 = -1000; { +#endif + SkFixed f_val = (int) (val * 65536.0f); + SkFixed f_val2 = (int) (val2 * 65536.0f); + float arctan = atan2f(val, val2); + SkFixed f_arctan = SkCordicATan2(f_val, f_val2); + float arctan2 = (float) f_arctan / 65536.0f; + float error = fabsf(arctan - arctan2); + if (error > 0.001) + SkDebugf("atan2 error : val = %g ; val2 = %g ; atan2 = %g ; cordic = %g\n", val, val2, arctan, arctan2); + } + } + // log +#if 1 + for (val = 0.125f; val <= 8.f; val *= 2.0f) { +#else + val = .5; { +#endif + SkFixed f_val = (int) (val * 65536.0f); + // acos + float log = logf(val); + SkFixed f_log = SkCordicLog(f_val); + float log2 = (float) f_log / 65536.0f; + float error = fabsf(log - log2); + if (error > 0.001) + SkDebugf("log error : val = %g ; log = %g ; cordic = %g\n", val, log, log2); + } + // exp +#endif +} + +#endif diff --git a/skia/corecg/SkCordic.h b/skia/corecg/SkCordic.h new file mode 100644 index 0000000..70c70b5 --- /dev/null +++ b/skia/corecg/SkCordic.h @@ -0,0 +1,37 @@ +/* libs/corecg/SkCordic.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkCordic_DEFINED +#define SkCordic_DEFINED + +#include "SkTypes.h" +#include "SkFixed.h" + +SkFixed SkCordicACos(SkFixed a); +SkFixed SkCordicASin(SkFixed a); +SkFixed SkCordicATan2(SkFixed y, SkFixed x); +SkFixed SkCordicExp(SkFixed a); +SkFixed SkCordicLog(SkFixed a); +SkFixed SkCordicSinCos(SkFixed radians, SkFixed* cosp); +SkFixed SkCordicTan(SkFixed a); + +#ifdef SK_DEBUG + void SkCordic_UnitTest(); +#endif + +#endif // SkCordic + diff --git a/skia/corecg/SkDebug.cpp b/skia/corecg/SkDebug.cpp new file mode 100644 index 0000000..5d5518d --- /dev/null +++ b/skia/corecg/SkDebug.cpp @@ -0,0 +1,59 @@ +/* libs/corecg/SkDebug.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkTypes.h" + +#ifdef SK_DEBUG + +int8_t SkToS8(long x) +{ + SkASSERT((int8_t)x == x); + return (int8_t)x; +} + +uint8_t SkToU8(size_t x) +{ + SkASSERT((uint8_t)x == x); + return (uint8_t)x; +} + +int16_t SkToS16(long x) +{ + SkASSERT((int16_t)x == x); + return (int16_t)x; +} + +uint16_t SkToU16(size_t x) +{ + SkASSERT((uint16_t)x == x); + return (uint16_t)x; +} + +int32_t SkToS32(long x) +{ + SkASSERT((int32_t)x == x); + return (int32_t)x; +} + +uint32_t SkToU32(size_t x) +{ + SkASSERT((uint32_t)x == x); + return (uint32_t)x; +} + +#endif + diff --git a/skia/corecg/SkDebug_stdio.cpp b/skia/corecg/SkDebug_stdio.cpp new file mode 100644 index 0000000..6da09af --- /dev/null +++ b/skia/corecg/SkDebug_stdio.cpp @@ -0,0 +1,61 @@ +/* libs/corecg/SkDebug_stdio.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkTypes.h" + +static const size_t kBufferSize = 256; + +#ifdef ANDROID + +#define LOG_TAG "skia" +#include <utils/Log.h> + +void Android_SkDebugf(const char* file, int line, const char* function, + const char* format, ...) +{ + if (format[0] == '\n' && format[1] == '\0') + return; + va_list args; + va_start(args, format); +#ifdef HAVE_ANDROID_OS + char buffer[kBufferSize + 1]; + vsnprintf(buffer, kBufferSize, format, args); + if (buffer[0] != 0) + __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "%s", buffer); +#else + android_vprintLog(ANDROID_LOG_DEBUG, NULL, LOG_TAG, format, args); +#endif + va_end(args); +} + +#elif defined(SK_DEBUG) + +#include <stdarg.h> +#include <stdio.h> + +void SkDebugf(const char format[], ...) +{ + char buffer[kBufferSize + 1]; + va_list args; + va_start(args, format); + vsnprintf(buffer, kBufferSize, format, args); + va_end(args); + fprintf(stderr, buffer); +} + +#endif + diff --git a/skia/corecg/SkFloat.cpp b/skia/corecg/SkFloat.cpp new file mode 100644 index 0000000..bb96947 --- /dev/null +++ b/skia/corecg/SkFloat.cpp @@ -0,0 +1,404 @@ +/* + * Copyright (C) 2006-2008 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "SkFloat.h" +#include "SkMath.h" + +#define EXP_BIAS (127+23) + +static int get_unsigned_exp(uint32_t packed) +{ + return (packed << 1 >> 24); +} + +static unsigned get_unsigned_value(uint32_t packed) +{ + return (packed << 9 >> 9) | (1 << 23); +} + +static int get_signed_value(int32_t packed) +{ + return SkApplySign(get_unsigned_value(packed), SkExtractSign(packed)); +} + +///////////////////////////////////////////////////////////////////////// + +int SkFloat::GetShift(int32_t packed, int shift) +{ + if (packed == 0) + return 0; + + int exp = get_unsigned_exp(packed) - EXP_BIAS - shift; + int value = get_unsigned_value(packed); + + if (exp >= 0) + { + if (exp > 8) // overflow + value = SK_MaxS32; + else + value <<= exp; + } + else + { + exp = -exp; + if (exp > 23) // underflow + value = 0; + else + value >>= exp; + } + return SkApplySign(value, SkExtractSign(packed)); +} + +///////////////////////////////////////////////////////////////////////////////////// + +int32_t SkFloat::SetShift(int value, int shift) +{ + if (value == 0) + return 0; + + // record the sign and make value positive + int sign = SkExtractSign(value); + value = SkApplySign(value, sign); + + if (value >> 24) // value is too big (has more than 24 bits set) + { + int bias = 8 - SkCLZ(value); + SkASSERT(bias > 0 && bias < 8); + value >>= bias; + shift += bias; + } + else + { + int zeros = SkCLZ(value << 8); + SkASSERT(zeros >= 0 && zeros <= 23); + value <<= zeros; + shift -= zeros; + } + // now value is left-aligned to 24 bits + SkASSERT((value >> 23) == 1); + + shift += EXP_BIAS; + if (shift < 0) // underflow + return 0; + else + { + if (shift > 255) // overflow + { + shift = 255; + value = 0x00FFFFFF; + } + int32_t packed = sign << 31; // set the sign-bit + packed |= shift << 23; // store the packed exponent + packed |= ((unsigned)(value << 9) >> 9); // clear 24th bit of value (its implied) + +#ifdef SK_DEBUG + { + int n; + + n = SkExtractSign(packed); + SkASSERT(n == sign); + n = get_unsigned_exp(packed); + SkASSERT(n == shift); + n = get_unsigned_value(packed); + SkASSERT(n == value); + } +#endif + return packed; + } +} + +int32_t SkFloat::Neg(int32_t packed) +{ + if (packed) + packed = packed ^ (1 << 31); + return packed; +} + +int32_t SkFloat::Add(int32_t packed_a, int32_t packed_b) +{ + if (packed_a == 0) + return packed_b; + if (packed_b == 0) + return packed_a; + + int exp_a = get_unsigned_exp(packed_a); + int exp_b = get_unsigned_exp(packed_b); + int exp_diff = exp_a - exp_b; + + int shift_a = 0, shift_b = 0; + int exp; + + if (exp_diff >= 0) + { + if (exp_diff > 24) // B is too small to contribute + return packed_a; + shift_b = exp_diff; + exp = exp_a; + } + else + { + exp_diff = -exp_diff; + if (exp_diff > 24) // A is too small to contribute + return packed_b; + shift_a = exp_diff; + exp = exp_b; + } + + int value_a = get_signed_value(packed_a) >> shift_a; + int value_b = get_signed_value(packed_b) >> shift_b; + + return SkFloat::SetShift(value_a + value_b, exp - EXP_BIAS); +} + +#include "Sk64.h" + +static inline int32_t mul24(int32_t a, int32_t b) +{ + Sk64 tmp; + + tmp.setMul(a, b); + tmp.roundRight(24); + return tmp.get32(); +} + +int32_t SkFloat::Mul(int32_t packed_a, int32_t packed_b) +{ + if (packed_a == 0 || packed_b == 0) + return 0; + + int exp_a = get_unsigned_exp(packed_a); + int exp_b = get_unsigned_exp(packed_b); + + int value_a = get_signed_value(packed_a); + int value_b = get_signed_value(packed_b); + + return SkFloat::SetShift(mul24(value_a, value_b), exp_a + exp_b - 2*EXP_BIAS + 24); +} + +int32_t SkFloat::MulInt(int32_t packed, int n) +{ + return Mul(packed, SetShift(n, 0)); +} + +int32_t SkFloat::Div(int32_t packed_n, int32_t packed_d) +{ + SkASSERT(packed_d != 0); + + if (packed_n == 0) + return 0; + + int exp_n = get_unsigned_exp(packed_n); + int exp_d = get_unsigned_exp(packed_d); + + int value_n = get_signed_value(packed_n); + int value_d = get_signed_value(packed_d); + + return SkFloat::SetShift(SkDivBits(value_n, value_d, 24), exp_n - exp_d - 24); +} + +int32_t SkFloat::DivInt(int32_t packed, int n) +{ + return Div(packed, SetShift(n, 0)); +} + +int32_t SkFloat::Invert(int32_t packed) +{ + return Div(packed, SetShift(1, 0)); +} + +int32_t SkFloat::Sqrt(int32_t packed) +{ + if (packed < 0) + { + SkASSERT(!"can't sqrt a negative number"); + return 0; + } + + int exp = get_unsigned_exp(packed); + int value = get_unsigned_value(packed); + + int nexp = exp - EXP_BIAS; + int root = SkSqrtBits(value << (nexp & 1), 26); + nexp >>= 1; + return SkFloat::SetShift(root, nexp - 11); +} + +#if defined _WIN32 && _MSC_VER >= 1300 // disable warning : unreachable code +#pragma warning ( push ) +#pragma warning ( disable : 4702 ) +#endif + +int32_t SkFloat::CubeRoot(int32_t packed) +{ + sk_throw(); + return 0; +} + +#if defined _WIN32 && _MSC_VER >= 1300 +#pragma warning ( pop ) +#endif + +static inline int32_t clear_high_bit(int32_t n) +{ + return ((uint32_t)(n << 1)) >> 1; +} + +static inline int int_sign(int32_t a, int32_t b) +{ + return a > b ? 1 : (a < b ? -1 : 0); +} + +int SkFloat::Cmp(int32_t packed_a, int32_t packed_b) +{ + packed_a = SkApplySign(clear_high_bit(packed_a), SkExtractSign(packed_a)); + packed_b = SkApplySign(clear_high_bit(packed_b), SkExtractSign(packed_b)); + + return int_sign(packed_a, packed_b); +} + +///////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_DEBUG + +#include "SkRandom.h" +#ifdef SK_CAN_USE_FLOAT + #include "SkFloatingPoint.h" +#endif + +void SkFloat::UnitTest() +{ +#ifdef SK_SUPPORT_UNITTEST + SkFloat a, b, c, d; + int n; + + a.setZero(); + n = a.getInt(); + SkASSERT(n == 0); + + b.setInt(5); + n = b.getInt(); + SkASSERT(n == 5); + + c.setInt(-3); + n = c.getInt(); + SkASSERT(n == -3); + + d.setAdd(c, b); + SkDebugf("SkFloat: %d + %d = %d\n", c.getInt(), b.getInt(), d.getInt()); + + SkRandom rand; + +#ifdef SK_CAN_USE_FLOAT + int i; + for (i = 0; i < 1000; i++) + { + float fa, fb; + int aa = rand.nextS() >> 14; + int bb = rand.nextS() >> 14; + a.setInt(aa); + b.setInt(bb); + SkASSERT(a.getInt() == aa); + SkASSERT(b.getInt() == bb); + + c.setAdd(a, b); + int cc = c.getInt(); + SkASSERT(cc == aa + bb); + + c.setSub(a, b); + cc = c.getInt(); + SkASSERT(cc == aa - bb); + + aa >>= 5; + bb >>= 5; + a.setInt(aa); + b.setInt(bb); + c.setMul(a, b); + cc = c.getInt(); + SkASSERT(cc == aa * bb); + ///////////////////////////////////// + + aa = rand.nextS() >> 11; + a.setFixed(aa); + cc = a.getFixed(); + SkASSERT(aa == cc); + + bb = rand.nextS() >> 11; + b.setFixed(bb); + cc = b.getFixed(); + SkASSERT(bb == cc); + + cc = SkFixedMul(aa, bb); + c.setMul(a, b); + SkFixed dd = c.getFixed(); + int diff = cc - dd; + SkASSERT(SkAbs32(diff) <= 1); + + fa = (float)aa / 65536.0f; + fb = (float)bb / 65536.0f; + a.assertEquals(fa); + b.assertEquals(fb); + fa = a.getFloat(); + fb = b.getFloat(); + + c.assertEquals(fa * fb, 1); + + c.setDiv(a, b); + cc = SkFixedDiv(aa, bb); + dd = c.getFixed(); + diff = cc - dd; + SkASSERT(SkAbs32(diff) <= 3); + + c.assertEquals(fa / fb, 1); + + SkASSERT((aa == bb) == (a == b)); + SkASSERT((aa != bb) == (a != b)); + SkASSERT((aa < bb) == (a < b)); + SkASSERT((aa <= bb) == (a <= b)); + SkASSERT((aa > bb) == (a > b)); + SkASSERT((aa >= bb) == (a >= b)); + + if (aa < 0) + { + aa = -aa; + fa = -fa; + } + a.setFixed(aa); + c.setSqrt(a); + cc = SkFixedSqrt(aa); + dd = c.getFixed(); + SkASSERT(dd == cc); + + c.assertEquals(sk_float_sqrt(fa), 2); + + // cuberoot +#if 0 + a.setInt(1); + a.cubeRoot(); + a.assertEquals(1.0f, 0); + a.setInt(8); + a.cubeRoot(); + a.assertEquals(2.0f, 0); + a.setInt(27); + a.cubeRoot(); + a.assertEquals(3.0f, 0); +#endif + } +#endif +#endif +} + +#endif diff --git a/skia/corecg/SkFloat.h b/skia/corecg/SkFloat.h new file mode 100644 index 0000000..2a2abac --- /dev/null +++ b/skia/corecg/SkFloat.h @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2006-2008 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SkFloat_DEFINED +#define SkFloat_DEFINED + +#include "SkFixed.h" + +class SkFloat { +public: + SkFloat() {} + + void setZero() { fPacked = 0; } +// void setShift(int value, int shift) { fPacked = SetShift(value, shift); } + void setInt(int value) { fPacked = SetShift(value, 0); } + void setFixed(SkFixed value) { fPacked = SetShift(value, -16); } + void setFract(SkFract value) { fPacked = SetShift(value, -30); } + +// int getShift(int shift) const { return GetShift(fPacked, shift); } + int getInt() const { return GetShift(fPacked, 0); } + SkFixed getFixed() const { return GetShift(fPacked, -16); } + SkFract getFract() const { return GetShift(fPacked, -30); } + + void abs() { fPacked = Abs(fPacked); } + void negate() { fPacked = Neg(fPacked); } + + void shiftLeft(int bits) { fPacked = Shift(fPacked, bits); } + void setShiftLeft(const SkFloat& a, int bits) { fPacked = Shift(a.fPacked, bits); } + + void shiftRight(int bits) { fPacked = Shift(fPacked, -bits); } + void setShiftRight(const SkFloat& a, int bits) { fPacked = Shift(a.fPacked, -bits); } + + void add(const SkFloat& a) { fPacked = Add(fPacked, a.fPacked); } + void setAdd(const SkFloat& a, const SkFloat& b) { fPacked = Add(a.fPacked, b.fPacked); } + + void sub(const SkFloat& a) { fPacked = Add(fPacked, Neg(a.fPacked)); } + void setSub(const SkFloat& a, const SkFloat& b) { fPacked = Add(a.fPacked, Neg(b.fPacked)); } + + void mul(const SkFloat& a) { fPacked = Mul(fPacked, a.fPacked); } + void setMul(const SkFloat& a, const SkFloat& b) { fPacked = Mul(a.fPacked, b.fPacked); } + + void div(const SkFloat& a) { fPacked = Div(fPacked, a.fPacked); } + void setDiv(const SkFloat& a, const SkFloat& b) { fPacked = Div(a.fPacked, b.fPacked); } + + void sqrt() { fPacked = Sqrt(fPacked); } + void setSqrt(const SkFloat& a) { fPacked = Sqrt(a.fPacked); } + void cubeRoot() { fPacked = CubeRoot(fPacked); } + void setCubeRoot(const SkFloat& a) { fPacked = CubeRoot(a.fPacked); } + + friend bool operator==(const SkFloat& a, const SkFloat& b) { return a.fPacked == b.fPacked; } + friend bool operator!=(const SkFloat& a, const SkFloat& b) { return a.fPacked != b.fPacked; } + friend bool operator<(const SkFloat& a, const SkFloat& b) { return Cmp(a.fPacked, b.fPacked) < 0; } + friend bool operator<=(const SkFloat& a, const SkFloat& b) { return Cmp(a.fPacked, b.fPacked) <= 0; } + friend bool operator>(const SkFloat& a, const SkFloat& b) { return Cmp(a.fPacked, b.fPacked) > 0; } + friend bool operator>=(const SkFloat& a, const SkFloat& b) { return Cmp(a.fPacked, b.fPacked) >= 0; } + +#ifdef SK_DEBUG + static void UnitTest(); + + void assertEquals(float f, int tolerance = 0) + { + union { + float fFloat; + int32_t fPacked; + } tmp; + + tmp.fFloat = f; + int d = tmp.fPacked - fPacked; + SkASSERT(SkAbs32(d) <= tolerance); + } + float getFloat() const + { + union { + float fFloat; + int32_t fPacked; + } tmp; + + tmp.fPacked = fPacked; + return tmp.fFloat; + } +#endif + +private: + int32_t fPacked; + + SkFloat(int32_t packed) : fPacked(fPacked) {} + +public: + static int GetShift(int32_t packed, int shift); + static int32_t SetShift(int value, int shift); + static int32_t Neg(int32_t); + static int32_t Abs(int32_t packed) { return (uint32_t)(packed << 1) >> 1; } + static int32_t Shift(int32_t, int bits); + static int32_t Add(int32_t, int32_t); + static int32_t Mul(int32_t, int32_t); + static int32_t MulInt(int32_t, int); + static int32_t Div(int32_t, int32_t); + static int32_t DivInt(int32_t, int); + static int32_t Invert(int32_t); + static int32_t Sqrt(int32_t); + static int32_t CubeRoot(int32_t); + static int Cmp(int32_t, int32_t); +}; + +#endif diff --git a/skia/corecg/SkFloatBits.h b/skia/corecg/SkFloatBits.h new file mode 100644 index 0000000..012770a --- /dev/null +++ b/skia/corecg/SkFloatBits.h @@ -0,0 +1,43 @@ +#ifndef SkFloatBits_DEFINED +#define SkFloatBits_DEFINED + +#include "SkTypes.h" + +/** Convert a sign-bit int (i.e. float interpreted as int) into a 2s compliement + int. This also converts -0 (0x80000000) to 0. Doing this to a float allows + it to be compared using normal C operators (<, <=, etc.) +*/ +static inline int32_t SkSignBitTo2sCompliment(int32_t x) { + if (x < 0) { + x &= 0x7FFFFFFF; + x = -x; + } + return x; +} + +#ifdef SK_CAN_USE_FLOAT + +union SkFloatIntUnion { + float fFloat; + int32_t fSignBitInt; +}; + +/** Return the float as a 2s compliment int. Just just be used to compare floats + to each other or against positive float-bit-constants (like 0) +*/ +static int32_t SkFloatAsInt(float x) { + SkFloatIntUnion data; + data.fFloat = x; + return SkSignBitTo2sCompliment(data.fSignBitInt); +} + +#endif + +#ifdef SK_SCALAR_IS_FLOAT + #define SkScalarAsInt(x) SkFloatAsInt(x) +#else + #define SkScalarAsInt(x) (x) +#endif + +#endif + diff --git a/skia/corecg/SkInterpolator.cpp b/skia/corecg/SkInterpolator.cpp new file mode 100644 index 0000000..61ebf2b --- /dev/null +++ b/skia/corecg/SkInterpolator.cpp @@ -0,0 +1,339 @@ +/* + * Copyright (C) 2006-2008 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "SkInterpolator.h" +#include "SkMath.h" +#include "SkTSearch.h" + +SkInterpolatorBase::SkInterpolatorBase() { + fStorage = NULL; + fTimes = NULL; + SkDEBUGCODE(fTimesArray = NULL;) +} + +SkInterpolatorBase::~SkInterpolatorBase() { + if (fStorage) { + sk_free(fStorage); + } +} + +void SkInterpolatorBase::reset(int elemCount, int frameCount) { + fFlags = 0; + fElemCount = SkToU8(elemCount); + fFrameCount = SkToS16(frameCount); + fRepeat = SK_Scalar1; + if (fStorage) { + sk_free(fStorage); + fStorage = NULL; + fTimes = NULL; + SkDEBUGCODE(fTimesArray = NULL); + } +} + +/* Each value[] run is formated as: + <time (in msec)> + <blend> + <data[fElemCount]> + + Totaling fElemCount+2 entries per keyframe +*/ + +bool SkInterpolatorBase::getDuration(SkMSec* startTime, SkMSec* endTime) const { + if (fFrameCount == 0) { + return false; + } + + if (startTime) { + *startTime = fTimes[0].fTime; + } + if (endTime) { + *endTime = fTimes[fFrameCount - 1].fTime; + } + return true; +} + +SkScalar SkInterpolatorBase::ComputeRelativeT(SkMSec time, SkMSec prevTime, + SkMSec nextTime, const SkScalar blend[4]) { + SkASSERT(time > prevTime && time < nextTime); + + SkScalar t = SkScalarDiv((SkScalar)(time - prevTime), + (SkScalar)(nextTime - prevTime)); + return blend ? + SkUnitCubicInterp(t, blend[0], blend[1], blend[2], blend[3]) : t; +} + +SkInterpolatorBase::Result SkInterpolatorBase::timeToT(SkMSec time, SkScalar* T, + int* indexPtr, SkBool* exactPtr) const { + SkASSERT(fFrameCount > 0); + Result result = kNormal_Result; + if (fRepeat != SK_Scalar1) { + SkMSec startTime, endTime; + this->getDuration(&startTime, &endTime); + SkMSec totalTime = endTime - startTime; + SkMSec offsetTime = time - startTime; + endTime = SkScalarMulFloor(fRepeat, totalTime); + if (offsetTime >= endTime) { + SkScalar fraction = SkScalarFraction(fRepeat); + offsetTime = fraction == 0 && fRepeat > 0 ? totalTime : + SkScalarMulFloor(fraction, totalTime); + result = kFreezeEnd_Result; + } else { + int mirror = fFlags & kMirror; + offsetTime = offsetTime % (totalTime << mirror); + if (offsetTime > totalTime) { // can only be true if fMirror is true + offsetTime = (totalTime << 1) - offsetTime; + } + } + time = offsetTime + startTime; + } + + int index = SkTSearch<SkMSec>(&fTimes[0].fTime, fFrameCount, time, + sizeof(SkTimeCode)); + + bool exact = true; + + if (index < 0) { + index = ~index; + if (index == 0) { + result = kFreezeStart_Result; + } else if (index == fFrameCount) { + if (fFlags & kReset) { + index = 0; + } else { + index -= 1; + } + result = kFreezeEnd_Result; + } else { + exact = false; + } + } + SkASSERT(index < fFrameCount); + const SkTimeCode* nextTime = &fTimes[index]; + SkMSec nextT = nextTime[0].fTime; + if (exact) { + *T = 0; + } else { + SkMSec prevT = nextTime[-1].fTime; + *T = ComputeRelativeT(time, prevT, nextT, nextTime[-1].fBlend); + } + *indexPtr = index; + *exactPtr = exact; + return result; +} + + +SkInterpolator::SkInterpolator() { + INHERITED::reset(0, 0); + fValues = NULL; + SkDEBUGCODE(fScalarsArray = NULL;) +} + +SkInterpolator::SkInterpolator(int elemCount, int frameCount) { + SkASSERT(elemCount > 0); + this->reset(elemCount, frameCount); +} + +void SkInterpolator::reset(int elemCount, int frameCount) { + INHERITED::reset(elemCount, frameCount); + fStorage = sk_malloc_throw((sizeof(SkScalar) * elemCount + + sizeof(SkTimeCode)) * frameCount); + fTimes = (SkTimeCode*) fStorage; + fValues = (SkScalar*) ((char*) fStorage + sizeof(SkTimeCode) * frameCount); +#ifdef SK_DEBUG + fTimesArray = (SkTimeCode(*)[10]) fTimes; + fScalarsArray = (SkScalar(*)[10]) fValues; +#endif +} + +#define SK_Fixed1Third (SK_Fixed1/3) +#define SK_Fixed2Third (SK_Fixed1*2/3) + +static const SkScalar gIdentityBlend[4] = { +#ifdef SK_SCALAR_IS_FLOAT + 0.33333333f, 0.33333333f, 0.66666667f, 0.66666667f +#else + SK_Fixed1Third, SK_Fixed1Third, SK_Fixed2Third, SK_Fixed2Third +#endif +}; + +bool SkInterpolator::setKeyFrame(int index, SkMSec time, + const SkScalar values[], const SkScalar blend[4]) { + SkASSERT(values != NULL); + + if (blend == NULL) { + blend = gIdentityBlend; + } + + bool success = ~index == SkTSearch<SkMSec>(&fTimes->fTime, index, time, + sizeof(SkTimeCode)); + SkASSERT(success); + if (success) { + SkTimeCode* timeCode = &fTimes[index]; + timeCode->fTime = time; + memcpy(timeCode->fBlend, blend, sizeof(timeCode->fBlend)); + SkScalar* dst = &fValues[fElemCount * index]; + memcpy(dst, values, fElemCount * sizeof(SkScalar)); + } + return success; +} + +SkInterpolator::Result SkInterpolator::timeToValues(SkMSec time, + SkScalar values[]) const { + SkScalar T; + int index; + SkBool exact; + Result result = timeToT(time, &T, &index, &exact); + if (values) { + const SkScalar* nextSrc = &fValues[index * fElemCount]; + + if (exact) { + memcpy(values, nextSrc, fElemCount * sizeof(SkScalar)); + } else { + SkASSERT(index > 0); + + const SkScalar* prevSrc = nextSrc - fElemCount; + + for (int i = fElemCount - 1; i >= 0; --i) { + values[i] = SkScalarInterp(prevSrc[i], nextSrc[i], T); + } + } + } + return result; +} + +/////////////////////////////////////////////////////////////////////////////// + +typedef int Dot14; +#define Dot14_ONE (1 << 14) +#define Dot14_HALF (1 << 13) + +#define Dot14ToFloat(x) ((x) / 16384.f) + +static inline Dot14 Dot14Mul(Dot14 a, Dot14 b) { + return (a * b + Dot14_HALF) >> 14; +} + +static inline Dot14 eval_cubic(Dot14 t, Dot14 A, Dot14 B, Dot14 C) { + return Dot14Mul(Dot14Mul(Dot14Mul(C, t) + B, t) + A, t); +} + +static inline Dot14 pin_and_convert(SkScalar x) { + if (x <= 0) { + return 0; + } + if (x >= SK_Scalar1) { + return Dot14_ONE; + } + return SkScalarToFixed(x) >> 2; +} + +SkScalar SkUnitCubicInterp(SkScalar value, SkScalar bx, SkScalar by, + SkScalar cx, SkScalar cy) { + // pin to the unit-square, and convert to 2.14 + Dot14 x = pin_and_convert(value); + + if (x == 0) return 0; + if (x == Dot14_ONE) return SK_Scalar1; + + Dot14 b = pin_and_convert(bx); + Dot14 c = pin_and_convert(cx); + + // Now compute our coefficients from the control points + // t -> 3b + // t^2 -> 3c - 6b + // t^3 -> 3b - 3c + 1 + Dot14 A = 3*b; + Dot14 B = 3*(c - 2*b); + Dot14 C = 3*(b - c) + Dot14_ONE; + + // Now search for a t value given x + Dot14 t = Dot14_HALF; + Dot14 dt = Dot14_HALF; + for (int i = 0; i < 13; i++) { + dt >>= 1; + Dot14 guess = eval_cubic(t, A, B, C); + if (x < guess) { + t -= dt; + } else { + t += dt; + } + } + + // Now we have t, so compute the coeff for Y and evaluate + b = pin_and_convert(by); + c = pin_and_convert(cy); + A = 3*b; + B = 3*(c - 2*b); + C = 3*(b - c) + Dot14_ONE; + return SkFixedToScalar(eval_cubic(t, A, B, C) << 2); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_DEBUG + +#ifdef SK_SUPPORT_UNITTEST + static SkScalar* iset(SkScalar array[3], int a, int b, int c) { + array[0] = SkIntToScalar(a); + array[1] = SkIntToScalar(b); + array[2] = SkIntToScalar(c); + return array; + } +#endif + +void SkInterpolator::UnitTest() { +#ifdef SK_SUPPORT_UNITTEST + SkInterpolator inter(3, 2); + SkScalar v1[3], v2[3], v[3], vv[3]; + Result result; + + inter.setKeyFrame(0, 100, iset(v1, 10, 20, 30), 0); + inter.setKeyFrame(1, 200, iset(v2, 110, 220, 330)); + + result = inter.timeToValues(0, v); + SkASSERT(result == kFreezeStart_Result); + SkASSERT(memcmp(v, v1, sizeof(v)) == 0); + + result = inter.timeToValues(99, v); + SkASSERT(result == kFreezeStart_Result); + SkASSERT(memcmp(v, v1, sizeof(v)) == 0); + + result = inter.timeToValues(100, v); + SkASSERT(result == kNormal_Result); + SkASSERT(memcmp(v, v1, sizeof(v)) == 0); + + result = inter.timeToValues(200, v); + SkASSERT(result == kNormal_Result); + SkASSERT(memcmp(v, v2, sizeof(v)) == 0); + + result = inter.timeToValues(201, v); + SkASSERT(result == kFreezeEnd_Result); + SkASSERT(memcmp(v, v2, sizeof(v)) == 0); + + result = inter.timeToValues(150, v); + SkASSERT(result == kNormal_Result); + SkASSERT(memcmp(v, iset(vv, 60, 120, 180), sizeof(v)) == 0); + + result = inter.timeToValues(125, v); + SkASSERT(result == kNormal_Result); + result = inter.timeToValues(175, v); + SkASSERT(result == kNormal_Result); +#endif +} + +#endif + diff --git a/skia/corecg/SkMath.cpp b/skia/corecg/SkMath.cpp new file mode 100644 index 0000000..42ea107 --- /dev/null +++ b/skia/corecg/SkMath.cpp @@ -0,0 +1,820 @@ +/* + * Copyright (C) 2006-2008 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "SkMath.h" +#include "SkCordic.h" +#include "SkFloatingPoint.h" +#include "Sk64.h" +#include "SkScalar.h" + +#ifdef SK_SCALAR_IS_FLOAT + const uint32_t gIEEENotANumber = 0x7FFFFFFF; + const uint32_t gIEEEInfinity = 0x7F800000; +#endif + +#define sub_shift(zeros, x, n) \ + zeros -= n; \ + x >>= n + +int SkCLZ_portable(uint32_t x) { + if (x == 0) { + return 32; + } + +#ifdef SK_CPU_HAS_CONDITIONAL_INSTR + int zeros = 31; + if (x & 0xFFFF0000) { + sub_shift(zeros, x, 16); + } + if (x & 0xFF00) { + sub_shift(zeros, x, 8); + } + if (x & 0xF0) { + sub_shift(zeros, x, 4); + } + if (x & 0xC) { + sub_shift(zeros, x, 2); + } + if (x & 0x2) { + sub_shift(zeros, x, 1); + } +#else + int zeros = ((x >> 16) - 1) >> 31 << 4; + x <<= zeros; + + int nonzero = ((x >> 24) - 1) >> 31 << 3; + zeros += nonzero; + x <<= nonzero; + + nonzero = ((x >> 28) - 1) >> 31 << 2; + zeros += nonzero; + x <<= nonzero; + + nonzero = ((x >> 30) - 1) >> 31 << 1; + zeros += nonzero; + x <<= nonzero; + + zeros += (~x) >> 31; +#endif + + return zeros; +} + +int32_t SkMulDiv(int32_t numer1, int32_t numer2, int32_t denom) { + SkASSERT(denom); + + Sk64 tmp; + tmp.setMul(numer1, numer2); + tmp.div(denom, Sk64::kTrunc_DivOption); + return tmp.get32(); +} + +int32_t SkMulShift(int32_t a, int32_t b, unsigned shift) { + int sign = SkExtractSign(a ^ b); + + if (shift > 63) { + return sign; + } + + a = SkAbs32(a); + b = SkAbs32(b); + + uint32_t ah = a >> 16; + uint32_t al = a & 0xFFFF; + uint32_t bh = b >> 16; + uint32_t bl = b & 0xFFFF; + + uint32_t A = ah * bh; + uint32_t B = ah * bl + al * bh; + uint32_t C = al * bl; + + /* [ A ] + [ B ] + [ C ] + */ + uint32_t lo = C + (B << 16); + int32_t hi = A + (B >> 16) + (lo < C); + + if (sign < 0) { + hi = -hi - Sk32ToBool(lo); + lo = 0 - lo; + } + + if (shift == 0) { +#ifdef SK_DEBUGx + SkASSERT(((int32_t)lo >> 31) == hi); +#endif + return lo; + } else if (shift >= 32) { + return hi >> (shift - 32); + } else { +#ifdef SK_DEBUGx + int32_t tmp = hi >> shift; + SkASSERT(tmp == 0 || tmp == -1); +#endif + // we want (hi << (32 - shift)) | (lo >> shift) but rounded + int roundBit = (lo >> (shift - 1)) & 1; + return ((hi << (32 - shift)) | (lo >> shift)) + roundBit; + } +} + +SkFixed SkFixedMul_portable(SkFixed a, SkFixed b) { +#if 0 + Sk64 tmp; + + tmp.setMul(a, b); + tmp.shiftRight(16); + return tmp.fLo; +#elif defined(SkLONGLONG) + return (SkLONGLONG)a * b >> 16; +#else + int sa = SkExtractSign(a); + int sb = SkExtractSign(b); + // now make them positive + a = SkApplySign(a, sa); + b = SkApplySign(b, sb); + + uint32_t ah = a >> 16; + uint32_t al = a & 0xFFFF; + uint32_t bh = b >> 16; + uint32_t bl = b & 0xFFFF; + + uint32_t R = ah * b + al * bh + (al * bl >> 16); + + return SkApplySign(R, sa ^ sb); +#endif +} + +SkFract SkFractMul_portable(SkFract a, SkFract b) { +#if 0 + Sk64 tmp; + tmp.setMul(a, b); + return tmp.getFract(); +#elif defined(SkLONGLONG) + return (SkLONGLONG)a * b >> 30; +#else + int sa = SkExtractSign(a); + int sb = SkExtractSign(b); + // now make them positive + a = SkApplySign(a, sa); + b = SkApplySign(b, sb); + + uint32_t ah = a >> 16; + uint32_t al = a & 0xFFFF; + uint32_t bh = b >> 16; + uint32_t bl = b & 0xFFFF; + + uint32_t A = ah * bh; + uint32_t B = ah * bl + al * bh; + uint32_t C = al * bl; + + /* [ A ] + [ B ] + [ C ] + */ + uint32_t Lo = C + (B << 16); + uint32_t Hi = A + (B >>16) + (Lo < C); + + SkASSERT((Hi >> 29) == 0); // else overflow + + int32_t R = (Hi << 2) + (Lo >> 30); + + return SkApplySign(R, sa ^ sb); +#endif +} + +int SkFixedMulCommon(SkFixed a, int b, int bias) { + // this function only works if b is 16bits + SkASSERT(b == (int16_t)b); + SkASSERT(b >= 0); + + int sa = SkExtractSign(a); + a = SkApplySign(a, sa); + uint32_t ah = a >> 16; + uint32_t al = a & 0xFFFF; + uint32_t R = ah * b + ((al * b + bias) >> 16); + return SkApplySign(R, sa); +} + +#ifdef SK_DEBUGx + #define TEST_FASTINVERT +#endif + +SkFixed SkFixedFastInvert(SkFixed x) { +/* Adapted (stolen) from Mathias' gglRecip() +*/ + + if (x == SK_Fixed1) { + return SK_Fixed1; + } + + int sign = SkExtractSign(x); + uint32_t a = SkApplySign(x, sign); + + if (a <= 2) { + return SkApplySign(SK_MaxS32, sign); + } + +#ifdef TEST_FASTINVERT + SkFixed orig = a; + uint32_t slow = SkFixedDiv(SK_Fixed1, a); +#endif + + // normalize a + int lz = SkCLZ(a); + a = a << lz >> 16; + + // compute 1/a approximation (0.5 <= a < 1.0) + uint32_t r = 0x17400 - a; // (2.90625 (~2.914) - 2*a) >> 1 + + // Newton-Raphson iteration: + // x = r*(2 - a*r) = ((r/2)*(1 - a*r/2))*4 + r = ( (0x10000 - ((a*r)>>16)) * r ) >> 15; + r = ( (0x10000 - ((a*r)>>16)) * r ) >> (30 - lz); + +#ifdef TEST_FASTINVERT + SkDebugf("SkFixedFastInvert(%x %g) = %x %g Slow[%x %g]\n", + orig, orig/65536., + r, r/65536., + slow, slow/65536.); +#endif + + return SkApplySign(r, sign); +} + +/////////////////////////////////////////////////////////////////////////////// + +#define DIVBITS_ITER(n) \ + case n: \ + if ((numer = (numer << 1) - denom) >= 0) \ + result |= 1 << (n - 1); else numer += denom + +int32_t SkDivBits(int32_t numer, int32_t denom, int shift_bias) { + SkASSERT(denom != 0); + if (numer == 0) { + return 0; + } + + // make numer and denom positive, and sign hold the resulting sign + int32_t sign = SkExtractSign(numer ^ denom); + numer = SkAbs32(numer); + denom = SkAbs32(denom); + + int nbits = SkCLZ(numer) - 1; + int dbits = SkCLZ(denom) - 1; + int bits = shift_bias - nbits + dbits; + + if (bits < 0) { // answer will underflow + return 0; + } + if (bits > 31) { // answer will overflow + return SkApplySign(SK_MaxS32, sign); + } + + denom <<= dbits; + numer <<= nbits; + + SkFixed result = 0; + + // do the first one + if ((numer -= denom) >= 0) { + result = 1; + } else { + numer += denom; + } + + // Now fall into our switch statement if there are more bits to compute + if (bits > 0) { + // make room for the rest of the answer bits + result <<= bits; + switch (bits) { + DIVBITS_ITER(31); DIVBITS_ITER(30); DIVBITS_ITER(29); + DIVBITS_ITER(28); DIVBITS_ITER(27); DIVBITS_ITER(26); + DIVBITS_ITER(25); DIVBITS_ITER(24); DIVBITS_ITER(23); + DIVBITS_ITER(22); DIVBITS_ITER(21); DIVBITS_ITER(20); + DIVBITS_ITER(19); DIVBITS_ITER(18); DIVBITS_ITER(17); + DIVBITS_ITER(16); DIVBITS_ITER(15); DIVBITS_ITER(14); + DIVBITS_ITER(13); DIVBITS_ITER(12); DIVBITS_ITER(11); + DIVBITS_ITER(10); DIVBITS_ITER( 9); DIVBITS_ITER( 8); + DIVBITS_ITER( 7); DIVBITS_ITER( 6); DIVBITS_ITER( 5); + DIVBITS_ITER( 4); DIVBITS_ITER( 3); DIVBITS_ITER( 2); + // we merge these last two together, makes GCC make better ARM + default: + DIVBITS_ITER( 1); + } + } + + if (result < 0) { + result = SK_MaxS32; + } + return SkApplySign(result, sign); +} + +/* mod(float numer, float denom) seems to always return the sign + of the numer, so that's what we do too +*/ +SkFixed SkFixedMod(SkFixed numer, SkFixed denom) { + int sn = SkExtractSign(numer); + int sd = SkExtractSign(denom); + + numer = SkApplySign(numer, sn); + denom = SkApplySign(denom, sd); + + if (numer < denom) { + return SkApplySign(numer, sn); + } else if (numer == denom) { + return 0; + } else { + SkFixed div = SkFixedDiv(numer, denom); + return SkApplySign(SkFixedMul(denom, div & 0xFFFF), sn); + } +} + +/* www.worldserver.com/turk/computergraphics/FixedSqrt.pdf +*/ +int32_t SkSqrtBits(int32_t x, int count) { + SkASSERT(x >= 0 && count > 0 && (unsigned)count <= 30); + + uint32_t root = 0; + uint32_t remHi = 0; + uint32_t remLo = x; + + do { + root <<= 1; + + remHi = (remHi<<2) | (remLo>>30); + remLo <<= 2; + + uint32_t testDiv = (root << 1) + 1; + if (remHi >= testDiv) { + remHi -= testDiv; + root++; + } + } while (--count >= 0); + + return root; +} + +int32_t SkCubeRootBits(int32_t value, int bits) { + SkASSERT(bits > 0); + + int sign = SkExtractSign(value); + value = SkApplySign(value, sign); + + uint32_t root = 0; + uint32_t curr = (uint32_t)value >> 30; + value <<= 2; + + do { + root <<= 1; + uint32_t guess = root * root + root; + guess = (guess << 1) + guess; // guess *= 3 + if (guess < curr) { + curr -= guess + 1; + root |= 1; + } + curr = (curr << 3) | ((uint32_t)value >> 29); + value <<= 3; + } while (--bits); + + return SkApplySign(root, sign); +} + +SkFixed SkFixedMean(SkFixed a, SkFixed b) { + Sk64 tmp; + + tmp.setMul(a, b); + return tmp.getSqrt(); +} + +/////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_SCALAR_IS_FLOAT +float SkScalarSinCos(float radians, float* cosValue) { + float sinValue = sk_float_sin(radians); + + if (cosValue) { + *cosValue = sk_float_cos(radians); + if (SkScalarNearlyZero(*cosValue)) { + *cosValue = 0; + } + } + + if (SkScalarNearlyZero(sinValue)) { + sinValue = 0; + } + return sinValue; +} +#endif + +#define INTERP_SINTABLE +#define BUILD_TABLE_AT_RUNTIMEx + +#define kTableSize 256 + +#ifdef BUILD_TABLE_AT_RUNTIME + static uint16_t gSkSinTable[kTableSize]; + + static void build_sintable(uint16_t table[]) { + for (int i = 0; i < kTableSize; i++) { + double rad = i * 3.141592653589793 / (2*kTableSize); + double val = sin(rad); + int ival = (int)(val * SK_Fixed1); + table[i] = SkToU16(ival); + } + } +#else + #include "SkSinTable.h" +#endif + +#define SK_Fract1024SizeOver2PI 0x28BE60 /* floatToFract(1024 / 2PI) */ + +#ifdef INTERP_SINTABLE +static SkFixed interp_table(const uint16_t table[], int index, int partial255) { + SkASSERT((unsigned)index < kTableSize); + SkASSERT((unsigned)partial255 <= 255); + + SkFixed lower = table[index]; + SkFixed upper = (index == kTableSize - 1) ? SK_Fixed1 : table[index + 1]; + + SkASSERT(lower < upper); + SkASSERT(lower >= 0); + SkASSERT(upper <= SK_Fixed1); + + partial255 += (partial255 >> 7); + return lower + ((upper - lower) * partial255 >> 8); +} +#endif + +SkFixed SkFixedSinCos(SkFixed radians, SkFixed* cosValuePtr) { + SkASSERT(SK_ARRAY_COUNT(gSkSinTable) == kTableSize); + +#ifdef BUILD_TABLE_AT_RUNTIME + static bool gFirstTime = true; + if (gFirstTime) { + build_sintable(gSinTable); + gFirstTime = false; + } +#endif + + // make radians positive + SkFixed sinValue, cosValue; + int32_t cosSign = 0; + int32_t sinSign = SkExtractSign(radians); + radians = SkApplySign(radians, sinSign); + // scale it to 0...1023 ... + +#ifdef INTERP_SINTABLE + radians = SkMulDiv(radians, 2 * kTableSize * 256, SK_FixedPI); + int findex = radians & (kTableSize * 256 - 1); + int index = findex >> 8; + int partial = findex & 255; + sinValue = interp_table(gSkSinTable, index, partial); + + findex = kTableSize * 256 - findex - 1; + index = findex >> 8; + partial = findex & 255; + cosValue = interp_table(gSkSinTable, index, partial); + + int quad = ((unsigned)radians / (kTableSize * 256)) & 3; +#else + radians = SkMulDiv(radians, 2 * kTableSize, SK_FixedPI); + int index = radians & (kTableSize - 1); + + if (index == 0) { + sinValue = 0; + cosValue = SK_Fixed1; + } else { + sinValue = gSkSinTable[index]; + cosValue = gSkSinTable[kTableSize - index]; + } + int quad = ((unsigned)radians / kTableSize) & 3; +#endif + + if (quad & 1) { + SkTSwap<SkFixed>(sinValue, cosValue); + } + if (quad & 2) { + sinSign = ~sinSign; + } + if (((quad - 1) & 2) == 0) { + cosSign = ~cosSign; + } + + // restore the sign for negative angles + sinValue = SkApplySign(sinValue, sinSign); + cosValue = SkApplySign(cosValue, cosSign); + +#ifdef SK_DEBUG + if (1) { + SkFixed sin2 = SkFixedMul(sinValue, sinValue); + SkFixed cos2 = SkFixedMul(cosValue, cosValue); + int diff = cos2 + sin2 - SK_Fixed1; + SkASSERT(SkAbs32(diff) <= 7); + } +#endif + + if (cosValuePtr) { + *cosValuePtr = cosValue; + } + return sinValue; +} + +/////////////////////////////////////////////////////////////////////////////// + +SkFixed SkFixedTan(SkFixed radians) { return SkCordicTan(radians); } +SkFixed SkFixedASin(SkFixed x) { return SkCordicASin(x); } +SkFixed SkFixedACos(SkFixed x) { return SkCordicACos(x); } +SkFixed SkFixedATan2(SkFixed y, SkFixed x) { return SkCordicATan2(y, x); } +SkFixed SkFixedExp(SkFixed x) { return SkCordicExp(x); } +SkFixed SkFixedLog(SkFixed x) { return SkCordicLog(x); } + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_DEBUG + +#include "SkRandom.h" + +#ifdef SkLONGLONG +static int symmetric_fixmul(int a, int b) { + int sa = SkExtractSign(a); + int sb = SkExtractSign(b); + + a = SkApplySign(a, sa); + b = SkApplySign(b, sb); + +#if 1 + int c = (int)(((SkLONGLONG)a * b) >> 16); + + return SkApplySign(c, sa ^ sb); +#else + SkLONGLONG ab = (SkLONGLONG)a * b; + if (sa ^ sb) { + ab = -ab; + } + return ab >> 16; +#endif +} +#endif + +#include "SkPoint.h" + +#ifdef SK_SUPPORT_UNITTEST +static void check_length(const SkPoint& p, SkScalar targetLen) { + float x = SkScalarToFloat(p.fX); + float y = SkScalarToFloat(p.fY); + float len = sk_float_sqrt(x*x + y*y); + + len /= SkScalarToFloat(targetLen); + + SkASSERT(len > 0.999f && len < 1.001f); +} +#endif + +static void test_muldiv255() { + for (int a = 0; a <= 255; a++) { + for (int b = 0; b <= 255; b++) { + int ab = a * b; + float s = ab / 255.0f; + int round = (int)floorf(s + 0.5f); + int trunc = (int)floorf(s); + + int iround = SkMulDiv255Round(a, b); + int itrunc = SkMulDiv255Trunc(a, b); + + SkASSERT(iround == round); + SkASSERT(itrunc == trunc); + + SkASSERT(itrunc <= iround); + SkASSERT(iround <= a); + SkASSERT(iround <= b); + } + } +} + +void SkMath::UnitTest() { +#ifdef SK_SUPPORT_UNITTEST + int i; + int32_t x; + SkRandom rand; + + SkToS8(127); SkToS8(-128); SkToU8(255); + SkToS16(32767); SkToS16(-32768); SkToU16(65535); + SkToS32(2*1024*1024); SkToS32(-2*1024*1024); SkToU32(4*1024*1024); + + SkCordic_UnitTest(); + + // these should assert +#if 0 + SkToS8(128); + SkToS8(-129); + SkToU8(256); + SkToU8(-5); + + SkToS16(32768); + SkToS16(-32769); + SkToU16(65536); + SkToU16(-5); + + if (sizeof(size_t) > 4) { + SkToS32(4*1024*1024); + SkToS32(-4*1024*1024); + SkToU32(5*1024*1024); + SkToU32(-5); + } +#endif + + test_muldiv255(); + +#ifdef SK_DEBUG + { + SkScalar x = SK_ScalarNaN; + SkASSERT(SkScalarIsNaN(x)); + } +#endif + + for (i = 1; i <= 10; i++) { + x = SkCubeRootBits(i*i*i, 11); + SkASSERT(x == i); + } + + x = SkFixedSqrt(SK_Fixed1); + SkASSERT(x == SK_Fixed1); + x = SkFixedSqrt(SK_Fixed1/4); + SkASSERT(x == SK_Fixed1/2); + x = SkFixedSqrt(SK_Fixed1*4); + SkASSERT(x == SK_Fixed1*2); + + x = SkFractSqrt(SK_Fract1); + SkASSERT(x == SK_Fract1); + x = SkFractSqrt(SK_Fract1/4); + SkASSERT(x == SK_Fract1/2); + x = SkFractSqrt(SK_Fract1/16); + SkASSERT(x == SK_Fract1/4); + + for (i = 1; i < 100; i++) { + x = SkFixedSqrt(SK_Fixed1 * i * i); + SkASSERT(x == SK_Fixed1 * i); + } + + for (i = 0; i < 1000; i++) { + int value = rand.nextS16(); + int max = rand.nextU16(); + + int clamp = SkClampMax(value, max); + int clamp2 = value < 0 ? 0 : (value > max ? max : value); + SkASSERT(clamp == clamp2); + } + + for (i = 0; i < 100000; i++) { + SkPoint p; + + p.setLength(rand.nextS(), rand.nextS(), SK_Scalar1); + check_length(p, SK_Scalar1); + p.setLength(rand.nextS() >> 13, rand.nextS() >> 13, SK_Scalar1); + check_length(p, SK_Scalar1); + } + + { + SkFixed result = SkFixedDiv(100, 100); + SkASSERT(result == SK_Fixed1); + result = SkFixedDiv(1, SK_Fixed1); + SkASSERT(result == 1); + } + + + +#ifdef SkLONGLONG + for (i = 0; i < 100000; i++) { + SkFixed numer = rand.nextS(); + SkFixed denom = rand.nextS(); + SkFixed result = SkFixedDiv(numer, denom); + SkLONGLONG check = ((SkLONGLONG)numer << 16) / denom; + + (void)SkCLZ(numer); + (void)SkCLZ(denom); + + SkASSERT(result != (SkFixed)SK_NaN32); + if (check > SK_MaxS32) { + check = SK_MaxS32; + } else if (check < -SK_MaxS32) { + check = SK_MinS32; + } + SkASSERT(result == (int32_t)check); + + result = SkFractDiv(numer, denom); + check = ((SkLONGLONG)numer << 30) / denom; + + SkASSERT(result != (SkFixed)SK_NaN32); + if (check > SK_MaxS32) { + check = SK_MaxS32; + } else if (check < -SK_MaxS32) { + check = SK_MinS32; + } + SkASSERT(result == (int32_t)check); + + // make them <= 2^24, so we don't overflow in fixmul + numer = numer << 8 >> 8; + denom = denom << 8 >> 8; + + result = SkFixedMul(numer, denom); + SkFixed r2 = symmetric_fixmul(numer, denom); +// SkASSERT(result == r2); + + result = SkFixedMul(numer, numer); + r2 = SkFixedSquare(numer); + SkASSERT(result == r2); + +#ifdef SK_CAN_USE_FLOAT + if (numer >= 0 && denom >= 0) { + SkFixed mean = SkFixedMean(numer, denom); + float fm = sk_float_sqrt(sk_float_abs(SkFixedToFloat(numer) * SkFixedToFloat(denom))); + SkFixed mean2 = SkFloatToFixed(fm); + int diff = SkAbs32(mean - mean2); + SkASSERT(diff <= 1); + } + + { + SkFixed mod = SkFixedMod(numer, denom); + float n = SkFixedToFloat(numer); + float d = SkFixedToFloat(denom); + float m = sk_float_mod(n, d); +#if 0 + SkDebugf("%g mod %g = %g [%g]\n", + SkFixedToFloat(numer), SkFixedToFloat(denom), + SkFixedToFloat(mod), m); +#endif + SkASSERT(mod == 0 || (mod < 0) == (m < 0)); // ensure the same sign + int diff = SkAbs32(mod - SkFloatToFixed(m)); + SkASSERT((diff >> 7) == 0); + } +#endif + } +#endif + +#ifdef SK_CAN_USE_FLOAT + for (i = 0; i < 100000; i++) { + SkFract x = rand.nextU() >> 1; + double xx = (double)x / SK_Fract1; + SkFract xr = SkFractSqrt(x); + SkFract check = SkFloatToFract(sqrt(xx)); + SkASSERT(xr == check || xr == check-1 || xr == check+1); + + xr = SkFixedSqrt(x); + xx = (double)x / SK_Fixed1; + check = SkFloatToFixed(sqrt(xx)); + SkASSERT(xr == check || xr == check-1); + + xr = SkSqrt32(x); + xx = (double)x; + check = (int32_t)sqrt(xx); + SkASSERT(xr == check || xr == check-1); + } +#endif + +#if !defined(SK_SCALAR_IS_FLOAT) && defined(SK_CAN_USE_FLOAT) + { + SkFixed s, c; + s = SkFixedSinCos(0, &c); + SkASSERT(s == 0); + SkASSERT(c == SK_Fixed1); + } + + int maxDiff = 0; + for (i = 0; i < 10000; i++) { + SkFixed rads = rand.nextS() >> 10; + double frads = SkFixedToFloat(rads); + + SkFixed s, c; + s = SkScalarSinCos(rads, &c); + + double fs = sin(frads); + double fc = cos(frads); + + SkFixed is = SkFloatToFixed(fs); + SkFixed ic = SkFloatToFixed(fc); + + maxDiff = SkMax32(maxDiff, SkAbs32(is - s)); + maxDiff = SkMax32(maxDiff, SkAbs32(ic - c)); + } + SkDebugf("SinCos: maximum error = %d\n", maxDiff); +#endif +#endif +} + +#endif + diff --git a/skia/corecg/SkMatrix.cpp b/skia/corecg/SkMatrix.cpp new file mode 100644 index 0000000..78f6688 --- /dev/null +++ b/skia/corecg/SkMatrix.cpp @@ -0,0 +1,1678 @@ +/* libs/corecg/SkMatrix.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkMatrix.h" +#include "Sk64.h" +#include "SkFloatBits.h" + +#ifdef SK_SCALAR_IS_FLOAT + #define kMatrix22Elem SK_Scalar1 +#else + #define kMatrix22Elem SK_Fract1 +#endif + +/* [scale-x skew-x trans-x] [X] [X'] + [skew-y scale-y trans-y] * [Y] = [Y'] + [persp-0 persp-1 persp-2] [1] [1 ] +*/ + +void SkMatrix::reset() { + fMat[kMScaleX] = fMat[kMScaleY] = SK_Scalar1; + fMat[kMSkewX] = fMat[kMSkewY] = + fMat[kMTransX] = fMat[kMTransY] = + fMat[kMPersp0] = fMat[kMPersp1] = 0; + fMat[kMPersp2] = kMatrix22Elem; + + this->setTypeMask(kIdentity_Mask | kRectStaysRect_Mask); +} + +static inline int has_perspective(const SkMatrix& matrix) { + return matrix.getType() & SkMatrix::kPerspective_Mask; +} + +// this guy aligns with the masks, so we can compute a mask from a varaible 0/1 +enum { + kTranslate_Shift, + kScale_Shift, + kAffine_Shift, + kPerspective_Shift, + kRectStaysRect_Shift +}; + +#ifdef SK_SCALAR_IS_FLOAT + static const int32_t kScalar1Int = 0x3f800000; + static const int32_t kPersp1Int = 0x3f800000; +#else + #define scalarAsInt(x) (x) + static const int32_t kScalar1Int = (1 << 16); + static const int32_t kPersp1Int = (1 << 30); +#endif + +uint8_t SkMatrix::computeTypeMask() const { + unsigned mask = 0; + + if (SkScalarAsInt(fMat[kMPersp0]) | SkScalarAsInt(fMat[kMPersp1]) | + (SkScalarAsInt(fMat[kMPersp2]) - kPersp1Int)) { + mask |= kPerspective_Mask; + } + + if (SkScalarAsInt(fMat[kMTransX]) | SkScalarAsInt(fMat[kMTransY])) { + mask |= kTranslate_Mask; + } + + int m00 = SkScalarAsInt(fMat[SkMatrix::kMScaleX]); + int m01 = SkScalarAsInt(fMat[SkMatrix::kMSkewX]); + int m10 = SkScalarAsInt(fMat[SkMatrix::kMSkewY]); + int m11 = SkScalarAsInt(fMat[SkMatrix::kMScaleY]); + + if (m01 | m10) { + mask |= kAffine_Mask; + } + + if ((m00 - kScalar1Int) | (m11 - kScalar1Int)) { + mask |= kScale_Mask; + } + + if ((mask & kPerspective_Mask) == 0) { + // map non-zero to 1 + m00 = m00 != 0; + m01 = m01 != 0; + m10 = m10 != 0; + m11 = m11 != 0; + + // record if the (p)rimary and (s)econdary diagonals are all 0 or + // all non-zero (answer is 0 or 1) + int dp0 = (m00 | m11) ^ 1; // true if both are 0 + int dp1 = m00 & m11; // true if both are 1 + int ds0 = (m01 | m10) ^ 1; // true if both are 0 + int ds1 = m01 & m10; // true if both are 1 + + // return 1 if primary is 1 and secondary is 0 or + // primary is 0 and secondary is 1 + mask |= ((dp0 & ds1) | (dp1 & ds0)) << kRectStaysRect_Shift; + } + + return SkToU8(mask); +} + +/////////////////////////////////////////////////////////////////////////////// + +void SkMatrix::setTranslate(SkScalar dx, SkScalar dy) { + if (SkScalarAsInt(dx) | SkScalarAsInt(dy)) { + fMat[kMTransX] = dx; + fMat[kMTransY] = dy; + + fMat[kMScaleX] = fMat[kMScaleY] = SK_Scalar1; + fMat[kMSkewX] = fMat[kMSkewY] = + fMat[kMPersp0] = fMat[kMPersp1] = 0; + fMat[kMPersp2] = kMatrix22Elem; + + this->setTypeMask(kTranslate_Mask | kRectStaysRect_Mask); + } else { + this->reset(); + } +} + +bool SkMatrix::preTranslate(SkScalar dx, SkScalar dy) { + if (has_perspective(*this)) { + SkMatrix m; + m.setTranslate(dx, dy); + return this->preConcat(m); + } + + if (SkScalarAsInt(dx) | SkScalarAsInt(dy)) { + fMat[kMTransX] += SkScalarMul(fMat[kMScaleX], dx) + + SkScalarMul(fMat[kMSkewX], dy); + fMat[kMTransY] += SkScalarMul(fMat[kMSkewY], dx) + + SkScalarMul(fMat[kMScaleY], dy); + + this->setTypeMask(kUnknown_Mask); + } + return true; +} + +bool SkMatrix::postTranslate(SkScalar dx, SkScalar dy) { + if (has_perspective(*this)) { + SkMatrix m; + m.setTranslate(dx, dy); + return this->postConcat(m); + } + + if (SkScalarAsInt(dx) | SkScalarAsInt(dy)) { + fMat[kMTransX] += dx; + fMat[kMTransY] += dy; + this->setTypeMask(kUnknown_Mask); + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +void SkMatrix::setScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) { + fMat[kMScaleX] = sx; + fMat[kMScaleY] = sy; + fMat[kMTransX] = px - SkScalarMul(sx, px); + fMat[kMTransY] = py - SkScalarMul(sy, py); + fMat[kMPersp2] = kMatrix22Elem; + + fMat[kMSkewX] = fMat[kMSkewY] = + fMat[kMPersp0] = fMat[kMPersp1] = 0; + + this->setTypeMask(kScale_Mask | kTranslate_Mask | kRectStaysRect_Mask); +} + +void SkMatrix::setScale(SkScalar sx, SkScalar sy) { + fMat[kMScaleX] = sx; + fMat[kMScaleY] = sy; + fMat[kMPersp2] = kMatrix22Elem; + + fMat[kMTransX] = fMat[kMTransY] = + fMat[kMSkewX] = fMat[kMSkewY] = + fMat[kMPersp0] = fMat[kMPersp1] = 0; + + this->setTypeMask(kScale_Mask | kRectStaysRect_Mask); +} + +bool SkMatrix::preScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) { + SkMatrix m; + m.setScale(sx, sy, px, py); + return this->preConcat(m); +} + +bool SkMatrix::preScale(SkScalar sx, SkScalar sy) { + SkMatrix m; + m.setScale(sx, sy); + return this->preConcat(m); +} + +bool SkMatrix::postScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) { + SkMatrix m; + m.setScale(sx, sy, px, py); + return this->postConcat(m); +} + +bool SkMatrix::postScale(SkScalar sx, SkScalar sy) { + SkMatrix m; + m.setScale(sx, sy); + return this->postConcat(m); +} + +#ifdef SK_SCALAR_IS_FIXED + static inline SkFixed roundidiv(SkFixed numer, int denom) { + int ns = numer >> 31; + int ds = denom >> 31; + numer = (numer ^ ns) - ns; + denom = (denom ^ ds) - ds; + + SkFixed answer = (numer + (denom >> 1)) / denom; + int as = ns ^ ds; + return (answer ^ as) - as; + } +#endif + +// this guy perhaps can go away, if we have a fract/high-precision way to +// scale matrices +bool SkMatrix::postIDiv(int divx, int divy) { + if (divx == 0 || divy == 0) { + return false; + } + +#ifdef SK_SCALAR_IS_FIXED + fMat[kMScaleX] = roundidiv(fMat[kMScaleX], divx); + fMat[kMSkewX] = roundidiv(fMat[kMSkewX], divx); + fMat[kMTransX] = roundidiv(fMat[kMTransX], divx); + + fMat[kMScaleY] = roundidiv(fMat[kMScaleY], divy); + fMat[kMSkewY] = roundidiv(fMat[kMSkewY], divy); + fMat[kMTransY] = roundidiv(fMat[kMTransY], divy); +#else + const float invX = 1.f / divx; + const float invY = 1.f / divy; + + fMat[kMScaleX] *= invX; + fMat[kMSkewX] *= invX; + fMat[kMTransX] *= invX; + + fMat[kMScaleY] *= invY; + fMat[kMSkewY] *= invY; + fMat[kMTransY] *= invY; +#endif + + this->setTypeMask(kUnknown_Mask); + return true; +} + +//////////////////////////////////////////////////////////////////////////////////// + +void SkMatrix::setSinCos(SkScalar sinV, SkScalar cosV, + SkScalar px, SkScalar py) { + const SkScalar oneMinusCosV = SK_Scalar1 - cosV; + + fMat[kMScaleX] = cosV; + fMat[kMSkewX] = -sinV; + fMat[kMTransX] = SkScalarMul(sinV, py) + SkScalarMul(oneMinusCosV, px); + + fMat[kMSkewY] = sinV; + fMat[kMScaleY] = cosV; + fMat[kMTransY] = SkScalarMul(-sinV, px) + SkScalarMul(oneMinusCosV, py); + + fMat[kMPersp0] = fMat[kMPersp1] = 0; + fMat[kMPersp2] = kMatrix22Elem; + + this->setTypeMask(kUnknown_Mask); +} + +void SkMatrix::setSinCos(SkScalar sinV, SkScalar cosV) { + fMat[kMScaleX] = cosV; + fMat[kMSkewX] = -sinV; + fMat[kMTransX] = 0; + + fMat[kMSkewY] = sinV; + fMat[kMScaleY] = cosV; + fMat[kMTransY] = 0; + + fMat[kMPersp0] = fMat[kMPersp1] = 0; + fMat[kMPersp2] = kMatrix22Elem; + + this->setTypeMask(kUnknown_Mask); +} + +void SkMatrix::setRotate(SkScalar degrees, SkScalar px, SkScalar py) { + SkScalar sinV, cosV; + sinV = SkScalarSinCos(SkDegreesToRadians(degrees), &cosV); + this->setSinCos(sinV, cosV, px, py); +} + +void SkMatrix::setRotate(SkScalar degrees) { + SkScalar sinV, cosV; + sinV = SkScalarSinCos(SkDegreesToRadians(degrees), &cosV); + this->setSinCos(sinV, cosV); +} + +bool SkMatrix::preRotate(SkScalar degrees, SkScalar px, SkScalar py) { + SkMatrix m; + m.setRotate(degrees, px, py); + return this->preConcat(m); +} + +bool SkMatrix::preRotate(SkScalar degrees) { + SkMatrix m; + m.setRotate(degrees); + return this->preConcat(m); +} + +bool SkMatrix::postRotate(SkScalar degrees, SkScalar px, SkScalar py) { + SkMatrix m; + m.setRotate(degrees, px, py); + return this->postConcat(m); +} + +bool SkMatrix::postRotate(SkScalar degrees) { + SkMatrix m; + m.setRotate(degrees); + return this->postConcat(m); +} + +//////////////////////////////////////////////////////////////////////////////////// + +void SkMatrix::setSkew(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) { + fMat[kMScaleX] = SK_Scalar1; + fMat[kMSkewX] = sx; + fMat[kMTransX] = SkScalarMul(-sx, py); + + fMat[kMSkewY] = sy; + fMat[kMScaleY] = SK_Scalar1; + fMat[kMTransY] = SkScalarMul(-sy, px); + + fMat[kMPersp0] = fMat[kMPersp1] = 0; + fMat[kMPersp2] = kMatrix22Elem; + + this->setTypeMask(kUnknown_Mask); +} + +void SkMatrix::setSkew(SkScalar sx, SkScalar sy) { + fMat[kMScaleX] = SK_Scalar1; + fMat[kMSkewX] = sx; + fMat[kMTransX] = 0; + + fMat[kMSkewY] = sy; + fMat[kMScaleY] = SK_Scalar1; + fMat[kMTransY] = 0; + + fMat[kMPersp0] = fMat[kMPersp1] = 0; + fMat[kMPersp2] = kMatrix22Elem; + + this->setTypeMask(kUnknown_Mask); +} + +bool SkMatrix::preSkew(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) { + SkMatrix m; + m.setSkew(sx, sy, px, py); + return this->preConcat(m); +} + +bool SkMatrix::preSkew(SkScalar sx, SkScalar sy) { + SkMatrix m; + m.setSkew(sx, sy); + return this->preConcat(m); +} + +bool SkMatrix::postSkew(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) { + SkMatrix m; + m.setSkew(sx, sy, px, py); + return this->postConcat(m); +} + +bool SkMatrix::postSkew(SkScalar sx, SkScalar sy) { + SkMatrix m; + m.setSkew(sx, sy); + return this->postConcat(m); +} + +/////////////////////////////////////////////////////////////////////////////// + +bool SkMatrix::setRectToRect(const SkRect& src, const SkRect& dst, + ScaleToFit align) +{ + if (src.isEmpty()) { + this->reset(); + return false; + } + + if (dst.isEmpty()) { + bzero(fMat, 8 * sizeof(SkScalar)); + this->setTypeMask(kScale_Mask | kRectStaysRect_Mask); + } else { + SkScalar tx, sx = SkScalarDiv(dst.width(), src.width()); + SkScalar ty, sy = SkScalarDiv(dst.height(), src.height()); + bool xLarger = false; + + if (align != kFill_ScaleToFit) { + if (sx > sy) { + xLarger = true; + sx = sy; + } else { + sy = sx; + } + } + + tx = dst.fLeft - SkScalarMul(src.fLeft, sx); + ty = dst.fTop - SkScalarMul(src.fTop, sy); + if (align == kCenter_ScaleToFit || align == kEnd_ScaleToFit) { + SkScalar diff; + + if (xLarger) { + diff = dst.width() - SkScalarMul(src.width(), sy); + } else { + diff = dst.height() - SkScalarMul(src.height(), sy); + } + + if (align == kCenter_ScaleToFit) { + diff = SkScalarHalf(diff); + } + + if (xLarger) { + tx += diff; + } else { + ty += diff; + } + } + + fMat[kMScaleX] = sx; + fMat[kMScaleY] = sy; + fMat[kMTransX] = tx; + fMat[kMTransY] = ty; + fMat[kMSkewX] = fMat[kMSkewY] = + fMat[kMPersp0] = fMat[kMPersp1] = 0; + + this->setTypeMask(kScale_Mask | kTranslate_Mask | kRectStaysRect_Mask); + } + // shared cleanup + fMat[kMPersp2] = kMatrix22Elem; + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_SCALAR_IS_FLOAT + static inline int fixmuladdmul(float a, float b, float c, float d, + float* result) { + *result = a * b + c * d; + return true; + } + + static inline int fixmuladdmulshiftmul(float a, float b, float c, float d, + int /*shift not used*/, float scale, float* result) { + *result = (a * b + c * d) * scale; + return true; + } + + static inline bool rowcol3(const float row[], const float col[], + float* result) { + *result = row[0] * col[0] + row[1] * col[3] + row[2] * col[6]; + return true; + } + + static inline int negifaddoverflows(float& result, float a, float b) { + result = a + b; + return 0; + } +#else + static inline bool fixmuladdmul(SkFixed a, SkFixed b, SkFixed c, SkFixed d, + SkFixed* result) { + Sk64 tmp1, tmp2; + tmp1.setMul(a, b); + tmp2.setMul(c, d); + tmp1.add(tmp2); + if (tmp1.isFixed()) { + *result = tmp1.getFixed(); + return true; + } + return false; + } + + static inline bool fixmuladdmulshiftmul(SkFixed a, SkFixed b, SkFixed c, + SkFixed d, int shift, SkFixed scale, SkFixed* result) { + Sk64 tmp1, tmp2; + tmp1.setMul(a, b); + tmp2.setMul(c, d); + tmp1.add(tmp2); + + int32_t hi = SkAbs32(tmp1.fHi); + int afterShift = 16; + if (hi >> 15) { + int clz = 17 - SkCLZ(hi); + SkASSERT(clz > 0 && clz <= 16); + afterShift -= clz; + shift += clz; + } + + tmp1.roundRight(shift + 16); + SkASSERT(tmp1.is32()); + + tmp1.setMul(tmp1.get32(), scale); + tmp1.roundRight(afterShift); + if (tmp1.is32()) { + *result = tmp1.get32(); + return true; + } + return false; + } + + static inline SkFixed fracmuladdmul(SkFixed a, SkFract b, SkFixed c, + SkFract d) { + Sk64 tmp1, tmp2; + tmp1.setMul(a, b); + tmp2.setMul(c, d); + tmp1.add(tmp2); + return tmp1.getFract(); + } + + static inline bool rowcol3(const SkFixed row[], const SkFixed col[], + SkFixed* result) { + Sk64 tmp1, tmp2; + + tmp1.setMul(row[0], col[0]); // N * fixed + tmp2.setMul(row[1], col[3]); // N * fixed + tmp1.add(tmp2); + + tmp2.setMul(row[2], col[6]); // N * fract + tmp2.roundRight(14); // make it fixed + tmp1.add(tmp2); + + if (tmp1.isFixed()) { + *result = tmp1.getFixed(); + return true; + } + return false; + } + + static inline int negifaddoverflows(SkFixed& result, SkFixed a, SkFixed b) { + SkFixed c = a + b; + result = c; + return (c ^ a) & (c ^ b); + } +#endif + +static void normalize_perspective(SkScalar mat[9]) { + if (SkScalarAbs(mat[SkMatrix::kMPersp2]) > kMatrix22Elem) { + for (int i = 0; i < 9; i++) + mat[i] = SkScalarHalf(mat[i]); + } +} + +bool SkMatrix::setConcat(const SkMatrix& a, const SkMatrix& b) { + TypeMask aType = a.getType(); + TypeMask bType = b.getType(); + + if (0 == aType) { + *this = b; + } else if (0 == bType) { + *this = a; + } else { + SkMatrix tmp; + + if ((aType | bType) & kPerspective_Mask) { + if (!rowcol3(&a.fMat[0], &b.fMat[0], &tmp.fMat[kMScaleX])) { + return false; + } + if (!rowcol3(&a.fMat[0], &b.fMat[1], &tmp.fMat[kMSkewX])) { + return false; + } + if (!rowcol3(&a.fMat[0], &b.fMat[2], &tmp.fMat[kMTransX])) { + return false; + } + + if (!rowcol3(&a.fMat[3], &b.fMat[0], &tmp.fMat[kMSkewY])) { + return false; + } + if (!rowcol3(&a.fMat[3], &b.fMat[1], &tmp.fMat[kMScaleY])) { + return false; + } + if (!rowcol3(&a.fMat[3], &b.fMat[2], &tmp.fMat[kMTransY])) { + return false; + } + + if (!rowcol3(&a.fMat[6], &b.fMat[0], &tmp.fMat[kMPersp0])) { + return false; + } + if (!rowcol3(&a.fMat[6], &b.fMat[1], &tmp.fMat[kMPersp1])) { + return false; + } + if (!rowcol3(&a.fMat[6], &b.fMat[2], &tmp.fMat[kMPersp2])) { + return false; + } + + normalize_perspective(tmp.fMat); + } else { // not perspective + if (!fixmuladdmul(a.fMat[kMScaleX], b.fMat[kMScaleX], + a.fMat[kMSkewX], b.fMat[kMSkewY], &tmp.fMat[kMScaleX])) { + return false; + } + if (!fixmuladdmul(a.fMat[kMScaleX], b.fMat[kMSkewX], + a.fMat[kMSkewX], b.fMat[kMScaleY], &tmp.fMat[kMSkewX])) { + return false; + } + if (!fixmuladdmul(a.fMat[kMScaleX], b.fMat[kMTransX], + a.fMat[kMSkewX], b.fMat[kMTransY], &tmp.fMat[kMTransX])) { + return false; + } + if (negifaddoverflows(tmp.fMat[kMTransX], tmp.fMat[kMTransX], + a.fMat[kMTransX]) < 0) { + return false; + } + + if (!fixmuladdmul(a.fMat[kMSkewY], b.fMat[kMScaleX], + a.fMat[kMScaleY], b.fMat[kMSkewY], &tmp.fMat[kMSkewY])) { + return false; + } + if (!fixmuladdmul(a.fMat[kMSkewY], b.fMat[kMSkewX], + a.fMat[kMScaleY], b.fMat[kMScaleY], &tmp.fMat[kMScaleY])) { + return false; + } + if (!fixmuladdmul(a.fMat[kMSkewY], b.fMat[kMTransX], + a.fMat[kMScaleY], b.fMat[kMTransY], &tmp.fMat[kMTransY])) { + return false; + } + if (negifaddoverflows(tmp.fMat[kMTransY], tmp.fMat[kMTransY], + a.fMat[kMTransY]) < 0) { + return false; + } + + tmp.fMat[kMPersp0] = tmp.fMat[kMPersp1] = 0; + tmp.fMat[kMPersp2] = kMatrix22Elem; + } + *this = tmp; + } + this->setTypeMask(kUnknown_Mask); + return true; +} + +bool SkMatrix::preConcat(const SkMatrix& mat) { + return this->setConcat(*this, mat); +} + +bool SkMatrix::postConcat(const SkMatrix& mat) { + return this->setConcat(mat, *this); +} + +/////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_SCALAR_IS_FLOAT + #define SkPerspMul(a, b) SkScalarMul(a, b) + #define SkScalarMulShift(a, b, s) SkScalarMul(a, b) + static float sk_inv_determinant(const float mat[9], int isPerspective, + int* /* (only used in Fixed case) */) { + double det; + + if (isPerspective) { + det = mat[SkMatrix::kMScaleX] * ((double)mat[SkMatrix::kMScaleY] * mat[SkMatrix::kMPersp2] - (double)mat[SkMatrix::kMTransY] * mat[SkMatrix::kMPersp1]) + + mat[SkMatrix::kMSkewX] * ((double)mat[SkMatrix::kMTransY] * mat[SkMatrix::kMPersp0] - (double)mat[SkMatrix::kMSkewY] * mat[SkMatrix::kMPersp2]) + + mat[SkMatrix::kMTransX] * ((double)mat[SkMatrix::kMSkewY] * mat[SkMatrix::kMPersp1] - (double)mat[SkMatrix::kMScaleY] * mat[SkMatrix::kMPersp0]); + } else { + det = (double)mat[SkMatrix::kMScaleX] * mat[SkMatrix::kMScaleY] - (double)mat[SkMatrix::kMSkewX] * mat[SkMatrix::kMSkewY]; + } + + if (SkScalarNearlyZero((float)det)) { + return 0; + } + return (float)(1.0 / det); + } +#else + #define SkPerspMul(a, b) SkFractMul(a, b) + #define SkScalarMulShift(a, b, s) SkMulShift(a, b, s) + static void set_muladdmul(Sk64* dst, int32_t a, int32_t b, int32_t c, + int32_t d) { + Sk64 tmp; + dst->setMul(a, b); + tmp.setMul(c, d); + dst->add(tmp); + } + + static SkFixed sk_inv_determinant(const SkFixed mat[9], int isPerspective, + int* shift) { + Sk64 tmp1, tmp2; + + if (isPerspective) { + tmp1.setMul(mat[SkMatrix::kMScaleX], fracmuladdmul(mat[SkMatrix::kMScaleY], mat[SkMatrix::kMPersp2], -mat[SkMatrix::kMTransY], mat[SkMatrix::kMPersp1])); + tmp2.setMul(mat[SkMatrix::kMSkewX], fracmuladdmul(mat[SkMatrix::kMTransY], mat[SkMatrix::kMPersp0], -mat[SkMatrix::kMSkewY], mat[SkMatrix::kMPersp2])); + tmp1.add(tmp2); + tmp2.setMul(mat[SkMatrix::kMTransX], fracmuladdmul(mat[SkMatrix::kMSkewY], mat[SkMatrix::kMPersp1], -mat[SkMatrix::kMScaleY], mat[SkMatrix::kMPersp0])); + tmp1.add(tmp2); + } else { + tmp1.setMul(mat[SkMatrix::kMScaleX], mat[SkMatrix::kMScaleY]); + tmp2.setMul(mat[SkMatrix::kMSkewX], mat[SkMatrix::kMSkewY]); + tmp1.sub(tmp2); + } + + int s = tmp1.getClzAbs(); + *shift = s; + + SkFixed denom; + if (s <= 32) { + denom = tmp1.getShiftRight(33 - s); + } else { + denom = (int32_t)tmp1.fLo << (s - 33); + } + + if (denom == 0) { + return 0; + } + /** This could perhaps be a special fractdiv function, since both of its + arguments are known to have bit 31 clear and bit 30 set (when they + are made positive), thus eliminating the need for calling clz() + */ + return SkFractDiv(SK_Fract1, denom); + } +#endif + +bool SkMatrix::invert(SkMatrix* inv) const { + int isPersp = has_perspective(*this); + int shift; + SkScalar scale = sk_inv_determinant(fMat, isPersp, &shift); + + if (scale == 0) { // underflow + return false; + } + + if (inv) { + SkMatrix tmp; + if (inv == this) + inv = &tmp; + + if (isPersp) { + shift = 61 - shift; + inv->fMat[kMScaleX] = SkScalarMulShift(SkPerspMul(fMat[kMScaleY], fMat[kMPersp2]) - SkPerspMul(fMat[kMTransY], fMat[kMPersp1]), scale, shift); + inv->fMat[kMSkewX] = SkScalarMulShift(SkPerspMul(fMat[kMTransX], fMat[kMPersp1]) - SkPerspMul(fMat[kMSkewX], fMat[kMPersp2]), scale, shift); + inv->fMat[kMTransX] = SkScalarMulShift(SkScalarMul(fMat[kMSkewX], fMat[kMTransY]) - SkScalarMul(fMat[kMTransX], fMat[kMScaleY]), scale, shift); + + inv->fMat[kMSkewY] = SkScalarMulShift(SkPerspMul(fMat[kMTransY], fMat[kMPersp0]) - SkPerspMul(fMat[kMSkewY], fMat[kMPersp2]), scale, shift); + inv->fMat[kMScaleY] = SkScalarMulShift(SkPerspMul(fMat[kMScaleX], fMat[kMPersp2]) - SkPerspMul(fMat[kMTransX], fMat[kMPersp0]), scale, shift); + inv->fMat[kMTransY] = SkScalarMulShift(SkScalarMul(fMat[kMTransX], fMat[kMSkewY]) - SkScalarMul(fMat[kMScaleX], fMat[kMTransY]), scale, shift); + + inv->fMat[kMPersp0] = SkScalarMulShift(SkScalarMul(fMat[kMSkewY], fMat[kMPersp1]) - SkScalarMul(fMat[kMScaleY], fMat[kMPersp0]), scale, shift); + inv->fMat[kMPersp1] = SkScalarMulShift(SkScalarMul(fMat[kMSkewX], fMat[kMPersp0]) - SkScalarMul(fMat[kMScaleX], fMat[kMPersp1]), scale, shift); + inv->fMat[kMPersp2] = SkScalarMulShift(SkScalarMul(fMat[kMScaleX], fMat[kMScaleY]) - SkScalarMul(fMat[kMSkewX], fMat[kMSkewY]), scale, shift); +#ifdef SK_SCALAR_IS_FIXED + if (SkAbs32(inv->fMat[kMPersp2]) > SK_Fixed1) { + Sk64 tmp; + + tmp.set(SK_Fract1); + tmp.shiftLeft(16); + tmp.div(inv->fMat[kMPersp2], Sk64::kRound_DivOption); + + SkFract scale = tmp.get32(); + + for (int i = 0; i < 9; i++) { + inv->fMat[i] = SkFractMul(inv->fMat[i], scale); + } + } + inv->fMat[kMPersp2] = SkFixedToFract(inv->fMat[kMPersp2]); +#endif + } else { // not perspective +#ifdef SK_SCALAR_IS_FIXED + Sk64 tx, ty; + int clzNumer; + + // check the 2x2 for overflow + { + int32_t value = SkAbs32(fMat[kMScaleY]); + value |= SkAbs32(fMat[kMSkewX]); + value |= SkAbs32(fMat[kMScaleX]); + value |= SkAbs32(fMat[kMSkewY]); + clzNumer = SkCLZ(value); + if (shift - clzNumer > 31) + return false; // overflow + } + + set_muladdmul(&tx, fMat[kMSkewX], fMat[kMTransY], -fMat[kMScaleY], fMat[kMTransX]); + set_muladdmul(&ty, fMat[kMSkewY], fMat[kMTransX], -fMat[kMScaleX], fMat[kMTransY]); + // check tx,ty for overflow + clzNumer = SkCLZ(SkAbs32(tx.fHi) | SkAbs32(ty.fHi)); + if (shift - clzNumer > 14) { + return false; // overflow + } + + int fixedShift = 61 - shift; + int sk64shift = 44 - shift + clzNumer; + + inv->fMat[kMScaleX] = SkMulShift(fMat[kMScaleY], scale, fixedShift); + inv->fMat[kMSkewX] = SkMulShift(-fMat[kMSkewX], scale, fixedShift); + inv->fMat[kMTransX] = SkMulShift(tx.getShiftRight(33 - clzNumer), scale, sk64shift); + + inv->fMat[kMSkewY] = SkMulShift(-fMat[kMSkewY], scale, fixedShift); + inv->fMat[kMScaleY] = SkMulShift(fMat[kMScaleX], scale, fixedShift); + inv->fMat[kMTransY] = SkMulShift(ty.getShiftRight(33 - clzNumer), scale, sk64shift); +#else + inv->fMat[kMScaleX] = SkScalarMul(fMat[kMScaleY], scale); + inv->fMat[kMSkewX] = SkScalarMul(-fMat[kMSkewX], scale); + if (!fixmuladdmulshiftmul(fMat[kMSkewX], fMat[kMTransY], -fMat[kMScaleY], fMat[kMTransX], shift, scale, &inv->fMat[kMTransX])) { + return false; + } + + inv->fMat[kMSkewY] = SkScalarMul(-fMat[kMSkewY], scale); + inv->fMat[kMScaleY] = SkScalarMul(fMat[kMScaleX], scale); + if (!fixmuladdmulshiftmul(fMat[kMSkewY], fMat[kMTransX], -fMat[kMScaleX], fMat[kMTransY], shift, scale, &inv->fMat[kMTransY])) { + return false; + } +#endif + inv->fMat[kMPersp0] = 0; + inv->fMat[kMPersp1] = 0; + inv->fMat[kMPersp2] = kMatrix22Elem; + } + + if (inv == &tmp) { + *(SkMatrix*)this = tmp; + } + inv->setTypeMask(kUnknown_Mask); + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +void SkMatrix::Identity_pts(const SkMatrix& m, SkPoint dst[], + const SkPoint src[], int count) { + SkASSERT(m.getType() == 0); + + if (dst != src && count > 0) + memcpy(dst, src, count * sizeof(SkPoint)); +} + +void SkMatrix::Trans_pts(const SkMatrix& m, SkPoint dst[], + const SkPoint src[], int count) { + SkASSERT(m.getType() == kTranslate_Mask); + + if (count > 0) { + SkScalar tx = m.fMat[kMTransX]; + SkScalar ty = m.fMat[kMTransY]; + do { + dst->fY = src->fY + ty; + dst->fX = src->fX + tx; + src += 1; + dst += 1; + } while (--count); + } +} + +void SkMatrix::Scale_pts(const SkMatrix& m, SkPoint dst[], + const SkPoint src[], int count) { + SkASSERT(m.getType() == kScale_Mask); + + if (count > 0) { + SkScalar mx = m.fMat[kMScaleX]; + SkScalar my = m.fMat[kMScaleY]; + do { + dst->fY = SkScalarMul(src->fY, my); + dst->fX = SkScalarMul(src->fX, mx); + src += 1; + dst += 1; + } while (--count); + } +} + +void SkMatrix::ScaleTrans_pts(const SkMatrix& m, SkPoint dst[], + const SkPoint src[], int count) { + SkASSERT(m.getType() == (kScale_Mask | kTranslate_Mask)); + + if (count > 0) { + SkScalar mx = m.fMat[kMScaleX]; + SkScalar my = m.fMat[kMScaleY]; + SkScalar tx = m.fMat[kMTransX]; + SkScalar ty = m.fMat[kMTransY]; + do { + dst->fY = SkScalarMulAdd(src->fY, my, ty); + dst->fX = SkScalarMulAdd(src->fX, mx, tx); + src += 1; + dst += 1; + } while (--count); + } +} + +void SkMatrix::Rot_pts(const SkMatrix& m, SkPoint dst[], + const SkPoint src[], int count) { + SkASSERT((m.getType() & (kPerspective_Mask | kTranslate_Mask)) == 0); + + if (count > 0) { + SkScalar mx = m.fMat[kMScaleX]; + SkScalar my = m.fMat[kMScaleY]; + SkScalar kx = m.fMat[kMSkewX]; + SkScalar ky = m.fMat[kMSkewY]; + do { + SkScalar sy = src->fY; + SkScalar sx = src->fX; + src += 1; + dst->fY = SkScalarMul(sx, ky) + SkScalarMul(sy, my); + dst->fX = SkScalarMul(sx, mx) + SkScalarMul(sy, kx); + dst += 1; + } while (--count); + } +} + +void SkMatrix::RotTrans_pts(const SkMatrix& m, SkPoint dst[], + const SkPoint src[], int count) { + SkASSERT((m.getType() & kPerspective_Mask) == 0); + + if (count > 0) { + SkScalar mx = m.fMat[kMScaleX]; + SkScalar my = m.fMat[kMScaleY]; + SkScalar kx = m.fMat[kMSkewX]; + SkScalar ky = m.fMat[kMSkewY]; + SkScalar tx = m.fMat[kMTransX]; + SkScalar ty = m.fMat[kMTransY]; + do { + SkScalar sy = src->fY; + SkScalar sx = src->fX; + src += 1; + dst->fY = SkScalarMul(sx, ky) + SkScalarMulAdd(sy, my, ty); + dst->fX = SkScalarMul(sx, mx) + SkScalarMulAdd(sy, kx, tx); + dst += 1; + } while (--count); + } +} + +void SkMatrix::Persp_pts(const SkMatrix& m, SkPoint dst[], + const SkPoint src[], int count) { + SkASSERT(m.getType() & kPerspective_Mask); + +#ifdef SK_SCALAR_IS_FIXED + SkFixed persp2 = SkFractToFixed(m.fMat[kMPersp2]); +#endif + + if (count > 0) { + do { + SkScalar sy = src->fY; + SkScalar sx = src->fX; + src += 1; + + SkScalar x = SkScalarMul(sx, m.fMat[kMScaleX]) + + SkScalarMul(sy, m.fMat[kMSkewX]) + m.fMat[kMTransX]; + SkScalar y = SkScalarMul(sx, m.fMat[kMSkewY]) + + SkScalarMul(sy, m.fMat[kMScaleY]) + m.fMat[kMTransY]; +#ifdef SK_SCALAR_IS_FIXED + SkFixed z = SkFractMul(sx, m.fMat[kMPersp0]) + + SkFractMul(sy, m.fMat[kMPersp1]) + persp2; +#else + float z = SkScalarMul(sx, m.fMat[kMPersp0]) + + SkScalarMulAdd(sy, m.fMat[kMPersp1], m.fMat[kMPersp2]); +#endif + if (z) { + z = SkScalarFastInvert(z); + } + + dst->fY = SkScalarMul(y, z); + dst->fX = SkScalarMul(x, z); + dst += 1; + } while (--count); + } +} + +const SkMatrix::MapPtsProc SkMatrix::gMapPtsProcs[] = { + SkMatrix::Identity_pts, SkMatrix::Trans_pts, + SkMatrix::Scale_pts, SkMatrix::ScaleTrans_pts, + SkMatrix::Rot_pts, SkMatrix::RotTrans_pts, + SkMatrix::Rot_pts, SkMatrix::RotTrans_pts, + // repeat the persp proc 8 times + SkMatrix::Persp_pts, SkMatrix::Persp_pts, + SkMatrix::Persp_pts, SkMatrix::Persp_pts, + SkMatrix::Persp_pts, SkMatrix::Persp_pts, + SkMatrix::Persp_pts, SkMatrix::Persp_pts +}; + +void SkMatrix::mapPoints(SkPoint dst[], const SkPoint src[], int count) const { + SkASSERT((dst && src && count > 0) || count == 0); + // no partial overlap + SkASSERT(src == dst || SkAbs32((int32_t)(src - dst)) >= count); + + this->getMapPtsProc()(*this, dst, src, count); +} + +/////////////////////////////////////////////////////////////////////////////// + +void SkMatrix::mapVectors(SkPoint dst[], const SkPoint src[], int count) const { + if (this->getType() & kPerspective_Mask) { + SkPoint origin; + + MapXYProc proc = this->getMapXYProc(); + proc(*this, 0, 0, &origin); + + for (int i = count - 1; i >= 0; --i) { + SkPoint tmp; + + proc(*this, src[i].fX, src[i].fY, &tmp); + dst[i].set(tmp.fX - origin.fX, tmp.fY - origin.fY); + } + } else { + SkMatrix tmp = *this; + + tmp.fMat[kMTransX] = tmp.fMat[kMTransY] = 0; + tmp.clearTypeMask(kTranslate_Mask); + tmp.mapPoints(dst, src, count); + } +} + +bool SkMatrix::mapRect(SkRect* dst, const SkRect& src) const { + SkASSERT(dst && &src); + + if (this->rectStaysRect()) { + this->mapPoints((SkPoint*)dst, (const SkPoint*)&src, 2); + dst->sort(); + return true; + } else { + SkPoint quad[4]; + + src.toQuad(quad); + this->mapPoints(quad, quad, 4); + dst->set(quad, 4); + return false; + } +} + +SkScalar SkMatrix::mapRadius(SkScalar radius) const { + SkVector vec[2]; + + vec[0].set(radius, 0); + vec[1].set(0, radius); + this->mapVectors(vec, 2); + + SkScalar d0 = vec[0].length(); + SkScalar d1 = vec[1].length(); + + return SkScalarMean(d0, d1); +} + +/////////////////////////////////////////////////////////////////////////////// + +void SkMatrix::Persp_xy(const SkMatrix& m, SkScalar sx, SkScalar sy, + SkPoint* pt) { + SkASSERT(m.getType() & kPerspective_Mask); + + SkScalar x = SkScalarMul(sx, m.fMat[kMScaleX]) + + SkScalarMul(sy, m.fMat[kMSkewX]) + m.fMat[kMTransX]; + SkScalar y = SkScalarMul(sx, m.fMat[kMSkewY]) + + SkScalarMul(sy, m.fMat[kMScaleY]) + m.fMat[kMTransY]; +#ifdef SK_SCALAR_IS_FIXED + SkFixed z = SkFractMul(sx, m.fMat[kMPersp0]) + + SkFractMul(sy, m.fMat[kMPersp1]) + + SkFractToFixed(m.fMat[kMPersp2]); +#else + float z = SkScalarMul(sx, m.fMat[kMPersp0]) + + SkScalarMul(sy, m.fMat[kMPersp1]) + m.fMat[kMPersp2]; +#endif + if (z) { + z = SkScalarFastInvert(z); + } + pt->fX = SkScalarMul(x, z); + pt->fY = SkScalarMul(y, z); +} + +#ifdef SK_SCALAR_IS_FIXED +static SkFixed fixmuladdmul(SkFixed a, SkFixed b, SkFixed c, SkFixed d) { + Sk64 tmp, tmp1; + + tmp.setMul(a, b); + tmp1.setMul(c, d); + return tmp.addGetFixed(tmp1); +// tmp.add(tmp1); +// return tmp.getFixed(); +} +#endif + +void SkMatrix::RotTrans_xy(const SkMatrix& m, SkScalar sx, SkScalar sy, + SkPoint* pt) { + SkASSERT((m.getType() & (kAffine_Mask | kPerspective_Mask)) == kAffine_Mask); + +#ifdef SK_SCALAR_IS_FIXED + pt->fX = fixmuladdmul(sx, m.fMat[kMScaleX], sy, m.fMat[kMSkewX]) + + m.fMat[kMTransX]; + pt->fY = fixmuladdmul(sx, m.fMat[kMSkewY], sy, m.fMat[kMScaleY]) + + m.fMat[kMTransY]; +#else + pt->fX = SkScalarMul(sx, m.fMat[kMScaleX]) + + SkScalarMulAdd(sy, m.fMat[kMSkewX], m.fMat[kMTransX]); + pt->fY = SkScalarMul(sx, m.fMat[kMSkewY]) + + SkScalarMulAdd(sy, m.fMat[kMScaleY], m.fMat[kMTransY]); +#endif +} + +void SkMatrix::Rot_xy(const SkMatrix& m, SkScalar sx, SkScalar sy, + SkPoint* pt) { + SkASSERT((m.getType() & (kAffine_Mask | kPerspective_Mask))== kAffine_Mask); + SkASSERT(0 == m.fMat[kMTransX]); + SkASSERT(0 == m.fMat[kMTransY]); + +#ifdef SK_SCALAR_IS_FIXED + pt->fX = fixmuladdmul(sx, m.fMat[kMScaleX], sy, m.fMat[kMSkewX]); + pt->fY = fixmuladdmul(sx, m.fMat[kMSkewY], sy, m.fMat[kMScaleY]); +#else + pt->fX = SkScalarMul(sx, m.fMat[kMScaleX]) + + SkScalarMulAdd(sy, m.fMat[kMSkewX], m.fMat[kMTransX]); + pt->fY = SkScalarMul(sx, m.fMat[kMSkewY]) + + SkScalarMulAdd(sy, m.fMat[kMScaleY], m.fMat[kMTransY]); +#endif +} + +void SkMatrix::ScaleTrans_xy(const SkMatrix& m, SkScalar sx, SkScalar sy, + SkPoint* pt) { + SkASSERT((m.getType() & (kScale_Mask | kAffine_Mask | kPerspective_Mask)) + == kScale_Mask); + + pt->fX = SkScalarMulAdd(sx, m.fMat[kMScaleX], m.fMat[kMTransX]); + pt->fY = SkScalarMulAdd(sy, m.fMat[kMScaleY], m.fMat[kMTransY]); +} + +void SkMatrix::Scale_xy(const SkMatrix& m, SkScalar sx, SkScalar sy, + SkPoint* pt) { + SkASSERT((m.getType() & (kScale_Mask | kAffine_Mask | kPerspective_Mask)) + == kScale_Mask); + SkASSERT(0 == m.fMat[kMTransX]); + SkASSERT(0 == m.fMat[kMTransY]); + + pt->fX = SkScalarMul(sx, m.fMat[kMScaleX]); + pt->fY = SkScalarMul(sy, m.fMat[kMScaleY]); +} + +void SkMatrix::Trans_xy(const SkMatrix& m, SkScalar sx, SkScalar sy, + SkPoint* pt) { + SkASSERT(m.getType() == kTranslate_Mask); + + pt->fX = sx + m.fMat[kMTransX]; + pt->fY = sy + m.fMat[kMTransY]; +} + +void SkMatrix::Identity_xy(const SkMatrix& m, SkScalar sx, SkScalar sy, + SkPoint* pt) { + SkASSERT(0 == m.getType()); + + pt->fX = sx; + pt->fY = sy; +} + +const SkMatrix::MapXYProc SkMatrix::gMapXYProcs[] = { + SkMatrix::Identity_xy, SkMatrix::Trans_xy, + SkMatrix::Scale_xy, SkMatrix::ScaleTrans_xy, + SkMatrix::Rot_xy, SkMatrix::RotTrans_xy, + SkMatrix::Rot_xy, SkMatrix::RotTrans_xy, + // repeat the persp proc 8 times + SkMatrix::Persp_xy, SkMatrix::Persp_xy, + SkMatrix::Persp_xy, SkMatrix::Persp_xy, + SkMatrix::Persp_xy, SkMatrix::Persp_xy, + SkMatrix::Persp_xy, SkMatrix::Persp_xy +}; + +/////////////////////////////////////////////////////////////////////////////// + +// if its nearly zero (just made up 26, perhaps it should be bigger or smaller) +#ifdef SK_SCALAR_IS_FIXED + typedef SkFract SkPerspElemType; + #define PerspNearlyZero(x) (SkAbs32(x) < (SK_Fract1 >> 26)) +#else + typedef float SkPerspElemType; + #define PerspNearlyZero(x) SkScalarNearlyZero(x, (1.0f / (1 << 26))) +#endif + +bool SkMatrix::fixedStepInX(SkScalar y, SkFixed* stepX, SkFixed* stepY) const { + if (PerspNearlyZero(fMat[kMPersp0])) { + if (stepX || stepY) { + if (PerspNearlyZero(fMat[kMPersp1]) && + PerspNearlyZero(fMat[kMPersp2] - kMatrix22Elem)) { + if (stepX) { + *stepX = SkScalarToFixed(fMat[kMScaleX]); + } + if (stepY) { + *stepY = SkScalarToFixed(fMat[kMSkewY]); + } + } else { +#ifdef SK_SCALAR_IS_FIXED + SkFixed z = SkFractMul(y, fMat[kMPersp1]) + + SkFractToFixed(fMat[kMPersp2]); +#else + float z = y * fMat[kMPersp1] + fMat[kMPersp2]; +#endif + if (stepX) { + *stepX = SkScalarToFixed(SkScalarDiv(fMat[kMScaleX], z)); + } + if (stepY) { + *stepY = SkScalarToFixed(SkScalarDiv(fMat[kMSkewY], z)); + } + } + } + return true; + } + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +#include "SkPerspIter.h" + +SkPerspIter::SkPerspIter(const SkMatrix& m, SkScalar x0, SkScalar y0, int count) + : fMatrix(m), fSX(x0), fSY(y0), fCount(count) { + SkPoint pt; + + SkMatrix::Persp_xy(m, x0, y0, &pt); + fX = SkScalarToFixed(pt.fX); + fY = SkScalarToFixed(pt.fY); +} + +int SkPerspIter::next() { + int n = fCount; + + if (0 == n) { + return 0; + } + SkPoint pt; + SkFixed x = fX; + SkFixed y = fY; + SkFixed dx, dy; + + if (n >= kCount) { + n = kCount; + fSX += SkIntToScalar(kCount); + SkMatrix::Persp_xy(fMatrix, fSX, fSY, &pt); + fX = SkScalarToFixed(pt.fX); + fY = SkScalarToFixed(pt.fY); + dx = (fX - x) >> kShift; + dy = (fY - y) >> kShift; + } else { + fSX += SkIntToScalar(n); + SkMatrix::Persp_xy(fMatrix, fSX, fSY, &pt); + fX = SkScalarToFixed(pt.fX); + fY = SkScalarToFixed(pt.fY); + dx = (fX - x) / n; + dy = (fY - y) / n; + } + + SkFixed* p = fStorage; + for (int i = 0; i < n; i++) { + *p++ = x; x += dx; + *p++ = y; y += dy; + } + + fCount -= n; + return n; +} + +/////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_SCALAR_IS_FIXED + +static inline bool poly_to_point(SkPoint* pt, const SkPoint poly[], int count) { + SkFixed x = SK_Fixed1, y = SK_Fixed1; + SkPoint pt1, pt2; + Sk64 w1, w2; + + if (count > 1) { + pt1.fX = poly[1].fX - poly[0].fX; + pt1.fY = poly[1].fY - poly[0].fY; + y = SkPoint::Length(pt1.fX, pt1.fY); + if (y == 0) { + return false; + } + switch (count) { + case 2: + break; + case 3: + pt2.fX = poly[0].fY - poly[2].fY; + pt2.fY = poly[2].fX - poly[0].fX; + goto CALC_X; + default: + pt2.fX = poly[0].fY - poly[3].fY; + pt2.fY = poly[3].fX - poly[0].fX; + CALC_X: + w1.setMul(pt1.fX, pt2.fX); + w2.setMul(pt1.fY, pt2.fY); + w1.add(w2); + w1.div(y, Sk64::kRound_DivOption); + if (!w1.is32()) { + return false; + } + x = w1.get32(); + break; + } + } + pt->set(x, y); + return true; +} + +bool SkMatrix::Poly2Proc(const SkPoint srcPt[], SkMatrix* dst, + const SkPoint& scalePt) { + // need to check if SkFixedDiv overflows... + + const SkFixed scale = scalePt.fY; + dst->fMat[kMScaleX] = SkFixedDiv(srcPt[1].fY - srcPt[0].fY, scale); + dst->fMat[kMSkewY] = SkFixedDiv(srcPt[0].fX - srcPt[1].fX, scale); + dst->fMat[kMPersp0] = 0; + dst->fMat[kMSkewX] = SkFixedDiv(srcPt[1].fX - srcPt[0].fX, scale); + dst->fMat[kMScaleY] = SkFixedDiv(srcPt[1].fY - srcPt[0].fY, scale); + dst->fMat[kMPersp1] = 0; + dst->fMat[kMTransX] = srcPt[0].fX; + dst->fMat[kMTransY] = srcPt[0].fY; + dst->fMat[kMPersp2] = SK_Fract1; + dst->setTypeMask(kUnknown_Mask); + return true; +} + +bool SkMatrix::Poly3Proc(const SkPoint srcPt[], SkMatrix* dst, + const SkPoint& scale) { + // really, need to check if SkFixedDiv overflow'd + + dst->fMat[kMScaleX] = SkFixedDiv(srcPt[2].fX - srcPt[0].fX, scale.fX); + dst->fMat[kMSkewY] = SkFixedDiv(srcPt[2].fY - srcPt[0].fY, scale.fX); + dst->fMat[kMPersp0] = 0; + dst->fMat[kMSkewX] = SkFixedDiv(srcPt[1].fX - srcPt[0].fX, scale.fY); + dst->fMat[kMScaleY] = SkFixedDiv(srcPt[1].fY - srcPt[0].fY, scale.fY); + dst->fMat[kMPersp1] = 0; + dst->fMat[kMTransX] = srcPt[0].fX; + dst->fMat[kMTransY] = srcPt[0].fY; + dst->fMat[kMPersp2] = SK_Fract1; + dst->setTypeMask(kUnknown_Mask); + return true; +} + +bool SkMatrix::Poly4Proc(const SkPoint srcPt[], SkMatrix* dst, + const SkPoint& scale) { + SkFract a1, a2; + SkFixed x0, y0, x1, y1, x2, y2; + + x0 = srcPt[2].fX - srcPt[0].fX; + y0 = srcPt[2].fY - srcPt[0].fY; + x1 = srcPt[2].fX - srcPt[1].fX; + y1 = srcPt[2].fY - srcPt[1].fY; + x2 = srcPt[2].fX - srcPt[3].fX; + y2 = srcPt[2].fY - srcPt[3].fY; + + /* check if abs(x2) > abs(y2) */ + if ( x2 > 0 ? y2 > 0 ? x2 > y2 : x2 > -y2 : y2 > 0 ? -x2 > y2 : x2 < y2) { + SkFixed denom = SkMulDiv(x1, y2, x2) - y1; + if (0 == denom) { + return false; + } + a1 = SkFractDiv(SkMulDiv(x0 - x1, y2, x2) - y0 + y1, denom); + } else { + SkFixed denom = x1 - SkMulDiv(y1, x2, y2); + if (0 == denom) { + return false; + } + a1 = SkFractDiv(x0 - x1 - SkMulDiv(y0 - y1, x2, y2), denom); + } + + /* check if abs(x1) > abs(y1) */ + if ( x1 > 0 ? y1 > 0 ? x1 > y1 : x1 > -y1 : y1 > 0 ? -x1 > y1 : x1 < y1) { + SkFixed denom = y2 - SkMulDiv(x2, y1, x1); + if (0 == denom) { + return false; + } + a2 = SkFractDiv(y0 - y2 - SkMulDiv(x0 - x2, y1, x1), denom); + } else { + SkFixed denom = SkMulDiv(y2, x1, y1) - x2; + if (0 == denom) { + return false; + } + a2 = SkFractDiv(SkMulDiv(y0 - y2, x1, y1) - x0 + x2, denom); + } + + // need to check if SkFixedDiv overflows... + dst->fMat[kMScaleX] = SkFixedDiv(SkFractMul(a2, srcPt[3].fX) + + srcPt[3].fX - srcPt[0].fX, scale.fX); + dst->fMat[kMSkewY] = SkFixedDiv(SkFractMul(a2, srcPt[3].fY) + + srcPt[3].fY - srcPt[0].fY, scale.fX); + dst->fMat[kMPersp0] = SkFixedDiv(a2, scale.fX); + dst->fMat[kMSkewX] = SkFixedDiv(SkFractMul(a1, srcPt[1].fX) + + srcPt[1].fX - srcPt[0].fX, scale.fY); + dst->fMat[kMScaleY] = SkFixedDiv(SkFractMul(a1, srcPt[1].fY) + + srcPt[1].fY - srcPt[0].fY, scale.fY); + dst->fMat[kMPersp1] = SkFixedDiv(a1, scale.fY); + dst->fMat[kMTransX] = srcPt[0].fX; + dst->fMat[kMTransY] = srcPt[0].fY; + dst->fMat[kMPersp2] = SK_Fract1; + dst->setTypeMask(kUnknown_Mask); + return true; +} + +#else /* Scalar is float */ + +static inline bool checkForZero(float x) { + return x*x == 0; +} + +static inline bool poly_to_point(SkPoint* pt, const SkPoint poly[], int count) { + float x = 1, y = 1; + SkPoint pt1, pt2; + + if (count > 1) { + pt1.fX = poly[1].fX - poly[0].fX; + pt1.fY = poly[1].fY - poly[0].fY; + y = SkPoint::Length(pt1.fX, pt1.fY); + if (checkForZero(y)) { + return false; + } + switch (count) { + case 2: + break; + case 3: + pt2.fX = poly[0].fY - poly[2].fY; + pt2.fY = poly[2].fX - poly[0].fX; + goto CALC_X; + default: + pt2.fX = poly[0].fY - poly[3].fY; + pt2.fY = poly[3].fX - poly[0].fX; + CALC_X: + x = SkScalarDiv(SkScalarMul(pt1.fX, pt2.fX) + + SkScalarMul(pt1.fY, pt2.fY), y); + break; + } + } + pt->set(x, y); + return true; +} + +bool SkMatrix::Poly2Proc(const SkPoint srcPt[], SkMatrix* dst, + const SkPoint& scale) { + float invScale = 1 / scale.fY; + + dst->fMat[kMScaleX] = (srcPt[1].fY - srcPt[0].fY) * invScale; + dst->fMat[kMSkewY] = (srcPt[0].fX - srcPt[1].fX) * invScale; + dst->fMat[kMPersp0] = 0; + dst->fMat[kMSkewX] = (srcPt[1].fX - srcPt[0].fX) * invScale; + dst->fMat[kMScaleY] = (srcPt[1].fY - srcPt[0].fY) * invScale; + dst->fMat[kMPersp1] = 0; + dst->fMat[kMTransX] = srcPt[0].fX; + dst->fMat[kMTransY] = srcPt[0].fY; + dst->fMat[kMPersp2] = 1; + dst->setTypeMask(kUnknown_Mask); + return true; +} + +bool SkMatrix::Poly3Proc(const SkPoint srcPt[], SkMatrix* dst, + const SkPoint& scale) { + float invScale = 1 / scale.fX; + dst->fMat[kMScaleX] = (srcPt[2].fX - srcPt[0].fX) * invScale; + dst->fMat[kMSkewY] = (srcPt[2].fY - srcPt[0].fY) * invScale; + dst->fMat[kMPersp0] = 0; + + invScale = 1 / scale.fY; + dst->fMat[kMSkewX] = (srcPt[1].fX - srcPt[0].fX) * invScale; + dst->fMat[kMScaleY] = (srcPt[1].fY - srcPt[0].fY) * invScale; + dst->fMat[kMPersp1] = 0; + + dst->fMat[kMTransX] = srcPt[0].fX; + dst->fMat[kMTransY] = srcPt[0].fY; + dst->fMat[kMPersp2] = 1; + dst->setTypeMask(kUnknown_Mask); + return true; +} + +bool SkMatrix::Poly4Proc(const SkPoint srcPt[], SkMatrix* dst, + const SkPoint& scale) { + float a1, a2; + float x0, y0, x1, y1, x2, y2; + + x0 = srcPt[2].fX - srcPt[0].fX; + y0 = srcPt[2].fY - srcPt[0].fY; + x1 = srcPt[2].fX - srcPt[1].fX; + y1 = srcPt[2].fY - srcPt[1].fY; + x2 = srcPt[2].fX - srcPt[3].fX; + y2 = srcPt[2].fY - srcPt[3].fY; + + /* check if abs(x2) > abs(y2) */ + if ( x2 > 0 ? y2 > 0 ? x2 > y2 : x2 > -y2 : y2 > 0 ? -x2 > y2 : x2 < y2) { + float denom = SkScalarMulDiv(x1, y2, x2) - y1; + if (checkForZero(denom)) { + return false; + } + a1 = SkScalarDiv(SkScalarMulDiv(x0 - x1, y2, x2) - y0 + y1, denom); + } else { + float denom = x1 - SkScalarMulDiv(y1, x2, y2); + if (checkForZero(denom)) { + return false; + } + a1 = SkScalarDiv(x0 - x1 - SkScalarMulDiv(y0 - y1, x2, y2), denom); + } + + /* check if abs(x1) > abs(y1) */ + if ( x1 > 0 ? y1 > 0 ? x1 > y1 : x1 > -y1 : y1 > 0 ? -x1 > y1 : x1 < y1) { + float denom = y2 - SkScalarMulDiv(x2, y1, x1); + if (checkForZero(denom)) { + return false; + } + a2 = SkScalarDiv(y0 - y2 - SkScalarMulDiv(x0 - x2, y1, x1), denom); + } else { + float denom = SkScalarMulDiv(y2, x1, y1) - x2; + if (checkForZero(denom)) { + return false; + } + a2 = SkScalarDiv(SkScalarMulDiv(y0 - y2, x1, y1) - x0 + x2, denom); + } + + float invScale = 1 / scale.fX; + dst->fMat[kMScaleX] = SkScalarMul(SkScalarMul(a2, srcPt[3].fX) + + srcPt[3].fX - srcPt[0].fX, invScale); + dst->fMat[kMSkewY] = SkScalarMul(SkScalarMul(a2, srcPt[3].fY) + + srcPt[3].fY - srcPt[0].fY, invScale); + dst->fMat[kMPersp0] = SkScalarMul(a2, invScale); + invScale = 1 / scale.fY; + dst->fMat[kMSkewX] = SkScalarMul(SkScalarMul(a1, srcPt[1].fX) + + srcPt[1].fX - srcPt[0].fX, invScale); + dst->fMat[kMScaleY] = SkScalarMul(SkScalarMul(a1, srcPt[1].fY) + + srcPt[1].fY - srcPt[0].fY, invScale); + dst->fMat[kMPersp1] = SkScalarMul(a1, invScale); + dst->fMat[kMTransX] = srcPt[0].fX; + dst->fMat[kMTransY] = srcPt[0].fY; + dst->fMat[kMPersp2] = 1; + dst->setTypeMask(kUnknown_Mask); + return true; +} + +#endif + +typedef bool (*PolyMapProc)(const SkPoint[], SkMatrix*, const SkPoint&); + +/* Taken from Rob Johnson's original sample code in QuickDraw GX +*/ +bool SkMatrix::setPolyToPoly(const SkPoint src[], const SkPoint dst[], + int count) { + if ((unsigned)count > 4) { + SkDebugf("--- SkMatrix::setPolyToPoly count out of range %d\n", count); + return false; + } + + if (0 == count) { + this->reset(); + return true; + } + if (1 == count) { + this->setTranslate(dst[0].fX - src[0].fX, dst[0].fY - src[0].fY); + return true; + } + + SkPoint scale; + if (!poly_to_point(&scale, src, count) || + SkScalarNearlyZero(scale.fX) || + SkScalarNearlyZero(scale.fY)) { + return false; + } + + static const PolyMapProc gPolyMapProcs[] = { + SkMatrix::Poly2Proc, SkMatrix::Poly3Proc, SkMatrix::Poly4Proc + }; + PolyMapProc proc = gPolyMapProcs[count - 2]; + + SkMatrix tempMap, result; + tempMap.setTypeMask(kUnknown_Mask); + + if (!proc(src, &tempMap, scale)) { + return false; + } + if (!tempMap.invert(&result)) { + return false; + } + if (!proc(dst, &tempMap, scale)) { + return false; + } + if (!result.setConcat(tempMap, result)) { + return false; + } + *this = result; + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +void SkMatrix::dump() const { + // ensure the fTypeMask is up2date + (void)this->getType(); +#ifdef SK_DEBUG + int mask = this->computeTypeMask(); + SkASSERT(mask == fTypeMask); +#endif + +#ifdef SK_CAN_USE_FLOAT + SkDebugf("[%8.4f %8.4f %8.4f] [%8.4f %8.4f %8.4f] [%8.4f %8.4f %8.4f] %x\n", +#ifdef SK_SCALAR_IS_FLOAT + fMat[0], fMat[1], fMat[2], fMat[3], fMat[4], fMat[5], + fMat[6], fMat[7], fMat[8], fTypeMask); +#else + SkFixedToFloat(fMat[0]), SkFixedToFloat(fMat[1]), SkFixedToFloat(fMat[2]), + SkFixedToFloat(fMat[3]), SkFixedToFloat(fMat[4]), SkFixedToFloat(fMat[5]), + SkFractToFloat(fMat[6]), SkFractToFloat(fMat[7]), SkFractToFloat(fMat[8]), + fTypeMask); +#endif +} + +/////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_DEBUG + +void SkMatrix::UnitTest() { +#ifdef SK_SUPPORT_UNITTEST + SkMatrix mat, inverse, iden1, iden2; + + mat.reset(); + mat.setTranslate(SK_Scalar1, SK_Scalar1); + mat.invert(&inverse); + inverse.dump(); + iden1.setConcat(mat, inverse); + iden1.dump(); + + mat.setScale(SkIntToScalar(2), SkIntToScalar(2)); + mat.invert(&inverse); + inverse.dump(); + iden1.setConcat(mat, inverse); + iden1.dump(); + + mat.setScale(SK_Scalar1/2, SK_Scalar1/2); + mat.invert(&inverse); + inverse.dump(); + iden1.setConcat(mat, inverse); + iden1.dump(); + SkASSERT(iden1.isIdentity()); + + mat.setScale(SkIntToScalar(3), SkIntToScalar(5), SkIntToScalar(20), 0); + mat.postRotate(SkIntToScalar(25)); + + SkASSERT(mat.invert(NULL)); + mat.invert(&inverse); + + iden1.setConcat(mat, inverse); + iden2.setConcat(inverse, mat); + + iden1.dump(); +// SkASSERT(iden1.isIdentity()); + iden2.dump(); +// SkASSERT(iden2.isIdentity()); + + // rectStaysRect test + { + static const struct { + SkScalar m00, m01, m10, m11; + bool mStaysRect; + } + gRectStaysRectSamples[] = { + { 0, 0, 0, 0, false }, + { 0, 0, 0, SK_Scalar1, false }, + { 0, 0, SK_Scalar1, 0, false }, + { 0, 0, SK_Scalar1, SK_Scalar1, false }, + { 0, SK_Scalar1, 0, 0, false }, + { 0, SK_Scalar1, 0, SK_Scalar1, false }, + { 0, SK_Scalar1, SK_Scalar1, 0, true }, + { 0, SK_Scalar1, SK_Scalar1, SK_Scalar1, false }, + { SK_Scalar1, 0, 0, 0, false }, + { SK_Scalar1, 0, 0, SK_Scalar1, true }, + { SK_Scalar1, 0, SK_Scalar1, 0, false }, + { SK_Scalar1, 0, SK_Scalar1, SK_Scalar1, false }, + { SK_Scalar1, SK_Scalar1, 0, 0, false }, + { SK_Scalar1, SK_Scalar1, 0, SK_Scalar1, false }, + { SK_Scalar1, SK_Scalar1, SK_Scalar1, 0, false }, + { SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1, false } + }; + + for (size_t i = 0; i < SK_ARRAY_COUNT(gRectStaysRectSamples); i++) { + SkMatrix m; + + m.reset(); + m.set(SkMatrix::kMScaleX, gRectStaysRectSamples[i].m00); + m.set(SkMatrix::kMSkewX, gRectStaysRectSamples[i].m01); + m.set(SkMatrix::kMSkewY, gRectStaysRectSamples[i].m10); + m.set(SkMatrix::kMScaleY, gRectStaysRectSamples[i].m11); + SkASSERT(m.rectStaysRect() == gRectStaysRectSamples[i].mStaysRect); + } + } +#endif +} + +#endif diff --git a/skia/corecg/SkMemory_stdlib.cpp b/skia/corecg/SkMemory_stdlib.cpp new file mode 100644 index 0000000..6ceca33 --- /dev/null +++ b/skia/corecg/SkMemory_stdlib.cpp @@ -0,0 +1,280 @@ +/* libs/corecg/SkMemory_stdlib.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkTypes.h" +#include <stdio.h> +#include <stdlib.h> + +#ifdef SK_DEBUG + #define SK_TAG_BLOCKS + // #define SK_TRACK_ALLOC // enable to see a printf for every alloc/free + // #define SK_CHECK_TAGS // enable to double-check debugging link list +#endif + +#ifdef SK_TAG_BLOCKS + +#include "SkThread.h" + +// size this (as a multiple of 4) so that the total offset to the internal data +// is at least a multiple of 8 (since some clients of our malloc may require +// that. +static const char kBlockHeaderTag[] = { 's', 'k', 'i', 'a', '1', '2', '3', '4' }; +static const char kBlockTrailerTag[] = { 'a', 'i', 'k', 's' }; +#define kByteFill 0xCD +#define kDeleteFill 0xEF + +static SkMutex gBlockMutex; +static struct SkBlockHeader* gHeader; + +struct SkBlockHeader { + SkBlockHeader* fNext; +#ifdef SK_CHECK_TAGS + SkBlockHeader** fTop; // set to verify in debugger that block was alloc'd / freed with same gHeader + SkBlockHeader* fPrevious; // set to see in debugger previous block when corruption happens +#endif + size_t fSize; + char fHeader[sizeof(kBlockHeaderTag)]; + // data goes here. The offset to this point must be a multiple of 8 + char fTrailer[sizeof(kBlockTrailerTag)]; + + void* add(size_t realSize) + { + SkAutoMutexAcquire ac(gBlockMutex); + InMutexValidate(); + fNext = gHeader; +#ifdef SK_CHECK_TAGS + fTop = &gHeader; + fPrevious = NULL; + if (fNext != NULL) + fNext->fPrevious = this; +#endif + gHeader = this; + fSize = realSize; + memcpy(fHeader, kBlockHeaderTag, sizeof(kBlockHeaderTag)); + void* result = fTrailer; + void* trailer = (char*)result + realSize; + memcpy(trailer, kBlockTrailerTag, sizeof(kBlockTrailerTag)); + return result; + } + + static void Dump() + { + SkAutoMutexAcquire ac(gBlockMutex); + InMutexValidate(); + SkBlockHeader* header = gHeader; + int count = 0; + size_t size = 0; + while (header != NULL) { + char scratch[256]; + char* pos = scratch; + size_t size = header->fSize; + int* data = (int*)(void*)header->fTrailer; + pos += sprintf(pos, "%p 0x%08zx (%7zd) ", + data, size, size); + size >>= 2; + size_t ints = size > 4 ? 4 : size; + size_t index; + for (index = 0; index < ints; index++) + pos += sprintf(pos, "0x%08x ", data[index]); + pos += sprintf(pos, " ("); + for (index = 0; index < ints; index++) + pos += sprintf(pos, "%g ", data[index] / 65536.0f); + if (ints > 0) + --pos; + pos += sprintf(pos, ") \""); + size_t chars = size > 16 ? 16 : size; + char* chPtr = (char*) data; + for (index = 0; index < chars; index++) { + char ch = chPtr[index]; + pos += sprintf(pos, "%c", ch >= ' ' && ch < 0x7f ? ch : '?'); + } + pos += sprintf(pos, "\""); + SkDebugf("%s\n", scratch); + count++; + size += header->fSize; + header = header->fNext; + } + SkDebugf("--- count %d size 0x%08x (%zd) ---\n", count, size, size); + } + + void remove() const + { + SkAutoMutexAcquire ac(gBlockMutex); + SkBlockHeader** findPtr = &gHeader; + do { + SkBlockHeader* find = *findPtr; + SkASSERT(find != NULL); + if (find == this) { + *findPtr = fNext; + break; + } + findPtr = &find->fNext; + } while (true); + InMutexValidate(); + SkASSERT(memcmp(fHeader, kBlockHeaderTag, sizeof(kBlockHeaderTag)) == 0); + const char* trailer = fTrailer + fSize; + SkASSERT(memcmp(trailer, kBlockTrailerTag, sizeof(kBlockTrailerTag)) == 0); + } + + static void Validate() + { + SkAutoMutexAcquire ac(gBlockMutex); + InMutexValidate(); + } + +private: + static void InMutexValidate() + { + SkBlockHeader* header = gHeader; + while (header != NULL) { + SkASSERT(memcmp(header->fHeader, kBlockHeaderTag, sizeof(kBlockHeaderTag)) == 0); + char* trailer = header->fTrailer + header->fSize; + SkASSERT(memcmp(trailer, kBlockTrailerTag, sizeof(kBlockTrailerTag)) == 0); + header = header->fNext; + } + } +}; + +void Dump() +{ + SkBlockHeader::Dump(); +} + +void ValidateHeap() +{ + SkBlockHeader::Validate(); +} +#else +void Dump() {} +void ValidateHeap() {} +#endif + +void sk_throw() +{ +#ifdef ANDROID + fprintf(stderr, "throwing...\n"); +#endif + SkASSERT(!"sk_throw"); + abort(); +} + +void sk_out_of_memory(void) +{ +#ifdef ANDROID + fprintf(stderr,"- out of memory in SGL -\n"); +#endif + SkASSERT(!"sk_out_of_memory"); + abort(); +} + +void* sk_malloc_throw(size_t size) +{ + return sk_malloc_flags(size, SK_MALLOC_THROW); +} + +void* sk_realloc_throw(void* addr, size_t size) +{ +#ifdef SK_TAG_BLOCKS + ValidateHeap(); + if (addr != NULL) { + SkBlockHeader* header = (SkBlockHeader*) + ((char*)addr - SK_OFFSETOF(SkBlockHeader, fTrailer)); + header->remove(); +#ifdef SK_TRACK_ALLOC + printf("sk_realloc_throw %p oldSize=%zd\n", addr, header->fSize); +#endif + addr = header; + } + size_t realSize = size; + if (size) + size += sizeof(SkBlockHeader); +#endif + + void* p = realloc(addr, size); + if (size == 0) + { + ValidateHeap(); + return p; + } + + if (p == NULL) + sk_throw(); +#ifdef SK_TAG_BLOCKS + else + { + SkBlockHeader* header = (SkBlockHeader*) p; + p = header->add(realSize); +#ifdef SK_TRACK_ALLOC + printf("sk_realloc_throw %p size=%zd\n", p, realSize); +#endif + } +#endif + ValidateHeap(); + return p; +} + +void sk_free(void* p) +{ + if (p) + { + ValidateHeap(); +#ifdef SK_TAG_BLOCKS + SkBlockHeader* header = (SkBlockHeader*) + ((char*)p - SK_OFFSETOF(SkBlockHeader, fTrailer)); + header->remove(); +#ifdef SK_TRACK_ALLOC + printf("sk_free %p size=%zd\n", p, header->fSize); +#endif + size_t size = header->fSize + sizeof(SkBlockHeader); + memset(header, kDeleteFill, size); + p = header; +#endif + ValidateHeap(); + free(p); + ValidateHeap(); + } +} + +void* sk_malloc_flags(size_t size, unsigned flags) +{ + ValidateHeap(); +#ifdef SK_TAG_BLOCKS + size_t realSize = size; + size += sizeof(SkBlockHeader); +#endif + + void* p = malloc(size); + if (p == NULL) + { + if (flags & SK_MALLOC_THROW) + sk_throw(); + } +#ifdef SK_TAG_BLOCKS + else + { + SkBlockHeader* header = (SkBlockHeader*) p; + p = header->add(realSize); + memset(p, kByteFill, realSize); +#ifdef SK_TRACK_ALLOC + printf("sk_malloc_flags %p size=%zd\n", p, realSize); +#endif + } +#endif + ValidateHeap(); + return p; +} + diff --git a/skia/corecg/SkPoint.cpp b/skia/corecg/SkPoint.cpp new file mode 100644 index 0000000..9d6acdf --- /dev/null +++ b/skia/corecg/SkPoint.cpp @@ -0,0 +1,334 @@ +/* + * Copyright (C) 2006-2008 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "SkPoint.h" + +void SkIPoint::rotateCW(SkIPoint* dst) const { + SkASSERT(dst); + + // use a tmp in case this == dst + int32_t tmp = fX; + dst->fX = -fY; + dst->fY = tmp; +} + +void SkIPoint::rotateCCW(SkIPoint* dst) const { + SkASSERT(dst); + + // use a tmp in case this == dst + int32_t tmp = fX; + dst->fX = fY; + dst->fY = -tmp; +} + +/////////////////////////////////////////////////////////////////////////////// + +void SkPoint::rotateCW(SkPoint* dst) const { + SkASSERT(dst); + + // use a tmp in case this == dst + SkScalar tmp = fX; + dst->fX = -fY; + dst->fY = tmp; +} + +void SkPoint::rotateCCW(SkPoint* dst) const { + SkASSERT(dst); + + // use a tmp in case this == dst + SkScalar tmp = fX; + dst->fX = fY; + dst->fY = -tmp; +} + +void SkPoint::scale(SkScalar scale, SkPoint* dst) const { + SkASSERT(dst); + dst->set(SkScalarMul(fX, scale), SkScalarMul(fY, scale)); +} + +#define kNearlyZero (SK_Scalar1 / 8092) + +bool SkPoint::normalize() { + return this->setLength(fX, fY, SK_Scalar1); +} + +bool SkPoint::setNormalize(SkScalar x, SkScalar y) { + return this->setLength(x, y, SK_Scalar1); +} + +bool SkPoint::setLength(SkScalar length) { + return this->setLength(fX, fY, length); +} + +#ifdef SK_SCALAR_IS_FLOAT + +SkScalar SkPoint::Length(SkScalar dx, SkScalar dy) { + return sk_float_sqrt(dx * dx + dy * dy); +} + +bool SkPoint::setLength(float x, float y, float length) { + float mag = sk_float_sqrt(x * x + y * y); + if (mag > kNearlyZero) { + length /= mag; + fX = x * length; + fY = y * length; + return true; + } + return false; +} + +#else + +#include "Sk64.h" + +SkScalar SkPoint::Length(SkScalar dx, SkScalar dy) { + Sk64 tmp1, tmp2; + + tmp1.setMul(dx, dx); + tmp2.setMul(dy, dy); + tmp1.add(tmp2); + + return tmp1.getSqrt(); +} + +#ifdef SK_DEBUGx +static SkFixed fixlen(SkFixed x, SkFixed y) { + float fx = (float)x; + float fy = (float)y; + + return (int)floorf(sqrtf(fx*fx + fy*fy) + 0.5f); +} +#endif + +static inline uint32_t squarefixed(unsigned x) { + x >>= 16; + return x*x; +} + +#if 1 // Newton iter for setLength + +static inline unsigned invsqrt_iter(unsigned V, unsigned U) { + unsigned x = V * U >> 14; + x = x * U >> 14; + x = (3 << 14) - x; + x = (U >> 1) * x >> 14; + return x; +} + +static const uint16_t gInvSqrt14GuessTable[] = { + 0x4000, 0x3c57, 0x393e, 0x3695, 0x3441, 0x3235, 0x3061, + 0x2ebd, 0x2d41, 0x2be7, 0x2aaa, 0x2987, 0x287a, 0x2780, + 0x2698, 0x25be, 0x24f3, 0x2434, 0x2380, 0x22d6, 0x2235, + 0x219d, 0x210c, 0x2083, 0x2000, 0x1f82, 0x1f0b, 0x1e99, + 0x1e2b, 0x1dc2, 0x1d5d, 0x1cfc, 0x1c9f, 0x1c45, 0x1bee, + 0x1b9b, 0x1b4a, 0x1afc, 0x1ab0, 0x1a67, 0x1a20, 0x19dc, + 0x1999, 0x1959, 0x191a, 0x18dd, 0x18a2, 0x1868, 0x1830, + 0x17fa, 0x17c4, 0x1791, 0x175e, 0x172d, 0x16fd, 0x16ce +}; + +#define BUILD_INVSQRT_TABLEx +#ifdef BUILD_INVSQRT_TABLE +static void build_invsqrt14_guess_table() { + for (int i = 8; i <= 63; i++) { + unsigned x = SkToU16((1 << 28) / SkSqrt32(i << 25)); + printf("0x%x, ", x); + } + printf("\n"); +} +#endif + +static unsigned fast_invsqrt(uint32_t x) { +#ifdef BUILD_INVSQRT_TABLE + unsigned top2 = x >> 25; + SkASSERT(top2 >= 8 && top2 <= 63); + + static bool gOnce; + if (!gOnce) { + build_invsqrt14_guess_table(); + gOnce = true; + } +#endif + + unsigned V = x >> 14; // make V .14 + + unsigned top = x >> 25; + SkASSERT(top >= 8 && top <= 63); + SkASSERT(top - 8 < SK_ARRAY_COUNT(gInvSqrt14GuessTable)); + unsigned U = gInvSqrt14GuessTable[top - 8]; + + U = invsqrt_iter(V, U); + return invsqrt_iter(V, U); +} + +/* We "normalize" x,y to be .14 values (so we can square them and stay 32bits. + Then we Newton-iterate this in .14 space to compute the invser-sqrt, and + scale by it at the end. The .14 space means we can execute our iterations + and stay in 32bits as well, making the multiplies much cheaper than calling + SkFixedMul. +*/ +bool SkPoint::setLength(SkFixed ox, SkFixed oy, SkFixed length) { + if (ox == 0) { + if (oy == 0) { + return false; + } + this->set(0, SkApplySign(length, SkExtractSign(oy))); + return true; + } + if (oy == 0) { + this->set(SkApplySign(length, SkExtractSign(ox)), 0); + return true; + } + + unsigned x = SkAbs32(ox); + unsigned y = SkAbs32(oy); + int zeros = SkCLZ(x | y); + + // make x,y 1.14 values so our fast sqr won't overflow + if (zeros > 17) { + x <<= zeros - 17; + y <<= zeros - 17; + } else { + x >>= 17 - zeros; + y >>= 17 - zeros; + } + SkASSERT((x | y) <= 0x7FFF); + + unsigned invrt = fast_invsqrt(x*x + y*y); + + x = x * invrt >> 12; + y = y * invrt >> 12; + + if (length != SK_Fixed1) { + x = SkFixedMul(x, length); + y = SkFixedMul(y, length); + } + this->set(SkApplySign(x, SkExtractSign(ox)), + SkApplySign(y, SkExtractSign(oy))); + return true; +} +#else +/* + Normalize x,y, and then scale them by length. + + The obvious way to do this would be the following: + S64 tmp1, tmp2; + tmp1.setMul(x,x); + tmp2.setMul(y,y); + tmp1.add(tmp2); + len = tmp1.getSqrt(); + x' = SkFixedDiv(x, len); + y' = SkFixedDiv(y, len); + This is fine, but slower than what we do below. + + The present technique does not compute the starting length, but + rather fiddles with x,y iteratively, all the while checking its + magnitude^2 (avoiding a sqrt). + + We normalize by first shifting x,y so that at least one of them + has bit 31 set (after taking the abs of them). + Then we loop, refining x,y by squaring them and comparing + against a very large 1.0 (1 << 28), and then adding or subtracting + a delta (which itself is reduced by half each time through the loop). + For speed we want the squaring to be with a simple integer mul. To keep + that from overflowing we shift our coordinates down until we are dealing + with at most 15 bits (2^15-1)^2 * 2 says withing 32 bits) + When our square is close to 1.0, we shift x,y down into fixed range. +*/ +bool SkPoint::setLength(SkFixed ox, SkFixed oy, SkFixed length) { + if (ox == 0) { + if (oy == 0) + return false; + this->set(0, SkApplySign(length, SkExtractSign(oy))); + return true; + } + if (oy == 0) { + this->set(SkApplySign(length, SkExtractSign(ox)), 0); + return true; + } + + SkFixed x = SkAbs32(ox); + SkFixed y = SkAbs32(oy); + + // shift x,y so that the greater of them is 15bits (1.14 fixed point) + { + int shift = SkCLZ(x | y); + // make them .30 + x <<= shift - 1; + y <<= shift - 1; + } + + SkFixed dx = x; + SkFixed dy = y; + + for (int i = 0; i < 17; i++) { + dx >>= 1; + dy >>= 1; + + U32 len2 = squarefixed(x) + squarefixed(y); + if (len2 >> 28) { + x -= dx; + y -= dy; + } else { + x += dx; + y += dy; + } + } + x >>= 14; + y >>= 14; + +#ifdef SK_DEBUGx // measure how far we are from unit-length + { + static int gMaxError; + static int gMaxDiff; + + SkFixed len = fixlen(x, y); + int err = len - SK_Fixed1; + err = SkAbs32(err); + + if (err > gMaxError) { + gMaxError = err; + SkDebugf("gMaxError %d\n", err); + } + + float fx = SkAbs32(ox)/65536.0f; + float fy = SkAbs32(oy)/65536.0f; + float mag = sqrtf(fx*fx + fy*fy); + fx /= mag; + fy /= mag; + SkFixed xx = (int)floorf(fx * 65536 + 0.5f); + SkFixed yy = (int)floorf(fy * 65536 + 0.5f); + err = SkMax32(SkAbs32(xx-x), SkAbs32(yy-y)); + if (err > gMaxDiff) { + gMaxDiff = err; + SkDebugf("gMaxDiff %d\n", err); + } + } +#endif + + x = SkApplySign(x, SkExtractSign(ox)); + y = SkApplySign(y, SkExtractSign(oy)); + if (length != SK_Fixed1) { + x = SkFixedMul(x, length); + y = SkFixedMul(y, length); + } + + this->set(x, y); + return true; +} +#endif + +#endif + diff --git a/skia/corecg/SkRect.cpp b/skia/corecg/SkRect.cpp new file mode 100644 index 0000000..f32b27e --- /dev/null +++ b/skia/corecg/SkRect.cpp @@ -0,0 +1,129 @@ +/* libs/corecg/SkRect.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkRect.h" + +void SkIRect::join(int32_t left, int32_t top, int32_t right, int32_t bottom) +{ + // do nothing if the params are empty + if (left >= right || top >= bottom) + return; + + // if we are empty, just assign + if (fLeft >= fRight || fTop >= fBottom) + this->set(left, top, right, bottom); + else + { + if (left < fLeft) fLeft = left; + if (top < fTop) fTop = top; + if (right > fRight) fRight = right; + if (bottom > fBottom) fBottom = bottom; + } +} + +void SkIRect::sort() +{ + if (fLeft > fRight) + SkTSwap<int32_t>(fLeft, fRight); + if (fTop > fBottom) + SkTSwap<int32_t>(fTop, fBottom); +} + +///////////////////////////////////////////////////////////////////////////// + +void SkRect::sort() +{ + if (fLeft > fRight) + SkTSwap<SkScalar>(fLeft, fRight); + if (fTop > fBottom) + SkTSwap<SkScalar>(fTop, fBottom); +} + +void SkRect::toQuad(SkPoint quad[4]) const +{ + SkASSERT(quad); + + quad[0].set(fLeft, fTop); + quad[1].set(fRight, fTop); + quad[2].set(fRight, fBottom); + quad[3].set(fLeft, fBottom); +} + +void SkRect::set(const SkPoint pts[], int count) +{ + SkASSERT((pts && count > 0) || count == 0); + + if (count <= 0) + memset(this, 0, sizeof(SkRect)); + else + { + SkScalar l, t, r, b; + + l = r = pts[0].fX; + t = b = pts[0].fY; + + for (int i = 1; i < count; i++) + { + SkScalar x = pts[i].fX; + SkScalar y = pts[i].fY; + + if (x < l) l = x; else if (x > r) r = x; + if (y < t) t = y; else if (y > b) b = y; + } + this->set(l, t, r, b); + } +} + +bool SkRect::intersect(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) +{ + if (left < right && top < bottom && !this->isEmpty() && // check for empties + fLeft < right && left < fRight && fTop < bottom && top < fBottom) + { + if (fLeft < left) fLeft = left; + if (fTop < top) fTop = top; + if (fRight > right) fRight = right; + if (fBottom > bottom) fBottom = bottom; + return true; + } + return false; +} + +bool SkRect::intersect(const SkRect& r) +{ + SkASSERT(&r); + return this->intersect(r.fLeft, r.fTop, r.fRight, r.fBottom); +} + +void SkRect::join(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) +{ + // do nothing if the params are empty + if (left >= right || top >= bottom) + return; + + // if we are empty, just assign + if (fLeft >= fRight || fTop >= fBottom) + this->set(left, top, right, bottom); + else + { + if (left < fLeft) fLeft = left; + if (top < fTop) fTop = top; + if (right > fRight) fRight = right; + if (bottom > fBottom) fBottom = bottom; + } +} + + diff --git a/skia/corecg/SkRegion.cpp b/skia/corecg/SkRegion.cpp new file mode 100644 index 0000000..a0860b6e --- /dev/null +++ b/skia/corecg/SkRegion.cpp @@ -0,0 +1,1248 @@ +/* libs/corecg/SkRegion.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkRegionPriv.h" +#include "SkTemplates.h" +#include "SkThread.h" + +SkDEBUGCODE(int32_t gRgnAllocCounter;) + +///////////////////////////////////////////////////////////////////////////////////////////////// + +/* Pass in a scanline, beginning with the Left value of the pair (i.e. not the Y beginning) +*/ +static SkRegion::RunType* skip_scanline(const SkRegion::RunType runs[]) +{ + while (runs[0] != SkRegion::kRunTypeSentinel) + { + SkASSERT(runs[0] < runs[1]); // valid span + runs += 2; + } + return (SkRegion::RunType*)(runs + 1); // return past the X-sentinel +} + +static SkRegion::RunType* find_y(const SkRegion::RunType runs[], int y) +{ + int top = *runs++; + if (top <= y) + { + for (;;) + { + int bot = *runs++; + if (bot > y) + { + if (bot == SkRegion::kRunTypeSentinel || *runs == SkRegion::kRunTypeSentinel) + break; + return (SkRegion::RunType*)runs; + } + top = bot; + runs = skip_scanline(runs); + } + } + return NULL; +} + +// returns true if runs are just a rect +bool SkRegion::ComputeRunBounds(const SkRegion::RunType runs[], int count, SkIRect* bounds) +{ + assert_sentinel(runs[0], false); // top + + if (count == kRectRegionRuns) + { + assert_sentinel(runs[1], false); // bottom + assert_sentinel(runs[2], false); // left + assert_sentinel(runs[3], false); // right + assert_sentinel(runs[4], true); + assert_sentinel(runs[5], true); + + SkASSERT(runs[0] < runs[1]); // valid height + SkASSERT(runs[2] < runs[3]); // valid width + + bounds->set(runs[2], runs[0], runs[3], runs[1]); + return true; + } + + int left = SK_MaxS32; + int rite = SK_MinS32; + int bot; + + bounds->fTop = *runs++; + do { + bot = *runs++; + if (*runs < SkRegion::kRunTypeSentinel) + { + if (left > *runs) + left = *runs; + runs = skip_scanline(runs); + if (rite < runs[-2]) + rite = runs[-2]; + } + else + runs += 1; // skip X-sentinel + } while (runs[0] < SkRegion::kRunTypeSentinel); + bounds->fLeft = left; + bounds->fRight = rite; + bounds->fBottom = bot; + return false; +} + +////////////////////////////////////////////////////////////////////////// + +SkRegion::SkRegion() +{ + fBounds.set(0, 0, 0, 0); + fRunHead = SkRegion_gEmptyRunHeadPtr; +} + +SkRegion::SkRegion(const SkRegion& src) +{ + fRunHead = SkRegion_gEmptyRunHeadPtr; // just need a value that won't trigger sk_free(fRunHead) + this->setRegion(src); +} + +SkRegion::SkRegion(const SkIRect& rect) +{ + fRunHead = SkRegion_gEmptyRunHeadPtr; // just need a value that won't trigger sk_free(fRunHead) + this->setRect(rect); +} + +SkRegion::~SkRegion() +{ + this->freeRuns(); +} + +void SkRegion::freeRuns() +{ + if (fRunHead->isComplex()) + { + SkASSERT(fRunHead->fRefCnt >= 1); + if (sk_atomic_dec(&fRunHead->fRefCnt) == 1) + { + //SkASSERT(gRgnAllocCounter > 0); + //SkDEBUGCODE(sk_atomic_dec(&gRgnAllocCounter)); + //SkDEBUGF(("************** gRgnAllocCounter::free %d\n", gRgnAllocCounter)); + sk_free(fRunHead); + } + } +} + +void SkRegion::allocateRuns(int count) +{ + fRunHead = RunHead::Alloc(count); +} + +SkRegion& SkRegion::operator=(const SkRegion& src) +{ + (void)this->setRegion(src); + return *this; +} + +void SkRegion::swap(SkRegion& other) +{ + SkTSwap<SkIRect>(fBounds, other.fBounds); + SkTSwap<RunHead*>(fRunHead, other.fRunHead); +} + +bool SkRegion::setEmpty() +{ + this->freeRuns(); + fBounds.set(0, 0, 0, 0); + fRunHead = SkRegion_gEmptyRunHeadPtr; + return false; +} + +bool SkRegion::setRect(int32_t left, int32_t top, int32_t right, int32_t bottom) +{ + if (left >= right || top >= bottom) + return this->setEmpty(); + + this->freeRuns(); + fBounds.set(left, top, right, bottom); + fRunHead = SkRegion_gRectRunHeadPtr; + return true; +} + +bool SkRegion::setRect(const SkIRect& r) +{ + return this->setRect(r.fLeft, r.fTop, r.fRight, r.fBottom); +} + +bool SkRegion::setRegion(const SkRegion& src) +{ + if (this != &src) + { + this->freeRuns(); + + fBounds = src.fBounds; + fRunHead = src.fRunHead; + if (fRunHead->isComplex()) + sk_atomic_inc(&fRunHead->fRefCnt); + } + return fRunHead != SkRegion_gEmptyRunHeadPtr; +} + +bool SkRegion::op(const SkIRect& rect, const SkRegion& rgn, Op op) +{ + SkRegion tmp(rect); + + return this->op(tmp, rgn, op); +} + +bool SkRegion::op(const SkRegion& rgn, const SkIRect& rect, Op op) +{ + SkRegion tmp(rect); + + return this->op(rgn, tmp, op); +} + +////////////////////////////////////////////////////////////////////////////////////// + +int SkRegion::count_runtype_values(int* itop, int* ibot) const +{ + if (this == NULL) + { + *itop = SK_MinS32; + *ibot = SK_MaxS32; + return 0; + } + + int maxT; + + if (this->isRect()) + maxT = 2; + else + { + SkASSERT(this->isComplex()); + // skip the top + const RunType* runs = fRunHead->readonly_runs() + 1; + maxT = 0; + + do { + const RunType* next = skip_scanline(runs + 1); + SkASSERT(next > runs); + int T = (int)(next - runs - 1); + if (maxT < T) + maxT = T; + runs = next; + } while (runs[0] < SkRegion::kRunTypeSentinel); + } + *itop = fBounds.fTop; + *ibot = fBounds.fBottom; + return maxT; +} + +bool SkRegion::setRuns(RunType runs[], int count) +{ + SkDEBUGCODE(this->validate();) + SkASSERT(count > 0); + + if (count <= 2) + { + // SkDEBUGF(("setRuns: empty\n")); + assert_sentinel(runs[count-1], true); + return this->setEmpty(); + } + + // trim off any empty spans from the top and bottom + // weird I should need this, perhaps op() could be smarter... + if (count > kRectRegionRuns) + { + RunType* stop = runs + count; + assert_sentinel(runs[0], false); // top + assert_sentinel(runs[1], false); // bottom + if (runs[2] == SkRegion::kRunTypeSentinel) // should be first left... + { + runs += 2; // skip empty initial span + runs[0] = runs[-1]; // set new top to prev bottom + assert_sentinel(runs[1], false); // bot: a sentinal would mean two in a row + assert_sentinel(runs[2], false); // left + assert_sentinel(runs[3], false); // right + } + + // now check for a trailing empty span + assert_sentinel(stop[-1], true); + assert_sentinel(stop[-2], true); + assert_sentinel(stop[-3], false); // should be last right + if (stop[-4] == SkRegion::kRunTypeSentinel) // eek, stop[-3] was a bottom with no x-runs + { + stop[-3] = SkRegion::kRunTypeSentinel; // kill empty last span + stop -= 2; + assert_sentinel(stop[-1], true); + assert_sentinel(stop[-2], true); + assert_sentinel(stop[-3], false); + assert_sentinel(stop[-4], false); + assert_sentinel(stop[-5], false); + } + count = (int)(stop - runs); + } + + SkASSERT(count >= kRectRegionRuns); + + if (ComputeRunBounds(runs, count, &fBounds)) + { + // SkDEBUGF(("setRuns: rect[%d %d %d %d]\n", fBounds.fLeft, fBounds.fTop, fBounds.fRight, fBounds.fBottom)); + return this->setRect(fBounds); + } + + // if we get here, we need to become a complex region + + if (!fRunHead->isComplex() || fRunHead->fRunCount != count) + { +#ifdef SK_DEBUGx + SkDebugf("setRuns: rgn ["); + { + const RunType* r = runs; + + SkDebugf(" top: %d\n", *r++); + while (*r < SkRegion::kRunTypeSentinel) + { + SkDebugf(" bottom: %d", *r++); + while (*r < SkRegion::kRunTypeSentinel) + { + SkDebugf(" [%d %d]", r[0], r[1]); + r += 2; + } + SkDebugf("\n"); + } + } +#endif + this->freeRuns(); + this->allocateRuns(count); + } + + // must call this before we can write directly into runs() + // in case we are sharing the buffer with another region (copy on write) + fRunHead = fRunHead->ensureWritable(); + memcpy(fRunHead->writable_runs(), runs, count * sizeof(RunType)); + + SkDEBUGCODE(this->validate();) + + return true; +} + +void SkRegion::BuildRectRuns(const SkIRect& bounds, + RunType runs[kRectRegionRuns]) +{ + runs[0] = bounds.fTop; + runs[1] = bounds.fBottom; + runs[2] = bounds.fLeft; + runs[3] = bounds.fRight; + runs[4] = kRunTypeSentinel; + runs[5] = kRunTypeSentinel; +} + +static SkRegion::RunType* find_scanline(const SkRegion::RunType runs[], int y) +{ + SkASSERT(y >= runs[0]); // if this fails, we didn't do a quick check on the boudns + + runs += 1; // skip top-Y + for (;;) + { + if (runs[0] == SkRegion::kRunTypeSentinel) + break; + if (y < runs[0]) + return (SkRegion::RunType*)&runs[1]; + runs = skip_scanline(runs + 1); // skip the Y value before calling + } + return NULL; +} + +bool SkRegion::contains(int x, int y) const +{ + if (!fBounds.contains(x, y)) + return false; + + if (this->isRect()) + return true; + + SkASSERT(this->isComplex()); + const RunType* runs = find_scanline(fRunHead->readonly_runs(), y); + + if (runs) + { for (;;) + { if (x < runs[0]) + break; + if (x < runs[1]) + return true; + runs += 2; + } + } + return false; +} + +bool SkRegion::contains(const SkIRect& r) const +{ + SkRegion tmp(r); + + return this->contains(tmp); +} + +bool SkRegion::contains(const SkRegion& rgn) const +{ + if (this->isEmpty() || rgn.isEmpty() || !fBounds.contains(rgn.fBounds)) + return false; + + if (this->isRect()) + return true; + + SkRegion tmp; + + tmp.op(*this, rgn, kUnion_Op); + return tmp == *this; +} + +const SkRegion::RunType* SkRegion::getRuns(RunType tmpStorage[], int* count) const +{ + SkASSERT(tmpStorage && count); + const RunType* runs = tmpStorage; + + if (this->isEmpty()) + { + tmpStorage[0] = kRunTypeSentinel; + *count = 1; + } + else if (this->isRect()) + { + BuildRectRuns(fBounds, tmpStorage); + *count = kRectRegionRuns; + } + else + { + *count = fRunHead->fRunCount; + runs = fRunHead->readonly_runs(); + } + return runs; +} + +///////////////////////////////////////////////////////////////////////////////////// + +int operator==(const SkRegion& a, const SkRegion& b) +{ + SkDEBUGCODE(a.validate();) + SkDEBUGCODE(b.validate();) + + if (&a == &b) + return true; + if (a.fBounds != b.fBounds) + return false; + + const SkRegion::RunHead* ah = a.fRunHead; + const SkRegion::RunHead* bh = b.fRunHead; + + // this catches empties and rects being equal + if (ah == bh) + return true; + + // now we insist that both are complex (but different ptrs) + if (!ah->isComplex() || !bh->isComplex()) + return false; + + return ah->fRunCount == bh->fRunCount && + !memcmp(ah->readonly_runs(), bh->readonly_runs(), + ah->fRunCount * sizeof(SkRegion::RunType)); +} + +void SkRegion::translate(int dx, int dy, SkRegion* dst) const +{ + SkDEBUGCODE(this->validate();) + + if (NULL == dst) + return; + + if (this->isEmpty()) + dst->setEmpty(); + else if (this->isRect()) + dst->setRect(fBounds.fLeft + dx, fBounds.fTop + dy, + fBounds.fRight + dx, fBounds.fBottom + dy); + else + { + if (this == dst) + { + dst->fRunHead = dst->fRunHead->ensureWritable(); + } + else + { + SkRegion tmp; + tmp.allocateRuns(fRunHead->fRunCount); + tmp.fBounds = fBounds; + dst->swap(tmp); + } + + dst->fBounds.offset(dx, dy); + + const RunType* sruns = fRunHead->readonly_runs(); + RunType* druns = dst->fRunHead->writable_runs(); + + *druns++ = (SkRegion::RunType)(*sruns++ + dy); // top + for (;;) + { + int bottom = *sruns++; + if (bottom == kRunTypeSentinel) + break; + *druns++ = (SkRegion::RunType)(bottom + dy); // bottom; + for (;;) + { + int x = *sruns++; + if (x == kRunTypeSentinel) + break; + *druns++ = (SkRegion::RunType)(x + dx); + *druns++ = (SkRegion::RunType)(*sruns++ + dx); + } + *druns++ = kRunTypeSentinel; // x sentinel + } + *druns++ = kRunTypeSentinel; // y sentinel + + SkASSERT(sruns - fRunHead->readonly_runs() == fRunHead->fRunCount); + SkASSERT(druns - dst->fRunHead->readonly_runs() == dst->fRunHead->fRunCount); + } + + SkDEBUGCODE(this->validate();) +} + +///////////////////////////////////////////////////////////////////////////////////// + +#if defined _WIN32 && _MSC_VER >= 1300 // disable warning : local variable used without having been initialized +#pragma warning ( push ) +#pragma warning ( disable : 4701 ) +#endif + +#ifdef SK_DEBUG +static void assert_valid_pair(int left, int rite) +{ + SkASSERT(left == SkRegion::kRunTypeSentinel || left < rite); +} +#else + #define assert_valid_pair(left, rite) +#endif + +struct spanRec { + const SkRegion::RunType* fA_runs; + const SkRegion::RunType* fB_runs; + int fA_left, fA_rite, fB_left, fB_rite; + int fLeft, fRite, fInside; + + void init(const SkRegion::RunType a_runs[], const SkRegion::RunType b_runs[]) + { + fA_left = *a_runs++; + fA_rite = *a_runs++; + fB_left = *b_runs++; + fB_rite = *b_runs++; + + fA_runs = a_runs; + fB_runs = b_runs; + } + + bool done() const + { + SkASSERT(fA_left <= SkRegion::kRunTypeSentinel); + SkASSERT(fB_left <= SkRegion::kRunTypeSentinel); + return fA_left == SkRegion::kRunTypeSentinel && fB_left == SkRegion::kRunTypeSentinel; + } + + void next() + { + assert_valid_pair(fA_left, fA_rite); + assert_valid_pair(fB_left, fB_rite); + + int inside, left, rite SK_INIT_TO_AVOID_WARNING; + bool a_flush = false; + bool b_flush = false; + + int a_left = fA_left; + int a_rite = fA_rite; + int b_left = fB_left; + int b_rite = fB_rite; + + if (a_left < b_left) + { + inside = 1; + left = a_left; + if (a_rite <= b_left) // [...] <...> + { + rite = a_rite; + a_flush = true; + } + else // [...<..]...> or [...<...>...] + rite = a_left = b_left; + } + else if (b_left < a_left) + { + inside = 2; + left = b_left; + if (b_rite <= a_left) // [...] <...> + { + rite = b_rite; + b_flush = true; + } + else // [...<..]...> or [...<...>...] + rite = b_left = a_left; + } + else // a_left == b_left + { + inside = 3; + left = a_left; // or b_left + if (a_rite <= b_rite) + { + rite = b_left = a_rite; + a_flush = true; + } + if (b_rite <= a_rite) + { + rite = a_left = b_rite; + b_flush = true; + } + } + + if (a_flush) + { + a_left = *fA_runs++; + a_rite = *fA_runs++; + } + if (b_flush) + { + b_left = *fB_runs++; + b_rite = *fB_runs++; + } + + SkASSERT(left <= rite); + + // now update our state + fA_left = a_left; + fA_rite = a_rite; + fB_left = b_left; + fB_rite = b_rite; + + fLeft = left; + fRite = rite; + fInside = inside; + } +}; + +static SkRegion::RunType* operate_on_span(const SkRegion::RunType a_runs[], + const SkRegion::RunType b_runs[], + SkRegion::RunType dst[], + int min, int max) +{ + spanRec rec; + bool firstInterval = true; + + rec.init(a_runs, b_runs); + + while (!rec.done()) + { + rec.next(); + + int left = rec.fLeft; + int rite = rec.fRite; + + // add left,rite to our dst buffer (checking for coincidence + if ((unsigned)(rec.fInside - min) <= (unsigned)(max - min) && + left < rite) // skip if equal + { + if (firstInterval || dst[-1] < left) + { + *dst++ = (SkRegion::RunType)(left); + *dst++ = (SkRegion::RunType)(rite); + firstInterval = false; + } + else // update the right edge + dst[-1] = (SkRegion::RunType)(rite); + } + } + + *dst++ = SkRegion::kRunTypeSentinel; + return dst; +} + +#if defined _WIN32 && _MSC_VER >= 1300 +#pragma warning ( pop ) +#endif + +static const struct { + uint8_t fMin; + uint8_t fMax; +} gOpMinMax[] = { + { 1, 1 }, // Difference + { 3, 3 }, // Intersection + { 1, 3 }, // Union + { 1, 2 } // XOR +}; + +class RgnOper { +public: + RgnOper(int top, SkRegion::RunType dst[], SkRegion::Op op) + { + // need to ensure that the op enum lines up with our minmax array + SkASSERT(SkRegion::kDifference_Op == 0); + SkASSERT(SkRegion::kIntersect_Op == 1); + SkASSERT(SkRegion::kUnion_Op == 2); + SkASSERT(SkRegion::kXOR_Op == 3); + SkASSERT((unsigned)op <= 3); + + fStartDst = dst; + fPrevDst = dst + 1; + fPrevLen = 0; // will never match a length from operate_on_span + fTop = (SkRegion::RunType)(top); // just a first guess, we might update this + + fMin = gOpMinMax[op].fMin; + fMax = gOpMinMax[op].fMax; + } + + void addSpan(int bottom, const SkRegion::RunType a_runs[], const SkRegion::RunType b_runs[]) + { + SkRegion::RunType* start = fPrevDst + fPrevLen + 1; // skip X values and slot for the next Y + SkRegion::RunType* stop = operate_on_span(a_runs, b_runs, start, fMin, fMax); + size_t len = stop - start; + + if (fPrevLen == len && !memcmp(fPrevDst, start, len * sizeof(SkRegion::RunType))) // update Y value + fPrevDst[-1] = (SkRegion::RunType)(bottom); + else // accept the new span + { + if (len == 1 && fPrevLen == 0) { + fTop = (SkRegion::RunType)(bottom); // just update our bottom + } else { + start[-1] = (SkRegion::RunType)(bottom); + fPrevDst = start; + fPrevLen = len; + } + } + } + + int flush() + { + fStartDst[0] = fTop; + fPrevDst[fPrevLen] = SkRegion::kRunTypeSentinel; + return (int)(fPrevDst - fStartDst + fPrevLen + 1); + } + + uint8_t fMin, fMax; + +private: + SkRegion::RunType* fStartDst; + SkRegion::RunType* fPrevDst; + size_t fPrevLen; + SkRegion::RunType fTop; +}; + +static int operate( const SkRegion::RunType a_runs[], + const SkRegion::RunType b_runs[], + SkRegion::RunType dst[], + SkRegion::Op op) +{ + const SkRegion::RunType sentinel = SkRegion::kRunTypeSentinel; + + int a_top = *a_runs++; + int a_bot = *a_runs++; + int b_top = *b_runs++; + int b_bot = *b_runs++; + + assert_sentinel(a_top, false); + assert_sentinel(a_bot, false); + assert_sentinel(b_top, false); + assert_sentinel(b_bot, false); + + RgnOper oper(SkMin32(a_top, b_top), dst, op); + + bool firstInterval = true; + int prevBot = SkRegion::kRunTypeSentinel; // so we fail the first test + + while (a_bot < SkRegion::kRunTypeSentinel || b_bot < SkRegion::kRunTypeSentinel) + { + int top, bot SK_INIT_TO_AVOID_WARNING; + const SkRegion::RunType* run0 = &sentinel; + const SkRegion::RunType* run1 = &sentinel; + bool a_flush = false; + bool b_flush = false; + int inside; + + if (a_top < b_top) + { + inside = 1; + top = a_top; + run0 = a_runs; + if (a_bot <= b_top) // [...] <...> + { + bot = a_bot; + a_flush = true; + } + else // [...<..]...> or [...<...>...] + bot = a_top = b_top; + } + else if (b_top < a_top) + { + inside = 2; + top = b_top; + run1 = b_runs; + if (b_bot <= a_top) // [...] <...> + { + bot = b_bot; + b_flush = true; + } + else // [...<..]...> or [...<...>...] + bot = b_top = a_top; + } + else // a_top == b_top + { + inside = 3; + top = a_top; // or b_top + run0 = a_runs; + run1 = b_runs; + if (a_bot <= b_bot) + { + bot = b_top = a_bot; + a_flush = true; + } + if (b_bot <= a_bot) + { + bot = a_top = b_bot; + b_flush = true; + } + } + + if (top > prevBot) + oper.addSpan(top, &sentinel, &sentinel); + +// if ((unsigned)(inside - oper.fMin) <= (unsigned)(oper.fMax - oper.fMin)) + { + oper.addSpan(bot, run0, run1); + firstInterval = false; + } + + if (a_flush) + { + a_runs = skip_scanline(a_runs); + a_top = a_bot; + a_bot = *a_runs++; + if (a_bot == SkRegion::kRunTypeSentinel) + a_top = a_bot; + } + if (b_flush) + { + b_runs = skip_scanline(b_runs); + b_top = b_bot; + b_bot = *b_runs++; + if (b_bot == SkRegion::kRunTypeSentinel) + b_top = b_bot; + } + + prevBot = bot; + } + return oper.flush(); +} + +bool SkRegion::op(const SkRegion& rgnaOrig, const SkRegion& rgnbOrig, Op op) +{ + SkDEBUGCODE(this->validate();) + + SkASSERT((unsigned)op < kOpCount); + + if (kReplace_Op == op) + return this->set(rgnbOrig); + + // swith to using pointers, so we can swap them as needed + const SkRegion* rgna = &rgnaOrig; + const SkRegion* rgnb = &rgnbOrig; + // after this point, do not refer to rgnaOrig or rgnbOrig!!! + + // collaps difference and reverse-difference into just difference + if (kReverseDifference_Op == op) + { + SkTSwap<const SkRegion*>(rgna, rgnb); + op = kDifference_Op; + } + + SkIRect bounds; + bool a_empty = rgna->isEmpty(); + bool b_empty = rgnb->isEmpty(); + bool a_rect = rgna->isRect(); + bool b_rect = rgnb->isRect(); + + switch (op) { + case kDifference_Op: + if (a_empty) + return this->setEmpty(); + if (b_empty || !SkIRect::Intersects(rgna->fBounds, rgnb->fBounds)) + return this->setRegion(*rgna); + break; + + case kIntersect_Op: + if ((a_empty | b_empty) + || !bounds.intersect(rgna->fBounds, rgnb->fBounds)) + return this->setEmpty(); + if (a_rect & b_rect) + return this->setRect(bounds); + break; + + case kUnion_Op: + if (a_empty) + return this->setRegion(*rgnb); + if (b_empty) + return this->setRegion(*rgna); + if (a_rect && rgna->fBounds.contains(rgnb->fBounds)) + return this->setRegion(*rgna); + if (b_rect && rgnb->fBounds.contains(rgna->fBounds)) + return this->setRegion(*rgnb); + break; + + case kXOR_Op: + if (a_empty) + return this->setRegion(*rgnb); + if (b_empty) + return this->setRegion(*rgna); + break; + default: + SkASSERT(!"unknown region op"); + return !this->isEmpty(); + } + + RunType tmpA[kRectRegionRuns]; + RunType tmpB[kRectRegionRuns]; + + int a_count, b_count; + const RunType* a_runs = rgna->getRuns(tmpA, &a_count); + const RunType* b_runs = rgnb->getRuns(tmpB, &b_count); + + int dstCount = 3 * SkMax32(a_count, b_count); + SkAutoSTMalloc<32, RunType> array(dstCount); + + int count = operate(a_runs, b_runs, array.get(), op); + SkASSERT(count <= dstCount); + return this->setRuns(array.get(), count); +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "SkBuffer.h" + +uint32_t SkRegion::flatten(void* storage) const { + if (NULL == storage) { + uint32_t size = sizeof(int32_t); // -1 (empty), 0 (rect), runCount + if (!this->isEmpty()) { + size += sizeof(fBounds); + if (this->isComplex()) { + size += fRunHead->fRunCount * sizeof(RunType); + } + } + return size; + } + + SkWBuffer buffer(storage); + + if (this->isEmpty()) { + buffer.write32(-1); + } else { + bool isRect = this->isRect(); + + buffer.write32(isRect ? 0 : fRunHead->fRunCount); + buffer.write(&fBounds, sizeof(fBounds)); + + if (!isRect) { + buffer.write(fRunHead->readonly_runs(), + fRunHead->fRunCount * sizeof(RunType)); + } + } + return buffer.pos(); +} + +uint32_t SkRegion::unflatten(const void* storage) { + SkRBuffer buffer(storage); + SkRegion tmp; + int32_t count; + + count = buffer.readS32(); + if (count >= 0) { + buffer.read(&tmp.fBounds, sizeof(tmp.fBounds)); + if (count == 0) { + tmp.fRunHead = SkRegion_gRectRunHeadPtr; + } else { + tmp.allocateRuns(count); + buffer.read(tmp.fRunHead->writable_runs(), count * sizeof(RunType)); + } + } + this->swap(tmp); + return buffer.pos(); +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_DEBUG + +static const SkRegion::RunType* validate_line(const SkRegion::RunType run[], const SkIRect& bounds) +{ + // *run is the bottom of the current span + SkASSERT(*run > bounds.fTop); + SkASSERT(*run <= bounds.fBottom); + run += 1; + + // check for empty span + if (*run != SkRegion::kRunTypeSentinel) + { + int prevRite = bounds.fLeft - 1; + do { + int left = *run++; + int rite = *run++; + SkASSERT(left < rite); + SkASSERT(left > prevRite); + SkASSERT(rite <= bounds.fRight); + prevRite = rite; + } while (*run < SkRegion::kRunTypeSentinel); + } + return run + 1; // skip sentinel +} + +void SkRegion::validate() const +{ + if (this->isEmpty()) + { + // check for explicit empty (the zero rect), so we can compare rects to know when + // two regions are equal (i.e. emptyRectA == emptyRectB) + // this is stricter than just asserting fBounds.isEmpty() + SkASSERT(fBounds.fLeft == 0 && fBounds.fTop == 0 && fBounds.fRight == 0 && fBounds.fBottom == 0); + } + else + { + SkASSERT(!fBounds.isEmpty()); + if (!this->isRect()) + { + SkASSERT(fRunHead->fRefCnt >= 1); + SkASSERT(fRunHead->fRunCount >= kRectRegionRuns); + + const RunType* run = fRunHead->readonly_runs(); + const RunType* stop = run + fRunHead->fRunCount; + + // check that our bounds match our runs + { + SkIRect bounds; + bool isARect = ComputeRunBounds(run, stop - run, &bounds); + SkASSERT(!isARect); + SkASSERT(bounds == fBounds); + } + + SkASSERT(*run == fBounds.fTop); + run++; + do { + run = validate_line(run, fBounds); + } while (*run < kRunTypeSentinel); + SkASSERT(run + 1 == stop); + } + } +} + +void SkRegion::dump() const +{ + if (this->isEmpty()) + SkDebugf(" rgn: empty\n"); + else + { + SkDebugf(" rgn: [%d %d %d %d]", fBounds.fLeft, fBounds.fTop, fBounds.fRight, fBounds.fBottom); + if (this->isComplex()) + { + const RunType* runs = fRunHead->readonly_runs(); + for (int i = 0; i < fRunHead->fRunCount; i++) + SkDebugf(" %d", runs[i]); + } + SkDebugf("\n"); + } +} + +#endif + +///////////////////////////////////////////////////////////////////////////////////// + +SkRegion::Iterator::Iterator(const SkRegion& rgn) { + this->reset(rgn); +} + +bool SkRegion::Iterator::rewind() { + if (fRgn) { + this->reset(*fRgn); + return true; + } + return false; +} + +void SkRegion::Iterator::reset(const SkRegion& rgn) { + fRgn = &rgn; + if (rgn.isEmpty()) { + fDone = true; + } else { + fDone = false; + if (rgn.isRect()) { + fRect = rgn.fBounds; + fRuns = NULL; + } else { + fRuns = rgn.fRunHead->readonly_runs(); + fRect.set(fRuns[2], fRuns[0], fRuns[3], fRuns[1]); + fRuns += 4; + } + } +} + +void SkRegion::Iterator::next() { + if (fDone) { + return; + } + + if (fRuns == NULL) { // rect case + fDone = true; + return; + } + + const RunType* runs = fRuns; + + if (runs[0] < kRunTypeSentinel) { // valid X value + fRect.fLeft = runs[0]; + fRect.fRight = runs[1]; + runs += 2; + } else { // we're at the end of a line + runs += 1; + if (runs[0] < kRunTypeSentinel) { // valid Y value + if (runs[1] == kRunTypeSentinel) { // empty line + fRect.fTop = runs[0]; + runs += 2; + } else { + fRect.fTop = fRect.fBottom; + } + + fRect.fBottom = runs[0]; + assert_sentinel(runs[1], false); + fRect.fLeft = runs[1]; + fRect.fRight = runs[2]; + runs += 3; + } else { // end of rgn + fDone = true; + } + } + fRuns = runs; +} + +SkRegion::Cliperator::Cliperator(const SkRegion& rgn, const SkIRect& clip) + : fIter(rgn), fClip(clip), fDone(true) { + const SkIRect& r = fIter.rect(); + + while (!fIter.done()) { + if (r.fTop >= clip.fBottom) { + break; + } + if (fRect.intersect(clip, r)) { + fDone = false; + break; + } + fIter.next(); + } +} + +void SkRegion::Cliperator::next() { + if (fDone) { + return; + } + + const SkIRect& r = fIter.rect(); + + fDone = true; + fIter.next(); + while (!fIter.done()) { + if (r.fTop >= fClip.fBottom) { + break; + } + if (fRect.intersect(fClip, r)) { + fDone = false; + break; + } + fIter.next(); + } +} + +////////////////////////////////////////////////////////////////////// + +SkRegion::Spanerator::Spanerator(const SkRegion& rgn, int y, int left, int right) +{ + SkDEBUGCODE(rgn.validate();) + + const SkIRect& r = rgn.getBounds(); + + fDone = true; + if (!rgn.isEmpty() && y >= r.fTop && y < r.fBottom && right > r.fLeft && left < r.fRight) + { + if (rgn.isRect()) + { + if (left < r.fLeft) + left = r.fLeft; + if (right > r.fRight) + right = r.fRight; + + fLeft = left; + fRight = right; + fRuns = NULL; // means we're a rect, not a rgn + fDone = false; + } + else + { + const SkRegion::RunType* runs = find_y(rgn.fRunHead->readonly_runs(), y); + if (runs) + { + for (;;) + { + if (runs[0] >= right) // runs[0..1] is to the right of the span, so we're done + break; + if (runs[1] <= left) // runs[0..1] is to the left of the span, so continue + { + runs += 2; + continue; + } + // runs[0..1] intersects the span + fRuns = runs; + fLeft = left; + fRight = right; + fDone = false; + break; + } + } + } + } +} + +bool SkRegion::Spanerator::next(int* left, int* right) +{ + if (fDone) return false; + + if (fRuns == NULL) // we're a rect + { + fDone = true; // ok, now we're done + if (left) *left = fLeft; + if (right) *right = fRight; + return true; // this interval is legal + } + + const SkRegion::RunType* runs = fRuns; + + if (runs[0] >= fRight) + { + fDone = true; + return false; + } + + SkASSERT(runs[1] > fLeft); + + if (left) + *left = SkMax32(fLeft, runs[0]); + if (right) + *right = SkMin32(fRight, runs[1]); + fRuns = runs + 2; + return true; +} + diff --git a/skia/corecg/SkRegionPriv.h b/skia/corecg/SkRegionPriv.h new file mode 100644 index 0000000..80e2e83 --- /dev/null +++ b/skia/corecg/SkRegionPriv.h @@ -0,0 +1,89 @@ +/* libs/corecg/SkRegionPriv.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkRegionPriv_DEFINED +#define SkRegionPriv_DEFINED + +#include "SkRegion.h" +#include "SkThread.h" + +#define assert_sentinel(value, isSentinel) \ + SkASSERT(((value) == SkRegion::kRunTypeSentinel) == isSentinel) + +//SkDEBUGCODE(extern int32_t gRgnAllocCounter;) + +struct SkRegion::RunHead { + int32_t fRefCnt; + int32_t fRunCount; + + static RunHead* Alloc(int count) + { + //SkDEBUGCODE(sk_atomic_inc(&gRgnAllocCounter);) + //SkDEBUGF(("************** gRgnAllocCounter::alloc %d\n", gRgnAllocCounter)); + + SkASSERT(count >= SkRegion::kRectRegionRuns); + + RunHead* head = (RunHead*)sk_malloc_throw(sizeof(RunHead) + count * sizeof(RunType)); + head->fRefCnt = 1; + head->fRunCount = count; + return head; + } + + bool isComplex() const + { + return this != SkRegion_gEmptyRunHeadPtr && this != SkRegion_gRectRunHeadPtr; + } + + SkRegion::RunType* writable_runs() + { + SkASSERT(this->isComplex()); + SkASSERT(fRefCnt == 1); + return (SkRegion::RunType*)(this + 1); + } + const SkRegion::RunType* readonly_runs() const + { + SkASSERT(this->isComplex()); + return (const SkRegion::RunType*)(this + 1); + } + + RunHead* ensureWritable() + { + SkASSERT(this->isComplex()); + + RunHead* writable = this; + if (fRefCnt > 1) + { + // We need to alloc & copy the current region before we call + // sk_atomic_dec because it could be freed in the meantime, + // otherwise. + writable = Alloc(fRunCount); + memcpy(writable->writable_runs(), this->readonly_runs(), + fRunCount * sizeof(RunType)); + + // fRefCount might have changed since we last checked. + // If we own the last reference at this point, we need to + // free the memory. + if (sk_atomic_dec(&fRefCnt) == 1) + { + sk_free(this); + } + } + return writable; + } +}; + +#endif diff --git a/skia/corecg/SkSinTable.h b/skia/corecg/SkSinTable.h new file mode 100644 index 0000000..eb3a31c --- /dev/null +++ b/skia/corecg/SkSinTable.h @@ -0,0 +1,285 @@ +/* libs/corecg/SkSinTable.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkSinTable_DEFINED +#define SkSinTable_DEFINED + +#include "SkTypes.h" + +/* Fixed point values (low 16 bits) of sin(radians) for + radians in [0...PI/2) +*/ +static const uint16_t gSkSinTable[256] = { + 0x0000, + 0x0192, + 0x0324, + 0x04B6, + 0x0648, + 0x07DA, + 0x096C, + 0x0AFE, + 0x0C8F, + 0x0E21, + 0x0FB2, + 0x1144, + 0x12D5, + 0x1466, + 0x15F6, + 0x1787, + 0x1917, + 0x1AA7, + 0x1C37, + 0x1DC7, + 0x1F56, + 0x20E5, + 0x2273, + 0x2402, + 0x2590, + 0x271D, + 0x28AA, + 0x2A37, + 0x2BC4, + 0x2D50, + 0x2EDB, + 0x3066, + 0x31F1, + 0x337B, + 0x3505, + 0x368E, + 0x3817, + 0x399F, + 0x3B26, + 0x3CAD, + 0x3E33, + 0x3FB9, + 0x413E, + 0x42C3, + 0x4447, + 0x45CA, + 0x474D, + 0x48CE, + 0x4A50, + 0x4BD0, + 0x4D50, + 0x4ECF, + 0x504D, + 0x51CA, + 0x5347, + 0x54C3, + 0x563E, + 0x57B8, + 0x5931, + 0x5AAA, + 0x5C22, + 0x5D98, + 0x5F0E, + 0x6083, + 0x61F7, + 0x636A, + 0x64DC, + 0x664D, + 0x67BD, + 0x692D, + 0x6A9B, + 0x6C08, + 0x6D74, + 0x6EDF, + 0x7049, + 0x71B1, + 0x7319, + 0x7480, + 0x75E5, + 0x774A, + 0x78AD, + 0x7A0F, + 0x7B70, + 0x7CD0, + 0x7E2E, + 0x7F8B, + 0x80E7, + 0x8242, + 0x839C, + 0x84F4, + 0x864B, + 0x87A1, + 0x88F5, + 0x8A48, + 0x8B9A, + 0x8CEA, + 0x8E39, + 0x8F87, + 0x90D3, + 0x921E, + 0x9368, + 0x94B0, + 0x95F6, + 0x973C, + 0x987F, + 0x99C2, + 0x9B02, + 0x9C42, + 0x9D7F, + 0x9EBC, + 0x9FF6, + 0xA12F, + 0xA267, + 0xA39D, + 0xA4D2, + 0xA605, + 0xA736, + 0xA866, + 0xA994, + 0xAAC0, + 0xABEB, + 0xAD14, + 0xAE3B, + 0xAF61, + 0xB085, + 0xB1A8, + 0xB2C8, + 0xB3E7, + 0xB504, + 0xB620, + 0xB73A, + 0xB852, + 0xB968, + 0xBA7C, + 0xBB8F, + 0xBCA0, + 0xBDAE, + 0xBEBC, + 0xBFC7, + 0xC0D0, + 0xC1D8, + 0xC2DE, + 0xC3E2, + 0xC4E3, + 0xC5E4, + 0xC6E2, + 0xC7DE, + 0xC8D8, + 0xC9D1, + 0xCAC7, + 0xCBBB, + 0xCCAE, + 0xCD9F, + 0xCE8D, + 0xCF7A, + 0xD064, + 0xD14D, + 0xD233, + 0xD318, + 0xD3FA, + 0xD4DB, + 0xD5B9, + 0xD695, + 0xD770, + 0xD848, + 0xD91E, + 0xD9F2, + 0xDAC4, + 0xDB94, + 0xDC61, + 0xDD2D, + 0xDDF6, + 0xDEBE, + 0xDF83, + 0xE046, + 0xE106, + 0xE1C5, + 0xE282, + 0xE33C, + 0xE3F4, + 0xE4AA, + 0xE55E, + 0xE60F, + 0xE6BE, + 0xE76B, + 0xE816, + 0xE8BF, + 0xE965, + 0xEA09, + 0xEAAB, + 0xEB4B, + 0xEBE8, + 0xEC83, + 0xED1C, + 0xEDB2, + 0xEE46, + 0xEED8, + 0xEF68, + 0xEFF5, + 0xF080, + 0xF109, + 0xF18F, + 0xF213, + 0xF294, + 0xF314, + 0xF391, + 0xF40B, + 0xF484, + 0xF4FA, + 0xF56D, + 0xF5DE, + 0xF64D, + 0xF6BA, + 0xF724, + 0xF78B, + 0xF7F1, + 0xF853, + 0xF8B4, + 0xF912, + 0xF96E, + 0xF9C7, + 0xFA1E, + 0xFA73, + 0xFAC5, + 0xFB14, + 0xFB61, + 0xFBAC, + 0xFBF5, + 0xFC3B, + 0xFC7E, + 0xFCBF, + 0xFCFE, + 0xFD3A, + 0xFD74, + 0xFDAB, + 0xFDE0, + 0xFE13, + 0xFE43, + 0xFE70, + 0xFE9B, + 0xFEC4, + 0xFEEA, + 0xFF0E, + 0xFF2F, + 0xFF4E, + 0xFF6A, + 0xFF84, + 0xFF9C, + 0xFFB1, + 0xFFC3, + 0xFFD3, + 0xFFE1, + 0xFFEC, + 0xFFF4, + 0xFFFB, + 0xFFFE +}; + +#endif diff --git a/skia/corecg/SkTSort.h b/skia/corecg/SkTSort.h new file mode 100644 index 0000000..44f05bb --- /dev/null +++ b/skia/corecg/SkTSort.h @@ -0,0 +1,65 @@ +/* libs/corecg/SkTSort.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkTSort_DEFINED +#define SkTSort_DEFINED + +#include "SkTypes.h" + +template <typename T> +void SkTHeapSort_SiftDown(T array[], int root, int bottom) +{ + int root2 = root << 1; + + while (root2 <= bottom) + { + int maxChild; + + if (root2 == bottom) + maxChild = root2; + else if (array[root2] > array[root2 + 1]) + maxChild = root2; + else + maxChild = root2 + 1; + + if (array[root] < array[maxChild]) + { + SkTSwap<T>(array[root], array[maxChild]); + root = maxChild; + root2 = root << 1; + } + else + break; + } +} + +template <typename T> +void SkTHeapSort(T array[], int count) +{ + int i; + + for (i = count/2 - 1; i >= 0; --i) + SkTHeapSort_SiftDown<T>(array, i, count); + + for (i = count - 2; i >= 0; --i) + { + SkTSwap<T>(array[0], array[i + 1]); + SkTHeapSort_SiftDown<T>(array, 0, i); + } +} + +#endif diff --git a/skia/effects/Sk1DPathEffect.cpp b/skia/effects/Sk1DPathEffect.cpp new file mode 100644 index 0000000..325d402 --- /dev/null +++ b/skia/effects/Sk1DPathEffect.cpp @@ -0,0 +1,206 @@ +/* libs/graphics/effects/Sk1DPathEffect.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "Sk1DPathEffect.h" +#include "SkPathMeasure.h" + +bool Sk1DPathEffect::filterPath(SkPath* dst, const SkPath& src, SkScalar* width) +{ + SkPathMeasure meas(src, false); + do { + SkScalar length = meas.getLength(); + SkScalar distance = this->begin(length); + while (distance < length) + { + SkScalar delta = this->next(dst, distance, meas); + if (delta <= 0) + break; + distance += delta; + } + } while (meas.nextContour()); + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////// + +SkPath1DPathEffect::SkPath1DPathEffect(const SkPath& path, SkScalar advance, + SkScalar phase, Style style) : fPath(path) +{ + if (advance <= 0 || path.isEmpty()) + { + SkDEBUGF(("SkPath1DPathEffect can't use advance <= 0\n")); + fAdvance = 0; // signals we can't draw anything + } + else + { + // cleanup their phase parameter, inverting it so that it becomes an + // offset along the path (to match the interpretation in PostScript) + if (phase < 0) + { + phase = -phase; + if (phase > advance) + phase = SkScalarMod(phase, advance); + } + else + { + if (phase > advance) + phase = SkScalarMod(phase, advance); + phase = advance - phase; + } + // now catch the edge case where phase == advance (within epsilon) + if (phase >= advance) + phase = 0; + SkASSERT(phase >= 0); + + fAdvance = advance; + fInitialOffset = phase; + + if ((unsigned)style >= kStyleCount) { + SkDEBUGF(("SkPath1DPathEffect style enum out of range %d\n", style)); + } + fStyle = style; + } +} + +bool SkPath1DPathEffect::filterPath(SkPath* dst, const SkPath& src, SkScalar* width) +{ + if (fAdvance > 0) + { + *width = -1; + return this->INHERITED::filterPath(dst, src, width); + } + return false; +} + +static void morphpoints(SkPoint dst[], const SkPoint src[], int count, + SkPathMeasure& meas, SkScalar dist) +{ + for (int i = 0; i < count; i++) + { + SkPoint pos; + SkVector tangent; + + SkScalar sx = src[i].fX; + SkScalar sy = src[i].fY; + + meas.getPosTan(dist + sx, &pos, &tangent); + + SkMatrix matrix; + SkPoint pt; + + pt.set(sx, sy); + matrix.setSinCos(tangent.fY, tangent.fX, 0, 0); + matrix.preTranslate(-sx, 0); + matrix.postTranslate(pos.fX, pos.fY); + matrix.mapPoints(&dst[i], &pt, 1); + } +} + +/* TODO + +Need differentially more subdivisions when the follow-path is curvy. Not sure how to +determine that, but we need it. I guess a cheap answer is let the caller tell us, +but that seems like a cop-out. Another answer is to get Rob Johnson to figure it out. +*/ +static void morphpath(SkPath* dst, const SkPath& src, SkPathMeasure& meas, SkScalar dist) +{ + SkPath::Iter iter(src, false); + SkPoint srcP[4], dstP[3]; + SkPath::Verb verb; + + while ((verb = iter.next(srcP)) != SkPath::kDone_Verb) + { + switch (verb) { + case SkPath::kMove_Verb: + morphpoints(dstP, srcP, 1, meas, dist); + dst->moveTo(dstP[0]); + break; + case SkPath::kLine_Verb: + srcP[2] = srcP[1]; + srcP[1].set(SkScalarAve(srcP[0].fX, srcP[2].fX), + SkScalarAve(srcP[0].fY, srcP[2].fY)); + // fall through to quad + case SkPath::kQuad_Verb: + morphpoints(dstP, &srcP[1], 2, meas, dist); + dst->quadTo(dstP[0], dstP[1]); + break; + case SkPath::kCubic_Verb: + morphpoints(dstP, &srcP[1], 3, meas, dist); + dst->cubicTo(dstP[0], dstP[1], dstP[2]); + break; + case SkPath::kClose_Verb: + dst->close(); + break; + default: + SkASSERT(!"unknown verb"); + break; + } + } +} + +SkPath1DPathEffect::SkPath1DPathEffect(SkFlattenableReadBuffer& buffer) +{ + fAdvance = buffer.readScalar(); + if (fAdvance > 0) { + fPath.unflatten(buffer); + fInitialOffset = buffer.readScalar(); + fStyle = (Style) buffer.readU8(); + } +} + +SkScalar SkPath1DPathEffect::begin(SkScalar contourLength) +{ + return fInitialOffset; +} + +void SkPath1DPathEffect::flatten(SkFlattenableWriteBuffer& buffer) +{ + buffer.writeScalar(fAdvance); + if (fAdvance > 0) { + fPath.flatten(buffer); + buffer.writeScalar(fInitialOffset); + buffer.write8(fStyle); + } +} + +SkScalar SkPath1DPathEffect::next(SkPath* dst, SkScalar distance, SkPathMeasure& meas) +{ + switch (fStyle) { + case kTranslate_Style: + { + SkPoint pos; + meas.getPosTan(distance, &pos, NULL); + dst->addPath(fPath, pos.fX, pos.fY); + } + break; + case kRotate_Style: + { + SkMatrix matrix; + meas.getMatrix(distance, &matrix); + dst->addPath(fPath, matrix); + } + break; + case kMorph_Style: + morphpath(dst, fPath, meas, distance); + break; + default: + SkASSERT(!"unknown Style enum"); + break; + } + return fAdvance; +} + diff --git a/skia/effects/Sk2DPathEffect.cpp b/skia/effects/Sk2DPathEffect.cpp new file mode 100644 index 0000000..48c3a4c --- /dev/null +++ b/skia/effects/Sk2DPathEffect.cpp @@ -0,0 +1,107 @@ +/* libs/graphics/effects/Sk2DPathEffect.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "Sk2DPathEffect.h" +#include "SkBlitter.h" +#include "SkPath.h" +#include "SkScan.h" + +class Sk2DPathEffectBlitter : public SkBlitter { +public: + Sk2DPathEffectBlitter(Sk2DPathEffect* pe, SkPath* dst) + : fPE(pe), fDst(dst) + {} + virtual void blitH(int x, int y, int count) + { + fPE->nextSpan(x, y, count, fDst); + } +private: + Sk2DPathEffect* fPE; + SkPath* fDst; +}; + +//////////////////////////////////////////////////////////////////////////////////// + +Sk2DPathEffect::Sk2DPathEffect(const SkMatrix& mat) : fMatrix(mat) +{ + mat.invert(&fInverse); +} + +bool Sk2DPathEffect::filterPath(SkPath* dst, const SkPath& src, SkScalar* width) +{ + Sk2DPathEffectBlitter blitter(this, dst); + SkPath tmp; + SkRect bounds; + SkIRect ir; + + src.transform(fInverse, &tmp); + tmp.computeBounds(&bounds, SkPath::kExact_BoundsType); + bounds.round(&ir); + if (!ir.isEmpty()) { + // need to pass a clip to fillpath, required for inverse filltypes, + // even though those do not make sense for this patheffect + SkRegion clip(ir); + + this->begin(ir, dst); + SkScan::FillPath(tmp, clip, &blitter); + this->end(dst); + } + return true; +} + +void Sk2DPathEffect::nextSpan(int x, int y, int count, SkPath* path) +{ + const SkMatrix& mat = this->getMatrix(); + SkPoint src, dst; + + src.set(SkIntToScalar(x) + SK_ScalarHalf, SkIntToScalar(y) + SK_ScalarHalf); + do { + mat.mapPoints(&dst, &src, 1); + this->next(dst, x++, y, path); + src.fX += SK_Scalar1; + } while (--count > 0); +} + +void Sk2DPathEffect::begin(const SkIRect& uvBounds, SkPath* dst) {} +void Sk2DPathEffect::next(const SkPoint& loc, int u, int v, SkPath* dst) {} +void Sk2DPathEffect::end(SkPath* dst) {} + +//////////////////////////////////////////////////////////////////////////////// + +void Sk2DPathEffect::flatten(SkFlattenableWriteBuffer& buffer) +{ + buffer.writeMul4(&fMatrix, sizeof(fMatrix)); +} + +Sk2DPathEffect::Sk2DPathEffect(SkFlattenableReadBuffer& buffer) +{ + buffer.read(&fMatrix, sizeof(fMatrix)); + fMatrix.invert(&fInverse); +} + +SkFlattenable::Factory Sk2DPathEffect::getFactory() +{ + return CreateProc; +} + +SkFlattenable* Sk2DPathEffect::CreateProc(SkFlattenableReadBuffer& buffer) +{ + return SkNEW_ARGS(Sk2DPathEffect, (buffer)); +} + + + diff --git a/skia/effects/SkAvoidXfermode.cpp b/skia/effects/SkAvoidXfermode.cpp new file mode 100644 index 0000000..6781e9f --- /dev/null +++ b/skia/effects/SkAvoidXfermode.cpp @@ -0,0 +1,257 @@ +/* libs/graphics/effects/SkAvoidXfermode.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkAvoidXfermode.h" +#include "SkColorPriv.h" + +SkAvoidXfermode::SkAvoidXfermode(SkColor opColor, U8CPU tolerance, Mode mode) +{ + if (tolerance > 255) { + tolerance = 255; + } + + fOpColor = opColor; + fDistMul = (256 << 14) / (tolerance + 1); + fMode = mode; +} + +SkAvoidXfermode::SkAvoidXfermode(SkFlattenableReadBuffer& buffer) + : INHERITED(buffer) +{ + fOpColor = buffer.readU32(); + fDistMul = buffer.readU32(); + fMode = (Mode)buffer.readU8(); +} + +void SkAvoidXfermode::flatten(SkFlattenableWriteBuffer& buffer) +{ + this->INHERITED::flatten(buffer); + + buffer.write32(fOpColor); + buffer.write32(fDistMul); + buffer.write8(fMode); +} + +SkFlattenable* SkAvoidXfermode::Create(SkFlattenableReadBuffer& rb) +{ + return SkNEW_ARGS(SkAvoidXfermode, (rb)); +} + +SkFlattenable::Factory SkAvoidXfermode::getFactory() +{ + return Create; +} + +// returns 0..31 +static unsigned color_dist16(uint16_t c, unsigned r, unsigned g, unsigned b) +{ + SkASSERT(r <= SK_R16_MASK); + SkASSERT(g <= SK_G16_MASK); + SkASSERT(b <= SK_B16_MASK); + + unsigned dr = SkAbs32(SkGetPackedR16(c) - r); + unsigned dg = SkAbs32(SkGetPackedG16(c) - g) >> (SK_G16_BITS - SK_R16_BITS); + unsigned db = SkAbs32(SkGetPackedB16(c) - b); + + return SkMax32(dr, SkMax32(dg, db)); +} + +// returns 0..15 +static unsigned color_dist4444(uint16_t c, unsigned r, unsigned g, unsigned b) +{ + SkASSERT(r <= 0xF); + SkASSERT(g <= 0xF); + SkASSERT(b <= 0xF); + + unsigned dr = SkAbs32(SkGetPackedR4444(c) - r); + unsigned dg = SkAbs32(SkGetPackedG4444(c) - g); + unsigned db = SkAbs32(SkGetPackedB4444(c) - b); + + return SkMax32(dr, SkMax32(dg, db)); +} + +// returns 0..255 +static unsigned color_dist32(SkPMColor c, U8CPU r, U8CPU g, U8CPU b) +{ + SkASSERT(r <= 0xFF); + SkASSERT(g <= 0xFF); + SkASSERT(b <= 0xFF); + + unsigned dr = SkAbs32(SkGetPackedR32(c) - r); + unsigned dg = SkAbs32(SkGetPackedG32(c) - g); + unsigned db = SkAbs32(SkGetPackedB32(c) - b); + + return SkMax32(dr, SkMax32(dg, db)); +} + +static int scale_dist_14(int dist, uint32_t mul, uint32_t sub) +{ + int tmp = dist * mul - sub; + int result = (tmp + (1 << 13)) >> 14; + + return result; +} + +static SkPMColor SkFourByteInterp(SkPMColor src, SkPMColor dst, unsigned scale) +{ + unsigned a = SkAlphaBlend(SkGetPackedA32(src), SkGetPackedA32(dst), scale); + unsigned r = SkAlphaBlend(SkGetPackedR32(src), SkGetPackedR32(dst), scale); + unsigned g = SkAlphaBlend(SkGetPackedG32(src), SkGetPackedG32(dst), scale); + unsigned b = SkAlphaBlend(SkGetPackedB32(src), SkGetPackedB32(dst), scale); + + return SkPackARGB32(a, r, g, b); +} + +void SkAvoidXfermode::xfer32(SkPMColor dst[], const SkPMColor src[], int count, + const SkAlpha aa[]) +{ + unsigned opR = SkColorGetR(fOpColor); + unsigned opG = SkColorGetG(fOpColor); + unsigned opB = SkColorGetB(fOpColor); + uint32_t mul = fDistMul; + uint32_t sub = (fDistMul - (1 << 14)) << 8; + + int MAX, mask; + + if (kTargetColor_Mode == fMode) { + mask = -1; + MAX = 255; + } else { + mask = 0; + MAX = 0; + } + + for (int i = 0; i < count; i++) { + int d = color_dist32(dst[i], opR, opG, opB); + // now reverse d if we need to + d = MAX + (d ^ mask) - mask; + SkASSERT((unsigned)d <= 255); + d = SkAlpha255To256(d); + + d = scale_dist_14(d, mul, sub); + SkASSERT(d <= 256); + + if (d > 0) { + if (NULL != aa) { + d = SkAlphaMul(d, SkAlpha255To256(*aa++)); + if (0 == d) { + continue; + } + } + dst[i] = SkFourByteInterp(src[i], dst[i], d); + } + } +} + +static inline U16CPU SkBlend3216(SkPMColor src, U16CPU dst, unsigned scale) +{ + SkASSERT(scale <= 32); + scale <<= 3; + + return SkPackRGB16( SkAlphaBlend(SkPacked32ToR16(src), SkGetPackedR16(dst), scale), + SkAlphaBlend(SkPacked32ToG16(src), SkGetPackedG16(dst), scale), + SkAlphaBlend(SkPacked32ToB16(src), SkGetPackedB16(dst), scale)); +} + +void SkAvoidXfermode::xfer16(uint16_t dst[], const SkPMColor src[], int count, + const SkAlpha aa[]) +{ + unsigned opR = SkColorGetR(fOpColor) >> (8 - SK_R16_BITS); + unsigned opG = SkColorGetG(fOpColor) >> (8 - SK_G16_BITS); + unsigned opB = SkColorGetB(fOpColor) >> (8 - SK_R16_BITS); + uint32_t mul = fDistMul; + uint32_t sub = (fDistMul - (1 << 14)) << 8; + + int MAX, mask; + + if (kTargetColor_Mode == fMode) { + mask = -1; + MAX = 31; + } else { + mask = 0; + MAX = 0; + } + + for (int i = 0; i < count; i++) { + int d = color_dist16(dst[i], opR, opG, opB); + // now reverse d if we need to + d = MAX + (d ^ mask) - mask; + SkASSERT((unsigned)d <= 31); + // convert from 0..31 to 0..32 + d += d >> 4; + + d = scale_dist_14(d, mul, sub); + SkASSERT(d <= 32); + + if (d > 0) { + if (NULL != aa) { + d = SkAlphaMul(d, SkAlpha255To256(*aa++)); + if (0 == d) { + continue; + } + } + dst[i] = SkBlend3216(src[i], dst[i], d); + } + } +} + +void SkAvoidXfermode::xfer4444(uint16_t dst[], const SkPMColor src[], int count, + const SkAlpha aa[]) +{ + unsigned opR = SkColorGetR(fOpColor) >> 4; + unsigned opG = SkColorGetG(fOpColor) >> 4; + unsigned opB = SkColorGetB(fOpColor) >> 4; + uint32_t mul = fDistMul; + uint32_t sub = (fDistMul - (1 << 14)) << 8; + + int MAX, mask; + + if (kTargetColor_Mode == fMode) { + mask = -1; + MAX = 15; + } else { + mask = 0; + MAX = 0; + } + + for (int i = 0; i < count; i++) { + int d = color_dist4444(dst[i], opR, opG, opB); + // now reverse d if we need to + d = MAX + (d ^ mask) - mask; + SkASSERT((unsigned)d <= 15); + d = SkAlpha255To256(d); + + d = scale_dist_14(d, mul, sub); + SkASSERT(d <= 16); + + if (d > 0) { + if (NULL != aa) { + d = SkAlphaMul(d, SkAlpha255To256(*aa++)); + if (0 == d) { + continue; + } + } + dst[i] = SkBlend4444(SkPixel32ToPixel4444(src[i]), dst[i], d); + } + } +} + +void SkAvoidXfermode::xferA8(SkAlpha dst[], const SkPMColor src[], int count, const SkAlpha aa[]) +{ + // override in subclass +} + diff --git a/skia/effects/SkBlurDrawLooper.cpp b/skia/effects/SkBlurDrawLooper.cpp new file mode 100644 index 0000000..61b69eb --- /dev/null +++ b/skia/effects/SkBlurDrawLooper.cpp @@ -0,0 +1,86 @@ +#include "SkBlurDrawLooper.h" +#include "SkBlurMaskFilter.h" +#include "SkCanvas.h" +#include "SkPaint.h" +#include "SkMaskFilter.h" + +SkBlurDrawLooper::SkBlurDrawLooper(SkScalar radius, SkScalar dx, SkScalar dy, + SkColor color) + : fDx(dx), fDy(dy), fBlurColor(color) +{ + if (radius > 0) + fBlur = SkBlurMaskFilter::Create(radius, + SkBlurMaskFilter::kNormal_BlurStyle); + else + fBlur = NULL; +} + +SkBlurDrawLooper::SkBlurDrawLooper(SkFlattenableReadBuffer& buffer) +{ + fDx = buffer.readScalar(); + fDy = buffer.readScalar(); + fBlurColor = buffer.readU32(); + fBlur = static_cast<SkMaskFilter*>(buffer.readFlattenable()); +} + +SkBlurDrawLooper::~SkBlurDrawLooper() +{ + fBlur->safeUnref(); +} + +void SkBlurDrawLooper::flatten(SkFlattenableWriteBuffer& buffer) +{ + buffer.writeScalar(fDx); + buffer.writeScalar(fDy); + buffer.write32(fBlurColor); + buffer.writeFlattenable(fBlur); +} + +void SkBlurDrawLooper::init(SkCanvas* canvas, SkPaint* paint) +{ + // we do nothing if a maskfilter is already installed + if (paint->getMaskFilter() != NULL) + fState = kDone; + else + { + fState = kBeforeEdge; + fPaint = paint; + fCanvas = canvas; + fSaveCount = canvas->getSaveCount(); + } +} + +bool SkBlurDrawLooper::next() +{ + switch (fState) { + case kBeforeEdge: + fSavedColor = fPaint->getColor(); + fPaint->setColor(fBlurColor); + fPaint->setMaskFilter(fBlur); + fCanvas->save(SkCanvas::kMatrix_SaveFlag); + fCanvas->translate(fDx, fDy); + fState = kAfterEdge; + return true; + case kAfterEdge: + fPaint->setColor(fSavedColor); + fPaint->setMaskFilter(NULL); + fCanvas->restore(); // to remove the translate we did earlier + fState = kDone; + return true; + default: + SkASSERT(kDone == fState); + return false; + } +} + +void SkBlurDrawLooper::restore() +{ + if (kAfterEdge == fState) + { + fPaint->setColor(fSavedColor); + fPaint->setMaskFilter(NULL); + fCanvas->restore(); // to remove the translate we did earlier + fState = kDone; + } +} + diff --git a/skia/effects/SkBlurMask.cpp b/skia/effects/SkBlurMask.cpp new file mode 100644 index 0000000..f2c07a8 --- /dev/null +++ b/skia/effects/SkBlurMask.cpp @@ -0,0 +1,332 @@ +/* libs/graphics/effects/SkBlurMask.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkBlurMask.h" +#include "SkTemplates.h" + +static void build_sum_buffer(uint32_t dst[], int w, int h, const uint8_t src[], int srcRB) +{ + SkASSERT(srcRB >= w); + // mod srcRB so we can apply it after each row + srcRB -= w; + + int x, y; + + // special case first row + uint32_t X = 0; + for (x = w - 1; x >= 0; --x) + { + X = *src++ + X; + *dst++ = X; + } + src += srcRB; + + // now do the rest of the rows + for (y = h - 1; y > 0; --y) + { + uint32_t L = 0; + uint32_t C = 0; + for (x = w - 1; x >= 0; --x) + { + uint32_t T = dst[-w]; + X = *src++ + L + T - C; + *dst++ = X; + L = X; + C = T; + } + src += srcRB; + } +} + +static void apply_kernel(uint8_t dst[], int rx, int ry, const uint32_t src[], int sw, int sh) +{ + uint32_t scale = (1 << 24) / ((2*rx + 1)*(2*ry + 1)); + + int rowBytes = sw; + + int dw = sw + 2*rx; + int dh = sh + 2*ry; + + sw -= 1; // now it is max_x + sh -= 1; // now it is max_y + + int prev_y = -ry - 1 -ry; + int next_y = ry -ry; + + for (int y = 0; y < dh; y++) + { + int py = SkClampPos(prev_y) * rowBytes; + int ny = SkFastMin32(next_y, sh) * rowBytes; + + int prev_x = -rx - 1 -rx; + int next_x = rx -rx; + + for (int x = 0; x < dw; x++) + { + int px = SkClampPos(prev_x); + int nx = SkFastMin32(next_x, sw); + + uint32_t sum = src[px+py] + src[nx+ny] - src[nx+py] - src[px+ny]; + *dst++ = SkToU8(sum * scale >> 24); + + prev_x += 1; + next_x += 1; + } + prev_y += 1; + next_y += 1; + } +} + +static void apply_kernel_interp(uint8_t dst[], int rx, int ry, const uint32_t src[], int sw, int sh, U8CPU outer_weight) +{ + SkASSERT(rx > 0 && ry > 0); + SkASSERT(outer_weight <= 255); + + int inner_weight = 255 - outer_weight; + + // round these guys up if they're bigger than 127 + outer_weight += outer_weight >> 7; + inner_weight += inner_weight >> 7; + + uint32_t outer_scale = (outer_weight << 16) / ((2*rx + 1)*(2*ry + 1)); + uint32_t inner_scale = (inner_weight << 16) / ((2*rx - 1)*(2*ry - 1)); + + int rowBytes = sw; + + int dw = sw + 2*rx; + int dh = sh + 2*ry; + + sw -= 1; // now it is max_x + sh -= 1; // now it is max_y + + int prev_y = -ry - 1 -ry; + int next_y = ry -ry; + + for (int y = 0; y < dh; y++) + { + int py = SkClampPos(prev_y) * rowBytes; + int ny = SkFastMin32(next_y, sh) * rowBytes; + + int ipy = SkClampPos(prev_y + 1) * rowBytes; + int iny = SkClampMax(next_y - 1, sh) * rowBytes; + + int prev_x = -rx - 1 -rx; + int next_x = rx -rx; + + for (int x = 0; x < dw; x++) + { + int px = SkClampPos(prev_x); + int nx = SkFastMin32(next_x, sw); + + int ipx = SkClampPos(prev_x + 1); + int inx = SkClampMax(next_x - 1, sw); + + uint32_t outer_sum = src[px+py] + src[nx+ny] - src[nx+py] - src[px+ny]; + uint32_t inner_sum = src[ipx+ipy] + src[inx+iny] - src[inx+ipy] - src[ipx+iny]; + *dst++ = SkToU8((outer_sum * outer_scale + inner_sum * inner_scale) >> 24); + + prev_x += 1; + next_x += 1; + } + prev_y += 1; + next_y += 1; + } +} + +#include "SkColorPriv.h" + +static void merge_src_with_blur(uint8_t dst[], + const uint8_t src[], int sw, int sh, + const uint8_t blur[], int blurRowBytes) +{ + while (--sh >= 0) + { + for (int x = sw - 1; x >= 0; --x) + { + *dst = SkToU8(SkAlphaMul(*blur, SkAlpha255To256(*src))); + dst += 1; + src += 1; + blur += 1; + } + blur += blurRowBytes - sw; + } +} + +static void clamp_with_orig(uint8_t dst[], int dstRowBytes, + const uint8_t src[], int sw, int sh, + SkBlurMask::Style style) +{ + int x; + while (--sh >= 0) + { + switch (style) { + case SkBlurMask::kSolid_Style: + for (x = sw - 1; x >= 0; --x) + { + *dst = SkToU8(*src + SkAlphaMul(*dst, SkAlpha255To256(255 - *src))); + dst += 1; + src += 1; + } + break; + case SkBlurMask::kOuter_Style: + for (x = sw - 1; x >= 0; --x) + { + if (*src) + *dst = SkToU8(SkAlphaMul(*dst, SkAlpha255To256(255 - *src))); + dst += 1; + src += 1; + } + break; + default: + SkASSERT(!"Unexpected blur style here"); + break; + } + dst += dstRowBytes - sw; + } +} + +//////////////////////////////////////////////////////////////////////// + +// we use a local funciton to wrap the class static method to work around +// a bug in gcc98 +void SkMask_FreeImage(uint8_t* image); +void SkMask_FreeImage(uint8_t* image) +{ + SkMask::FreeImage(image); +} + +bool SkBlurMask::Blur(SkMask* dst, const SkMask& src, + SkScalar radius, Style style) +{ + if (src.fFormat != SkMask::kA8_Format) + return false; + + int rx = SkScalarCeil(radius); + int outer_weight = 255 - SkScalarRound((SkIntToScalar(rx) - radius) * 255); + + SkASSERT(rx >= 0); + SkASSERT((unsigned)outer_weight <= 255); + + if (rx == 0) + return false; + + int ry = rx; // only do square blur for now + + dst->fBounds.set(src.fBounds.fLeft - rx, src.fBounds.fTop - ry, + src.fBounds.fRight + rx, src.fBounds.fBottom + ry); + dst->fRowBytes = SkToU16(dst->fBounds.width()); + dst->fFormat = SkMask::kA8_Format; + dst->fImage = NULL; + + if (src.fImage) + { + int sw = src.fBounds.width(); + int sh = src.fBounds.height(); + const uint8_t* sp = src.fImage; + uint8_t* dp = SkMask::AllocImage(dst->computeImageSize()); + + SkAutoTCallVProc<uint8_t, SkMask_FreeImage> autoCall(dp); + + // build the blurry destination + { + SkAutoTMalloc<uint32_t> storage(sw * sh); + uint32_t* sumBuffer = storage.get(); + + build_sum_buffer(sumBuffer, sw, sh, sp, src.fRowBytes); + if (outer_weight == 255) + apply_kernel(dp, rx, ry, sumBuffer, sw, sh); + else + apply_kernel_interp(dp, rx, ry, sumBuffer, sw, sh, outer_weight); + } + + dst->fImage = dp; + // if need be, alloc the "real" dst (same size as src) and copy/merge + // the blur into it (applying the src) + if (style == kInner_Style) + { + dst->fImage = SkMask::AllocImage(src.computeImageSize()); + merge_src_with_blur(dst->fImage, sp, sw, sh, + dp + rx + ry*dst->fBounds.width(), + dst->fBounds.width()); + SkMask::FreeImage(dp); + } + else if (style != kNormal_Style) + { + clamp_with_orig(dp + rx + ry*dst->fBounds.width(), + dst->fBounds.width(), + sp, sw, sh, + style); + } + (void)autoCall.detach(); + } + + if (style == kInner_Style) + { + dst->fBounds = src.fBounds; // restore trimmed bounds + dst->fRowBytes = SkToU16(dst->fBounds.width()); + } + +#if 0 + if (gamma && dst->fImage) + { + uint8_t* image = dst->fImage; + uint8_t* stop = image + dst->computeImageSize(); + + for (; image < stop; image += 1) + *image = gamma[*image]; + } +#endif + return true; +} + +#if 0 +void SkBlurMask::BuildSqrtGamma(uint8_t gamma[256], SkScalar percent) +{ + SkASSERT(gamma); + SkASSERT(percent >= 0 && percent <= SK_Scalar1); + + int scale = SkScalarRound(percent * 256); + + for (int i = 0; i < 256; i++) + { + SkFixed n = i * 257; + n += n >> 15; + SkASSERT(n >= 0 && n <= SK_Fixed1); + n = SkFixedSqrt(n); + n = n * 255 >> 16; + n = SkAlphaBlend(n, i, scale); + gamma[i] = SkToU8(n); + } +} + +void SkBlurMask::BuildSqrGamma(uint8_t gamma[256], SkScalar percent) +{ + SkASSERT(gamma); + SkASSERT(percent >= 0 && percent <= SK_Scalar1); + + int scale = SkScalarRound(percent * 256); + SkFixed div255 = SK_Fixed1 / 255; + + for (int i = 0; i < 256; i++) + { + int square = i * i; + int linear = i * 255; + int n = SkAlphaBlend(square, linear, scale); + gamma[i] = SkToU8(n * div255 >> 16); + } +} +#endif diff --git a/skia/effects/SkBlurMask.h b/skia/effects/SkBlurMask.h new file mode 100644 index 0000000..2df5731 --- /dev/null +++ b/skia/effects/SkBlurMask.h @@ -0,0 +1,40 @@ +/* libs/graphics/effects/SkBlurMask.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkBlurMask_DEFINED +#define SkBlurMask_DEFINED + +#include "SkShader.h" + +class SkBlurMask { +public: + enum Style { + kNormal_Style, //!< fuzzy inside and outside + kSolid_Style, //!< solid inside, fuzzy outside + kOuter_Style, //!< nothing inside, fuzzy outside + kInner_Style, //!< fuzzy inside, nothing outside + + kStyleCount + }; + + static bool Blur(SkMask* dst, const SkMask& src, SkScalar radius, Style); +}; + +#endif + + + diff --git a/skia/effects/SkBlurMaskFilter.cpp b/skia/effects/SkBlurMaskFilter.cpp new file mode 100644 index 0000000..3ff1d81 --- /dev/null +++ b/skia/effects/SkBlurMaskFilter.cpp @@ -0,0 +1,117 @@ +/* libs/graphics/effects/SkBlurMaskFilter.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkBlurMaskFilter.h" +#include "SkBlurMask.h" +#include "SkBuffer.h" +#include "SkMaskFilter.h" + +class SkBlurMaskFilterImpl : public SkMaskFilter { +public: + SkBlurMaskFilterImpl(SkScalar radius, SkBlurMaskFilter::BlurStyle style); + + // overrides from SkMaskFilter + virtual SkMask::Format getFormat(); + virtual bool filterMask(SkMask* dst, const SkMask& src, const SkMatrix& matrix, SkIPoint* margin); + + // overrides from SkFlattenable + // This method is not exported to java. + virtual Factory getFactory(); + // This method is not exported to java. + virtual void flatten(SkFlattenableWriteBuffer&); + +private: + SkScalar fRadius; + SkBlurMaskFilter::BlurStyle fBlurStyle; + + static SkFlattenable* CreateProc(SkFlattenableReadBuffer&); + SkBlurMaskFilterImpl(SkFlattenableReadBuffer&); + + typedef SkMaskFilter INHERITED; +}; + +SkMaskFilter* SkBlurMaskFilter::Create(SkScalar radius, SkBlurMaskFilter::BlurStyle style) +{ + if (radius <= 0 || (unsigned)style >= SkBlurMaskFilter::kBlurStyleCount) + return NULL; + + return SkNEW_ARGS(SkBlurMaskFilterImpl, (radius, style)); +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////// + +SkBlurMaskFilterImpl::SkBlurMaskFilterImpl(SkScalar radius, SkBlurMaskFilter::BlurStyle style) + : fRadius(radius), fBlurStyle(style) +{ +#if 0 + fGamma = NULL; + if (gammaScale) + { + fGamma = new U8[256]; + if (gammaScale > 0) + SkBlurMask::BuildSqrGamma(fGamma, gammaScale); + else + SkBlurMask::BuildSqrtGamma(fGamma, -gammaScale); + } +#endif + SkASSERT(radius >= 0); + SkASSERT((unsigned)style < SkBlurMaskFilter::kBlurStyleCount); +} + +SkMask::Format SkBlurMaskFilterImpl::getFormat() +{ + return SkMask::kA8_Format; +} + +bool SkBlurMaskFilterImpl::filterMask(SkMask* dst, const SkMask& src, const SkMatrix& matrix, SkIPoint* margin) +{ + SkScalar radius = matrix.mapRadius(fRadius); + + if (SkBlurMask::Blur(dst, src, radius, (SkBlurMask::Style)fBlurStyle)) + { + if (margin) + margin->set(SkScalarCeil(radius), SkScalarCeil(radius)); + return true; + } + return false; +} + +SkFlattenable* SkBlurMaskFilterImpl::CreateProc(SkFlattenableReadBuffer& buffer) +{ + return SkNEW_ARGS(SkBlurMaskFilterImpl, (buffer)); +} + +SkFlattenable::Factory SkBlurMaskFilterImpl::getFactory() +{ + return CreateProc; +} + +SkBlurMaskFilterImpl::SkBlurMaskFilterImpl(SkFlattenableReadBuffer& buffer) : SkMaskFilter(buffer) +{ + fRadius = buffer.readScalar(); + fBlurStyle = (SkBlurMaskFilter::BlurStyle)buffer.readS32(); + SkASSERT(fRadius >= 0); + SkASSERT((unsigned)fBlurStyle < SkBlurMaskFilter::kBlurStyleCount); +} + +void SkBlurMaskFilterImpl::flatten(SkFlattenableWriteBuffer& buffer) +{ + this->INHERITED::flatten(buffer); + buffer.writeScalar(fRadius); + buffer.write32(fBlurStyle); +} + diff --git a/skia/effects/SkCamera.cpp b/skia/effects/SkCamera.cpp new file mode 100644 index 0000000..469b175 --- /dev/null +++ b/skia/effects/SkCamera.cpp @@ -0,0 +1,449 @@ +/* libs/graphics/effects/SkCamera.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkCamera.h" + +static SkScalar SkScalarDotDiv(int count, const SkScalar a[], int step_a, + const SkScalar b[], int step_b, + SkScalar denom) +{ +#ifdef SK_SCALAR_IS_FLOAT + float prod = 0; + for (int i = 0; i < count; i++) + { + prod += a[0] * b[0]; + a += step_a; + b += step_b; + } + return prod / denom; +#else + Sk64 prod, tmp; + + prod.set(0); + for (int i = 0; i < count; i++) + { + tmp.setMul(a[0], b[0]); + prod.add(tmp); + a += step_a; + b += step_b; + } + prod.div(denom, Sk64::kRound_DivOption); + return prod.get32(); +#endif +} + +static SkScalar SkScalarDot(int count, const SkScalar a[], int step_a, + const SkScalar b[], int step_b) +{ +#ifdef SK_SCALAR_IS_FLOAT + float prod = 0; + for (int i = 0; i < count; i++) + { + prod += a[0] * b[0]; + a += step_a; + b += step_b; + } + return prod; +#else + Sk64 prod, tmp; + + prod.set(0); + for (int i = 0; i < count; i++) + { + tmp.setMul(a[0], b[0]); + prod.add(tmp); + a += step_a; + b += step_b; + } + return prod.getFixed(); +#endif +} + +////////////////////////////////////////////////////////////////////////// + +SkUnitScalar SkPoint3D::normalize(SkUnit3D* unit) const +{ +#ifdef SK_SCALAR_IS_FLOAT + float mag = sk_float_sqrt(fX*fX + fY*fY + fZ*fZ); + if (mag) + { + float scale = 1.0f / mag; + unit->fX = fX * scale; + unit->fY = fY * scale; + unit->fZ = fZ * scale; + } +#else + Sk64 tmp1, tmp2; + + tmp1.setMul(fX, fX); + tmp2.setMul(fY, fY); + tmp1.add(tmp2); + tmp2.setMul(fZ, fZ); + tmp1.add(tmp2); + + SkFixed mag = tmp1.getSqrt(); + if (mag) + { + // what if mag < SK_Fixed1 ??? we will underflow the fixdiv + SkFixed scale = SkFixedDiv(SK_Fract1, mag); + unit->fX = SkFixedMul(fX, scale); + unit->fY = SkFixedMul(fY, scale); + unit->fZ = SkFixedMul(fZ, scale); + } +#endif + return mag; +} + +SkUnitScalar SkUnit3D::Dot(const SkUnit3D& a, const SkUnit3D& b) +{ + return SkUnitScalarMul(a.fX, b.fX) + + SkUnitScalarMul(a.fY, b.fY) + + SkUnitScalarMul(a.fZ, b.fZ); +} + +void SkUnit3D::Cross(const SkUnit3D& a, const SkUnit3D& b, SkUnit3D* cross) +{ + SkASSERT(cross); + + // use x,y,z, in case &a == cross or &b == cross + + + SkScalar x = SkUnitScalarMul(a.fY, b.fZ) - SkUnitScalarMul(a.fZ, b.fY); + SkScalar y = SkUnitScalarMul(a.fZ, b.fX) - SkUnitScalarMul(a.fX, b.fY); + SkScalar z = SkUnitScalarMul(a.fX, b.fY) - SkUnitScalarMul(a.fY, b.fX); + + cross->set(x, y, z); +} + +/////////////////////////////////////////////////////////////////////////// + +SkPatch3D::SkPatch3D() +{ + this->reset(); +} + +void SkPatch3D::reset() +{ + fOrigin.set(0, 0, 0); + fU.set(SK_Scalar1, 0, 0); + fV.set(0, -SK_Scalar1, 0); +} + +void SkPatch3D::transform(const SkMatrix3D& m, SkPatch3D* dst) const +{ + if (dst == NULL) + dst = (SkPatch3D*)this; + + m.mapVector(fU, &dst->fU); + m.mapVector(fV, &dst->fV); + m.mapPoint(fOrigin, &dst->fOrigin); +} + +SkScalar SkPatch3D::dotWith(SkScalar dx, SkScalar dy, SkScalar dz) const +{ + SkScalar cx = SkScalarMul(fU.fY, fV.fZ) - SkScalarMul(fU.fZ, fV.fY); + SkScalar cy = SkScalarMul(fU.fZ, fV.fX) - SkScalarMul(fU.fX, fV.fY); + SkScalar cz = SkScalarMul(fU.fX, fV.fY) - SkScalarMul(fU.fY, fV.fX); + + return SkScalarMul(cx, dx) + SkScalarMul(cy, dy) + SkScalarMul(cz, dz); +} + +/////////////////////////////////////////////////////////////////////////// + +void SkMatrix3D::reset() +{ + memset(fMat, 0, sizeof(fMat)); + fMat[0][0] = fMat[1][1] = fMat[2][2] = SK_Scalar1; +} + +void SkMatrix3D::setTranslate(SkScalar x, SkScalar y, SkScalar z) +{ + memset(fMat, 0, sizeof(fMat)); + fMat[0][0] = x; + fMat[1][1] = y; + fMat[2][2] = z; +} + +void SkMatrix3D::setRotateX(SkScalar degX) +{ + SkScalar s, c; + + s = SkScalarSinCos(SkDegreesToRadians(degX), &c); + this->setRow(0, SK_Scalar1, 0, 0); + this->setRow(1, 0, c, -s); + this->setRow(2, 0, s, c); +} + +void SkMatrix3D::setRotateY(SkScalar degY) +{ + SkScalar s, c; + + s = SkScalarSinCos(SkDegreesToRadians(degY), &c); + this->setRow(0, c, 0, -s); + this->setRow(1, 0, SK_Scalar1, 0); + this->setRow(2, s, 0, c); +} + +void SkMatrix3D::setRotateZ(SkScalar degZ) +{ + SkScalar s, c; + + s = SkScalarSinCos(SkDegreesToRadians(degZ), &c); + this->setRow(0, c, -s, 0); + this->setRow(1, s, c, 0); + this->setRow(2, 0, 0, SK_Scalar1); +} + +void SkMatrix3D::preTranslate(SkScalar x, SkScalar y, SkScalar z) +{ + SkScalar col[3] = { x, y, z}; + + for (int i = 0; i < 3; i++) + fMat[i][3] += SkScalarDot(3, &fMat[i][0], 1, col, 1); +} + +void SkMatrix3D::preRotateX(SkScalar degX) +{ + SkMatrix3D m; + m.setRotateX(degX); + this->setConcat(*this, m); +} + +void SkMatrix3D::preRotateY(SkScalar degY) +{ + SkMatrix3D m; + m.setRotateY(degY); + this->setConcat(*this, m); +} + +void SkMatrix3D::preRotateZ(SkScalar degZ) +{ + SkMatrix3D m; + m.setRotateZ(degZ); + this->setConcat(*this, m); +} + +void SkMatrix3D::setConcat(const SkMatrix3D& a, const SkMatrix3D& b) +{ + SkMatrix3D tmp; + SkMatrix3D* c = this; + + if (this == &a || this == &b) + c = &tmp; + + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) + c->fMat[i][j] = SkScalarDot(3, &a.fMat[i][0], 1, &b.fMat[0][j], 4); + c->fMat[i][3] = SkScalarDot(3, &a.fMat[i][0], 1, &b.fMat[0][3], 4) + a.fMat[i][3]; + } + + if (c == &tmp) + *this = tmp; +} + +void SkMatrix3D::mapPoint(const SkPoint3D& src, SkPoint3D* dst) const +{ + SkScalar x = SkScalarDot(3, &fMat[0][0], 1, &src.fX, 1) + fMat[0][3]; + SkScalar y = SkScalarDot(3, &fMat[1][0], 1, &src.fX, 1) + fMat[1][3]; + SkScalar z = SkScalarDot(3, &fMat[2][0], 1, &src.fX, 1) + fMat[2][3]; + dst->set(x, y, z); +} + +void SkMatrix3D::mapVector(const SkVector3D& src, SkVector3D* dst) const +{ + SkScalar x = SkScalarDot(3, &fMat[0][0], 1, &src.fX, 1); + SkScalar y = SkScalarDot(3, &fMat[1][0], 1, &src.fX, 1); + SkScalar z = SkScalarDot(3, &fMat[2][0], 1, &src.fX, 1); + dst->set(x, y, z); +} + +/////////////////////////////////////////////////////////////////////////// + +SkCamera3D::SkCamera3D() +{ + this->reset(); +} + +void SkCamera3D::reset() +{ + fLocation.set(0, 0, -SkIntToScalar(576)); // 8 inches backward + fAxis.set(0, 0, SK_Scalar1); // forward + fZenith.set(0, -SK_Scalar1, 0); // up + + fObserver.set(0, 0, fLocation.fZ); + + fNeedToUpdate = true; +} + +void SkCamera3D::update() +{ + fNeedToUpdate = true; +} + +void SkCamera3D::doUpdate() const +{ + SkUnit3D axis, zenith, cross; + + fAxis.normalize(&axis); + + { + SkScalar dot = SkUnit3D::Dot(*(const SkUnit3D*)(const void*)&fZenith, axis); + + zenith.fX = fZenith.fX - SkUnitScalarMul(dot, axis.fX); + zenith.fY = fZenith.fY - SkUnitScalarMul(dot, axis.fY); + zenith.fZ = fZenith.fZ - SkUnitScalarMul(dot, axis.fZ); + + (void)((SkPoint3D*)(void*)&zenith)->normalize(&zenith); + } + + SkUnit3D::Cross(axis, zenith, &cross); + + { + SkMatrix* orien = &fOrientation; + SkScalar x = fObserver.fX; + SkScalar y = fObserver.fY; + SkScalar z = fObserver.fZ; + + orien->set(SkMatrix::kMScaleX, SkUnitScalarMul(x, axis.fX) - SkUnitScalarMul(z, cross.fX)); + orien->set(SkMatrix::kMSkewX, SkUnitScalarMul(x, axis.fY) - SkUnitScalarMul(z, cross.fY)); + orien->set(SkMatrix::kMTransX, SkUnitScalarMul(x, axis.fZ) - SkUnitScalarMul(z, cross.fZ)); + orien->set(SkMatrix::kMSkewY, SkUnitScalarMul(y, axis.fX) - SkUnitScalarMul(z, zenith.fX)); + orien->set(SkMatrix::kMScaleY, SkUnitScalarMul(y, axis.fY) - SkUnitScalarMul(z, zenith.fY)); + orien->set(SkMatrix::kMTransY, SkUnitScalarMul(y, axis.fZ) - SkUnitScalarMul(z, zenith.fZ)); + orien->set(SkMatrix::kMPersp0, axis.fX); + orien->set(SkMatrix::kMPersp1, axis.fY); + orien->set(SkMatrix::kMPersp2, axis.fZ); + } +} + +void SkCamera3D::patchToMatrix(const SkPatch3D& quilt, SkMatrix* matrix) const +{ + if (fNeedToUpdate) + { + this->doUpdate(); + fNeedToUpdate = false; + } + + const SkScalar* mapPtr = (const SkScalar*)(const void*)&fOrientation; + const SkScalar* patchPtr; + SkPoint3D diff; + SkScalar dot; + + diff.fX = quilt.fOrigin.fX - fLocation.fX; + diff.fY = quilt.fOrigin.fY - fLocation.fY; + diff.fZ = quilt.fOrigin.fZ - fLocation.fZ; + + dot = SkUnit3D::Dot(*(const SkUnit3D*)(const void*)&diff, + *(const SkUnit3D*)(((const SkScalar*)(const void*)&fOrientation) + 6)); + + patchPtr = (const SkScalar*)&quilt; + matrix->set(SkMatrix::kMScaleX, SkScalarDotDiv(3, patchPtr, 1, mapPtr, 1, dot)); + matrix->set(SkMatrix::kMSkewY, SkScalarDotDiv(3, patchPtr, 1, mapPtr+3, 1, dot)); + matrix->set(SkMatrix::kMPersp0, SkScalarDotDiv(3, patchPtr, 1, mapPtr+6, 1, dot)); + + patchPtr += 3; + matrix->set(SkMatrix::kMSkewX, SkScalarDotDiv(3, patchPtr, 1, mapPtr, 1, dot)); + matrix->set(SkMatrix::kMScaleY, SkScalarDotDiv(3, patchPtr, 1, mapPtr+3, 1, dot)); + matrix->set(SkMatrix::kMPersp1, SkScalarDotDiv(3, patchPtr, 1, mapPtr+6, 1, dot)); + + patchPtr = (const SkScalar*)(const void*)&diff; + matrix->set(SkMatrix::kMTransX, SkScalarDotDiv(3, patchPtr, 1, mapPtr, 1, dot)); + matrix->set(SkMatrix::kMTransY, SkScalarDotDiv(3, patchPtr, 1, mapPtr+3, 1, dot)); + matrix->set(SkMatrix::kMPersp2, SK_UnitScalar1); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +Sk3DView::Sk3DView() +{ + fInitialRec.fMatrix.reset(); + fRec = &fInitialRec; +} + +Sk3DView::~Sk3DView() +{ + Rec* rec = fRec; + while (rec != &fInitialRec) { + Rec* next = rec->fNext; + SkDELETE(rec); + rec = next; + } +} + +void Sk3DView::save() +{ + Rec* rec = SkNEW(Rec); + rec->fNext = fRec; + rec->fMatrix = fRec->fMatrix; + fRec = rec; +} + +void Sk3DView::restore() +{ + SkASSERT(fRec != &fInitialRec); + Rec* next = fRec->fNext; + SkDELETE(fRec); + fRec = next; +} + +void Sk3DView::translate(SkScalar x, SkScalar y, SkScalar z) +{ + fRec->fMatrix.preTranslate(x, y, z); +} + +void Sk3DView::rotateX(SkScalar deg) +{ + fRec->fMatrix.preRotateX(deg); +} + +void Sk3DView::rotateY(SkScalar deg) +{ + fRec->fMatrix.preRotateY(deg); +} + +void Sk3DView::rotateZ(SkScalar deg) +{ + fRec->fMatrix.preRotateZ(deg); +} + +SkScalar Sk3DView::dotWithNormal(SkScalar x, SkScalar y, SkScalar z) const +{ + SkPatch3D patch; + patch.transform(fRec->fMatrix); + return patch.dotWith(x, y, z); +} + +void Sk3DView::getMatrix(SkMatrix* matrix) const +{ + if (matrix != NULL) + { + SkPatch3D patch; + patch.transform(fRec->fMatrix); + fCamera.patchToMatrix(patch, matrix); + } +} + +#include "SkCanvas.h" + +void Sk3DView::applyToCanvas(SkCanvas* canvas) const +{ + SkMatrix matrix; + + this->getMatrix(&matrix); + canvas->concat(matrix); +} + diff --git a/skia/effects/SkColorFilters.cpp b/skia/effects/SkColorFilters.cpp new file mode 100644 index 0000000..f5178a8 --- /dev/null +++ b/skia/effects/SkColorFilters.cpp @@ -0,0 +1,553 @@ +/* libs/graphics/effects/SkColorFilters.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkColorFilter.h" +#include "SkColorPriv.h" +#include "SkPorterDuff.h" +#include "SkUtils.h" + +//#define TRACE_CreatePorterDuffFilter + +// common baseclass +class Sk_XfermodeColorFilter : public SkColorFilter { +protected: + Sk_XfermodeColorFilter(SkColor color) : fColor(SkPreMultiplyColor(color)) {} + + virtual void flatten(SkFlattenableWriteBuffer& buffer) + { + buffer.write32(fColor); + } + + Sk_XfermodeColorFilter(SkFlattenableReadBuffer& buffer) + { + fColor = buffer.readU32(); + } + + SkPMColor fColor; +}; + +class SkSrc_XfermodeColorFilter : public Sk_XfermodeColorFilter { +public: + SkSrc_XfermodeColorFilter(SkColor color) : INHERITED(color) {} + + virtual uint32_t getFlags() + { + if (SkGetPackedA32(fColor) == 0xFF) + return kAlphaUnchanged_Flag | kHasFilter16_Flag; + else + return 0; + } + + virtual void filterSpan(const SkPMColor shader[], int count, SkPMColor result[]) + { + sk_memset32(result, fColor, count); + } + + virtual void filterSpan16(const uint16_t shader[], int count, uint16_t result[]) + { + SkASSERT(this->getFlags() & kHasFilter16_Flag); + + sk_memset16(result, SkPixel32ToPixel16(fColor), count); + } + +protected: + virtual Factory getFactory() { return CreateProc; } + + SkSrc_XfermodeColorFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {} + +private: + static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) + { + return SkNEW_ARGS(SkSrc_XfermodeColorFilter, (buffer)); + } + + typedef Sk_XfermodeColorFilter INHERITED; +}; + +class SkSrcOver_XfermodeColorFilter : public Sk_XfermodeColorFilter { +public: + SkSrcOver_XfermodeColorFilter(SkColor color) : INHERITED(color) {} + + virtual uint32_t getFlags() + { + if (SkGetPackedA32(fColor) == 0xFF) + return kAlphaUnchanged_Flag | kHasFilter16_Flag; + else + return 0; + } + + virtual void filterSpan(const SkPMColor shader[], int count, SkPMColor result[]) + { + SkPMColor src = fColor; + unsigned scale = SkAlpha255To256(255 - SkGetPackedA32(src)); + + for (int i = 0; i < count; i++) + result[i] = src + SkAlphaMulQ(shader[i], scale); + } + + virtual void filterSpan16(const uint16_t shader[], int count, uint16_t result[]) + { + SkASSERT(this->getFlags() & kHasFilter16_Flag); + + sk_memset16(result, SkPixel32ToPixel16(fColor), count); + } + +protected: + virtual Factory getFactory() { return CreateProc; } + + SkSrcOver_XfermodeColorFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {} + +private: + static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) + { + return SkNEW_ARGS(SkSrcOver_XfermodeColorFilter, (buffer)); + } + + typedef Sk_XfermodeColorFilter INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +class SkXfermodeColorFilter : public Sk_XfermodeColorFilter { +public: + SkXfermodeColorFilter(SkColor color, SkXfermodeProc proc, + SkXfermodeProc16 proc16) : INHERITED(color) + { + fProc = proc; + fProc16 = proc16; + } + + virtual uint32_t getFlags() + { + return fProc16 ? (kAlphaUnchanged_Flag | kHasFilter16_Flag) : 0; + } + + virtual void filterSpan(const SkPMColor shader[], int count, SkPMColor result[]) + { + SkPMColor color = fColor; + SkXfermodeProc proc = fProc; + + for (int i = 0; i < count; i++) + result[i] = proc(color, shader[i]); + } + + virtual void filterSpan16(const uint16_t shader[], int count, uint16_t result[]) + { + SkASSERT(this->getFlags() & kHasFilter16_Flag); + + SkPMColor color = fColor; + SkXfermodeProc16 proc16 = fProc16; + + for (int i = 0; i < count; i++) + result[i] = proc16(color, shader[i]); + } + +protected: + virtual void flatten(SkFlattenableWriteBuffer& buffer) { + this->INHERITED::flatten(buffer); + buffer.writeFunctionPtr((void*)fProc); + buffer.writeFunctionPtr((void*)fProc16); + } + + virtual Factory getFactory() { + return CreateProc; + } + + SkXfermodeColorFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) { + fProc = (SkXfermodeProc) buffer.readFunctionPtr(); + fProc16 = (SkXfermodeProc16) buffer.readFunctionPtr(); + } +private: + static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) { + return SkNEW_ARGS(SkXfermodeColorFilter, (buffer)); + } + + SkXfermodeProc fProc; + SkXfermodeProc16 fProc16; + + typedef Sk_XfermodeColorFilter INHERITED; +}; + +SkColorFilter* SkColorFilter::CreatXfermodeProcFilter(SkColor color, + SkXfermodeProc proc, + SkXfermodeProc16 proc16) +{ + return proc ? + SkNEW_ARGS(SkXfermodeColorFilter, (color, proc, proc16)) : + NULL; +} + +/////////////////////////////////////////////////////////////////////////////// + +SkColorFilter* SkColorFilter::CreatePorterDuffFilter(SkColor color, + SkPorterDuff::Mode mode) +{ + unsigned alpha = SkColorGetA(color); + + // first collaps some modes if possible + + if (SkPorterDuff::kClear_Mode == mode) + { + color = 0; + mode = SkPorterDuff::kSrc_Mode; + } + else if (SkPorterDuff::kSrcOver_Mode == mode) + { + if (0 == alpha) + { + mode = SkPorterDuff::kDst_Mode; + } + else if (255 == alpha) + { + mode = SkPorterDuff::kSrc_Mode; + } + // else just stay srcover + } + + // weed out combinations that are noops, and just return null + if (SkPorterDuff::kDst_Mode == mode || + (0 == alpha && (SkPorterDuff::kSrcOver_Mode == mode || + SkPorterDuff::kDstOver_Mode == mode || + SkPorterDuff::kDstOut_Mode == mode || + SkPorterDuff::kSrcATop_Mode == mode || + SkPorterDuff::kXor_Mode == mode || + SkPorterDuff::kDarken_Mode == mode)) || + (0xFF == alpha && SkPorterDuff::kDstIn_Mode == mode)) + { + return NULL; + } + + switch (mode) { + case SkPorterDuff::kSrc_Mode: + return SkNEW_ARGS(SkSrc_XfermodeColorFilter, (color)); + case SkPorterDuff::kSrcOver_Mode: + return SkNEW_ARGS(SkSrcOver_XfermodeColorFilter, (color)); + default: + return SkColorFilter::CreatXfermodeProcFilter(color, + SkPorterDuff::GetXfermodeProc(mode), + SkPorterDuff::GetXfermodeProc16(mode, color)); + } +} + +///////////////////////////////////////////////////////////////////////////////////////////////// + +static inline unsigned pin(unsigned value, unsigned max) +{ + if (value > max) + value = max; + return value; +} + +static inline unsigned SkUClampMax(unsigned value, unsigned max) +{ + SkASSERT((int32_t)value >= 0); + SkASSERT((int32_t)max >= 0); + + int diff = max - value; + // clear diff if diff is positive + diff &= diff >> 31; + + return value + diff; +} + +class SkLightingColorFilter : public SkColorFilter { +public: + SkLightingColorFilter(SkColor mul, SkColor add) : fMul(mul), fAdd(add) {} + + virtual void filterSpan(const SkPMColor shader[], int count, SkPMColor result[]) + { + unsigned scaleR = SkAlpha255To256(SkColorGetR(fMul)); + unsigned scaleG = SkAlpha255To256(SkColorGetG(fMul)); + unsigned scaleB = SkAlpha255To256(SkColorGetB(fMul)); + + unsigned addR = SkColorGetR(fAdd); + unsigned addG = SkColorGetG(fAdd); + unsigned addB = SkColorGetB(fAdd); + + for (int i = 0; i < count; i++) + { + SkPMColor c = shader[i]; + if (c) + { + unsigned a = SkGetPackedA32(c); + unsigned scaleA = SkAlpha255To256(a); + unsigned r = pin(SkAlphaMul(SkGetPackedR32(c), scaleR) + SkAlphaMul(addR, scaleA), a); + unsigned g = pin(SkAlphaMul(SkGetPackedG32(c), scaleG) + SkAlphaMul(addG, scaleA), a); + unsigned b = pin(SkAlphaMul(SkGetPackedB32(c), scaleB) + SkAlphaMul(addB, scaleA), a); + c = SkPackARGB32(a, r, g, b); + } + result[i] = c; + } + } + +protected: + virtual void flatten(SkFlattenableWriteBuffer& buffer) + { + buffer.write32(fMul); + buffer.write32(fAdd); + } + + virtual Factory getFactory() + { + return CreateProc; + } + + SkLightingColorFilter(SkFlattenableReadBuffer& buffer) + { + fMul = buffer.readU32(); + fAdd = buffer.readU32(); + } + + SkColor fMul, fAdd; + +private: + static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) + { + return SkNEW_ARGS(SkLightingColorFilter, (buffer)); + } +}; + +class SkLightingColorFilter_JustAdd : public SkLightingColorFilter { +public: + SkLightingColorFilter_JustAdd(SkColor mul, SkColor add) + : INHERITED(mul, add) {} + + virtual void filterSpan(const SkPMColor shader[], int count, SkPMColor result[]) + { + unsigned addR = SkColorGetR(fAdd); + unsigned addG = SkColorGetG(fAdd); + unsigned addB = SkColorGetB(fAdd); + + for (int i = 0; i < count; i++) + { + SkPMColor c = shader[i]; + if (c) + { + unsigned a = SkGetPackedA32(c); + unsigned scaleA = SkAlpha255To256(a); + unsigned r = pin(SkGetPackedR32(c) + SkAlphaMul(addR, scaleA), a); + unsigned g = pin(SkGetPackedG32(c) + SkAlphaMul(addG, scaleA), a); + unsigned b = pin(SkGetPackedB32(c) + SkAlphaMul(addB, scaleA), a); + c = SkPackARGB32(a, r, g, b); + } + result[i] = c; + } + } + +protected: + virtual Factory getFactory() { return CreateProc; } + + SkLightingColorFilter_JustAdd(SkFlattenableReadBuffer& buffer) + : INHERITED(buffer) {} + +private: + static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) + { + return SkNEW_ARGS(SkLightingColorFilter_JustAdd, (buffer)); + } + typedef SkLightingColorFilter INHERITED; +}; + +class SkLightingColorFilter_JustMul : public SkLightingColorFilter { +public: + SkLightingColorFilter_JustMul(SkColor mul, SkColor add) + : INHERITED(mul, add) {} + + virtual void filterSpan(const SkPMColor shader[], int count, SkPMColor result[]) + { + unsigned scaleR = SkAlpha255To256(SkColorGetR(fMul)); + unsigned scaleG = SkAlpha255To256(SkColorGetG(fMul)); + unsigned scaleB = SkAlpha255To256(SkColorGetB(fMul)); + + for (int i = 0; i < count; i++) + { + SkPMColor c = shader[i]; + if (c) + { + unsigned a = SkGetPackedA32(c); + unsigned r = SkAlphaMul(SkGetPackedR32(c), scaleR); + unsigned g = SkAlphaMul(SkGetPackedG32(c), scaleG); + unsigned b = SkAlphaMul(SkGetPackedB32(c), scaleB); + c = SkPackARGB32(a, r, g, b); + } + result[i] = c; + } + } + +protected: + virtual Factory getFactory() { return CreateProc; } + + SkLightingColorFilter_JustMul(SkFlattenableReadBuffer& buffer) + : INHERITED(buffer) {} + +private: + static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) + { + return SkNEW_ARGS(SkLightingColorFilter_JustMul, (buffer)); + } + + typedef SkLightingColorFilter INHERITED; +}; + +class SkLightingColorFilter_SingleMul : public SkLightingColorFilter { +public: + SkLightingColorFilter_SingleMul(SkColor mul, SkColor add) + : INHERITED(mul, add) + { + SkASSERT(SkColorGetR(add) == 0); + SkASSERT(SkColorGetG(add) == 0); + SkASSERT(SkColorGetB(add) == 0); + SkASSERT(SkColorGetR(mul) == SkColorGetG(mul)); + SkASSERT(SkColorGetR(mul) == SkColorGetB(mul)); + } + + virtual uint32_t getFlags() + { + return this->INHERITED::getFlags() | (kAlphaUnchanged_Flag | kHasFilter16_Flag); + } + + virtual void filterSpan16(const uint16_t shader[], int count, uint16_t result[]) + { + // all mul components are the same + unsigned scale = SkAlpha255To256(SkColorGetR(fMul)); + + if (count > 0) + do { + *result++ = SkAlphaMulRGB16(*shader++, scale); + } while (--count > 0); + } + +protected: + virtual Factory getFactory() { return CreateProc; } + + SkLightingColorFilter_SingleMul(SkFlattenableReadBuffer& buffer) + : INHERITED(buffer) {} + +private: + static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) + { + return SkNEW_ARGS(SkLightingColorFilter_SingleMul, (buffer)); + } + + typedef SkLightingColorFilter INHERITED; +}; + +class SkLightingColorFilter_NoPin : public SkLightingColorFilter { +public: + SkLightingColorFilter_NoPin(SkColor mul, SkColor add) + : INHERITED(mul, add) {} + + virtual void filterSpan(const SkPMColor shader[], int count, SkPMColor result[]) + { + unsigned scaleR = SkAlpha255To256(SkColorGetR(fMul)); + unsigned scaleG = SkAlpha255To256(SkColorGetG(fMul)); + unsigned scaleB = SkAlpha255To256(SkColorGetB(fMul)); + + unsigned addR = SkColorGetR(fAdd); + unsigned addG = SkColorGetG(fAdd); + unsigned addB = SkColorGetB(fAdd); + + for (int i = 0; i < count; i++) + { + SkPMColor c = shader[i]; + if (c) + { + unsigned a = SkGetPackedA32(c); + unsigned scaleA = SkAlpha255To256(a); + unsigned r = SkAlphaMul(SkGetPackedR32(c), scaleR) + SkAlphaMul(addR, scaleA); + unsigned g = SkAlphaMul(SkGetPackedG32(c), scaleG) + SkAlphaMul(addG, scaleA); + unsigned b = SkAlphaMul(SkGetPackedB32(c), scaleB) + SkAlphaMul(addB, scaleA); + c = SkPackARGB32(a, r, g, b); + } + result[i] = c; + } + } + +protected: + virtual Factory getFactory() { return CreateProc; } + + SkLightingColorFilter_NoPin(SkFlattenableReadBuffer& buffer) + : INHERITED(buffer) {} + +private: + static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) + { + return SkNEW_ARGS(SkLightingColorFilter_NoPin, (buffer)); + } + + typedef SkLightingColorFilter INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////////////// + +class SkSimpleColorFilter : public SkColorFilter { +protected: + void filterSpan(const SkPMColor src[], int count, SkPMColor result[]) + { + if (result != src) + memcpy(result, src, count * sizeof(SkPMColor)); + } + + virtual void flatten(SkFlattenableWriteBuffer& buffer) + { + } + + virtual Factory getFactory() + { + return CreateProc; + } + + static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) + { + return SkNEW(SkSimpleColorFilter); + } +}; + +SkColorFilter* SkColorFilter::CreateLightingFilter(SkColor mul, SkColor add) +{ + mul &= 0x00FFFFFF; + add &= 0x00FFFFFF; + + if (0xFFFFFF == mul) + { + if (0 == add) + return SkNEW(SkSimpleColorFilter); // no change to the colors + else + return SkNEW_ARGS(SkLightingColorFilter_JustAdd, (mul, add)); + } + + if (0 == add) + { + if (SkColorGetR(mul) == SkColorGetG(mul) && + SkColorGetR(mul) == SkColorGetB(mul)) + { + return SkNEW_ARGS(SkLightingColorFilter_SingleMul, (mul, add)); + } + else + { + return SkNEW_ARGS(SkLightingColorFilter_JustMul, (mul, add)); + } + } + + if (SkColorGetR(mul) + SkColorGetR(add) <= 255 && + SkColorGetG(mul) + SkColorGetG(add) <= 255 && + SkColorGetB(mul) + SkColorGetB(add) <= 255) + return SkNEW_ARGS(SkLightingColorFilter_NoPin, (mul, add)); + + return SkNEW_ARGS(SkLightingColorFilter, (mul, add)); +} + diff --git a/skia/effects/SkColorMatrix.cpp b/skia/effects/SkColorMatrix.cpp new file mode 100644 index 0000000..0a20990 --- /dev/null +++ b/skia/effects/SkColorMatrix.cpp @@ -0,0 +1,165 @@ +#include "SkColorMatrix.h" + +#define kRScale 0 +#define kGScale 6 +#define kBScale 12 +#define kAScale 18 + +void SkColorMatrix::setIdentity() +{ + memset(fMat, 0, sizeof(fMat)); + fMat[kRScale] = fMat[kGScale] = fMat[kBScale] = fMat[kAScale] = SK_Scalar1; +} + +void SkColorMatrix::setScale(SkScalar rScale, SkScalar gScale, SkScalar bScale, + SkScalar aScale) +{ + memset(fMat, 0, sizeof(fMat)); + fMat[kRScale] = rScale; + fMat[kGScale] = gScale; + fMat[kBScale] = bScale; + fMat[kAScale] = aScale; +} + +/////////////////////////////////////////////////////////////////////////////// + +void SkColorMatrix::setRotate(Axis axis, SkScalar degrees) +{ + SkScalar S, C; + + S = SkScalarSinCos(SkDegreesToRadians(degrees), &C); + + this->setSinCos(axis, S, C); +} + +void SkColorMatrix::setSinCos(Axis axis, SkScalar sine, SkScalar cosine) +{ + SkASSERT((unsigned)axis < 3); + + static const uint8_t gRotateIndex[] = { + 6, 7, 11, 12, + 0, 2, 15, 17, + 0, 1, 5, 6, + }; + const uint8_t* index = gRotateIndex + axis * 4; + + this->setIdentity(); + fMat[index[0]] = cosine; + fMat[index[1]] = sine; + fMat[index[2]] = -sine; + fMat[index[3]] = cosine; +} + +void SkColorMatrix::preRotate(Axis axis, SkScalar degrees) +{ + SkColorMatrix tmp; + tmp.setRotate(axis, degrees); + this->preConcat(tmp); +} + +void SkColorMatrix::postRotate(Axis axis, SkScalar degrees) +{ + SkColorMatrix tmp; + tmp.setRotate(axis, degrees); + this->postConcat(tmp); +} + +/////////////////////////////////////////////////////////////////////////////// + +void SkColorMatrix::setConcat(const SkColorMatrix& matA, + const SkColorMatrix& matB) +{ + SkScalar tmp[20]; + SkScalar* result = fMat; + + if (&matA == this || &matB == this) + result = tmp; + + const SkScalar* a = matA.fMat; + const SkScalar* b = matB.fMat; + + int index = 0; + for (int j = 0; j < 20; j += 5) + { + for (int i = 0; i < 4; i++) + { + result[index++] = SkScalarMul(a[j + 0], b[i + 0]) + + SkScalarMul(a[j + 1], b[i + 5]) + + SkScalarMul(a[j + 2], b[i + 10]) + + SkScalarMul(a[j + 3], b[i + 15]); + } + result[index++] = SkScalarMul(a[j + 0], b[4]) + + SkScalarMul(a[j + 1], b[9]) + + SkScalarMul(a[j + 2], b[14]) + + SkScalarMul(a[j + 3], b[19]) + + a[j + 4]; + } + + if (fMat != result) + memcpy(fMat, result, sizeof(fMat)); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void setrow(SkScalar row[], SkScalar r, SkScalar g, SkScalar b) +{ + row[0] = r; + row[1] = g; + row[2] = b; +} + +static const SkScalar kHueR = SkFloatToScalar(0.213f); +static const SkScalar kHueG = SkFloatToScalar(0.715f); +static const SkScalar kHueB = SkFloatToScalar(0.072f); + +void SkColorMatrix::setSaturation(SkScalar sat) +{ + memset(fMat, 0, sizeof(fMat)); + + const SkScalar R = SkScalarMul(kHueR, SK_Scalar1 - sat); + const SkScalar G = SkScalarMul(kHueG, SK_Scalar1 - sat); + const SkScalar B = SkScalarMul(kHueB, SK_Scalar1 - sat); + + setrow(fMat + 0, R + sat, G, B); + setrow(fMat + 5, R, G + sat, B); + setrow(fMat + 10, R, G, B + sat); + fMat[18] = SK_Scalar1; +} + +static const SkScalar kR2Y = SkFloatToScalar(0.299f); +static const SkScalar kG2Y = SkFloatToScalar(0.587f); +static const SkScalar kB2Y = SkFloatToScalar(0.114f); + +static const SkScalar kR2U = SkFloatToScalar(-0.16874f); +static const SkScalar kG2U = SkFloatToScalar(-0.33126f); +static const SkScalar kB2U = SkFloatToScalar(0.5f); + +static const SkScalar kR2V = SkFloatToScalar(0.5f); +static const SkScalar kG2V = SkFloatToScalar(-0.41869f); +static const SkScalar kB2V = SkFloatToScalar(-0.08131f); + +void SkColorMatrix::setRGB2YUV() +{ + memset(fMat, 0, sizeof(fMat)); + + setrow(fMat + 0, kR2Y, kG2Y, kB2Y); + setrow(fMat + 5, kR2U, kG2U, kB2U); + setrow(fMat + 10, kR2V, kG2V, kB2V); + fMat[18] = SK_Scalar1; +} + +static const SkScalar kV2R = SkFloatToScalar(1.402f); +static const SkScalar kU2G = SkFloatToScalar(-0.34414f); +static const SkScalar kV2G = SkFloatToScalar(-0.71414f); +static const SkScalar kU2B = SkFloatToScalar(1.772f); + +void SkColorMatrix::setYUV2RGB() +{ + memset(fMat, 0, sizeof(fMat)); + + setrow(fMat + 0, SK_Scalar1, 0, kV2R); + setrow(fMat + 5, SK_Scalar1, kU2G, kV2G); + setrow(fMat + 10, SK_Scalar1, kU2B, 0); + fMat[18] = SK_Scalar1; +} + diff --git a/skia/effects/SkColorMatrixFilter.cpp b/skia/effects/SkColorMatrixFilter.cpp new file mode 100644 index 0000000..751cd30 --- /dev/null +++ b/skia/effects/SkColorMatrixFilter.cpp @@ -0,0 +1,330 @@ +#include "SkColorMatrixFilter.h" +#include "SkColorMatrix.h" +#include "SkColorPriv.h" +#include "SkUnPreMultiply.h" + +static int32_t rowmul4(const int32_t array[], unsigned r, unsigned g, + unsigned b, unsigned a) { + return array[0] * r + array[1] * g + array[2] * b + array[3] * a + array[4]; +} + +static int32_t rowmul3(const int32_t array[], unsigned r, unsigned g, + unsigned b) { + return array[0] * r + array[1] * g + array[2] * b + array[4]; +} + +static void General(SkColorMatrixFilter::State* state, + unsigned r, unsigned g, unsigned b, unsigned a) { + const int32_t* SK_RESTRICT array = state->fArray; + const int shift = state->fShift; + int32_t* SK_RESTRICT result = state->fResult; + + result[0] = rowmul4(&array[0], r, g, b, a) >> shift; + result[1] = rowmul4(&array[5], r, g, b, a) >> shift; + result[2] = rowmul4(&array[10], r, g, b, a) >> shift; + result[3] = rowmul4(&array[15], r, g, b, a) >> shift; +} + +static void General16(SkColorMatrixFilter::State* state, + unsigned r, unsigned g, unsigned b, unsigned a) { + const int32_t* SK_RESTRICT array = state->fArray; + int32_t* SK_RESTRICT result = state->fResult; + + result[0] = rowmul4(&array[0], r, g, b, a) >> 16; + result[1] = rowmul4(&array[5], r, g, b, a) >> 16; + result[2] = rowmul4(&array[10], r, g, b, a) >> 16; + result[3] = rowmul4(&array[15], r, g, b, a) >> 16; +} + +static void AffineAdd(SkColorMatrixFilter::State* state, + unsigned r, unsigned g, unsigned b, unsigned a) { + const int32_t* SK_RESTRICT array = state->fArray; + const int shift = state->fShift; + int32_t* SK_RESTRICT result = state->fResult; + + result[0] = rowmul3(&array[0], r, g, b) >> shift; + result[1] = rowmul3(&array[5], r, g, b) >> shift; + result[2] = rowmul3(&array[10], r, g, b) >> shift; + result[3] = a; +} + +static void AffineAdd16(SkColorMatrixFilter::State* state, + unsigned r, unsigned g, unsigned b, unsigned a) { + const int32_t* SK_RESTRICT array = state->fArray; + int32_t* SK_RESTRICT result = state->fResult; + + result[0] = rowmul3(&array[0], r, g, b) >> 16; + result[1] = rowmul3(&array[5], r, g, b) >> 16; + result[2] = rowmul3(&array[10], r, g, b) >> 16; + result[3] = a; +} + +static void ScaleAdd(SkColorMatrixFilter::State* state, + unsigned r, unsigned g, unsigned b, unsigned a) { + const int32_t* SK_RESTRICT array = state->fArray; + const int shift = state->fShift; + int32_t* SK_RESTRICT result = state->fResult; + + result[0] = (array[0] * r + array[4]) >> shift; + result[1] = (array[6] * g + array[9]) >> shift; + result[2] = (array[12] * b + array[14]) >> shift; + result[3] = a; +} + +static void ScaleAdd16(SkColorMatrixFilter::State* state, + unsigned r, unsigned g, unsigned b, unsigned a) { + const int32_t* SK_RESTRICT array = state->fArray; + int32_t* SK_RESTRICT result = state->fResult; + + result[0] = (array[0] * r + array[4]) >> 16; + result[1] = (array[6] * g + array[9]) >> 16; + result[2] = (array[12] * b + array[14]) >> 16; + result[3] = a; +} + +static void Add(SkColorMatrixFilter::State* state, + unsigned r, unsigned g, unsigned b, unsigned a) { + const int32_t* SK_RESTRICT array = state->fArray; + const int shift = state->fShift; + int32_t* SK_RESTRICT result = state->fResult; + + result[0] = r + (array[4] >> shift); + result[1] = g + (array[9] >> shift); + result[2] = b + (array[14] >> shift); + result[3] = a; +} + +static void Add16(SkColorMatrixFilter::State* state, + unsigned r, unsigned g, unsigned b, unsigned a) { + const int32_t* SK_RESTRICT array = state->fArray; + int32_t* SK_RESTRICT result = state->fResult; + + result[0] = r + (array[4] >> 16); + result[1] = g + (array[9] >> 16); + result[2] = b + (array[14] >> 16); + result[3] = a; +} + +#define kNO_ALPHA_FLAGS (SkColorFilter::kAlphaUnchanged_Flag | \ + SkColorFilter::kHasFilter16_Flag) + +void SkColorMatrixFilter::setup(const SkScalar SK_RESTRICT src[20]) { + if (NULL == src) { + fProc = NULL; // signals identity + fFlags = kNO_ALPHA_FLAGS; + // fState is undefined, but that is OK, since we shouldn't look at it + return; + } + + int32_t* SK_RESTRICT array = fState.fArray; + + int i; + SkFixed max = 0; + + for (int i = 0; i < 20; i++) { + SkFixed value = SkScalarToFixed(src[i]); + array[i] = value; + value = SkAbs32(value); + max = SkMax32(max, value); + } + + /* All of fArray[] values must fit in 23 bits, to safely allow me to + multiply them by 8bit unsigned values, and get a signed answer without + overflow. This means clz needs to be 9 or bigger + */ + int bits = SkCLZ(max); + int32_t one = SK_Fixed1; + + fState.fShift = 16; // we are starting out as fixed 16.16 + if (bits < 9) { + bits = 8 - bits; + fState.fShift -= bits; + for (i = 0; i < 20; i++) { + array[i] >>= bits; + } + one >>= bits; + } + + // check if we have to munge Alpha + int32_t changesAlpha = (array[15] | array[16] | array[17] | + (array[18] - one) | array[19]); + int32_t usesAlpha = (array[3] | array[8] | array[13]); + bool shiftIs16 = (16 == fState.fShift); + + if (changesAlpha | usesAlpha) { + fProc = shiftIs16 ? General16 : General; + fFlags = changesAlpha ? 0 : SkColorFilter::kAlphaUnchanged_Flag; + } else { + fFlags = kNO_ALPHA_FLAGS; + + int32_t needsScale = (array[0] - one) | // red axis + (array[6] - one) | // green axis + (array[12] - one); // blue axis + + int32_t needs3x3 = array[1] | array[2] | // red off-axis + array[5] | array[7] | // green off-axis + array[10] | array[11]; // blue off-axis + + if (needs3x3) { + fProc = shiftIs16 ? AffineAdd16 : AffineAdd; + } else if (needsScale) { + fProc = shiftIs16 ? ScaleAdd16 : ScaleAdd; + } else if (array[4] | array[9] | array[14]) { // needs add + fProc = shiftIs16 ? Add16 : Add; + } else { + fProc = NULL; // identity + } + } + + /* preround our add values so we get a rounded shift. We do this after we + analyze the array, so we don't miss the case where the caller has zeros + which could make us accidentally take the General or Add case. + */ + if (NULL != fProc) { + int32_t add = 1 << (fState.fShift - 1); + array[4] += add; + array[9] += add; + array[14] += add; + array[19] += add; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static int32_t pin(int32_t value, int32_t max) { + if (value < 0) { + value = 0; + } + if (value > max) { + value = max; + } + return value; +} + +SkColorMatrixFilter::SkColorMatrixFilter() { + this->setup(NULL); +} + +SkColorMatrixFilter::SkColorMatrixFilter(const SkColorMatrix& cm) { + this->setup(cm.fMat); +} + +SkColorMatrixFilter::SkColorMatrixFilter(const SkScalar array[20]) { + this->setup(array); +} + +uint32_t SkColorMatrixFilter::getFlags() { + return this->INHERITED::getFlags() | fFlags; +} + +void SkColorMatrixFilter::filterSpan(const SkPMColor src[], int count, + SkPMColor dst[]) { + Proc proc = fProc; + State* state = &fState; + int32_t* SK_RESTRICT result = state->fResult; + + if (NULL == proc) { + if (src != dst) { + memcpy(dst, src, count * sizeof(SkPMColor)); + } + return; + } + + const SkUnPreMultiply::Scale* table = SkUnPreMultiply::GetScaleTable(); + + for (int i = 0; i < count; i++) { + SkPMColor c = src[i]; + + unsigned r = SkGetPackedR32(c); + unsigned g = SkGetPackedG32(c); + unsigned b = SkGetPackedB32(c); + unsigned a = SkGetPackedA32(c); + + // need our components to be un-premultiplied + if (255 != a) { + SkUnPreMultiply::Scale scale = table[a]; + r = SkUnPreMultiply::ApplyScale(scale, r); + g = SkUnPreMultiply::ApplyScale(scale, g); + b = SkUnPreMultiply::ApplyScale(scale, b); + + SkASSERT(r <= 255); + SkASSERT(g <= 255); + SkASSERT(b <= 255); + } + + proc(state, r, g, b, a); + + r = pin(result[0], SK_R32_MASK); + g = pin(result[1], SK_G32_MASK); + b = pin(result[2], SK_B32_MASK); + a = pin(result[3], SK_A32_MASK); + // re-prepremultiply if needed + if (255 != a) { + int scale = SkAlpha255To256(a); + r = SkAlphaMul(r, scale); + g = SkAlphaMul(g, scale); + b = SkAlphaMul(b, scale); + } + dst[i] = SkPackARGB32(a, r, g, b); + } +} + +#define SK_RG_BITS_DIFF (SK_G16_BITS - SK_R16_BITS) +#define SK_BG_BITS_DIFF (SK_G16_BITS - SK_B16_BITS) + +void SkColorMatrixFilter::filterSpan16(const uint16_t src[], int count, + uint16_t dst[]) { + SkASSERT(fFlags & SkColorFilter::kHasFilter16_Flag); + + Proc proc = fProc; + State* state = &fState; + int32_t* SK_RESTRICT result = state->fResult; + + if (NULL == proc) { + if (src != dst) { + memcpy(dst, src, count * sizeof(uint16_t)); + } + return; + } + + for (int i = 0; i < count; i++) { + uint16_t c = src[i]; + + unsigned r = SkGetPackedR16(c); + unsigned g = SkGetPackedG16(c); + unsigned b = SkGetPackedB16(c); + + r = (r << SK_RG_BITS_DIFF) | (r >> (SK_R16_BITS - 1)); + b = (b << SK_BG_BITS_DIFF) | (b >> (SK_B16_BITS - 1)); + proc(state, r, g, b, 0); + + r = pin(result[0], SK_G16_MASK) >> SK_RG_BITS_DIFF; + g = pin(result[1], SK_G16_MASK); + b = pin(result[2], SK_G16_MASK) >> SK_BG_BITS_DIFF; + dst[i] = SkPackRGB16(r, g, b); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void SkColorMatrixFilter::flatten(SkFlattenableWriteBuffer& buffer) { + this->INHERITED::flatten(buffer); + + buffer.writeFunctionPtr((void*)fProc); + buffer.writeMul4(&fState, sizeof(fState)); + buffer.write32(fFlags); +} + +SkFlattenable::Factory SkColorMatrixFilter::getFactory() { return CreateProc; } + +SkColorMatrixFilter::SkColorMatrixFilter(SkFlattenableReadBuffer& buffer) + : INHERITED(buffer) { + fProc = (Proc)buffer.readFunctionPtr(); + buffer.read(&fState, sizeof(fState)); + fFlags = buffer.readU32(); +} + +SkFlattenable* SkColorMatrixFilter::CreateProc(SkFlattenableReadBuffer& buf) { + return SkNEW_ARGS(SkColorMatrixFilter, (buf)); +} + diff --git a/skia/effects/SkCornerPathEffect.cpp b/skia/effects/SkCornerPathEffect.cpp new file mode 100644 index 0000000..ad92af1 --- /dev/null +++ b/skia/effects/SkCornerPathEffect.cpp @@ -0,0 +1,157 @@ +/* libs/graphics/effects/SkCornerPathEffect.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkCornerPathEffect.h" +#include "SkPath.h" +#include "SkPoint.h" +#include "SkBuffer.h" + +SkCornerPathEffect::SkCornerPathEffect(SkScalar radius) : fRadius(radius) +{ +} + +SkCornerPathEffect::~SkCornerPathEffect() +{ +} + +static bool ComputeStep(const SkPoint& a, const SkPoint& b, SkScalar radius, SkPoint* step) +{ + SkScalar dist = SkPoint::Distance(a, b); + + step->set(b.fX - a.fX, b.fY - a.fY); + + if (dist <= radius * 2) { + step->scale(SK_ScalarHalf); + return false; + } + else { + step->scale(SkScalarDiv(radius, dist)); + return true; + } +} + +bool SkCornerPathEffect::filterPath(SkPath* dst, const SkPath& src, SkScalar* width) +{ + if (fRadius == 0) + return false; + + SkPath::Iter iter(src, false); + SkPath::Verb verb, prevVerb = (SkPath::Verb)-1; + SkPoint pts[4]; + + bool closed; + SkPoint moveTo, lastCorner; + SkVector firstStep, step; + bool prevIsValid = true; + + // to avoid warnings + moveTo.set(0, 0); + firstStep.set(0, 0); + lastCorner.set(0, 0); + + for (;;) { + switch (verb = iter.next(pts)) { + case SkPath::kMove_Verb: + closed = iter.isClosedContour(); + if (closed) { + moveTo = pts[0]; + prevIsValid = false; + } + else { + dst->moveTo(pts[0]); + prevIsValid = true; + } + break; + case SkPath::kLine_Verb: + { + bool drawSegment = ComputeStep(pts[0], pts[1], fRadius, &step); + // prev corner + if (!prevIsValid) { + dst->moveTo(moveTo + step); + prevIsValid = true; + } + else { + dst->quadTo(pts[0].fX, pts[0].fY, pts[0].fX + step.fX, pts[0].fY + step.fY); + } + if (drawSegment) { + dst->lineTo(pts[1].fX - step.fX, pts[1].fY - step.fY); + } + lastCorner = pts[1]; + prevIsValid = true; + } + break; + case SkPath::kQuad_Verb: + // TBD - just replicate the curve for now + if (!prevIsValid) + { + dst->moveTo(pts[0]); + prevIsValid = true; + } + dst->quadTo(pts[1], pts[2]); + lastCorner = pts[2]; + firstStep.set(0, 0); + break; + case SkPath::kCubic_Verb: + if (!prevIsValid) + { + dst->moveTo(pts[0]); + prevIsValid = true; + } + // TBD - just replicate the curve for now + dst->cubicTo(pts[1], pts[2], pts[3]); + lastCorner = pts[3]; + firstStep.set(0, 0); + break; + case SkPath::kClose_Verb: + if (firstStep.fX || firstStep.fY) + dst->quadTo(lastCorner.fX, lastCorner.fY, + lastCorner.fX + firstStep.fX, + lastCorner.fY + firstStep.fY); + dst->close(); + break; + case SkPath::kDone_Verb: + goto DONE; + } + + if (SkPath::kMove_Verb == prevVerb) + firstStep = step; + prevVerb = verb; + } +DONE: + return true; +} + +SkFlattenable::Factory SkCornerPathEffect::getFactory() +{ + return CreateProc; +} + +void SkCornerPathEffect::flatten(SkFlattenableWriteBuffer& buffer) +{ + buffer.writeScalar(fRadius); +} + +SkFlattenable* SkCornerPathEffect::CreateProc(SkFlattenableReadBuffer& buffer) +{ + return SkNEW_ARGS(SkCornerPathEffect, (buffer)); +} + +SkCornerPathEffect::SkCornerPathEffect(SkFlattenableReadBuffer& buffer) +{ + fRadius = buffer.readScalar(); +} + diff --git a/skia/effects/SkCullPoints.cpp b/skia/effects/SkCullPoints.cpp new file mode 100644 index 0000000..d272d01 --- /dev/null +++ b/skia/effects/SkCullPoints.cpp @@ -0,0 +1,168 @@ +/* libs/graphics/effects/SkCullPoints.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkCullPoints.h" +#include "Sk64.h" + +static bool cross_product_is_neg(const SkIPoint& v, int dx, int dy) +{ +#if 0 + return v.fX * dy - v.fY * dx < 0; +#else + Sk64 tmp0, tmp1; + + tmp0.setMul(v.fX, dy); + tmp1.setMul(dx, v.fY); + tmp0.sub(tmp1); + return tmp0.isNeg(); +#endif +} + +bool SkCullPoints::sect_test(int x0, int y0, int x1, int y1) const +{ + const SkIRect& r = fR; + + if (x0 < r.fLeft && x1 < r.fLeft || + x0 > r.fRight && x1 > r.fRight || + y0 < r.fTop && y1 < r.fTop || + y0 > r.fBottom && y1 > r.fBottom) + return false; + + // since the crossprod test is a little expensive, check for easy-in cases first + if (r.contains(x0, y0) || r.contains(x1, y1)) + return true; + + // At this point we're not sure, so we do a crossprod test + SkIPoint vec; + const SkIPoint* rAsQuad = fAsQuad; + + vec.set(x1 - x0, y1 - y0); + bool isNeg = cross_product_is_neg(vec, x0 - rAsQuad[0].fX, y0 - rAsQuad[0].fY); + for (int i = 1; i < 4; i++) { + if (cross_product_is_neg(vec, x0 - rAsQuad[i].fX, y0 - rAsQuad[i].fY) != isNeg) + { + return true; + } + } + return false; // we didn't intersect +} + +static void toQuad(const SkIRect& r, SkIPoint quad[4]) +{ + SkASSERT(quad); + + quad[0].set(r.fLeft, r.fTop); + quad[1].set(r.fRight, r.fTop); + quad[2].set(r.fRight, r.fBottom); + quad[3].set(r.fLeft, r.fBottom); +} + +SkCullPoints::SkCullPoints() +{ + SkIRect r; + r.setEmpty(); + this->reset(r); +} + +SkCullPoints::SkCullPoints(const SkIRect& r) +{ + this->reset(r); +} + +void SkCullPoints::reset(const SkIRect& r) +{ + fR = r; + toQuad(fR, fAsQuad); + fPrevPt.set(0, 0); + fPrevResult = kNo_Result; +} + +void SkCullPoints::moveTo(int x, int y) +{ + fPrevPt.set(x, y); + fPrevResult = kNo_Result; // so we trigger a movetolineto later +} + +SkCullPoints::LineToResult SkCullPoints::lineTo(int x, int y, SkIPoint line[]) +{ + SkASSERT(line != NULL); + + LineToResult result = kNo_Result; + int x0 = fPrevPt.fX; + int y0 = fPrevPt.fY; + + // need to upgrade sect_test to chop the result + // and to correctly return kLineTo_Result when the result is connected + // to the previous call-out + if (this->sect_test(x0, y0, x, y)) + { + line[0].set(x0, y0); + line[1].set(x, y); + + if (fPrevResult != kNo_Result && fPrevPt.equals(x0, y0)) + result = kLineTo_Result; + else + result = kMoveToLineTo_Result; + } + + fPrevPt.set(x, y); + fPrevResult = result; + + return result; +} + +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "SkPath.h" + +SkCullPointsPath::SkCullPointsPath() + : fCP(), fPath(NULL) +{ +} + +SkCullPointsPath::SkCullPointsPath(const SkIRect& r, SkPath* dst) + : fCP(r), fPath(dst) +{ +} + +void SkCullPointsPath::reset(const SkIRect& r, SkPath* dst) +{ + fCP.reset(r); + fPath = dst; +} + +void SkCullPointsPath::moveTo(int x, int y) +{ + fCP.moveTo(x, y); +} + +void SkCullPointsPath::lineTo(int x, int y) +{ + SkIPoint pts[2]; + + switch (fCP.lineTo(x, y, pts)) { + case SkCullPoints::kMoveToLineTo_Result: + fPath->moveTo(SkIntToScalar(pts[0].fX), SkIntToScalar(pts[0].fY)); + // fall through to the lineto case + case SkCullPoints::kLineTo_Result: + fPath->lineTo(SkIntToScalar(pts[1].fX), SkIntToScalar(pts[1].fY)); + break; + default: + break; + } +} + diff --git a/skia/effects/SkDashPathEffect.cpp b/skia/effects/SkDashPathEffect.cpp new file mode 100644 index 0000000..2c29009 --- /dev/null +++ b/skia/effects/SkDashPathEffect.cpp @@ -0,0 +1,178 @@ +/* libs/graphics/effects/SkDashPathEffect.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkDashPathEffect.h" +#include "SkBuffer.h" +#include "SkPathMeasure.h" + +static inline int is_even(int x) +{ + return (~x) << 31; +} + +static SkScalar FindFirstInterval(const SkScalar intervals[], SkScalar phase, int32_t* index) +{ + int i; + + for (i = 0; phase > intervals[i]; i++) + phase -= intervals[i]; + *index = i; + return intervals[i] - phase; +} + +SkDashPathEffect::SkDashPathEffect(const SkScalar intervals[], int count, SkScalar phase, bool scaleToFit) + : fScaleToFit(scaleToFit) +{ + SkASSERT(intervals); + SkASSERT(count > 1 && SkAlign2(count) == count); + + fIntervals = (SkScalar*)sk_malloc_throw(sizeof(SkScalar) * count); + fCount = count; + + SkScalar len = 0; + for (int i = 0; i < count; i++) + { + SkASSERT(intervals[i] >= 0); + fIntervals[i] = intervals[i]; + len += intervals[i]; + } + fIntervalLength = len; + + if (len > 0) // we don't handle 0 length dash arrays + { + if (phase < 0) + { + phase = -phase; + if (phase > len) + phase = SkScalarMod(phase, len); + phase = len - phase; + } + else if (phase >= len) + phase = SkScalarMod(phase, len); + + SkASSERT(phase >= 0 && phase < len); + fInitialDashLength = FindFirstInterval(intervals, phase, &fInitialDashIndex); + + SkASSERT(fInitialDashLength >= 0); + SkASSERT(fInitialDashIndex >= 0 && fInitialDashIndex < fCount); + } + else + fInitialDashLength = -1; // signal bad dash intervals +} + +SkDashPathEffect::~SkDashPathEffect() +{ + sk_free(fIntervals); +} + +bool SkDashPathEffect::filterPath(SkPath* dst, const SkPath& src, SkScalar* width) +{ + // we do nothing if the src wants to be filled, or if our dashlength is 0 + if (*width < 0 || fInitialDashLength < 0) + return false; + + SkPathMeasure meas(src, false); + const SkScalar* intervals = fIntervals; + + do { + bool skipFirstSegment = meas.isClosed(); + bool addedSegment = false; + SkScalar length = meas.getLength(); + int index = fInitialDashIndex; + SkScalar scale = SK_Scalar1; + + if (fScaleToFit) + { + if (fIntervalLength >= length) + scale = SkScalarDiv(length, fIntervalLength); + else + { + SkScalar div = SkScalarDiv(length, fIntervalLength); + int n = SkScalarFloor(div); + scale = SkScalarDiv(length, n * fIntervalLength); + } + } + + SkScalar distance = 0; + SkScalar dlen = SkScalarMul(fInitialDashLength, scale); + + while (distance < length) + { + SkASSERT(dlen >= 0); + addedSegment = false; + if (is_even(index) && dlen > 0 && !skipFirstSegment) + { + addedSegment = true; + meas.getSegment(distance, distance + dlen, dst, true); + } + distance += dlen; + + // clear this so we only respect it the first time around + skipFirstSegment = false; + + // wrap around our intervals array if necessary + index += 1; + SkASSERT(index <= fCount); + if (index == fCount) + index = 0; + + // fetch our next dlen + dlen = SkScalarMul(intervals[index], scale); + } + + // extend if we ended on a segment and we need to join up with the (skipped) initial segment + if (meas.isClosed() && is_even(fInitialDashIndex) && fInitialDashLength > 0) + meas.getSegment(0, SkScalarMul(fInitialDashLength, scale), dst, !addedSegment); + } while (meas.nextContour()); + return true; +} + +SkFlattenable::Factory SkDashPathEffect::getFactory() +{ + return fInitialDashLength < 0 ? NULL : CreateProc; +} + +void SkDashPathEffect::flatten(SkFlattenableWriteBuffer& buffer) +{ + SkASSERT(fInitialDashLength >= 0); + + buffer.write32(fCount); + buffer.write32(fInitialDashIndex); + buffer.writeScalar(fInitialDashLength); + buffer.writeScalar(fIntervalLength); + buffer.write32(fScaleToFit); + buffer.writeMul4(fIntervals, fCount * sizeof(fIntervals[0])); +} + +SkFlattenable* SkDashPathEffect::CreateProc(SkFlattenableReadBuffer& buffer) +{ + return SkNEW_ARGS(SkDashPathEffect, (buffer)); +} + +SkDashPathEffect::SkDashPathEffect(SkFlattenableReadBuffer& buffer) +{ + fCount = buffer.readS32(); + fInitialDashIndex = buffer.readS32(); + fInitialDashLength = buffer.readScalar(); + fIntervalLength = buffer.readScalar(); + fScaleToFit = (buffer.readS32() != 0); + + fIntervals = (SkScalar*)sk_malloc_throw(sizeof(SkScalar) * fCount); + buffer.read(fIntervals, fCount * sizeof(fIntervals[0])); +} + + diff --git a/skia/effects/SkDiscretePathEffect.cpp b/skia/effects/SkDiscretePathEffect.cpp new file mode 100644 index 0000000..d19b23a --- /dev/null +++ b/skia/effects/SkDiscretePathEffect.cpp @@ -0,0 +1,105 @@ +/* libs/graphics/effects/SkDiscretePathEffect.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkDiscretePathEffect.h" +#include "SkBuffer.h" +#include "SkPathMeasure.h" +#include "SkRandom.h" + +static void Perterb(SkPoint* p, const SkVector& tangent, SkScalar scale) +{ + SkVector normal = tangent; + normal.rotateCCW(); + normal.setLength(scale); + *p += normal; +} + + +SkDiscretePathEffect::SkDiscretePathEffect(SkScalar segLength, SkScalar deviation) + : fSegLength(segLength), fPerterb(deviation) +{ +} + +bool SkDiscretePathEffect::filterPath(SkPath* dst, const SkPath& src, SkScalar* width) +{ + bool doFill = *width < 0; + + SkPathMeasure meas(src, doFill); + uint32_t seed = SkScalarRound(meas.getLength()); + SkRandom rand(seed ^ ((seed << 16) | (seed >> 16))); + SkScalar scale = fPerterb; + SkPoint p; + SkVector v; + + do { + SkScalar length = meas.getLength(); + + if (fSegLength * (2 + doFill) > length) + { + meas.getSegment(0, length, dst, true); // to short for us to mangle + } + else + { + int n = SkScalarRound(SkScalarDiv(length, fSegLength)); + SkScalar delta = length / n; + SkScalar distance = 0; + + if (meas.isClosed()) + { + n -= 1; + distance += delta/2; + } + meas.getPosTan(distance, &p, &v); + Perterb(&p, v, SkScalarMul(rand.nextSScalar1(), scale)); + dst->moveTo(p); + while (--n >= 0) + { + distance += delta; + meas.getPosTan(distance, &p, &v); + Perterb(&p, v, SkScalarMul(rand.nextSScalar1(), scale)); + dst->lineTo(p); + } + if (meas.isClosed()) + dst->close(); + } + } while (meas.nextContour()); + return true; +} + +SkFlattenable::Factory SkDiscretePathEffect::getFactory() +{ + return CreateProc; +} + +SkFlattenable* SkDiscretePathEffect::CreateProc(SkFlattenableReadBuffer& buffer) +{ + return SkNEW_ARGS(SkDiscretePathEffect, (buffer)); +} + +void SkDiscretePathEffect::flatten(SkFlattenableWriteBuffer& buffer) +{ + buffer.writeScalar(fSegLength); + buffer.writeScalar(fPerterb); +} + +SkDiscretePathEffect::SkDiscretePathEffect(SkFlattenableReadBuffer& buffer) +{ + fSegLength = buffer.readScalar(); + fPerterb = buffer.readScalar(); +} + + diff --git a/skia/effects/SkEmbossMask.cpp b/skia/effects/SkEmbossMask.cpp new file mode 100644 index 0000000..9a77df1 --- /dev/null +++ b/skia/effects/SkEmbossMask.cpp @@ -0,0 +1,182 @@ +/* libs/graphics/effects/SkEmbossMask.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkEmbossMask.h" + +static inline int nonzero_to_one(int x) +{ +#if 0 + return x != 0; +#else + return ((unsigned)(x | -x)) >> 31; +#endif +} + +static inline int neq_to_one(int x, int max) +{ +#if 0 + return x != max; +#else + SkASSERT(x >= 0 && x <= max); + return ((unsigned)(x - max)) >> 31; +#endif +} + +static inline int neq_to_mask(int x, int max) +{ +#if 0 + return -(x != max); +#else + SkASSERT(x >= 0 && x <= max); + return (x - max) >> 31; +#endif +} + +static inline unsigned div255(unsigned x) +{ + SkASSERT(x <= (255*255)); + return x * ((1 << 24) / 255) >> 24; +} + +#define kDelta 32 // small enough to show off angle differences + +#include "SkEmbossMask_Table.h" + +#if defined(SK_BUILD_FOR_WIN32) && defined(SK_DEBUG) + +#include <stdio.h> + +void SkEmbossMask_BuildTable() +{ + // build it 0..127 x 0..127, so we use 2^15 - 1 in the numerator for our "fixed" table + + FILE* file = ::fopen("SkEmbossMask_Table.h", "w"); + SkASSERT(file); + ::fprintf(file, "#include \"SkTypes.h\"\n\n"); + ::fprintf(file, "static const U16 gInvSqrtTable[128 * 128] = {\n"); + for (int dx = 0; dx <= 255/2; dx++) + { + for (int dy = 0; dy <= 255/2; dy++) + { + if ((dy & 15) == 0) + ::fprintf(file, "\t"); + + U16 value = SkToU16((1 << 15) / SkSqrt32(dx * dx + dy * dy + kDelta*kDelta/4)); + + ::fprintf(file, "0x%04X", value); + if (dx * 128 + dy < 128*128-1) + ::fprintf(file, ", "); + if ((dy & 15) == 15) + ::fprintf(file, "\n"); + } + } + ::fprintf(file, "};\n#define kDeltaUsedToBuildTable\t%d\n", kDelta); + ::fclose(file); +} + +#endif + +void SkEmbossMask::Emboss(SkMask* mask, const SkEmbossMaskFilter::Light& light) +{ + SkASSERT(kDelta == kDeltaUsedToBuildTable); + + SkASSERT(mask->fFormat == SkMask::k3D_Format); + + int specular = light.fSpecular; + int ambient = light.fAmbient; + SkFixed lx = SkScalarToFixed(light.fDirection[0]); + SkFixed ly = SkScalarToFixed(light.fDirection[1]); + SkFixed lz = SkScalarToFixed(light.fDirection[2]); + SkFixed lz_dot_nz = lz * kDelta; + int lz_dot8 = lz >> 8; + + size_t planeSize = mask->computeImageSize(); + uint8_t* alpha = mask->fImage; + uint8_t* multiply = (uint8_t*)alpha + planeSize; + uint8_t* additive = multiply + planeSize; + + int rowBytes = mask->fRowBytes; + int maxy = mask->fBounds.height() - 1; + int maxx = mask->fBounds.width() - 1; + + int prev_row = 0; + for (int y = 0; y <= maxy; y++) + { + int next_row = neq_to_mask(y, maxy) & rowBytes; + + for (int x = 0; x <= maxx; x++) + { + if (alpha[x]) + { + int nx = alpha[x + neq_to_one(x, maxx)] - alpha[x - nonzero_to_one(x)]; + int ny = alpha[x + next_row] - alpha[x - prev_row]; + + SkFixed numer = lx * nx + ly * ny + lz_dot_nz; + int mul = ambient; + int add = 0; + + if (numer > 0) // preflight when numer/denom will be <= 0 + { +#if 0 + int denom = SkSqrt32(nx * nx + ny * ny + kDelta*kDelta); + SkFixed dot = numer / denom; + dot >>= 8; // now dot is 2^8 instead of 2^16 +#else + // can use full numer, but then we need to call SkFixedMul, since + // numer is 24 bits, and our table is 12 bits + + // SkFixed dot = SkFixedMul(numer, gTable[]) >> 8 + SkFixed dot = (unsigned)(numer >> 4) * gInvSqrtTable[(SkAbs32(nx) >> 1 << 7) | (SkAbs32(ny) >> 1)] >> 20; +#endif + mul = SkFastMin32(mul + dot, 255); + + // now for the reflection + + // R = 2 (Light * Normal) Normal - Light + // hilite = R * Eye(0, 0, 1) + + int hilite = (2 * dot - lz_dot8) * lz_dot8 >> 8; + if (hilite > 0) + { + // pin hilite to 255, since our fast math is also a little sloppy + hilite = SkClampMax(hilite, 255); + + // specular is 4.4 + // would really like to compute the fractional part of this + // and then possibly cache a 256 table for a given specular + // value in the light, and just pass that in to this function. + add = hilite; + for (int i = specular >> 4; i > 0; --i) + add = div255(add * hilite); + } + } + multiply[x] = SkToU8(mul); + additive[x] = SkToU8(add); + + // multiply[x] = 0xFF; + // additive[x] = 0; + // ((uint8_t*)alpha)[x] = alpha[x] * multiply[x] >> 8; + } + } + alpha += rowBytes; + multiply += rowBytes; + additive += rowBytes; + prev_row = rowBytes; + } +} + + diff --git a/skia/effects/SkEmbossMask.h b/skia/effects/SkEmbossMask.h new file mode 100644 index 0000000..093e53d --- /dev/null +++ b/skia/effects/SkEmbossMask.h @@ -0,0 +1,29 @@ +/* libs/graphics/effects/SkEmbossMask.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkEmbossMask_DEFINED +#define SkEmbossMask_DEFINED + +#include "SkEmbossMaskFilter.h" + +class SkEmbossMask { +public: + static void Emboss(SkMask* mask, const SkEmbossMaskFilter::Light&); +}; + +#endif + diff --git a/skia/effects/SkEmbossMaskFilter.cpp b/skia/effects/SkEmbossMaskFilter.cpp new file mode 100644 index 0000000..0af2ae05 --- /dev/null +++ b/skia/effects/SkEmbossMaskFilter.cpp @@ -0,0 +1,141 @@ +/* libs/graphics/effects/SkEmbossMaskFilter.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkEmbossMaskFilter.h" +#include "SkBlurMaskFilter.h" +#include "SkBlurMask.h" +#include "SkEmbossMask.h" +#include "SkBuffer.h" + +SkMaskFilter* SkBlurMaskFilter::CreateEmboss(const SkScalar direction[3], + SkScalar ambient, SkScalar specular, + SkScalar blurRadius) +{ + if (direction == NULL) + return NULL; + + // ambient should be 0...1 as a scalar + int am = SkScalarToFixed(ambient) >> 8; + if (am < 0) am = 0; + else if (am > 0xFF) am = 0xFF; + + // specular should be 0..15.99 as a scalar + int sp = SkScalarToFixed(specular) >> 12; + if (sp < 0) sp = 0; + else if (sp > 0xFF) sp = 0xFF; + + SkEmbossMaskFilter::Light light; + + memcpy(light.fDirection, direction, sizeof(light.fDirection)); + light.fAmbient = SkToU8(am); + light.fSpecular = SkToU8(sp); + + return SkNEW_ARGS(SkEmbossMaskFilter, (light, blurRadius)); +} + +///////////////////////////////////////////////////////////////////////////////////////////// + +static void normalize(SkScalar v[3]) +{ + SkScalar mag = SkScalarSquare(v[0]) + SkScalarSquare(v[1]) + SkScalarSquare(v[2]); + mag = SkScalarSqrt(mag); + + for (int i = 0; i < 3; i++) + v[i] = SkScalarDiv(v[i], mag); +} + +SkEmbossMaskFilter::SkEmbossMaskFilter(const Light& light, SkScalar blurRadius) + : fLight(light), fBlurRadius(blurRadius) +{ + normalize(fLight.fDirection); +} + +SkMask::Format SkEmbossMaskFilter::getFormat() +{ + return SkMask::k3D_Format; +} + +bool SkEmbossMaskFilter::filterMask(SkMask* dst, const SkMask& src, const SkMatrix& matrix, SkIPoint* margin) +{ + SkScalar radius = matrix.mapRadius(fBlurRadius); + + if (!SkBlurMask::Blur(dst, src, radius, SkBlurMask::kInner_Style)) + return false; + + dst->fFormat = SkMask::k3D_Format; + if (margin) + margin->set(SkScalarCeil(radius), SkScalarCeil(radius)); + + if (src.fImage == NULL) + return true; + + // create a larger buffer for the other two channels (should force fBlur to do this for us) + + { + uint8_t* alphaPlane = dst->fImage; + size_t planeSize = dst->computeImageSize(); + + dst->fImage = SkMask::AllocImage(planeSize * 3); + memcpy(dst->fImage, alphaPlane, planeSize); + SkMask::FreeImage(alphaPlane); + } + + // run the light direction through the matrix... + Light light = fLight; + matrix.mapVectors((SkVector*)(void*)light.fDirection, (SkVector*)(void*)fLight.fDirection, 1); + + // now restore the length of the XY component + // cast to SkVector so we can call setLength (this double cast silences alias warnings) + SkVector* vec = (SkVector*)(void*)light.fDirection; + vec->setLength(light.fDirection[0], + light.fDirection[1], + SkPoint::Length(fLight.fDirection[0], fLight.fDirection[1])); + + SkEmbossMask::Emboss(dst, light); + + // restore original alpha + memcpy(dst->fImage, src.fImage, src.computeImageSize()); + + return true; +} + +SkFlattenable* SkEmbossMaskFilter::CreateProc(SkFlattenableReadBuffer& buffer) +{ + return SkNEW_ARGS(SkEmbossMaskFilter, (buffer)); +} + +SkFlattenable::Factory SkEmbossMaskFilter::getFactory() +{ + return CreateProc; +} + +SkEmbossMaskFilter::SkEmbossMaskFilter(SkFlattenableReadBuffer& buffer) : SkMaskFilter(buffer) +{ + buffer.read(&fLight, sizeof(fLight)); + SkASSERT(fLight.fPad == 0); // for the font-cache lookup to be clean + fBlurRadius = buffer.readScalar(); +} + +void SkEmbossMaskFilter::flatten(SkFlattenableWriteBuffer& buffer) +{ + this->INHERITED::flatten(buffer); + + fLight.fPad = 0; // for the font-cache lookup to be clean + buffer.writeMul4(&fLight, sizeof(fLight)); + buffer.writeScalar(fBlurRadius); +} + diff --git a/skia/effects/SkEmbossMask_Table.h b/skia/effects/SkEmbossMask_Table.h new file mode 100644 index 0000000..d936c32 --- /dev/null +++ b/skia/effects/SkEmbossMask_Table.h @@ -0,0 +1,1046 @@ +/* libs/graphics/effects/SkEmbossMask_Table.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkTypes.h" + +static const uint16_t gInvSqrtTable[128 * 128] = { + 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0787, 0x0787, 0x0787, 0x071C, 0x071C, 0x06BC, 0x0666, 0x0666, 0x0618, 0x0618, + 0x05D1, 0x0590, 0x0555, 0x0555, 0x051E, 0x04EC, 0x04BD, 0x0492, 0x0492, 0x0469, 0x0444, 0x0421, 0x0400, 0x03E0, 0x03C3, 0x03C3, + 0x03A8, 0x038E, 0x0375, 0x035E, 0x0348, 0x0333, 0x031F, 0x030C, 0x02FA, 0x02E8, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C, + 0x028F, 0x0282, 0x0276, 0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, + 0x01F8, 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, + 0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, + 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, + 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, + 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0787, 0x0787, 0x0787, 0x071C, 0x071C, 0x06BC, 0x0666, 0x0666, 0x0618, 0x0618, + 0x05D1, 0x0590, 0x0555, 0x0555, 0x051E, 0x04EC, 0x04BD, 0x0492, 0x0492, 0x0469, 0x0444, 0x0421, 0x0400, 0x03E0, 0x03C3, 0x03C3, + 0x03A8, 0x038E, 0x0375, 0x035E, 0x0348, 0x0333, 0x031F, 0x030C, 0x02FA, 0x02E8, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C, + 0x028F, 0x0282, 0x0276, 0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, + 0x01F8, 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, + 0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, + 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, + 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, + 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0787, 0x0787, 0x071C, 0x071C, 0x071C, 0x06BC, 0x0666, 0x0666, 0x0618, 0x05D1, + 0x05D1, 0x0590, 0x0555, 0x0555, 0x051E, 0x04EC, 0x04BD, 0x0492, 0x0492, 0x0469, 0x0444, 0x0421, 0x0400, 0x03E0, 0x03C3, 0x03C3, + 0x03A8, 0x038E, 0x0375, 0x035E, 0x0348, 0x0333, 0x031F, 0x030C, 0x02FA, 0x02E8, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C, + 0x028F, 0x0282, 0x0276, 0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, + 0x01F0, 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, + 0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, + 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, + 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, + 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0787, 0x0787, 0x0787, 0x071C, 0x071C, 0x06BC, 0x06BC, 0x0666, 0x0666, 0x0618, 0x05D1, + 0x05D1, 0x0590, 0x0555, 0x051E, 0x051E, 0x04EC, 0x04BD, 0x0492, 0x0469, 0x0469, 0x0444, 0x0421, 0x0400, 0x03E0, 0x03C3, 0x03A8, + 0x03A8, 0x038E, 0x0375, 0x035E, 0x0348, 0x0333, 0x031F, 0x030C, 0x02FA, 0x02E8, 0x02D8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C, + 0x028F, 0x0282, 0x0276, 0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, + 0x01F0, 0x01E9, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, + 0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, + 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, + 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, + 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0787, 0x0787, 0x0787, 0x071C, 0x071C, 0x06BC, 0x06BC, 0x0666, 0x0618, 0x0618, 0x05D1, + 0x05D1, 0x0590, 0x0555, 0x051E, 0x051E, 0x04EC, 0x04BD, 0x0492, 0x0469, 0x0469, 0x0444, 0x0421, 0x0400, 0x03E0, 0x03C3, 0x03A8, + 0x038E, 0x038E, 0x0375, 0x035E, 0x0348, 0x0333, 0x031F, 0x030C, 0x02FA, 0x02E8, 0x02D8, 0x02C8, 0x02C8, 0x02B9, 0x02AA, 0x029C, + 0x028F, 0x0282, 0x0276, 0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, + 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, + 0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, + 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, + 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, + 0x0800, 0x0800, 0x0800, 0x0787, 0x0787, 0x0787, 0x0787, 0x071C, 0x071C, 0x06BC, 0x06BC, 0x0666, 0x0666, 0x0618, 0x0618, 0x05D1, + 0x0590, 0x0590, 0x0555, 0x051E, 0x04EC, 0x04EC, 0x04BD, 0x0492, 0x0469, 0x0444, 0x0444, 0x0421, 0x0400, 0x03E0, 0x03C3, 0x03A8, + 0x038E, 0x0375, 0x0375, 0x035E, 0x0348, 0x0333, 0x031F, 0x030C, 0x02FA, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x02AA, 0x029C, + 0x028F, 0x0282, 0x0276, 0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, + 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, + 0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, + 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, + 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, + 0x0787, 0x0787, 0x0787, 0x0787, 0x0787, 0x0787, 0x071C, 0x071C, 0x071C, 0x06BC, 0x06BC, 0x0666, 0x0666, 0x0618, 0x05D1, 0x05D1, + 0x0590, 0x0555, 0x0555, 0x051E, 0x04EC, 0x04BD, 0x04BD, 0x0492, 0x0469, 0x0444, 0x0421, 0x0421, 0x0400, 0x03E0, 0x03C3, 0x03A8, + 0x038E, 0x0375, 0x035E, 0x035E, 0x0348, 0x0333, 0x031F, 0x030C, 0x02FA, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 0x028F, + 0x028F, 0x0282, 0x0276, 0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, + 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, + 0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, + 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, + 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, + 0x0787, 0x0787, 0x0787, 0x0787, 0x0787, 0x071C, 0x071C, 0x071C, 0x06BC, 0x06BC, 0x0666, 0x0666, 0x0618, 0x0618, 0x05D1, 0x0590, + 0x0590, 0x0555, 0x051E, 0x051E, 0x04EC, 0x04BD, 0x0492, 0x0492, 0x0469, 0x0444, 0x0421, 0x0400, 0x03E0, 0x03E0, 0x03C3, 0x03A8, + 0x038E, 0x0375, 0x035E, 0x0348, 0x0333, 0x0333, 0x031F, 0x030C, 0x02FA, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 0x028F, + 0x0282, 0x0276, 0x0276, 0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, + 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A9, 0x01A4, 0x019E, 0x0199, + 0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, + 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, + 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, + 0x0787, 0x0787, 0x071C, 0x071C, 0x071C, 0x071C, 0x071C, 0x06BC, 0x06BC, 0x0666, 0x0666, 0x0618, 0x0618, 0x05D1, 0x05D1, 0x0590, + 0x0555, 0x0555, 0x051E, 0x04EC, 0x04EC, 0x04BD, 0x0492, 0x0469, 0x0469, 0x0444, 0x0421, 0x0400, 0x03E0, 0x03C3, 0x03C3, 0x03A8, + 0x038E, 0x0375, 0x035E, 0x0348, 0x0333, 0x031F, 0x030C, 0x030C, 0x02FA, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 0x028F, + 0x0282, 0x0276, 0x026A, 0x025E, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, + 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, + 0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, + 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, + 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, + 0x071C, 0x071C, 0x071C, 0x071C, 0x071C, 0x06BC, 0x06BC, 0x06BC, 0x0666, 0x0666, 0x0666, 0x0618, 0x0618, 0x05D1, 0x0590, 0x0590, + 0x0555, 0x051E, 0x051E, 0x04EC, 0x04BD, 0x04BD, 0x0492, 0x0469, 0x0444, 0x0421, 0x0421, 0x0400, 0x03E0, 0x03C3, 0x03A8, 0x038E, + 0x038E, 0x0375, 0x035E, 0x0348, 0x0333, 0x031F, 0x030C, 0x02FA, 0x02E8, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 0x028F, + 0x0282, 0x0276, 0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, + 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, + 0x018F, 0x018A, 0x0186, 0x0181, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, + 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, + 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, + 0x071C, 0x071C, 0x071C, 0x06BC, 0x06BC, 0x06BC, 0x06BC, 0x0666, 0x0666, 0x0666, 0x0618, 0x0618, 0x05D1, 0x05D1, 0x0590, 0x0555, + 0x0555, 0x051E, 0x04EC, 0x04EC, 0x04BD, 0x0492, 0x0492, 0x0469, 0x0444, 0x0421, 0x0400, 0x0400, 0x03E0, 0x03C3, 0x03A8, 0x038E, + 0x0375, 0x035E, 0x035E, 0x0348, 0x0333, 0x031F, 0x030C, 0x02FA, 0x02E8, 0x02D8, 0x02C8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 0x028F, + 0x0282, 0x0276, 0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, + 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, + 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, + 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, + 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, + 0x06BC, 0x06BC, 0x06BC, 0x06BC, 0x06BC, 0x0666, 0x0666, 0x0666, 0x0618, 0x0618, 0x0618, 0x05D1, 0x05D1, 0x0590, 0x0590, 0x0555, + 0x051E, 0x051E, 0x04EC, 0x04BD, 0x04BD, 0x0492, 0x0469, 0x0444, 0x0444, 0x0421, 0x0400, 0x03E0, 0x03C3, 0x03C3, 0x03A8, 0x038E, + 0x0375, 0x035E, 0x0348, 0x0333, 0x0333, 0x031F, 0x030C, 0x02FA, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 0x029C, 0x028F, + 0x0282, 0x0276, 0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x0200, 0x01F8, + 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, + 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0158, 0x0155, + 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, + 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, + 0x0666, 0x0666, 0x0666, 0x0666, 0x0666, 0x0666, 0x0666, 0x0618, 0x0618, 0x0618, 0x05D1, 0x05D1, 0x0590, 0x0590, 0x0555, 0x051E, + 0x051E, 0x04EC, 0x04EC, 0x04BD, 0x0492, 0x0469, 0x0469, 0x0444, 0x0421, 0x0400, 0x0400, 0x03E0, 0x03C3, 0x03A8, 0x038E, 0x038E, + 0x0375, 0x035E, 0x0348, 0x0333, 0x031F, 0x030C, 0x030C, 0x02FA, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 0x028F, 0x0282, + 0x0276, 0x0276, 0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0, + 0x01E9, 0x01E1, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, + 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, + 0x014E, 0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, + 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, + 0x0666, 0x0666, 0x0666, 0x0666, 0x0618, 0x0618, 0x0618, 0x0618, 0x05D1, 0x05D1, 0x05D1, 0x0590, 0x0590, 0x0555, 0x0555, 0x051E, + 0x04EC, 0x04EC, 0x04BD, 0x0492, 0x0492, 0x0469, 0x0444, 0x0444, 0x0421, 0x0400, 0x03E0, 0x03E0, 0x03C3, 0x03A8, 0x038E, 0x0375, + 0x035E, 0x035E, 0x0348, 0x0333, 0x031F, 0x030C, 0x02FA, 0x02E8, 0x02D8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 0x028F, 0x0282, + 0x0276, 0x026A, 0x025E, 0x0253, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0, + 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, + 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, + 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, + 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, + 0x0618, 0x0618, 0x0618, 0x0618, 0x0618, 0x0618, 0x05D1, 0x05D1, 0x05D1, 0x0590, 0x0590, 0x0590, 0x0555, 0x0555, 0x051E, 0x04EC, + 0x04EC, 0x04BD, 0x04BD, 0x0492, 0x0469, 0x0469, 0x0444, 0x0421, 0x0400, 0x0400, 0x03E0, 0x03C3, 0x03A8, 0x03A8, 0x038E, 0x0375, + 0x035E, 0x0348, 0x0333, 0x0333, 0x031F, 0x030C, 0x02FA, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02B9, 0x02AA, 0x029C, 0x028F, 0x0282, + 0x0276, 0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0, + 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, + 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, + 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, + 0x011F, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, + 0x0618, 0x0618, 0x05D1, 0x05D1, 0x05D1, 0x05D1, 0x05D1, 0x0590, 0x0590, 0x0590, 0x0555, 0x0555, 0x051E, 0x051E, 0x04EC, 0x04EC, + 0x04BD, 0x04BD, 0x0492, 0x0469, 0x0469, 0x0444, 0x0421, 0x0421, 0x0400, 0x03E0, 0x03C3, 0x03C3, 0x03A8, 0x038E, 0x0375, 0x0375, + 0x035E, 0x0348, 0x0333, 0x031F, 0x030C, 0x02FA, 0x02FA, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 0x028F, 0x028F, 0x0282, + 0x0276, 0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0, + 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x0194, + 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, + 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, + 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, + 0x05D1, 0x05D1, 0x05D1, 0x05D1, 0x05D1, 0x0590, 0x0590, 0x0590, 0x0555, 0x0555, 0x0555, 0x051E, 0x051E, 0x04EC, 0x04EC, 0x04BD, + 0x04BD, 0x0492, 0x0492, 0x0469, 0x0444, 0x0444, 0x0421, 0x0400, 0x0400, 0x03E0, 0x03C3, 0x03A8, 0x038E, 0x038E, 0x0375, 0x035E, + 0x0348, 0x0333, 0x0333, 0x031F, 0x030C, 0x02FA, 0x02E8, 0x02D8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 0x028F, 0x0282, 0x0276, + 0x026A, 0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0, 0x01F0, + 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, + 0x018A, 0x0186, 0x0181, 0x017D, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, + 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, + 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, + 0x0590, 0x0590, 0x0590, 0x0590, 0x0590, 0x0590, 0x0555, 0x0555, 0x0555, 0x051E, 0x051E, 0x051E, 0x04EC, 0x04EC, 0x04BD, 0x04BD, + 0x0492, 0x0492, 0x0469, 0x0444, 0x0444, 0x0421, 0x0400, 0x0400, 0x03E0, 0x03C3, 0x03C3, 0x03A8, 0x038E, 0x0375, 0x035E, 0x035E, + 0x0348, 0x0333, 0x031F, 0x030C, 0x030C, 0x02FA, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x02AA, 0x029C, 0x028F, 0x0282, 0x0276, + 0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0, 0x01E9, + 0x01E1, 0x01DA, 0x01D4, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, + 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, + 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, + 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, + 0x0555, 0x0555, 0x0555, 0x0555, 0x0555, 0x0555, 0x0555, 0x051E, 0x051E, 0x051E, 0x04EC, 0x04EC, 0x04EC, 0x04BD, 0x04BD, 0x0492, + 0x0492, 0x0469, 0x0444, 0x0444, 0x0421, 0x0421, 0x0400, 0x03E0, 0x03C3, 0x03C3, 0x03A8, 0x038E, 0x038E, 0x0375, 0x035E, 0x0348, + 0x0333, 0x0333, 0x031F, 0x030C, 0x02FA, 0x02E8, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 0x028F, 0x0282, 0x0282, 0x0276, + 0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0, 0x01E9, + 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, + 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, + 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, + 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, + 0x0555, 0x0555, 0x0555, 0x051E, 0x051E, 0x051E, 0x051E, 0x051E, 0x04EC, 0x04EC, 0x04EC, 0x04BD, 0x04BD, 0x0492, 0x0492, 0x0469, + 0x0469, 0x0444, 0x0444, 0x0421, 0x0421, 0x0400, 0x03E0, 0x03E0, 0x03C3, 0x03A8, 0x03A8, 0x038E, 0x0375, 0x035E, 0x035E, 0x0348, + 0x0333, 0x031F, 0x030C, 0x030C, 0x02FA, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02B9, 0x02AA, 0x029C, 0x028F, 0x0282, 0x0276, 0x026A, + 0x025E, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x0200, 0x01F8, 0x01F0, 0x01E9, + 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x019E, 0x0199, 0x0194, 0x018F, + 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, + 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, + 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, + 0x051E, 0x051E, 0x051E, 0x051E, 0x051E, 0x04EC, 0x04EC, 0x04EC, 0x04EC, 0x04BD, 0x04BD, 0x04BD, 0x0492, 0x0492, 0x0469, 0x0469, + 0x0444, 0x0444, 0x0421, 0x0421, 0x0400, 0x03E0, 0x03E0, 0x03C3, 0x03A8, 0x03A8, 0x038E, 0x0375, 0x0375, 0x035E, 0x0348, 0x0333, + 0x0333, 0x031F, 0x030C, 0x02FA, 0x02E8, 0x02D8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 0x028F, 0x028F, 0x0282, 0x0276, 0x026A, + 0x025E, 0x0253, 0x0249, 0x023E, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0, 0x01E9, 0x01E1, + 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, 0x018A, + 0x0186, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, + 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0129, 0x0127, 0x0124, 0x0121, + 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, + 0x04EC, 0x04EC, 0x04EC, 0x04EC, 0x04EC, 0x04EC, 0x04BD, 0x04BD, 0x04BD, 0x04BD, 0x0492, 0x0492, 0x0469, 0x0469, 0x0469, 0x0444, + 0x0444, 0x0421, 0x0421, 0x0400, 0x03E0, 0x03E0, 0x03C3, 0x03A8, 0x03A8, 0x038E, 0x0375, 0x0375, 0x035E, 0x0348, 0x0348, 0x0333, + 0x031F, 0x030C, 0x02FA, 0x02FA, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02B9, 0x02AA, 0x029C, 0x028F, 0x0282, 0x0276, 0x026A, 0x026A, + 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0, 0x01E9, 0x01E1, + 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, 0x018A, + 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, + 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, + 0x011C, 0x011A, 0x0118, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, + 0x04BD, 0x04BD, 0x04BD, 0x04BD, 0x04BD, 0x04BD, 0x04BD, 0x0492, 0x0492, 0x0492, 0x0492, 0x0469, 0x0469, 0x0444, 0x0444, 0x0421, + 0x0421, 0x0400, 0x0400, 0x03E0, 0x03E0, 0x03C3, 0x03C3, 0x03A8, 0x038E, 0x038E, 0x0375, 0x035E, 0x0348, 0x0348, 0x0333, 0x031F, + 0x030C, 0x030C, 0x02FA, 0x02E8, 0x02D8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 0x028F, 0x028F, 0x0282, 0x0276, 0x026A, 0x025E, + 0x0253, 0x0249, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x0200, 0x01F8, 0x01F0, 0x01E9, 0x01E1, + 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, 0x018A, + 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, + 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, + 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, + 0x0492, 0x0492, 0x0492, 0x0492, 0x0492, 0x0492, 0x0492, 0x0492, 0x0469, 0x0469, 0x0469, 0x0444, 0x0444, 0x0444, 0x0421, 0x0421, + 0x0400, 0x0400, 0x03E0, 0x03E0, 0x03C3, 0x03A8, 0x03A8, 0x038E, 0x038E, 0x0375, 0x035E, 0x035E, 0x0348, 0x0333, 0x031F, 0x031F, + 0x030C, 0x02FA, 0x02E8, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x02AA, 0x029C, 0x028F, 0x0282, 0x0276, 0x026A, 0x026A, 0x025E, + 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0, 0x01E9, 0x01E1, 0x01E1, + 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x0194, 0x018F, 0x018A, + 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, + 0x0147, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, + 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, + 0x0492, 0x0492, 0x0492, 0x0469, 0x0469, 0x0469, 0x0469, 0x0469, 0x0469, 0x0444, 0x0444, 0x0444, 0x0421, 0x0421, 0x0400, 0x0400, + 0x0400, 0x03E0, 0x03C3, 0x03C3, 0x03A8, 0x03A8, 0x038E, 0x038E, 0x0375, 0x035E, 0x035E, 0x0348, 0x0333, 0x0333, 0x031F, 0x030C, + 0x02FA, 0x02FA, 0x02E8, 0x02D8, 0x02C8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 0x028F, 0x028F, 0x0282, 0x0276, 0x026A, 0x025E, 0x0253, + 0x0249, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0, 0x01E9, 0x01E1, 0x01DA, + 0x01D4, 0x01CD, 0x01C7, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, 0x018A, 0x0186, + 0x0181, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, + 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, + 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, + 0x0469, 0x0469, 0x0469, 0x0469, 0x0469, 0x0444, 0x0444, 0x0444, 0x0444, 0x0421, 0x0421, 0x0421, 0x0400, 0x0400, 0x0400, 0x03E0, + 0x03E0, 0x03C3, 0x03C3, 0x03A8, 0x03A8, 0x038E, 0x038E, 0x0375, 0x035E, 0x035E, 0x0348, 0x0333, 0x0333, 0x031F, 0x030C, 0x030C, + 0x02FA, 0x02E8, 0x02D8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 0x029C, 0x028F, 0x0282, 0x0276, 0x026A, 0x026A, 0x025E, 0x0253, + 0x0249, 0x023E, 0x0234, 0x022B, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0, 0x01F0, 0x01E9, 0x01E1, 0x01DA, + 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, 0x018A, 0x0186, + 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, + 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0124, 0x0121, 0x011F, + 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, + 0x0444, 0x0444, 0x0444, 0x0444, 0x0444, 0x0444, 0x0421, 0x0421, 0x0421, 0x0421, 0x0400, 0x0400, 0x0400, 0x03E0, 0x03E0, 0x03C3, + 0x03C3, 0x03C3, 0x03A8, 0x03A8, 0x038E, 0x0375, 0x0375, 0x035E, 0x035E, 0x0348, 0x0333, 0x0333, 0x031F, 0x030C, 0x030C, 0x02FA, + 0x02E8, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02B9, 0x02AA, 0x029C, 0x028F, 0x0282, 0x0282, 0x0276, 0x026A, 0x025E, 0x0253, 0x0249, + 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4, + 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0199, 0x0194, 0x018F, 0x018A, 0x0186, + 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, + 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, + 0x011A, 0x0118, 0x0115, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, + 0x0421, 0x0421, 0x0421, 0x0421, 0x0421, 0x0421, 0x0421, 0x0400, 0x0400, 0x0400, 0x0400, 0x03E0, 0x03E0, 0x03E0, 0x03C3, 0x03C3, + 0x03A8, 0x03A8, 0x038E, 0x038E, 0x0375, 0x0375, 0x035E, 0x035E, 0x0348, 0x0333, 0x0333, 0x031F, 0x030C, 0x030C, 0x02FA, 0x02E8, + 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02B9, 0x02AA, 0x029C, 0x028F, 0x028F, 0x0282, 0x0276, 0x026A, 0x025E, 0x025E, 0x0253, 0x0249, + 0x023E, 0x0234, 0x022B, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F8, 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4, + 0x01CD, 0x01C7, 0x01C0, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, 0x018A, 0x0186, 0x0181, + 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x0147, + 0x0144, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, + 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, + 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x03E0, 0x03E0, 0x03E0, 0x03E0, 0x03C3, 0x03C3, 0x03C3, 0x03A8, 0x03A8, + 0x038E, 0x038E, 0x038E, 0x0375, 0x0375, 0x035E, 0x0348, 0x0348, 0x0333, 0x0333, 0x031F, 0x030C, 0x030C, 0x02FA, 0x02E8, 0x02E8, + 0x02D8, 0x02C8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 0x029C, 0x028F, 0x0282, 0x0276, 0x0276, 0x026A, 0x025E, 0x0253, 0x0249, 0x023E, + 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01DA, 0x01D4, + 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, 0x018A, 0x0186, 0x0181, + 0x017D, 0x0178, 0x0174, 0x0170, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x0147, + 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, + 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, + 0x03E0, 0x03E0, 0x03E0, 0x03E0, 0x03E0, 0x03E0, 0x03E0, 0x03E0, 0x03C3, 0x03C3, 0x03C3, 0x03C3, 0x03A8, 0x03A8, 0x03A8, 0x038E, + 0x038E, 0x0375, 0x0375, 0x035E, 0x035E, 0x0348, 0x0348, 0x0333, 0x0333, 0x031F, 0x030C, 0x030C, 0x02FA, 0x02E8, 0x02E8, 0x02D8, + 0x02C8, 0x02C8, 0x02B9, 0x02AA, 0x02AA, 0x029C, 0x028F, 0x0282, 0x0282, 0x0276, 0x026A, 0x025E, 0x0253, 0x0253, 0x0249, 0x023E, + 0x0234, 0x022B, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F8, 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, + 0x01C7, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x0194, 0x018F, 0x018A, 0x0186, 0x0181, + 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x0147, + 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0124, 0x0121, 0x011F, 0x011C, + 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, + 0x03C3, 0x03C3, 0x03C3, 0x03C3, 0x03C3, 0x03C3, 0x03C3, 0x03C3, 0x03C3, 0x03A8, 0x03A8, 0x03A8, 0x038E, 0x038E, 0x038E, 0x0375, + 0x0375, 0x035E, 0x035E, 0x035E, 0x0348, 0x0348, 0x0333, 0x031F, 0x031F, 0x030C, 0x030C, 0x02FA, 0x02E8, 0x02E8, 0x02D8, 0x02C8, + 0x02C8, 0x02B9, 0x02AA, 0x02AA, 0x029C, 0x028F, 0x028F, 0x0282, 0x0276, 0x026A, 0x025E, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, + 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0, 0x01E9, 0x01E1, 0x01E1, 0x01DA, 0x01D4, 0x01CD, + 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, + 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x014A, 0x0147, + 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, + 0x0118, 0x0115, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, + 0x03C3, 0x03C3, 0x03C3, 0x03A8, 0x03A8, 0x03A8, 0x03A8, 0x03A8, 0x03A8, 0x038E, 0x038E, 0x038E, 0x038E, 0x0375, 0x0375, 0x0375, + 0x035E, 0x035E, 0x0348, 0x0348, 0x0333, 0x0333, 0x031F, 0x031F, 0x030C, 0x030C, 0x02FA, 0x02E8, 0x02E8, 0x02D8, 0x02C8, 0x02C8, + 0x02B9, 0x02AA, 0x02AA, 0x029C, 0x028F, 0x028F, 0x0282, 0x0276, 0x026A, 0x026A, 0x025E, 0x0253, 0x0249, 0x0249, 0x023E, 0x0234, + 0x022B, 0x0222, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F8, 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, + 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0199, 0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, + 0x0178, 0x0174, 0x0170, 0x016C, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, + 0x0141, 0x013E, 0x013B, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, + 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, + 0x03A8, 0x03A8, 0x03A8, 0x03A8, 0x038E, 0x038E, 0x038E, 0x038E, 0x038E, 0x038E, 0x0375, 0x0375, 0x0375, 0x035E, 0x035E, 0x035E, + 0x0348, 0x0348, 0x0333, 0x0333, 0x0333, 0x031F, 0x030C, 0x030C, 0x02FA, 0x02FA, 0x02E8, 0x02E8, 0x02D8, 0x02C8, 0x02C8, 0x02B9, + 0x02AA, 0x02AA, 0x029C, 0x028F, 0x028F, 0x0282, 0x0276, 0x0276, 0x026A, 0x025E, 0x0253, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, + 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0208, 0x0200, 0x01F8, 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01DA, 0x01D4, 0x01CD, 0x01C7, + 0x01C0, 0x01BA, 0x01B4, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, 0x018A, 0x0186, 0x0186, 0x0181, 0x017D, + 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, + 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, + 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FC, 0x00FA, + 0x038E, 0x038E, 0x038E, 0x038E, 0x038E, 0x0375, 0x0375, 0x0375, 0x0375, 0x0375, 0x035E, 0x035E, 0x035E, 0x035E, 0x0348, 0x0348, + 0x0333, 0x0333, 0x0333, 0x031F, 0x031F, 0x030C, 0x030C, 0x02FA, 0x02FA, 0x02E8, 0x02E8, 0x02D8, 0x02C8, 0x02C8, 0x02B9, 0x02AA, + 0x02AA, 0x029C, 0x028F, 0x028F, 0x0282, 0x0276, 0x0276, 0x026A, 0x025E, 0x0253, 0x0253, 0x0249, 0x023E, 0x0234, 0x0234, 0x022B, + 0x0222, 0x0219, 0x0210, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0, 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C7, + 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x019E, 0x0199, 0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, + 0x0174, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x014A, 0x0147, 0x0144, + 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011C, 0x011A, + 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, + 0x0375, 0x0375, 0x0375, 0x0375, 0x0375, 0x0375, 0x035E, 0x035E, 0x035E, 0x035E, 0x035E, 0x0348, 0x0348, 0x0348, 0x0333, 0x0333, + 0x0333, 0x031F, 0x031F, 0x030C, 0x030C, 0x02FA, 0x02FA, 0x02E8, 0x02E8, 0x02D8, 0x02D8, 0x02C8, 0x02C8, 0x02B9, 0x02AA, 0x02AA, + 0x029C, 0x028F, 0x028F, 0x0282, 0x0276, 0x0276, 0x026A, 0x025E, 0x025E, 0x0253, 0x0249, 0x023E, 0x023E, 0x0234, 0x022B, 0x0222, + 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x0200, 0x01F8, 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, + 0x01BA, 0x01B4, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, + 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, + 0x013E, 0x013B, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, + 0x0115, 0x0113, 0x0111, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, + 0x035E, 0x035E, 0x035E, 0x035E, 0x035E, 0x035E, 0x035E, 0x0348, 0x0348, 0x0348, 0x0348, 0x0333, 0x0333, 0x0333, 0x0333, 0x031F, + 0x031F, 0x030C, 0x030C, 0x030C, 0x02FA, 0x02FA, 0x02E8, 0x02E8, 0x02D8, 0x02D8, 0x02C8, 0x02B9, 0x02B9, 0x02AA, 0x02AA, 0x029C, + 0x028F, 0x028F, 0x0282, 0x0276, 0x0276, 0x026A, 0x025E, 0x025E, 0x0253, 0x0249, 0x0249, 0x023E, 0x0234, 0x022B, 0x022B, 0x0222, + 0x0219, 0x0210, 0x0208, 0x0208, 0x0200, 0x01F8, 0x01F0, 0x01E9, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C7, 0x01C0, + 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x019E, 0x0199, 0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x017D, 0x0178, + 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0155, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, + 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, + 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, + 0x0348, 0x0348, 0x0348, 0x0348, 0x0348, 0x0348, 0x0348, 0x0333, 0x0333, 0x0333, 0x0333, 0x0333, 0x031F, 0x031F, 0x031F, 0x030C, + 0x030C, 0x030C, 0x02FA, 0x02FA, 0x02E8, 0x02E8, 0x02D8, 0x02D8, 0x02C8, 0x02C8, 0x02B9, 0x02B9, 0x02AA, 0x02AA, 0x029C, 0x028F, + 0x028F, 0x0282, 0x0276, 0x0276, 0x026A, 0x025E, 0x025E, 0x0253, 0x0249, 0x0249, 0x023E, 0x0234, 0x022B, 0x022B, 0x0222, 0x0219, + 0x0210, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0, 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, + 0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, + 0x0170, 0x016C, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0144, 0x0141, + 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011F, 0x011C, 0x011A, 0x0118, + 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F8, + 0x0333, 0x0333, 0x0333, 0x0333, 0x0333, 0x0333, 0x0333, 0x0333, 0x031F, 0x031F, 0x031F, 0x031F, 0x030C, 0x030C, 0x030C, 0x02FA, + 0x02FA, 0x02FA, 0x02E8, 0x02E8, 0x02D8, 0x02D8, 0x02D8, 0x02C8, 0x02C8, 0x02B9, 0x02B9, 0x02AA, 0x029C, 0x029C, 0x028F, 0x028F, + 0x0282, 0x0276, 0x0276, 0x026A, 0x025E, 0x025E, 0x0253, 0x0249, 0x0249, 0x023E, 0x0234, 0x0234, 0x022B, 0x0222, 0x0219, 0x0219, + 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F8, 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01C0, 0x01BA, + 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x019E, 0x0199, 0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x017D, 0x0178, 0x0174, + 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, + 0x013B, 0x0138, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, + 0x0113, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, + 0x031F, 0x031F, 0x031F, 0x031F, 0x031F, 0x031F, 0x031F, 0x031F, 0x030C, 0x030C, 0x030C, 0x030C, 0x030C, 0x02FA, 0x02FA, 0x02FA, + 0x02E8, 0x02E8, 0x02E8, 0x02D8, 0x02D8, 0x02C8, 0x02C8, 0x02B9, 0x02B9, 0x02AA, 0x02AA, 0x029C, 0x029C, 0x028F, 0x028F, 0x0282, + 0x0276, 0x0276, 0x026A, 0x025E, 0x025E, 0x0253, 0x0249, 0x0249, 0x023E, 0x0234, 0x0234, 0x022B, 0x0222, 0x0219, 0x0219, 0x0210, + 0x0208, 0x0200, 0x0200, 0x01F8, 0x01F0, 0x01E9, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, + 0x01AF, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, + 0x016C, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, + 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, + 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, + 0x030C, 0x030C, 0x030C, 0x030C, 0x030C, 0x030C, 0x030C, 0x030C, 0x030C, 0x02FA, 0x02FA, 0x02FA, 0x02FA, 0x02E8, 0x02E8, 0x02E8, + 0x02D8, 0x02D8, 0x02D8, 0x02C8, 0x02C8, 0x02B9, 0x02B9, 0x02AA, 0x02AA, 0x029C, 0x029C, 0x028F, 0x028F, 0x0282, 0x0282, 0x0276, + 0x0276, 0x026A, 0x025E, 0x025E, 0x0253, 0x0249, 0x0249, 0x023E, 0x0234, 0x0234, 0x022B, 0x0222, 0x0222, 0x0219, 0x0210, 0x0208, + 0x0208, 0x0200, 0x01F8, 0x01F0, 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01BA, 0x01B4, + 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0199, 0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x017D, 0x0178, 0x0174, 0x0170, + 0x016C, 0x0168, 0x0164, 0x0160, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013E, + 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011C, 0x011A, 0x0118, 0x0115, + 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00F8, 0x00F6, + 0x02FA, 0x02FA, 0x02FA, 0x02FA, 0x02FA, 0x02FA, 0x02FA, 0x02FA, 0x02FA, 0x02E8, 0x02E8, 0x02E8, 0x02E8, 0x02D8, 0x02D8, 0x02D8, + 0x02D8, 0x02C8, 0x02C8, 0x02B9, 0x02B9, 0x02B9, 0x02AA, 0x02AA, 0x029C, 0x029C, 0x028F, 0x028F, 0x0282, 0x0282, 0x0276, 0x026A, + 0x026A, 0x025E, 0x025E, 0x0253, 0x0249, 0x0249, 0x023E, 0x0234, 0x0234, 0x022B, 0x0222, 0x0222, 0x0219, 0x0210, 0x0208, 0x0208, + 0x0200, 0x01F8, 0x01F0, 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01C0, 0x01BA, 0x01B4, 0x01AF, + 0x01A9, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, 0x018A, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x0170, + 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, + 0x0138, 0x0135, 0x0132, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, + 0x0111, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, + 0x02E8, 0x02E8, 0x02E8, 0x02E8, 0x02E8, 0x02E8, 0x02E8, 0x02E8, 0x02E8, 0x02E8, 0x02D8, 0x02D8, 0x02D8, 0x02D8, 0x02C8, 0x02C8, + 0x02C8, 0x02B9, 0x02B9, 0x02B9, 0x02AA, 0x02AA, 0x029C, 0x029C, 0x028F, 0x028F, 0x0282, 0x0282, 0x0276, 0x0276, 0x026A, 0x026A, + 0x025E, 0x0253, 0x0253, 0x0249, 0x0249, 0x023E, 0x0234, 0x0234, 0x022B, 0x0222, 0x0222, 0x0219, 0x0210, 0x0210, 0x0208, 0x0200, + 0x01F8, 0x01F8, 0x01F0, 0x01E9, 0x01E1, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01AF, + 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, + 0x0168, 0x0164, 0x0160, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013B, + 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, + 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, + 0x02E8, 0x02E8, 0x02E8, 0x02D8, 0x02D8, 0x02D8, 0x02D8, 0x02D8, 0x02D8, 0x02D8, 0x02C8, 0x02C8, 0x02C8, 0x02C8, 0x02B9, 0x02B9, + 0x02B9, 0x02AA, 0x02AA, 0x02AA, 0x029C, 0x029C, 0x028F, 0x028F, 0x028F, 0x0282, 0x0282, 0x0276, 0x0276, 0x026A, 0x025E, 0x025E, + 0x0253, 0x0253, 0x0249, 0x0249, 0x023E, 0x0234, 0x0234, 0x022B, 0x0222, 0x0222, 0x0219, 0x0210, 0x0210, 0x0208, 0x0200, 0x01F8, + 0x01F8, 0x01F0, 0x01E9, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01BA, 0x01B4, 0x01AF, 0x01A9, + 0x01A4, 0x019E, 0x019E, 0x0199, 0x0194, 0x018F, 0x018A, 0x0186, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x016C, + 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, + 0x0135, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x011A, 0x0118, 0x0115, 0x0113, + 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, + 0x02D8, 0x02D8, 0x02D8, 0x02D8, 0x02C8, 0x02C8, 0x02C8, 0x02C8, 0x02C8, 0x02C8, 0x02C8, 0x02B9, 0x02B9, 0x02B9, 0x02B9, 0x02AA, + 0x02AA, 0x02AA, 0x029C, 0x029C, 0x028F, 0x028F, 0x028F, 0x0282, 0x0282, 0x0276, 0x0276, 0x026A, 0x026A, 0x025E, 0x025E, 0x0253, + 0x0253, 0x0249, 0x023E, 0x023E, 0x0234, 0x0234, 0x022B, 0x0222, 0x0222, 0x0219, 0x0210, 0x0210, 0x0208, 0x0200, 0x0200, 0x01F8, + 0x01F0, 0x01E9, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A9, + 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, + 0x0164, 0x0160, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, + 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, + 0x010E, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2, + 0x02C8, 0x02C8, 0x02C8, 0x02C8, 0x02C8, 0x02B9, 0x02B9, 0x02B9, 0x02B9, 0x02B9, 0x02B9, 0x02AA, 0x02AA, 0x02AA, 0x02AA, 0x029C, + 0x029C, 0x029C, 0x028F, 0x028F, 0x028F, 0x0282, 0x0282, 0x0276, 0x0276, 0x026A, 0x026A, 0x025E, 0x025E, 0x0253, 0x0253, 0x0249, + 0x0249, 0x023E, 0x023E, 0x0234, 0x022B, 0x022B, 0x0222, 0x0222, 0x0219, 0x0210, 0x0210, 0x0208, 0x0200, 0x0200, 0x01F8, 0x01F0, + 0x01E9, 0x01E9, 0x01E1, 0x01DA, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A4, + 0x019E, 0x0199, 0x0199, 0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x016C, 0x0168, + 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0138, + 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, + 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2, + 0x02B9, 0x02B9, 0x02B9, 0x02B9, 0x02B9, 0x02AA, 0x02AA, 0x02AA, 0x02AA, 0x02AA, 0x02AA, 0x029C, 0x029C, 0x029C, 0x029C, 0x028F, + 0x028F, 0x028F, 0x0282, 0x0282, 0x0282, 0x0276, 0x0276, 0x026A, 0x026A, 0x026A, 0x025E, 0x025E, 0x0253, 0x0253, 0x0249, 0x0249, + 0x023E, 0x0234, 0x0234, 0x022B, 0x022B, 0x0222, 0x0219, 0x0219, 0x0210, 0x0210, 0x0208, 0x0200, 0x0200, 0x01F8, 0x01F0, 0x01E9, + 0x01E9, 0x01E1, 0x01DA, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, + 0x019E, 0x0199, 0x0194, 0x018F, 0x018A, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, + 0x0160, 0x015C, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, + 0x0132, 0x012F, 0x012C, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0113, 0x0111, + 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2, + 0x02AA, 0x02AA, 0x02AA, 0x02AA, 0x02AA, 0x02AA, 0x029C, 0x029C, 0x029C, 0x029C, 0x029C, 0x029C, 0x028F, 0x028F, 0x028F, 0x028F, + 0x0282, 0x0282, 0x0282, 0x0276, 0x0276, 0x026A, 0x026A, 0x026A, 0x025E, 0x025E, 0x0253, 0x0253, 0x0249, 0x0249, 0x023E, 0x023E, + 0x0234, 0x0234, 0x022B, 0x022B, 0x0222, 0x0219, 0x0219, 0x0210, 0x0208, 0x0208, 0x0200, 0x0200, 0x01F8, 0x01F0, 0x01F0, 0x01E9, + 0x01E1, 0x01DA, 0x01DA, 0x01D4, 0x01CD, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x01A4, 0x019E, + 0x0199, 0x0194, 0x018F, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0168, 0x0164, + 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x013B, 0x0138, 0x0135, + 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, + 0x010C, 0x010A, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, + 0x029C, 0x029C, 0x029C, 0x029C, 0x029C, 0x029C, 0x028F, 0x028F, 0x028F, 0x028F, 0x028F, 0x028F, 0x0282, 0x0282, 0x0282, 0x0282, + 0x0276, 0x0276, 0x0276, 0x026A, 0x026A, 0x026A, 0x025E, 0x025E, 0x0253, 0x0253, 0x0249, 0x0249, 0x023E, 0x023E, 0x0234, 0x0234, + 0x022B, 0x022B, 0x0222, 0x0222, 0x0219, 0x0219, 0x0210, 0x0208, 0x0208, 0x0200, 0x01F8, 0x01F8, 0x01F0, 0x01E9, 0x01E9, 0x01E1, + 0x01DA, 0x01DA, 0x01D4, 0x01CD, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A9, 0x01A4, 0x019E, 0x0199, + 0x0194, 0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, + 0x015C, 0x0158, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, + 0x012F, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, + 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, + 0x028F, 0x028F, 0x028F, 0x028F, 0x028F, 0x028F, 0x028F, 0x0282, 0x0282, 0x0282, 0x0282, 0x0282, 0x0276, 0x0276, 0x0276, 0x0276, + 0x026A, 0x026A, 0x026A, 0x025E, 0x025E, 0x025E, 0x0253, 0x0253, 0x0249, 0x0249, 0x0249, 0x023E, 0x023E, 0x0234, 0x0234, 0x022B, + 0x022B, 0x0222, 0x0222, 0x0219, 0x0210, 0x0210, 0x0208, 0x0208, 0x0200, 0x01F8, 0x01F8, 0x01F0, 0x01E9, 0x01E9, 0x01E1, 0x01DA, + 0x01DA, 0x01D4, 0x01CD, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01BA, 0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0199, + 0x0194, 0x018F, 0x018A, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0164, 0x0160, + 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x013B, 0x0138, 0x0135, 0x0132, + 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010E, + 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F2, 0x00F0, + 0x0282, 0x0282, 0x0282, 0x0282, 0x0282, 0x0282, 0x0282, 0x0276, 0x0276, 0x0276, 0x0276, 0x0276, 0x0276, 0x026A, 0x026A, 0x026A, + 0x026A, 0x025E, 0x025E, 0x025E, 0x0253, 0x0253, 0x0249, 0x0249, 0x0249, 0x023E, 0x023E, 0x0234, 0x0234, 0x022B, 0x022B, 0x0222, + 0x0222, 0x0219, 0x0219, 0x0210, 0x0210, 0x0208, 0x0200, 0x0200, 0x01F8, 0x01F8, 0x01F0, 0x01E9, 0x01E9, 0x01E1, 0x01DA, 0x01DA, + 0x01D4, 0x01CD, 0x01CD, 0x01C7, 0x01C0, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x019E, 0x0199, 0x0194, + 0x018F, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0168, 0x0164, 0x0160, 0x015C, + 0x0158, 0x0155, 0x0155, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, + 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, + 0x010A, 0x0108, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00EF, + 0x0276, 0x0276, 0x0276, 0x0276, 0x0276, 0x0276, 0x0276, 0x0276, 0x026A, 0x026A, 0x026A, 0x026A, 0x026A, 0x025E, 0x025E, 0x025E, + 0x025E, 0x0253, 0x0253, 0x0253, 0x0249, 0x0249, 0x0249, 0x023E, 0x023E, 0x0234, 0x0234, 0x022B, 0x022B, 0x022B, 0x0222, 0x0222, + 0x0219, 0x0210, 0x0210, 0x0208, 0x0208, 0x0200, 0x0200, 0x01F8, 0x01F0, 0x01F0, 0x01E9, 0x01E9, 0x01E1, 0x01DA, 0x01DA, 0x01D4, + 0x01CD, 0x01CD, 0x01C7, 0x01C0, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A4, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, + 0x018F, 0x018A, 0x0186, 0x0181, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x015C, + 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0138, 0x0135, 0x0132, 0x012F, + 0x012C, 0x0129, 0x0127, 0x0124, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x0111, 0x010E, 0x010C, + 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00EF, + 0x026A, 0x026A, 0x026A, 0x026A, 0x026A, 0x026A, 0x026A, 0x026A, 0x025E, 0x025E, 0x025E, 0x025E, 0x025E, 0x0253, 0x0253, 0x0253, + 0x0253, 0x0249, 0x0249, 0x0249, 0x023E, 0x023E, 0x023E, 0x0234, 0x0234, 0x022B, 0x022B, 0x022B, 0x0222, 0x0222, 0x0219, 0x0219, + 0x0210, 0x0210, 0x0208, 0x0208, 0x0200, 0x01F8, 0x01F8, 0x01F0, 0x01F0, 0x01E9, 0x01E9, 0x01E1, 0x01DA, 0x01DA, 0x01D4, 0x01CD, + 0x01CD, 0x01C7, 0x01C0, 0x01C0, 0x01BA, 0x01B4, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x01A4, 0x019E, 0x0199, 0x0194, 0x0194, 0x018F, + 0x018A, 0x0186, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0164, 0x0160, 0x015C, 0x0158, + 0x0155, 0x0151, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, + 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, + 0x0108, 0x0106, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00EF, + 0x025E, 0x025E, 0x025E, 0x025E, 0x025E, 0x025E, 0x025E, 0x025E, 0x025E, 0x0253, 0x0253, 0x0253, 0x0253, 0x0253, 0x0249, 0x0249, + 0x0249, 0x023E, 0x023E, 0x023E, 0x023E, 0x0234, 0x0234, 0x022B, 0x022B, 0x022B, 0x0222, 0x0222, 0x0219, 0x0219, 0x0210, 0x0210, + 0x0208, 0x0208, 0x0200, 0x0200, 0x01F8, 0x01F8, 0x01F0, 0x01F0, 0x01E9, 0x01E1, 0x01E1, 0x01DA, 0x01DA, 0x01D4, 0x01CD, 0x01CD, + 0x01C7, 0x01C0, 0x01C0, 0x01BA, 0x01B4, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x01A4, 0x019E, 0x0199, 0x0199, 0x0194, 0x018F, 0x018A, + 0x0186, 0x0186, 0x0181, 0x017D, 0x0178, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0158, + 0x0155, 0x0151, 0x014E, 0x014A, 0x0147, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0135, 0x0132, 0x012F, 0x012C, + 0x0129, 0x0127, 0x0124, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x0111, 0x010E, 0x010C, 0x010A, + 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00ED, + 0x0253, 0x0253, 0x0253, 0x0253, 0x0253, 0x0253, 0x0253, 0x0253, 0x0253, 0x0249, 0x0249, 0x0249, 0x0249, 0x0249, 0x023E, 0x023E, + 0x023E, 0x023E, 0x0234, 0x0234, 0x0234, 0x022B, 0x022B, 0x022B, 0x0222, 0x0222, 0x0219, 0x0219, 0x0210, 0x0210, 0x0210, 0x0208, + 0x0208, 0x0200, 0x0200, 0x01F8, 0x01F0, 0x01F0, 0x01E9, 0x01E9, 0x01E1, 0x01E1, 0x01DA, 0x01D4, 0x01D4, 0x01CD, 0x01CD, 0x01C7, + 0x01C0, 0x01C0, 0x01BA, 0x01B4, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x01A4, 0x019E, 0x0199, 0x0199, 0x0194, 0x018F, 0x018A, 0x018A, + 0x0186, 0x0181, 0x017D, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x015C, 0x0158, 0x0155, + 0x0151, 0x014E, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x012C, + 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, + 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00ED, + 0x0249, 0x0249, 0x0249, 0x0249, 0x0249, 0x0249, 0x0249, 0x0249, 0x0249, 0x023E, 0x023E, 0x023E, 0x023E, 0x023E, 0x0234, 0x0234, + 0x0234, 0x0234, 0x022B, 0x022B, 0x022B, 0x0222, 0x0222, 0x0222, 0x0219, 0x0219, 0x0210, 0x0210, 0x0210, 0x0208, 0x0208, 0x0200, + 0x0200, 0x01F8, 0x01F8, 0x01F0, 0x01F0, 0x01E9, 0x01E9, 0x01E1, 0x01DA, 0x01DA, 0x01D4, 0x01D4, 0x01CD, 0x01C7, 0x01C7, 0x01C0, + 0x01BA, 0x01BA, 0x01B4, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x01A4, 0x019E, 0x0199, 0x0199, 0x0194, 0x018F, 0x018A, 0x018A, 0x0186, + 0x0181, 0x017D, 0x017D, 0x0178, 0x0174, 0x0170, 0x0170, 0x016C, 0x0168, 0x0164, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, + 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x0132, 0x012F, 0x012C, 0x0129, + 0x0127, 0x0124, 0x0121, 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, + 0x0106, 0x0104, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00ED, 0x00ED, + 0x023E, 0x023E, 0x023E, 0x023E, 0x023E, 0x023E, 0x023E, 0x023E, 0x023E, 0x023E, 0x0234, 0x0234, 0x0234, 0x0234, 0x0234, 0x022B, + 0x022B, 0x022B, 0x0222, 0x0222, 0x0222, 0x0219, 0x0219, 0x0219, 0x0210, 0x0210, 0x0210, 0x0208, 0x0208, 0x0200, 0x0200, 0x01F8, + 0x01F8, 0x01F0, 0x01F0, 0x01E9, 0x01E9, 0x01E1, 0x01E1, 0x01DA, 0x01DA, 0x01D4, 0x01CD, 0x01CD, 0x01C7, 0x01C7, 0x01C0, 0x01BA, + 0x01BA, 0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A4, 0x01A4, 0x019E, 0x0199, 0x0199, 0x0194, 0x018F, 0x018F, 0x018A, 0x0186, 0x0181, + 0x0181, 0x017D, 0x0178, 0x0174, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0164, 0x0160, 0x015C, 0x0158, 0x0158, 0x0155, 0x0151, + 0x014E, 0x014A, 0x0147, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0129, + 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0108, + 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00ED, 0x00EB, + 0x0234, 0x0234, 0x0234, 0x0234, 0x0234, 0x0234, 0x0234, 0x0234, 0x0234, 0x0234, 0x022B, 0x022B, 0x022B, 0x022B, 0x022B, 0x0222, + 0x0222, 0x0222, 0x0222, 0x0219, 0x0219, 0x0219, 0x0210, 0x0210, 0x0210, 0x0208, 0x0208, 0x0200, 0x0200, 0x01F8, 0x01F8, 0x01F8, + 0x01F0, 0x01F0, 0x01E9, 0x01E9, 0x01E1, 0x01DA, 0x01DA, 0x01D4, 0x01D4, 0x01CD, 0x01CD, 0x01C7, 0x01C7, 0x01C0, 0x01BA, 0x01BA, + 0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A4, 0x01A4, 0x019E, 0x0199, 0x0199, 0x0194, 0x018F, 0x018F, 0x018A, 0x0186, 0x0181, 0x0181, + 0x017D, 0x0178, 0x0174, 0x0174, 0x0170, 0x016C, 0x0168, 0x0168, 0x0164, 0x0160, 0x015C, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, + 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012F, 0x012C, 0x0129, 0x0127, + 0x0124, 0x0121, 0x011F, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, + 0x0104, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00EB, + 0x022B, 0x022B, 0x022B, 0x022B, 0x022B, 0x022B, 0x022B, 0x022B, 0x022B, 0x022B, 0x0222, 0x0222, 0x0222, 0x0222, 0x0222, 0x0219, + 0x0219, 0x0219, 0x0219, 0x0210, 0x0210, 0x0210, 0x0208, 0x0208, 0x0208, 0x0200, 0x0200, 0x01F8, 0x01F8, 0x01F8, 0x01F0, 0x01F0, + 0x01E9, 0x01E9, 0x01E1, 0x01E1, 0x01DA, 0x01DA, 0x01D4, 0x01D4, 0x01CD, 0x01C7, 0x01C7, 0x01C0, 0x01C0, 0x01BA, 0x01BA, 0x01B4, + 0x01AF, 0x01AF, 0x01A9, 0x01A4, 0x01A4, 0x019E, 0x0199, 0x0199, 0x0194, 0x018F, 0x018F, 0x018A, 0x0186, 0x0181, 0x0181, 0x017D, + 0x0178, 0x0178, 0x0174, 0x0170, 0x016C, 0x016C, 0x0168, 0x0164, 0x0160, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x0151, 0x014E, + 0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0127, + 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, + 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EA, + 0x0222, 0x0222, 0x0222, 0x0222, 0x0222, 0x0222, 0x0222, 0x0222, 0x0222, 0x0222, 0x0222, 0x0219, 0x0219, 0x0219, 0x0219, 0x0210, + 0x0210, 0x0210, 0x0210, 0x0208, 0x0208, 0x0208, 0x0200, 0x0200, 0x0200, 0x01F8, 0x01F8, 0x01F8, 0x01F0, 0x01F0, 0x01E9, 0x01E9, + 0x01E1, 0x01E1, 0x01DA, 0x01DA, 0x01D4, 0x01D4, 0x01CD, 0x01CD, 0x01C7, 0x01C7, 0x01C0, 0x01C0, 0x01BA, 0x01B4, 0x01B4, 0x01AF, + 0x01AF, 0x01A9, 0x01A4, 0x01A4, 0x019E, 0x0199, 0x0199, 0x0194, 0x018F, 0x018F, 0x018A, 0x0186, 0x0186, 0x0181, 0x017D, 0x0178, + 0x0178, 0x0174, 0x0170, 0x016C, 0x016C, 0x0168, 0x0164, 0x0160, 0x0160, 0x015C, 0x0158, 0x0155, 0x0155, 0x0151, 0x014E, 0x014A, + 0x0147, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x012C, 0x0129, 0x0127, 0x0124, + 0x0121, 0x011F, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, + 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EA, + 0x0219, 0x0219, 0x0219, 0x0219, 0x0219, 0x0219, 0x0219, 0x0219, 0x0219, 0x0219, 0x0219, 0x0210, 0x0210, 0x0210, 0x0210, 0x0210, + 0x0208, 0x0208, 0x0208, 0x0200, 0x0200, 0x0200, 0x0200, 0x01F8, 0x01F8, 0x01F0, 0x01F0, 0x01F0, 0x01E9, 0x01E9, 0x01E1, 0x01E1, + 0x01DA, 0x01DA, 0x01DA, 0x01D4, 0x01D4, 0x01CD, 0x01CD, 0x01C7, 0x01C0, 0x01C0, 0x01BA, 0x01BA, 0x01B4, 0x01B4, 0x01AF, 0x01A9, + 0x01A9, 0x01A4, 0x01A4, 0x019E, 0x0199, 0x0199, 0x0194, 0x018F, 0x018F, 0x018A, 0x0186, 0x0186, 0x0181, 0x017D, 0x0178, 0x0178, + 0x0174, 0x0170, 0x0170, 0x016C, 0x0168, 0x0164, 0x0164, 0x0160, 0x015C, 0x0158, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x014A, + 0x0147, 0x0144, 0x0141, 0x013E, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0124, + 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, + 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00EA, + 0x0210, 0x0210, 0x0210, 0x0210, 0x0210, 0x0210, 0x0210, 0x0210, 0x0210, 0x0210, 0x0210, 0x0208, 0x0208, 0x0208, 0x0208, 0x0208, + 0x0200, 0x0200, 0x0200, 0x0200, 0x01F8, 0x01F8, 0x01F8, 0x01F0, 0x01F0, 0x01F0, 0x01E9, 0x01E9, 0x01E1, 0x01E1, 0x01E1, 0x01DA, + 0x01DA, 0x01D4, 0x01D4, 0x01CD, 0x01CD, 0x01C7, 0x01C7, 0x01C0, 0x01C0, 0x01BA, 0x01BA, 0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A9, + 0x01A4, 0x019E, 0x019E, 0x0199, 0x0199, 0x0194, 0x018F, 0x018F, 0x018A, 0x0186, 0x0186, 0x0181, 0x017D, 0x0178, 0x0178, 0x0174, + 0x0170, 0x0170, 0x016C, 0x0168, 0x0164, 0x0164, 0x0160, 0x015C, 0x0158, 0x0158, 0x0155, 0x0151, 0x014E, 0x014E, 0x014A, 0x0147, + 0x0144, 0x0141, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0129, 0x0127, 0x0124, 0x0121, + 0x011F, 0x011C, 0x011A, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, + 0x0100, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00E8, + 0x0208, 0x0208, 0x0208, 0x0208, 0x0208, 0x0208, 0x0208, 0x0208, 0x0208, 0x0208, 0x0208, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x01F8, 0x01F8, 0x01F8, 0x01F8, 0x01F0, 0x01F0, 0x01F0, 0x01E9, 0x01E9, 0x01E9, 0x01E1, 0x01E1, 0x01DA, 0x01DA, 0x01DA, 0x01D4, + 0x01D4, 0x01CD, 0x01CD, 0x01C7, 0x01C7, 0x01C0, 0x01C0, 0x01BA, 0x01BA, 0x01B4, 0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A4, 0x01A4, + 0x019E, 0x019E, 0x0199, 0x0194, 0x0194, 0x018F, 0x018A, 0x018A, 0x0186, 0x0181, 0x0181, 0x017D, 0x0178, 0x0178, 0x0174, 0x0170, + 0x0170, 0x016C, 0x0168, 0x0164, 0x0164, 0x0160, 0x015C, 0x015C, 0x0158, 0x0155, 0x0151, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, + 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, + 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0106, 0x0104, 0x0102, + 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00EA, 0x00E8, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x01F8, 0x01F8, 0x01F8, 0x01F8, + 0x01F0, 0x01F0, 0x01F0, 0x01F0, 0x01E9, 0x01E9, 0x01E9, 0x01E1, 0x01E1, 0x01E1, 0x01DA, 0x01DA, 0x01DA, 0x01D4, 0x01D4, 0x01CD, + 0x01CD, 0x01C7, 0x01C7, 0x01C7, 0x01C0, 0x01C0, 0x01BA, 0x01BA, 0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A9, 0x01A4, 0x01A4, 0x019E, + 0x0199, 0x0199, 0x0194, 0x0194, 0x018F, 0x018A, 0x018A, 0x0186, 0x0181, 0x0181, 0x017D, 0x0178, 0x0178, 0x0174, 0x0170, 0x0170, + 0x016C, 0x0168, 0x0168, 0x0164, 0x0160, 0x015C, 0x015C, 0x0158, 0x0155, 0x0151, 0x0151, 0x014E, 0x014A, 0x0147, 0x0147, 0x0144, + 0x0141, 0x013E, 0x013B, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0124, 0x0121, 0x011F, + 0x011C, 0x011A, 0x0118, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, + 0x00FE, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E6, + 0x01F8, 0x01F8, 0x01F8, 0x01F8, 0x01F8, 0x01F8, 0x01F8, 0x01F8, 0x01F8, 0x01F8, 0x01F8, 0x01F8, 0x01F0, 0x01F0, 0x01F0, 0x01F0, + 0x01F0, 0x01E9, 0x01E9, 0x01E9, 0x01E1, 0x01E1, 0x01E1, 0x01E1, 0x01DA, 0x01DA, 0x01D4, 0x01D4, 0x01D4, 0x01CD, 0x01CD, 0x01C7, + 0x01C7, 0x01C7, 0x01C0, 0x01C0, 0x01BA, 0x01BA, 0x01B4, 0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A9, 0x01A4, 0x019E, 0x019E, 0x0199, + 0x0199, 0x0194, 0x018F, 0x018F, 0x018A, 0x018A, 0x0186, 0x0181, 0x0181, 0x017D, 0x0178, 0x0178, 0x0174, 0x0170, 0x0170, 0x016C, + 0x0168, 0x0168, 0x0164, 0x0160, 0x015C, 0x015C, 0x0158, 0x0155, 0x0151, 0x0151, 0x014E, 0x014A, 0x0147, 0x0147, 0x0144, 0x0141, + 0x013E, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, + 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, + 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E6, + 0x01F8, 0x01F8, 0x01F0, 0x01F0, 0x01F0, 0x01F0, 0x01F0, 0x01F0, 0x01F0, 0x01F0, 0x01F0, 0x01F0, 0x01E9, 0x01E9, 0x01E9, 0x01E9, + 0x01E9, 0x01E1, 0x01E1, 0x01E1, 0x01E1, 0x01DA, 0x01DA, 0x01DA, 0x01D4, 0x01D4, 0x01D4, 0x01CD, 0x01CD, 0x01C7, 0x01C7, 0x01C7, + 0x01C0, 0x01C0, 0x01BA, 0x01BA, 0x01B4, 0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A9, 0x01A4, 0x01A4, 0x019E, 0x019E, 0x0199, 0x0194, + 0x0194, 0x018F, 0x018F, 0x018A, 0x0186, 0x0186, 0x0181, 0x0181, 0x017D, 0x0178, 0x0178, 0x0174, 0x0170, 0x0170, 0x016C, 0x0168, + 0x0168, 0x0164, 0x0160, 0x015C, 0x015C, 0x0158, 0x0155, 0x0155, 0x0151, 0x014E, 0x014A, 0x014A, 0x0147, 0x0144, 0x0141, 0x0141, + 0x013E, 0x013B, 0x0138, 0x0135, 0x0135, 0x0132, 0x012F, 0x012C, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011F, 0x011C, + 0x011A, 0x0118, 0x0115, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, + 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E6, 0x00E5, + 0x01F0, 0x01F0, 0x01F0, 0x01E9, 0x01E9, 0x01E9, 0x01E9, 0x01E9, 0x01E9, 0x01E9, 0x01E9, 0x01E9, 0x01E1, 0x01E1, 0x01E1, 0x01E1, + 0x01E1, 0x01DA, 0x01DA, 0x01DA, 0x01DA, 0x01D4, 0x01D4, 0x01D4, 0x01CD, 0x01CD, 0x01CD, 0x01C7, 0x01C7, 0x01C7, 0x01C0, 0x01C0, + 0x01BA, 0x01BA, 0x01B4, 0x01B4, 0x01AF, 0x01AF, 0x01AF, 0x01A9, 0x01A9, 0x01A4, 0x019E, 0x019E, 0x0199, 0x0199, 0x0194, 0x0194, + 0x018F, 0x018F, 0x018A, 0x0186, 0x0186, 0x0181, 0x017D, 0x017D, 0x0178, 0x0178, 0x0174, 0x0170, 0x0170, 0x016C, 0x0168, 0x0168, + 0x0164, 0x0160, 0x015C, 0x015C, 0x0158, 0x0155, 0x0155, 0x0151, 0x014E, 0x014A, 0x014A, 0x0147, 0x0144, 0x0141, 0x0141, 0x013E, + 0x013B, 0x0138, 0x0138, 0x0135, 0x0132, 0x012F, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, + 0x0118, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0102, 0x0100, 0x00FE, + 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E6, 0x00E5, + 0x01E9, 0x01E9, 0x01E9, 0x01E9, 0x01E1, 0x01E1, 0x01E1, 0x01E1, 0x01E1, 0x01E1, 0x01E1, 0x01E1, 0x01E1, 0x01DA, 0x01DA, 0x01DA, + 0x01DA, 0x01D4, 0x01D4, 0x01D4, 0x01D4, 0x01CD, 0x01CD, 0x01CD, 0x01C7, 0x01C7, 0x01C7, 0x01C0, 0x01C0, 0x01C0, 0x01BA, 0x01BA, + 0x01B4, 0x01B4, 0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A9, 0x01A4, 0x01A4, 0x019E, 0x019E, 0x0199, 0x0199, 0x0194, 0x018F, 0x018F, + 0x018A, 0x018A, 0x0186, 0x0186, 0x0181, 0x017D, 0x017D, 0x0178, 0x0174, 0x0174, 0x0170, 0x0170, 0x016C, 0x0168, 0x0168, 0x0164, + 0x0160, 0x015C, 0x015C, 0x0158, 0x0155, 0x0155, 0x0151, 0x014E, 0x014E, 0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013B, + 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012F, 0x012C, 0x0129, 0x0127, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011C, 0x011A, + 0x0118, 0x0115, 0x0113, 0x0111, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, + 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E3, + 0x01E1, 0x01E1, 0x01E1, 0x01E1, 0x01DA, 0x01DA, 0x01DA, 0x01DA, 0x01DA, 0x01DA, 0x01DA, 0x01DA, 0x01DA, 0x01D4, 0x01D4, 0x01D4, + 0x01D4, 0x01D4, 0x01CD, 0x01CD, 0x01CD, 0x01C7, 0x01C7, 0x01C7, 0x01C7, 0x01C0, 0x01C0, 0x01C0, 0x01BA, 0x01BA, 0x01B4, 0x01B4, + 0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A9, 0x01A4, 0x01A4, 0x019E, 0x019E, 0x0199, 0x0199, 0x0194, 0x0194, 0x018F, 0x018F, 0x018A, + 0x018A, 0x0186, 0x0181, 0x0181, 0x017D, 0x017D, 0x0178, 0x0174, 0x0174, 0x0170, 0x016C, 0x016C, 0x0168, 0x0164, 0x0164, 0x0160, + 0x015C, 0x015C, 0x0158, 0x0155, 0x0155, 0x0151, 0x014E, 0x014E, 0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013B, 0x013B, + 0x0138, 0x0135, 0x0132, 0x0132, 0x012F, 0x012C, 0x0129, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011F, 0x011C, 0x011A, 0x0118, + 0x0115, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FC, + 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E3, + 0x01DA, 0x01DA, 0x01DA, 0x01DA, 0x01DA, 0x01D4, 0x01D4, 0x01D4, 0x01D4, 0x01D4, 0x01D4, 0x01D4, 0x01D4, 0x01CD, 0x01CD, 0x01CD, + 0x01CD, 0x01CD, 0x01C7, 0x01C7, 0x01C7, 0x01C7, 0x01C0, 0x01C0, 0x01C0, 0x01BA, 0x01BA, 0x01BA, 0x01B4, 0x01B4, 0x01AF, 0x01AF, + 0x01AF, 0x01A9, 0x01A9, 0x01A4, 0x01A4, 0x019E, 0x019E, 0x0199, 0x0199, 0x0194, 0x0194, 0x018F, 0x018F, 0x018A, 0x018A, 0x0186, + 0x0186, 0x0181, 0x0181, 0x017D, 0x0178, 0x0178, 0x0174, 0x0174, 0x0170, 0x016C, 0x016C, 0x0168, 0x0164, 0x0164, 0x0160, 0x015C, + 0x015C, 0x0158, 0x0155, 0x0155, 0x0151, 0x014E, 0x014E, 0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013E, 0x013B, 0x0138, + 0x0135, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0129, 0x0127, 0x0124, 0x0121, 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0118, + 0x0115, 0x0113, 0x0111, 0x010E, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, + 0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E3, 0x00E3, + 0x01D4, 0x01D4, 0x01D4, 0x01D4, 0x01D4, 0x01CD, 0x01CD, 0x01CD, 0x01CD, 0x01CD, 0x01CD, 0x01CD, 0x01CD, 0x01C7, 0x01C7, 0x01C7, + 0x01C7, 0x01C7, 0x01C0, 0x01C0, 0x01C0, 0x01C0, 0x01BA, 0x01BA, 0x01BA, 0x01B4, 0x01B4, 0x01B4, 0x01AF, 0x01AF, 0x01AF, 0x01A9, + 0x01A9, 0x01A4, 0x01A4, 0x019E, 0x019E, 0x019E, 0x0199, 0x0199, 0x0194, 0x0194, 0x018F, 0x018F, 0x018A, 0x018A, 0x0186, 0x0181, + 0x0181, 0x017D, 0x017D, 0x0178, 0x0178, 0x0174, 0x0170, 0x0170, 0x016C, 0x016C, 0x0168, 0x0164, 0x0164, 0x0160, 0x015C, 0x015C, + 0x0158, 0x0155, 0x0155, 0x0151, 0x014E, 0x014E, 0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013E, 0x013B, 0x0138, 0x0135, + 0x0135, 0x0132, 0x012F, 0x012C, 0x012C, 0x0129, 0x0127, 0x0124, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x011A, 0x0118, 0x0115, + 0x0113, 0x0111, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FA, + 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E5, 0x00E3, 0x00E1, + 0x01CD, 0x01CD, 0x01CD, 0x01CD, 0x01CD, 0x01CD, 0x01C7, 0x01C7, 0x01C7, 0x01C7, 0x01C7, 0x01C7, 0x01C7, 0x01C7, 0x01C0, 0x01C0, + 0x01C0, 0x01C0, 0x01BA, 0x01BA, 0x01BA, 0x01BA, 0x01B4, 0x01B4, 0x01B4, 0x01AF, 0x01AF, 0x01AF, 0x01A9, 0x01A9, 0x01A9, 0x01A4, + 0x01A4, 0x019E, 0x019E, 0x019E, 0x0199, 0x0199, 0x0194, 0x0194, 0x018F, 0x018F, 0x018A, 0x018A, 0x0186, 0x0186, 0x0181, 0x0181, + 0x017D, 0x017D, 0x0178, 0x0174, 0x0174, 0x0170, 0x0170, 0x016C, 0x0168, 0x0168, 0x0164, 0x0164, 0x0160, 0x015C, 0x015C, 0x0158, + 0x0155, 0x0155, 0x0151, 0x014E, 0x014E, 0x014A, 0x0147, 0x0147, 0x0144, 0x0141, 0x013E, 0x013E, 0x013B, 0x0138, 0x0135, 0x0135, + 0x0132, 0x012F, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0124, 0x0121, 0x011F, 0x011C, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, + 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, + 0x00F6, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E3, 0x00E1, 0x00E1, + 0x01C7, 0x01C7, 0x01C7, 0x01C7, 0x01C7, 0x01C7, 0x01C0, 0x01C0, 0x01C0, 0x01C0, 0x01C0, 0x01C0, 0x01C0, 0x01C0, 0x01BA, 0x01BA, + 0x01BA, 0x01BA, 0x01BA, 0x01B4, 0x01B4, 0x01B4, 0x01AF, 0x01AF, 0x01AF, 0x01AF, 0x01A9, 0x01A9, 0x01A9, 0x01A4, 0x01A4, 0x019E, + 0x019E, 0x019E, 0x0199, 0x0199, 0x0194, 0x0194, 0x018F, 0x018F, 0x018A, 0x018A, 0x0186, 0x0186, 0x0181, 0x0181, 0x017D, 0x017D, + 0x0178, 0x0178, 0x0174, 0x0174, 0x0170, 0x016C, 0x016C, 0x0168, 0x0168, 0x0164, 0x0160, 0x0160, 0x015C, 0x015C, 0x0158, 0x0155, + 0x0155, 0x0151, 0x014E, 0x014E, 0x014A, 0x0147, 0x0147, 0x0144, 0x0141, 0x013E, 0x013E, 0x013B, 0x0138, 0x0138, 0x0135, 0x0132, + 0x012F, 0x012F, 0x012C, 0x0129, 0x0127, 0x0127, 0x0124, 0x0121, 0x011F, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0115, 0x0113, + 0x0111, 0x010E, 0x010C, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00FA, 0x00F8, + 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E3, 0x00E1, 0x00E0, + 0x01C0, 0x01C0, 0x01C0, 0x01C0, 0x01C0, 0x01C0, 0x01BA, 0x01BA, 0x01BA, 0x01BA, 0x01BA, 0x01BA, 0x01BA, 0x01BA, 0x01B4, 0x01B4, + 0x01B4, 0x01B4, 0x01B4, 0x01AF, 0x01AF, 0x01AF, 0x01AF, 0x01A9, 0x01A9, 0x01A9, 0x01A4, 0x01A4, 0x01A4, 0x019E, 0x019E, 0x0199, + 0x0199, 0x0199, 0x0194, 0x0194, 0x018F, 0x018F, 0x018F, 0x018A, 0x018A, 0x0186, 0x0186, 0x0181, 0x0181, 0x017D, 0x017D, 0x0178, + 0x0174, 0x0174, 0x0170, 0x0170, 0x016C, 0x016C, 0x0168, 0x0164, 0x0164, 0x0160, 0x0160, 0x015C, 0x0158, 0x0158, 0x0155, 0x0151, + 0x0151, 0x014E, 0x014E, 0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013E, 0x013B, 0x0138, 0x0138, 0x0135, 0x0132, 0x012F, + 0x012F, 0x012C, 0x0129, 0x0127, 0x0127, 0x0124, 0x0121, 0x011F, 0x011F, 0x011C, 0x011A, 0x0118, 0x0118, 0x0115, 0x0113, 0x0111, + 0x010E, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, + 0x00F4, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E3, 0x00E1, 0x00E0, 0x00E0, + 0x01BA, 0x01BA, 0x01BA, 0x01BA, 0x01BA, 0x01BA, 0x01BA, 0x01B4, 0x01B4, 0x01B4, 0x01B4, 0x01B4, 0x01B4, 0x01B4, 0x01AF, 0x01AF, + 0x01AF, 0x01AF, 0x01AF, 0x01A9, 0x01A9, 0x01A9, 0x01A9, 0x01A4, 0x01A4, 0x01A4, 0x019E, 0x019E, 0x019E, 0x0199, 0x0199, 0x0199, + 0x0194, 0x0194, 0x018F, 0x018F, 0x018F, 0x018A, 0x018A, 0x0186, 0x0186, 0x0181, 0x0181, 0x017D, 0x017D, 0x0178, 0x0178, 0x0174, + 0x0174, 0x0170, 0x0170, 0x016C, 0x0168, 0x0168, 0x0164, 0x0164, 0x0160, 0x0160, 0x015C, 0x0158, 0x0158, 0x0155, 0x0151, 0x0151, + 0x014E, 0x014A, 0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013E, 0x013B, 0x0138, 0x0138, 0x0135, 0x0132, 0x012F, 0x012F, + 0x012C, 0x0129, 0x0129, 0x0127, 0x0124, 0x0121, 0x0121, 0x011F, 0x011C, 0x011A, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x0111, + 0x010E, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F8, 0x00F6, + 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E1, 0x00E0, 0x00DE, + 0x01B4, 0x01B4, 0x01B4, 0x01B4, 0x01B4, 0x01B4, 0x01B4, 0x01AF, 0x01AF, 0x01AF, 0x01AF, 0x01AF, 0x01AF, 0x01AF, 0x01AF, 0x01A9, + 0x01A9, 0x01A9, 0x01A9, 0x01A4, 0x01A4, 0x01A4, 0x01A4, 0x019E, 0x019E, 0x019E, 0x0199, 0x0199, 0x0199, 0x0194, 0x0194, 0x0194, + 0x018F, 0x018F, 0x018F, 0x018A, 0x018A, 0x0186, 0x0186, 0x0181, 0x0181, 0x017D, 0x017D, 0x0178, 0x0178, 0x0174, 0x0174, 0x0170, + 0x0170, 0x016C, 0x016C, 0x0168, 0x0168, 0x0164, 0x0164, 0x0160, 0x015C, 0x015C, 0x0158, 0x0158, 0x0155, 0x0151, 0x0151, 0x014E, + 0x014A, 0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013E, 0x013B, 0x0138, 0x0138, 0x0135, 0x0132, 0x0132, 0x012F, 0x012C, + 0x0129, 0x0129, 0x0127, 0x0124, 0x0121, 0x0121, 0x011F, 0x011C, 0x011A, 0x011A, 0x0118, 0x0115, 0x0113, 0x0113, 0x0111, 0x010E, + 0x010C, 0x010A, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F6, 0x00F4, + 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E5, 0x00E3, 0x00E1, 0x00E0, 0x00E0, 0x00DE, + 0x01AF, 0x01AF, 0x01AF, 0x01AF, 0x01AF, 0x01AF, 0x01AF, 0x01A9, 0x01A9, 0x01A9, 0x01A9, 0x01A9, 0x01A9, 0x01A9, 0x01A9, 0x01A4, + 0x01A4, 0x01A4, 0x01A4, 0x019E, 0x019E, 0x019E, 0x019E, 0x0199, 0x0199, 0x0199, 0x0199, 0x0194, 0x0194, 0x0194, 0x018F, 0x018F, + 0x018A, 0x018A, 0x018A, 0x0186, 0x0186, 0x0181, 0x0181, 0x017D, 0x017D, 0x017D, 0x0178, 0x0178, 0x0174, 0x0174, 0x0170, 0x0170, + 0x016C, 0x0168, 0x0168, 0x0164, 0x0164, 0x0160, 0x0160, 0x015C, 0x015C, 0x0158, 0x0155, 0x0155, 0x0151, 0x0151, 0x014E, 0x014A, + 0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013E, 0x013B, 0x0138, 0x0138, 0x0135, 0x0132, 0x0132, 0x012F, 0x012C, 0x0129, + 0x0129, 0x0127, 0x0124, 0x0121, 0x0121, 0x011F, 0x011C, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0113, 0x0111, 0x010E, 0x010C, + 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F4, + 0x00F2, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E0, 0x00DE, 0x00DD, + 0x01A9, 0x01A9, 0x01A9, 0x01A9, 0x01A9, 0x01A9, 0x01A9, 0x01A9, 0x01A4, 0x01A4, 0x01A4, 0x01A4, 0x01A4, 0x01A4, 0x01A4, 0x019E, + 0x019E, 0x019E, 0x019E, 0x019E, 0x0199, 0x0199, 0x0199, 0x0194, 0x0194, 0x0194, 0x0194, 0x018F, 0x018F, 0x018F, 0x018A, 0x018A, + 0x0186, 0x0186, 0x0186, 0x0181, 0x0181, 0x017D, 0x017D, 0x017D, 0x0178, 0x0178, 0x0174, 0x0174, 0x0170, 0x0170, 0x016C, 0x016C, + 0x0168, 0x0168, 0x0164, 0x0164, 0x0160, 0x015C, 0x015C, 0x0158, 0x0158, 0x0155, 0x0155, 0x0151, 0x014E, 0x014E, 0x014A, 0x0147, + 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013E, 0x013B, 0x0138, 0x0138, 0x0135, 0x0132, 0x0132, 0x012F, 0x012C, 0x0129, 0x0129, + 0x0127, 0x0124, 0x0124, 0x0121, 0x011F, 0x011C, 0x011C, 0x011A, 0x0118, 0x0115, 0x0115, 0x0113, 0x0111, 0x010E, 0x010E, 0x010C, + 0x010A, 0x0108, 0x0106, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F2, + 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E3, 0x00E1, 0x00E0, 0x00DE, 0x00DD, 0x00DD, + 0x01A4, 0x01A4, 0x01A4, 0x01A4, 0x01A4, 0x01A4, 0x01A4, 0x01A4, 0x019E, 0x019E, 0x019E, 0x019E, 0x019E, 0x019E, 0x019E, 0x0199, + 0x0199, 0x0199, 0x0199, 0x0199, 0x0194, 0x0194, 0x0194, 0x0194, 0x018F, 0x018F, 0x018F, 0x018A, 0x018A, 0x018A, 0x0186, 0x0186, + 0x0186, 0x0181, 0x0181, 0x017D, 0x017D, 0x017D, 0x0178, 0x0178, 0x0174, 0x0174, 0x0170, 0x0170, 0x016C, 0x016C, 0x0168, 0x0168, + 0x0164, 0x0164, 0x0160, 0x0160, 0x015C, 0x015C, 0x0158, 0x0158, 0x0155, 0x0151, 0x0151, 0x014E, 0x014E, 0x014A, 0x0147, 0x0147, + 0x0144, 0x0141, 0x0141, 0x013E, 0x013E, 0x013B, 0x0138, 0x0138, 0x0135, 0x0132, 0x0132, 0x012F, 0x012C, 0x0129, 0x0129, 0x0127, + 0x0124, 0x0124, 0x0121, 0x011F, 0x011C, 0x011C, 0x011A, 0x0118, 0x0115, 0x0115, 0x0113, 0x0111, 0x010E, 0x010E, 0x010C, 0x010A, + 0x0108, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, + 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DD, 0x00DB, + 0x019E, 0x019E, 0x019E, 0x019E, 0x019E, 0x019E, 0x019E, 0x019E, 0x0199, 0x0199, 0x0199, 0x0199, 0x0199, 0x0199, 0x0199, 0x0194, + 0x0194, 0x0194, 0x0194, 0x0194, 0x018F, 0x018F, 0x018F, 0x018F, 0x018A, 0x018A, 0x018A, 0x0186, 0x0186, 0x0186, 0x0181, 0x0181, + 0x0181, 0x017D, 0x017D, 0x017D, 0x0178, 0x0178, 0x0174, 0x0174, 0x0170, 0x0170, 0x016C, 0x016C, 0x016C, 0x0168, 0x0168, 0x0164, + 0x0164, 0x0160, 0x015C, 0x015C, 0x0158, 0x0158, 0x0155, 0x0155, 0x0151, 0x0151, 0x014E, 0x014A, 0x014A, 0x0147, 0x0147, 0x0144, + 0x0141, 0x0141, 0x013E, 0x013B, 0x013B, 0x0138, 0x0135, 0x0135, 0x0132, 0x012F, 0x012F, 0x012C, 0x0129, 0x0129, 0x0127, 0x0124, + 0x0124, 0x0121, 0x011F, 0x011C, 0x011C, 0x011A, 0x0118, 0x0118, 0x0115, 0x0113, 0x0111, 0x0111, 0x010E, 0x010C, 0x010A, 0x010A, + 0x0108, 0x0106, 0x0104, 0x0102, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F0, + 0x00EF, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E0, 0x00DE, 0x00DD, 0x00DB, 0x00DB, + 0x0199, 0x0199, 0x0199, 0x0199, 0x0199, 0x0199, 0x0199, 0x0199, 0x0194, 0x0194, 0x0194, 0x0194, 0x0194, 0x0194, 0x0194, 0x0194, + 0x018F, 0x018F, 0x018F, 0x018F, 0x018A, 0x018A, 0x018A, 0x018A, 0x0186, 0x0186, 0x0186, 0x0181, 0x0181, 0x0181, 0x017D, 0x017D, + 0x017D, 0x0178, 0x0178, 0x0178, 0x0174, 0x0174, 0x0170, 0x0170, 0x0170, 0x016C, 0x016C, 0x0168, 0x0168, 0x0164, 0x0164, 0x0160, + 0x0160, 0x015C, 0x015C, 0x0158, 0x0158, 0x0155, 0x0151, 0x0151, 0x014E, 0x014E, 0x014A, 0x014A, 0x0147, 0x0144, 0x0144, 0x0141, + 0x0141, 0x013E, 0x013B, 0x013B, 0x0138, 0x0135, 0x0135, 0x0132, 0x012F, 0x012F, 0x012C, 0x0129, 0x0129, 0x0127, 0x0124, 0x0124, + 0x0121, 0x011F, 0x011F, 0x011C, 0x011A, 0x0118, 0x0118, 0x0115, 0x0113, 0x0111, 0x0111, 0x010E, 0x010C, 0x010A, 0x010A, 0x0108, + 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00EF, + 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E3, 0x00E1, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DB, 0x00DA, + 0x0194, 0x0194, 0x0194, 0x0194, 0x0194, 0x0194, 0x0194, 0x0194, 0x0194, 0x018F, 0x018F, 0x018F, 0x018F, 0x018F, 0x018F, 0x018F, + 0x018A, 0x018A, 0x018A, 0x018A, 0x0186, 0x0186, 0x0186, 0x0186, 0x0181, 0x0181, 0x0181, 0x0181, 0x017D, 0x017D, 0x017D, 0x0178, + 0x0178, 0x0174, 0x0174, 0x0174, 0x0170, 0x0170, 0x016C, 0x016C, 0x016C, 0x0168, 0x0168, 0x0164, 0x0164, 0x0160, 0x0160, 0x015C, + 0x015C, 0x0158, 0x0158, 0x0155, 0x0155, 0x0151, 0x0151, 0x014E, 0x014E, 0x014A, 0x0147, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, + 0x013E, 0x013B, 0x013B, 0x0138, 0x0135, 0x0135, 0x0132, 0x012F, 0x012F, 0x012C, 0x0129, 0x0129, 0x0127, 0x0124, 0x0124, 0x0121, + 0x011F, 0x011F, 0x011C, 0x011A, 0x0118, 0x0118, 0x0115, 0x0113, 0x0111, 0x0111, 0x010E, 0x010C, 0x010C, 0x010A, 0x0108, 0x0106, + 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, + 0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E5, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DD, 0x00DB, 0x00DA, 0x00DA, + 0x018F, 0x018F, 0x018F, 0x018F, 0x018F, 0x018F, 0x018F, 0x018F, 0x018F, 0x018A, 0x018A, 0x018A, 0x018A, 0x018A, 0x018A, 0x018A, + 0x0186, 0x0186, 0x0186, 0x0186, 0x0186, 0x0181, 0x0181, 0x0181, 0x0181, 0x017D, 0x017D, 0x017D, 0x0178, 0x0178, 0x0178, 0x0174, + 0x0174, 0x0174, 0x0170, 0x0170, 0x016C, 0x016C, 0x016C, 0x0168, 0x0168, 0x0164, 0x0164, 0x0160, 0x0160, 0x015C, 0x015C, 0x0158, + 0x0158, 0x0155, 0x0155, 0x0151, 0x0151, 0x014E, 0x014E, 0x014A, 0x014A, 0x0147, 0x0147, 0x0144, 0x0141, 0x0141, 0x013E, 0x013E, + 0x013B, 0x0138, 0x0138, 0x0135, 0x0135, 0x0132, 0x012F, 0x012F, 0x012C, 0x0129, 0x0129, 0x0127, 0x0124, 0x0124, 0x0121, 0x011F, + 0x011F, 0x011C, 0x011A, 0x0118, 0x0118, 0x0115, 0x0113, 0x0113, 0x0111, 0x010E, 0x010C, 0x010C, 0x010A, 0x0108, 0x0106, 0x0106, + 0x0104, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00EF, 0x00ED, + 0x00EB, 0x00EB, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E0, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DA, 0x00D9, + 0x018A, 0x018A, 0x018A, 0x018A, 0x018A, 0x018A, 0x018A, 0x018A, 0x018A, 0x0186, 0x0186, 0x0186, 0x0186, 0x0186, 0x0186, 0x0186, + 0x0181, 0x0181, 0x0181, 0x0181, 0x0181, 0x017D, 0x017D, 0x017D, 0x017D, 0x0178, 0x0178, 0x0178, 0x0174, 0x0174, 0x0174, 0x0170, + 0x0170, 0x0170, 0x016C, 0x016C, 0x016C, 0x0168, 0x0168, 0x0164, 0x0164, 0x0160, 0x0160, 0x0160, 0x015C, 0x015C, 0x0158, 0x0158, + 0x0155, 0x0155, 0x0151, 0x0151, 0x014E, 0x014E, 0x014A, 0x0147, 0x0147, 0x0144, 0x0144, 0x0141, 0x0141, 0x013E, 0x013B, 0x013B, + 0x0138, 0x0138, 0x0135, 0x0132, 0x0132, 0x012F, 0x012F, 0x012C, 0x0129, 0x0129, 0x0127, 0x0124, 0x0124, 0x0121, 0x011F, 0x011F, + 0x011C, 0x011A, 0x0118, 0x0118, 0x0115, 0x0113, 0x0113, 0x0111, 0x010E, 0x010C, 0x010C, 0x010A, 0x0108, 0x0106, 0x0106, 0x0104, + 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00ED, 0x00ED, + 0x00EB, 0x00EA, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E3, 0x00E1, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DB, 0x00DA, 0x00D9, 0x00D7, + 0x0186, 0x0186, 0x0186, 0x0186, 0x0186, 0x0186, 0x0186, 0x0186, 0x0186, 0x0181, 0x0181, 0x0181, 0x0181, 0x0181, 0x0181, 0x0181, + 0x017D, 0x017D, 0x017D, 0x017D, 0x017D, 0x0178, 0x0178, 0x0178, 0x0178, 0x0174, 0x0174, 0x0174, 0x0170, 0x0170, 0x0170, 0x016C, + 0x016C, 0x016C, 0x0168, 0x0168, 0x0168, 0x0164, 0x0164, 0x0160, 0x0160, 0x0160, 0x015C, 0x015C, 0x0158, 0x0158, 0x0155, 0x0155, + 0x0151, 0x0151, 0x014E, 0x014E, 0x014A, 0x014A, 0x0147, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013E, 0x013B, 0x013B, 0x0138, + 0x0135, 0x0135, 0x0132, 0x0132, 0x012F, 0x012C, 0x012C, 0x0129, 0x0127, 0x0127, 0x0124, 0x0121, 0x0121, 0x011F, 0x011C, 0x011C, + 0x011A, 0x0118, 0x0118, 0x0115, 0x0113, 0x0113, 0x0111, 0x010E, 0x010C, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 0x0102, + 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00EB, + 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E5, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, + 0x0181, 0x0181, 0x0181, 0x0181, 0x0181, 0x0181, 0x0181, 0x0181, 0x0181, 0x0181, 0x017D, 0x017D, 0x017D, 0x017D, 0x017D, 0x017D, + 0x017D, 0x0178, 0x0178, 0x0178, 0x0178, 0x0174, 0x0174, 0x0174, 0x0174, 0x0170, 0x0170, 0x0170, 0x0170, 0x016C, 0x016C, 0x016C, + 0x0168, 0x0168, 0x0164, 0x0164, 0x0164, 0x0160, 0x0160, 0x0160, 0x015C, 0x015C, 0x0158, 0x0158, 0x0155, 0x0155, 0x0151, 0x0151, + 0x014E, 0x014E, 0x014A, 0x014A, 0x0147, 0x0147, 0x0144, 0x0144, 0x0141, 0x0141, 0x013E, 0x013E, 0x013B, 0x0138, 0x0138, 0x0135, + 0x0135, 0x0132, 0x012F, 0x012F, 0x012C, 0x012C, 0x0129, 0x0127, 0x0127, 0x0124, 0x0121, 0x0121, 0x011F, 0x011C, 0x011C, 0x011A, + 0x0118, 0x0118, 0x0115, 0x0113, 0x0113, 0x0111, 0x010E, 0x010E, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 0x0102, 0x0102, + 0x0100, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EA, + 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E0, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DA, 0x00D9, 0x00D7, 0x00D6, + 0x017D, 0x017D, 0x017D, 0x017D, 0x017D, 0x017D, 0x017D, 0x017D, 0x017D, 0x017D, 0x0178, 0x0178, 0x0178, 0x0178, 0x0178, 0x0178, + 0x0178, 0x0174, 0x0174, 0x0174, 0x0174, 0x0170, 0x0170, 0x0170, 0x0170, 0x016C, 0x016C, 0x016C, 0x016C, 0x0168, 0x0168, 0x0168, + 0x0164, 0x0164, 0x0164, 0x0160, 0x0160, 0x015C, 0x015C, 0x015C, 0x0158, 0x0158, 0x0155, 0x0155, 0x0151, 0x0151, 0x0151, 0x014E, + 0x014E, 0x014A, 0x014A, 0x0147, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013E, 0x013B, 0x013B, 0x0138, 0x0138, 0x0135, 0x0132, + 0x0132, 0x012F, 0x012F, 0x012C, 0x0129, 0x0129, 0x0127, 0x0127, 0x0124, 0x0121, 0x0121, 0x011F, 0x011C, 0x011C, 0x011A, 0x0118, + 0x0118, 0x0115, 0x0113, 0x0113, 0x0111, 0x010E, 0x010E, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 0x0102, 0x0102, 0x0100, + 0x00FE, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EB, 0x00EA, + 0x00E8, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E1, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DB, 0x00DA, 0x00D9, 0x00D7, 0x00D7, 0x00D6, + 0x0178, 0x0178, 0x0178, 0x0178, 0x0178, 0x0178, 0x0178, 0x0178, 0x0178, 0x0178, 0x0174, 0x0174, 0x0174, 0x0174, 0x0174, 0x0174, + 0x0174, 0x0170, 0x0170, 0x0170, 0x0170, 0x0170, 0x016C, 0x016C, 0x016C, 0x016C, 0x0168, 0x0168, 0x0168, 0x0164, 0x0164, 0x0164, + 0x0160, 0x0160, 0x0160, 0x015C, 0x015C, 0x015C, 0x0158, 0x0158, 0x0155, 0x0155, 0x0151, 0x0151, 0x0151, 0x014E, 0x014E, 0x014A, + 0x014A, 0x0147, 0x0147, 0x0144, 0x0144, 0x0141, 0x0141, 0x013E, 0x013E, 0x013B, 0x013B, 0x0138, 0x0135, 0x0135, 0x0132, 0x0132, + 0x012F, 0x012F, 0x012C, 0x0129, 0x0129, 0x0127, 0x0124, 0x0124, 0x0121, 0x0121, 0x011F, 0x011C, 0x011C, 0x011A, 0x0118, 0x0118, + 0x0115, 0x0113, 0x0113, 0x0111, 0x010E, 0x010E, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x00FE, + 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EA, 0x00E8, + 0x00E6, 0x00E6, 0x00E5, 0x00E3, 0x00E1, 0x00E0, 0x00E0, 0x00DE, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 0x00D6, 0x00D4, + 0x0174, 0x0174, 0x0174, 0x0174, 0x0174, 0x0174, 0x0174, 0x0174, 0x0174, 0x0174, 0x0170, 0x0170, 0x0170, 0x0170, 0x0170, 0x0170, + 0x0170, 0x016C, 0x016C, 0x016C, 0x016C, 0x016C, 0x0168, 0x0168, 0x0168, 0x0168, 0x0164, 0x0164, 0x0164, 0x0160, 0x0160, 0x0160, + 0x015C, 0x015C, 0x015C, 0x0158, 0x0158, 0x0158, 0x0155, 0x0155, 0x0151, 0x0151, 0x0151, 0x014E, 0x014E, 0x014A, 0x014A, 0x0147, + 0x0147, 0x0144, 0x0144, 0x0141, 0x0141, 0x013E, 0x013E, 0x013B, 0x013B, 0x0138, 0x0138, 0x0135, 0x0135, 0x0132, 0x012F, 0x012F, + 0x012C, 0x012C, 0x0129, 0x0129, 0x0127, 0x0124, 0x0124, 0x0121, 0x011F, 0x011F, 0x011C, 0x011C, 0x011A, 0x0118, 0x0118, 0x0115, + 0x0113, 0x0113, 0x0111, 0x010E, 0x010E, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FE, + 0x00FC, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E8, + 0x00E6, 0x00E5, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DD, 0x00DB, 0x00DB, 0x00DA, 0x00D9, 0x00D7, 0x00D6, 0x00D6, 0x00D4, + 0x0170, 0x0170, 0x0170, 0x0170, 0x0170, 0x0170, 0x0170, 0x0170, 0x0170, 0x0170, 0x016C, 0x016C, 0x016C, 0x016C, 0x016C, 0x016C, + 0x016C, 0x0168, 0x0168, 0x0168, 0x0168, 0x0168, 0x0164, 0x0164, 0x0164, 0x0164, 0x0160, 0x0160, 0x0160, 0x015C, 0x015C, 0x015C, + 0x015C, 0x0158, 0x0158, 0x0155, 0x0155, 0x0155, 0x0151, 0x0151, 0x0151, 0x014E, 0x014E, 0x014A, 0x014A, 0x0147, 0x0147, 0x0144, + 0x0144, 0x0144, 0x0141, 0x0141, 0x013E, 0x013E, 0x013B, 0x0138, 0x0138, 0x0135, 0x0135, 0x0132, 0x0132, 0x012F, 0x012F, 0x012C, + 0x012C, 0x0129, 0x0127, 0x0127, 0x0124, 0x0124, 0x0121, 0x011F, 0x011F, 0x011C, 0x011A, 0x011A, 0x0118, 0x0115, 0x0115, 0x0113, + 0x0111, 0x0111, 0x010E, 0x010C, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FE, 0x00FC, + 0x00FA, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E8, 0x00E6, + 0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E0, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DA, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D4, 0x00D3, + 0x016C, 0x016C, 0x016C, 0x016C, 0x016C, 0x016C, 0x016C, 0x016C, 0x016C, 0x016C, 0x016C, 0x0168, 0x0168, 0x0168, 0x0168, 0x0168, + 0x0168, 0x0164, 0x0164, 0x0164, 0x0164, 0x0164, 0x0160, 0x0160, 0x0160, 0x0160, 0x015C, 0x015C, 0x015C, 0x015C, 0x0158, 0x0158, + 0x0158, 0x0155, 0x0155, 0x0155, 0x0151, 0x0151, 0x014E, 0x014E, 0x014E, 0x014A, 0x014A, 0x0147, 0x0147, 0x0144, 0x0144, 0x0144, + 0x0141, 0x0141, 0x013E, 0x013E, 0x013B, 0x013B, 0x0138, 0x0138, 0x0135, 0x0135, 0x0132, 0x0132, 0x012F, 0x012C, 0x012C, 0x0129, + 0x0129, 0x0127, 0x0127, 0x0124, 0x0121, 0x0121, 0x011F, 0x011F, 0x011C, 0x011A, 0x011A, 0x0118, 0x0115, 0x0115, 0x0113, 0x0111, + 0x0111, 0x010E, 0x010C, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FA, + 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E6, 0x00E5, + 0x00E5, 0x00E3, 0x00E1, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DB, 0x00DA, 0x00D9, 0x00D9, 0x00D7, 0x00D6, 0x00D4, 0x00D3, 0x00D3, + 0x0168, 0x0168, 0x0168, 0x0168, 0x0168, 0x0168, 0x0168, 0x0168, 0x0168, 0x0168, 0x0168, 0x0164, 0x0164, 0x0164, 0x0164, 0x0164, + 0x0164, 0x0164, 0x0160, 0x0160, 0x0160, 0x0160, 0x015C, 0x015C, 0x015C, 0x015C, 0x0158, 0x0158, 0x0158, 0x0158, 0x0155, 0x0155, + 0x0155, 0x0151, 0x0151, 0x0151, 0x014E, 0x014E, 0x014E, 0x014A, 0x014A, 0x0147, 0x0147, 0x0144, 0x0144, 0x0144, 0x0141, 0x0141, + 0x013E, 0x013E, 0x013B, 0x013B, 0x0138, 0x0138, 0x0135, 0x0135, 0x0132, 0x0132, 0x012F, 0x012F, 0x012C, 0x012C, 0x0129, 0x0127, + 0x0127, 0x0124, 0x0124, 0x0121, 0x0121, 0x011F, 0x011C, 0x011C, 0x011A, 0x011A, 0x0118, 0x0115, 0x0115, 0x0113, 0x0111, 0x0111, + 0x010E, 0x010C, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00FA, + 0x00F8, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E5, + 0x00E3, 0x00E1, 0x00E0, 0x00E0, 0x00DE, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 0x00D6, 0x00D4, 0x00D4, 0x00D3, 0x00D2, + 0x0164, 0x0164, 0x0164, 0x0164, 0x0164, 0x0164, 0x0164, 0x0164, 0x0164, 0x0164, 0x0164, 0x0160, 0x0160, 0x0160, 0x0160, 0x0160, + 0x0160, 0x0160, 0x015C, 0x015C, 0x015C, 0x015C, 0x015C, 0x0158, 0x0158, 0x0158, 0x0158, 0x0155, 0x0155, 0x0155, 0x0151, 0x0151, + 0x0151, 0x014E, 0x014E, 0x014E, 0x014A, 0x014A, 0x014A, 0x0147, 0x0147, 0x0144, 0x0144, 0x0144, 0x0141, 0x0141, 0x013E, 0x013E, + 0x013B, 0x013B, 0x0138, 0x0138, 0x0135, 0x0135, 0x0132, 0x0132, 0x012F, 0x012F, 0x012C, 0x012C, 0x0129, 0x0129, 0x0127, 0x0127, + 0x0124, 0x0124, 0x0121, 0x011F, 0x011F, 0x011C, 0x011C, 0x011A, 0x0118, 0x0118, 0x0115, 0x0113, 0x0113, 0x0111, 0x0111, 0x010E, + 0x010C, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00FA, 0x00F8, + 0x00F6, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E3, + 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DD, 0x00DB, 0x00DB, 0x00DA, 0x00D9, 0x00D7, 0x00D6, 0x00D6, 0x00D4, 0x00D3, 0x00D2, 0x00D0, + 0x0160, 0x0160, 0x0160, 0x0160, 0x0160, 0x0160, 0x0160, 0x0160, 0x0160, 0x0160, 0x0160, 0x015C, 0x015C, 0x015C, 0x015C, 0x015C, + 0x015C, 0x015C, 0x0158, 0x0158, 0x0158, 0x0158, 0x0158, 0x0155, 0x0155, 0x0155, 0x0155, 0x0151, 0x0151, 0x0151, 0x014E, 0x014E, + 0x014E, 0x014A, 0x014A, 0x014A, 0x0147, 0x0147, 0x0147, 0x0144, 0x0144, 0x0144, 0x0141, 0x0141, 0x013E, 0x013E, 0x013B, 0x013B, + 0x013B, 0x0138, 0x0138, 0x0135, 0x0135, 0x0132, 0x0132, 0x012F, 0x012F, 0x012C, 0x012C, 0x0129, 0x0129, 0x0127, 0x0124, 0x0124, + 0x0121, 0x0121, 0x011F, 0x011F, 0x011C, 0x011A, 0x011A, 0x0118, 0x0118, 0x0115, 0x0113, 0x0113, 0x0111, 0x010E, 0x010E, 0x010C, + 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F6, + 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E3, 0x00E1, + 0x00E1, 0x00E0, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DA, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D4, 0x00D3, 0x00D2, 0x00D2, 0x00D0, + 0x015C, 0x015C, 0x015C, 0x015C, 0x015C, 0x015C, 0x015C, 0x015C, 0x015C, 0x015C, 0x015C, 0x0158, 0x0158, 0x0158, 0x0158, 0x0158, + 0x0158, 0x0158, 0x0155, 0x0155, 0x0155, 0x0155, 0x0155, 0x0151, 0x0151, 0x0151, 0x0151, 0x014E, 0x014E, 0x014E, 0x014A, 0x014A, + 0x014A, 0x014A, 0x0147, 0x0147, 0x0144, 0x0144, 0x0144, 0x0141, 0x0141, 0x0141, 0x013E, 0x013E, 0x013B, 0x013B, 0x013B, 0x0138, + 0x0138, 0x0135, 0x0135, 0x0132, 0x0132, 0x012F, 0x012F, 0x012C, 0x012C, 0x0129, 0x0129, 0x0127, 0x0127, 0x0124, 0x0124, 0x0121, + 0x011F, 0x011F, 0x011C, 0x011C, 0x011A, 0x011A, 0x0118, 0x0115, 0x0115, 0x0113, 0x0113, 0x0111, 0x010E, 0x010E, 0x010C, 0x010A, + 0x010A, 0x0108, 0x0106, 0x0106, 0x0104, 0x0102, 0x0102, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F6, 0x00F6, + 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E5, 0x00E3, 0x00E3, 0x00E1, + 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DB, 0x00DA, 0x00D9, 0x00D9, 0x00D7, 0x00D6, 0x00D4, 0x00D3, 0x00D3, 0x00D2, 0x00D0, 0x00CF, + 0x0158, 0x0158, 0x0158, 0x0158, 0x0158, 0x0158, 0x0158, 0x0158, 0x0158, 0x0158, 0x0158, 0x0158, 0x0155, 0x0155, 0x0155, 0x0155, + 0x0155, 0x0155, 0x0151, 0x0151, 0x0151, 0x0151, 0x0151, 0x014E, 0x014E, 0x014E, 0x014E, 0x014A, 0x014A, 0x014A, 0x014A, 0x0147, + 0x0147, 0x0147, 0x0144, 0x0144, 0x0144, 0x0141, 0x0141, 0x013E, 0x013E, 0x013E, 0x013B, 0x013B, 0x0138, 0x0138, 0x0138, 0x0135, + 0x0135, 0x0132, 0x0132, 0x012F, 0x012F, 0x012C, 0x012C, 0x0129, 0x0129, 0x0127, 0x0127, 0x0124, 0x0124, 0x0121, 0x0121, 0x011F, + 0x011F, 0x011C, 0x011C, 0x011A, 0x0118, 0x0118, 0x0115, 0x0115, 0x0113, 0x0111, 0x0111, 0x010E, 0x010E, 0x010C, 0x010A, 0x010A, + 0x0108, 0x0106, 0x0106, 0x0104, 0x0102, 0x0102, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F6, 0x00F6, 0x00F4, + 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E0, + 0x00DE, 0x00DE, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 0x00D6, 0x00D4, 0x00D4, 0x00D3, 0x00D2, 0x00D0, 0x00CF, 0x00CF, + 0x0155, 0x0155, 0x0155, 0x0155, 0x0155, 0x0155, 0x0155, 0x0155, 0x0155, 0x0155, 0x0155, 0x0155, 0x0151, 0x0151, 0x0151, 0x0151, + 0x0151, 0x0151, 0x014E, 0x014E, 0x014E, 0x014E, 0x014E, 0x014A, 0x014A, 0x014A, 0x014A, 0x0147, 0x0147, 0x0147, 0x0147, 0x0144, + 0x0144, 0x0144, 0x0141, 0x0141, 0x0141, 0x013E, 0x013E, 0x013E, 0x013B, 0x013B, 0x0138, 0x0138, 0x0138, 0x0135, 0x0135, 0x0132, + 0x0132, 0x012F, 0x012F, 0x012C, 0x012C, 0x012C, 0x0129, 0x0129, 0x0127, 0x0127, 0x0124, 0x0124, 0x0121, 0x011F, 0x011F, 0x011C, + 0x011C, 0x011A, 0x011A, 0x0118, 0x0118, 0x0115, 0x0113, 0x0113, 0x0111, 0x0111, 0x010E, 0x010C, 0x010C, 0x010A, 0x010A, 0x0108, + 0x0106, 0x0106, 0x0104, 0x0102, 0x0102, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F2, + 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E1, 0x00E0, 0x00E0, + 0x00DE, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 0x00D6, 0x00D6, 0x00D4, 0x00D3, 0x00D2, 0x00D0, 0x00D0, 0x00CF, 0x00CE, + 0x0151, 0x0151, 0x0151, 0x0151, 0x0151, 0x0151, 0x0151, 0x0151, 0x0151, 0x0151, 0x0151, 0x0151, 0x014E, 0x014E, 0x014E, 0x014E, + 0x014E, 0x014E, 0x014E, 0x014A, 0x014A, 0x014A, 0x014A, 0x0147, 0x0147, 0x0147, 0x0147, 0x0144, 0x0144, 0x0144, 0x0144, 0x0141, + 0x0141, 0x0141, 0x013E, 0x013E, 0x013E, 0x013B, 0x013B, 0x013B, 0x0138, 0x0138, 0x0135, 0x0135, 0x0135, 0x0132, 0x0132, 0x012F, + 0x012F, 0x012F, 0x012C, 0x012C, 0x0129, 0x0129, 0x0127, 0x0127, 0x0124, 0x0124, 0x0121, 0x0121, 0x011F, 0x011F, 0x011C, 0x011C, + 0x011A, 0x0118, 0x0118, 0x0115, 0x0115, 0x0113, 0x0113, 0x0111, 0x010E, 0x010E, 0x010C, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, + 0x0106, 0x0104, 0x0102, 0x0102, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F2, 0x00F0, + 0x00F0, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E1, 0x00E0, 0x00E0, 0x00DE, + 0x00DD, 0x00DB, 0x00DB, 0x00DA, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D4, 0x00D3, 0x00D2, 0x00D2, 0x00D0, 0x00CF, 0x00CE, 0x00CC, + 0x014E, 0x014E, 0x014E, 0x014E, 0x014E, 0x014E, 0x014E, 0x014E, 0x014E, 0x014E, 0x014E, 0x014E, 0x014A, 0x014A, 0x014A, 0x014A, + 0x014A, 0x014A, 0x014A, 0x0147, 0x0147, 0x0147, 0x0147, 0x0147, 0x0144, 0x0144, 0x0144, 0x0144, 0x0141, 0x0141, 0x0141, 0x013E, + 0x013E, 0x013E, 0x013B, 0x013B, 0x013B, 0x0138, 0x0138, 0x0138, 0x0135, 0x0135, 0x0135, 0x0132, 0x0132, 0x012F, 0x012F, 0x012F, + 0x012C, 0x012C, 0x0129, 0x0129, 0x0127, 0x0127, 0x0124, 0x0124, 0x0121, 0x0121, 0x011F, 0x011F, 0x011C, 0x011C, 0x011A, 0x011A, + 0x0118, 0x0118, 0x0115, 0x0115, 0x0113, 0x0111, 0x0111, 0x010E, 0x010E, 0x010C, 0x010A, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, + 0x0104, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00F0, + 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E1, 0x00E0, 0x00E0, 0x00DE, 0x00DD, + 0x00DB, 0x00DB, 0x00DA, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D4, 0x00D3, 0x00D3, 0x00D2, 0x00D0, 0x00CF, 0x00CE, 0x00CE, 0x00CC, + 0x014A, 0x014A, 0x014A, 0x014A, 0x014A, 0x014A, 0x014A, 0x014A, 0x014A, 0x014A, 0x014A, 0x014A, 0x0147, 0x0147, 0x0147, 0x0147, + 0x0147, 0x0147, 0x0147, 0x0144, 0x0144, 0x0144, 0x0144, 0x0144, 0x0141, 0x0141, 0x0141, 0x0141, 0x013E, 0x013E, 0x013E, 0x013B, + 0x013B, 0x013B, 0x013B, 0x0138, 0x0138, 0x0138, 0x0135, 0x0135, 0x0132, 0x0132, 0x0132, 0x012F, 0x012F, 0x012C, 0x012C, 0x012C, + 0x0129, 0x0129, 0x0127, 0x0127, 0x0124, 0x0124, 0x0121, 0x0121, 0x011F, 0x011F, 0x011F, 0x011C, 0x011A, 0x011A, 0x0118, 0x0118, + 0x0115, 0x0115, 0x0113, 0x0113, 0x0111, 0x0111, 0x010E, 0x010C, 0x010C, 0x010A, 0x010A, 0x0108, 0x0106, 0x0106, 0x0104, 0x0104, + 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, + 0x00ED, 0x00ED, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DD, 0x00DD, + 0x00DB, 0x00DA, 0x00D9, 0x00D9, 0x00D7, 0x00D6, 0x00D4, 0x00D4, 0x00D3, 0x00D2, 0x00D0, 0x00CF, 0x00CF, 0x00CE, 0x00CC, 0x00CB, + 0x0147, 0x0147, 0x0147, 0x0147, 0x0147, 0x0147, 0x0147, 0x0147, 0x0147, 0x0147, 0x0147, 0x0147, 0x0144, 0x0144, 0x0144, 0x0144, + 0x0144, 0x0144, 0x0144, 0x0141, 0x0141, 0x0141, 0x0141, 0x0141, 0x013E, 0x013E, 0x013E, 0x013E, 0x013B, 0x013B, 0x013B, 0x013B, + 0x0138, 0x0138, 0x0138, 0x0135, 0x0135, 0x0135, 0x0132, 0x0132, 0x0132, 0x012F, 0x012F, 0x012C, 0x012C, 0x012C, 0x0129, 0x0129, + 0x0127, 0x0127, 0x0124, 0x0124, 0x0124, 0x0121, 0x0121, 0x011F, 0x011F, 0x011C, 0x011C, 0x011A, 0x011A, 0x0118, 0x0118, 0x0115, + 0x0115, 0x0113, 0x0111, 0x0111, 0x010E, 0x010E, 0x010C, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0106, 0x0104, 0x0102, 0x0102, + 0x0100, 0x0100, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, + 0x00ED, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DD, 0x00DD, 0x00DB, + 0x00DA, 0x00D9, 0x00D9, 0x00D7, 0x00D6, 0x00D4, 0x00D4, 0x00D3, 0x00D2, 0x00D0, 0x00D0, 0x00CF, 0x00CE, 0x00CC, 0x00CB, 0x00CB, + 0x0144, 0x0144, 0x0144, 0x0144, 0x0144, 0x0144, 0x0144, 0x0144, 0x0144, 0x0144, 0x0144, 0x0144, 0x0144, 0x0141, 0x0141, 0x0141, + 0x0141, 0x0141, 0x0141, 0x013E, 0x013E, 0x013E, 0x013E, 0x013E, 0x013B, 0x013B, 0x013B, 0x013B, 0x0138, 0x0138, 0x0138, 0x0138, + 0x0135, 0x0135, 0x0135, 0x0132, 0x0132, 0x0132, 0x012F, 0x012F, 0x012F, 0x012C, 0x012C, 0x0129, 0x0129, 0x0129, 0x0127, 0x0127, + 0x0124, 0x0124, 0x0124, 0x0121, 0x0121, 0x011F, 0x011F, 0x011C, 0x011C, 0x011A, 0x011A, 0x0118, 0x0118, 0x0115, 0x0115, 0x0113, + 0x0113, 0x0111, 0x0111, 0x010E, 0x010E, 0x010C, 0x010A, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 0x0104, 0x0102, 0x0102, 0x0100, + 0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00ED, + 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DA, + 0x00DA, 0x00D9, 0x00D7, 0x00D6, 0x00D6, 0x00D4, 0x00D3, 0x00D2, 0x00D0, 0x00D0, 0x00CF, 0x00CE, 0x00CC, 0x00CC, 0x00CB, 0x00CA, + 0x0141, 0x0141, 0x0141, 0x0141, 0x0141, 0x0141, 0x0141, 0x0141, 0x0141, 0x0141, 0x0141, 0x0141, 0x0141, 0x013E, 0x013E, 0x013E, + 0x013E, 0x013E, 0x013E, 0x013B, 0x013B, 0x013B, 0x013B, 0x013B, 0x0138, 0x0138, 0x0138, 0x0138, 0x0135, 0x0135, 0x0135, 0x0135, + 0x0132, 0x0132, 0x0132, 0x012F, 0x012F, 0x012F, 0x012C, 0x012C, 0x012C, 0x0129, 0x0129, 0x0129, 0x0127, 0x0127, 0x0124, 0x0124, + 0x0124, 0x0121, 0x0121, 0x011F, 0x011F, 0x011C, 0x011C, 0x011A, 0x011A, 0x0118, 0x0118, 0x0115, 0x0115, 0x0113, 0x0113, 0x0111, + 0x0111, 0x010E, 0x010E, 0x010C, 0x010C, 0x010A, 0x010A, 0x0108, 0x0106, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x0100, 0x00FE, + 0x00FE, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00ED, 0x00EB, + 0x00EA, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DA, 0x00DA, + 0x00D9, 0x00D7, 0x00D6, 0x00D6, 0x00D4, 0x00D3, 0x00D2, 0x00D2, 0x00D0, 0x00CF, 0x00CE, 0x00CE, 0x00CC, 0x00CB, 0x00CA, 0x00C9, + 0x013E, 0x013E, 0x013E, 0x013E, 0x013E, 0x013E, 0x013E, 0x013E, 0x013E, 0x013E, 0x013E, 0x013E, 0x013E, 0x013B, 0x013B, 0x013B, + 0x013B, 0x013B, 0x013B, 0x013B, 0x0138, 0x0138, 0x0138, 0x0138, 0x0135, 0x0135, 0x0135, 0x0135, 0x0135, 0x0132, 0x0132, 0x0132, + 0x012F, 0x012F, 0x012F, 0x012C, 0x012C, 0x012C, 0x0129, 0x0129, 0x0129, 0x0127, 0x0127, 0x0127, 0x0124, 0x0124, 0x0121, 0x0121, + 0x0121, 0x011F, 0x011F, 0x011C, 0x011C, 0x011A, 0x011A, 0x011A, 0x0118, 0x0118, 0x0115, 0x0115, 0x0113, 0x0113, 0x0111, 0x0111, + 0x010E, 0x010C, 0x010C, 0x010A, 0x010A, 0x0108, 0x0108, 0x0106, 0x0106, 0x0104, 0x0102, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FC, + 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EA, + 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, + 0x00D7, 0x00D6, 0x00D6, 0x00D4, 0x00D3, 0x00D2, 0x00D2, 0x00D0, 0x00CF, 0x00CE, 0x00CE, 0x00CC, 0x00CB, 0x00CA, 0x00CA, 0x00C9, + 0x013B, 0x013B, 0x013B, 0x013B, 0x013B, 0x013B, 0x013B, 0x013B, 0x013B, 0x013B, 0x013B, 0x013B, 0x013B, 0x0138, 0x0138, 0x0138, + 0x0138, 0x0138, 0x0138, 0x0138, 0x0135, 0x0135, 0x0135, 0x0135, 0x0135, 0x0132, 0x0132, 0x0132, 0x0132, 0x012F, 0x012F, 0x012F, + 0x012C, 0x012C, 0x012C, 0x012C, 0x0129, 0x0129, 0x0129, 0x0127, 0x0127, 0x0124, 0x0124, 0x0124, 0x0121, 0x0121, 0x0121, 0x011F, + 0x011F, 0x011C, 0x011C, 0x011A, 0x011A, 0x011A, 0x0118, 0x0118, 0x0115, 0x0115, 0x0113, 0x0113, 0x0111, 0x0111, 0x010E, 0x010E, + 0x010C, 0x010C, 0x010A, 0x010A, 0x0108, 0x0106, 0x0106, 0x0104, 0x0104, 0x0102, 0x0102, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FC, + 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00E8, + 0x00E8, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, + 0x00D6, 0x00D6, 0x00D4, 0x00D3, 0x00D3, 0x00D2, 0x00D0, 0x00CF, 0x00CF, 0x00CE, 0x00CC, 0x00CB, 0x00CB, 0x00CA, 0x00C9, 0x00C7, + 0x0138, 0x0138, 0x0138, 0x0138, 0x0138, 0x0138, 0x0138, 0x0138, 0x0138, 0x0138, 0x0138, 0x0138, 0x0138, 0x0135, 0x0135, 0x0135, + 0x0135, 0x0135, 0x0135, 0x0135, 0x0132, 0x0132, 0x0132, 0x0132, 0x0132, 0x012F, 0x012F, 0x012F, 0x012F, 0x012C, 0x012C, 0x012C, + 0x012C, 0x0129, 0x0129, 0x0129, 0x0127, 0x0127, 0x0127, 0x0124, 0x0124, 0x0124, 0x0121, 0x0121, 0x011F, 0x011F, 0x011F, 0x011C, + 0x011C, 0x011A, 0x011A, 0x011A, 0x0118, 0x0118, 0x0115, 0x0115, 0x0113, 0x0113, 0x0111, 0x0111, 0x010E, 0x010E, 0x010C, 0x010C, + 0x010A, 0x010A, 0x0108, 0x0108, 0x0106, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FA, + 0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00E8, 0x00E8, + 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 0x00D7, + 0x00D6, 0x00D4, 0x00D3, 0x00D3, 0x00D2, 0x00D0, 0x00CF, 0x00CF, 0x00CE, 0x00CC, 0x00CB, 0x00CB, 0x00CA, 0x00C9, 0x00C7, 0x00C7, + 0x0135, 0x0135, 0x0135, 0x0135, 0x0135, 0x0135, 0x0135, 0x0135, 0x0135, 0x0135, 0x0135, 0x0135, 0x0135, 0x0132, 0x0132, 0x0132, + 0x0132, 0x0132, 0x0132, 0x0132, 0x012F, 0x012F, 0x012F, 0x012F, 0x012F, 0x012C, 0x012C, 0x012C, 0x012C, 0x0129, 0x0129, 0x0129, + 0x0129, 0x0127, 0x0127, 0x0127, 0x0124, 0x0124, 0x0124, 0x0121, 0x0121, 0x0121, 0x011F, 0x011F, 0x011F, 0x011C, 0x011C, 0x011A, + 0x011A, 0x011A, 0x0118, 0x0118, 0x0115, 0x0115, 0x0113, 0x0113, 0x0111, 0x0111, 0x010E, 0x010E, 0x010E, 0x010C, 0x010C, 0x010A, + 0x0108, 0x0108, 0x0106, 0x0106, 0x0104, 0x0104, 0x0102, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00FA, 0x00F8, + 0x00F8, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00E8, 0x00E8, 0x00E6, + 0x00E5, 0x00E5, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 0x00D7, 0x00D6, + 0x00D4, 0x00D3, 0x00D3, 0x00D2, 0x00D0, 0x00CF, 0x00CF, 0x00CE, 0x00CC, 0x00CB, 0x00CB, 0x00CA, 0x00C9, 0x00C7, 0x00C7, 0x00C6, + 0x0132, 0x0132, 0x0132, 0x0132, 0x0132, 0x0132, 0x0132, 0x0132, 0x0132, 0x0132, 0x0132, 0x0132, 0x0132, 0x0132, 0x012F, 0x012F, + 0x012F, 0x012F, 0x012F, 0x012F, 0x012C, 0x012C, 0x012C, 0x012C, 0x012C, 0x0129, 0x0129, 0x0129, 0x0129, 0x0127, 0x0127, 0x0127, + 0x0127, 0x0124, 0x0124, 0x0124, 0x0121, 0x0121, 0x0121, 0x011F, 0x011F, 0x011F, 0x011C, 0x011C, 0x011C, 0x011A, 0x011A, 0x0118, + 0x0118, 0x0118, 0x0115, 0x0115, 0x0113, 0x0113, 0x0111, 0x0111, 0x0111, 0x010E, 0x010E, 0x010C, 0x010C, 0x010A, 0x010A, 0x0108, + 0x0108, 0x0106, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F6, + 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E5, + 0x00E5, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D4, + 0x00D3, 0x00D3, 0x00D2, 0x00D0, 0x00D0, 0x00CF, 0x00CE, 0x00CC, 0x00CC, 0x00CB, 0x00CA, 0x00C9, 0x00C9, 0x00C7, 0x00C6, 0x00C5, + 0x012F, 0x012F, 0x012F, 0x012F, 0x012F, 0x012F, 0x012F, 0x012F, 0x012F, 0x012F, 0x012F, 0x012F, 0x012F, 0x012F, 0x012C, 0x012C, + 0x012C, 0x012C, 0x012C, 0x012C, 0x0129, 0x0129, 0x0129, 0x0129, 0x0129, 0x0127, 0x0127, 0x0127, 0x0127, 0x0124, 0x0124, 0x0124, + 0x0124, 0x0121, 0x0121, 0x0121, 0x011F, 0x011F, 0x011F, 0x011C, 0x011C, 0x011C, 0x011A, 0x011A, 0x011A, 0x0118, 0x0118, 0x0118, + 0x0115, 0x0115, 0x0113, 0x0113, 0x0111, 0x0111, 0x0111, 0x010E, 0x010E, 0x010C, 0x010C, 0x010A, 0x010A, 0x0108, 0x0108, 0x0106, + 0x0106, 0x0104, 0x0104, 0x0102, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F6, + 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E5, + 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D4, 0x00D3, + 0x00D3, 0x00D2, 0x00D0, 0x00D0, 0x00CF, 0x00CE, 0x00CC, 0x00CC, 0x00CB, 0x00CA, 0x00C9, 0x00C9, 0x00C7, 0x00C6, 0x00C5, 0x00C5, + 0x012C, 0x012C, 0x012C, 0x012C, 0x012C, 0x012C, 0x012C, 0x012C, 0x012C, 0x012C, 0x012C, 0x012C, 0x012C, 0x012C, 0x0129, 0x0129, + 0x0129, 0x0129, 0x0129, 0x0129, 0x0129, 0x0127, 0x0127, 0x0127, 0x0127, 0x0124, 0x0124, 0x0124, 0x0124, 0x0124, 0x0121, 0x0121, + 0x0121, 0x011F, 0x011F, 0x011F, 0x011F, 0x011C, 0x011C, 0x011C, 0x011A, 0x011A, 0x011A, 0x0118, 0x0118, 0x0115, 0x0115, 0x0115, + 0x0113, 0x0113, 0x0111, 0x0111, 0x0111, 0x010E, 0x010E, 0x010C, 0x010C, 0x010A, 0x010A, 0x0108, 0x0108, 0x0106, 0x0106, 0x0104, + 0x0104, 0x0102, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F4, + 0x00F2, 0x00F2, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E3, 0x00E3, + 0x00E1, 0x00E0, 0x00E0, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D4, 0x00D4, 0x00D3, + 0x00D2, 0x00D0, 0x00D0, 0x00CF, 0x00CE, 0x00CC, 0x00CC, 0x00CB, 0x00CA, 0x00CA, 0x00C9, 0x00C7, 0x00C6, 0x00C6, 0x00C5, 0x00C4, + 0x0129, 0x0129, 0x0129, 0x0129, 0x0129, 0x0129, 0x0129, 0x0129, 0x0129, 0x0129, 0x0129, 0x0129, 0x0129, 0x0129, 0x0127, 0x0127, + 0x0127, 0x0127, 0x0127, 0x0127, 0x0127, 0x0124, 0x0124, 0x0124, 0x0124, 0x0124, 0x0121, 0x0121, 0x0121, 0x0121, 0x011F, 0x011F, + 0x011F, 0x011C, 0x011C, 0x011C, 0x011C, 0x011A, 0x011A, 0x011A, 0x0118, 0x0118, 0x0118, 0x0115, 0x0115, 0x0113, 0x0113, 0x0113, + 0x0111, 0x0111, 0x0111, 0x010E, 0x010E, 0x010C, 0x010C, 0x010A, 0x010A, 0x0108, 0x0108, 0x0108, 0x0106, 0x0106, 0x0104, 0x0104, + 0x0102, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F4, 0x00F2, + 0x00F0, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E3, 0x00E3, 0x00E1, + 0x00E0, 0x00E0, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D4, 0x00D4, 0x00D3, 0x00D2, + 0x00D0, 0x00D0, 0x00CF, 0x00CE, 0x00CE, 0x00CC, 0x00CB, 0x00CA, 0x00CA, 0x00C9, 0x00C7, 0x00C6, 0x00C6, 0x00C5, 0x00C4, 0x00C3, + 0x0127, 0x0127, 0x0127, 0x0127, 0x0127, 0x0127, 0x0127, 0x0127, 0x0127, 0x0127, 0x0127, 0x0127, 0x0127, 0x0127, 0x0124, 0x0124, + 0x0124, 0x0124, 0x0124, 0x0124, 0x0124, 0x0121, 0x0121, 0x0121, 0x0121, 0x0121, 0x011F, 0x011F, 0x011F, 0x011F, 0x011C, 0x011C, + 0x011C, 0x011C, 0x011A, 0x011A, 0x011A, 0x0118, 0x0118, 0x0118, 0x0115, 0x0115, 0x0115, 0x0113, 0x0113, 0x0113, 0x0111, 0x0111, + 0x010E, 0x010E, 0x010E, 0x010C, 0x010C, 0x010A, 0x010A, 0x0108, 0x0108, 0x0108, 0x0106, 0x0106, 0x0104, 0x0104, 0x0102, 0x0102, + 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F2, 0x00F0, + 0x00F0, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E0, + 0x00E0, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D4, 0x00D4, 0x00D3, 0x00D2, 0x00D0, + 0x00D0, 0x00CF, 0x00CE, 0x00CE, 0x00CC, 0x00CB, 0x00CA, 0x00CA, 0x00C9, 0x00C7, 0x00C6, 0x00C6, 0x00C5, 0x00C4, 0x00C3, 0x00C3, + 0x0124, 0x0124, 0x0124, 0x0124, 0x0124, 0x0124, 0x0124, 0x0124, 0x0124, 0x0124, 0x0124, 0x0124, 0x0124, 0x0124, 0x0121, 0x0121, + 0x0121, 0x0121, 0x0121, 0x0121, 0x0121, 0x011F, 0x011F, 0x011F, 0x011F, 0x011F, 0x011C, 0x011C, 0x011C, 0x011C, 0x011A, 0x011A, + 0x011A, 0x011A, 0x0118, 0x0118, 0x0118, 0x0115, 0x0115, 0x0115, 0x0113, 0x0113, 0x0113, 0x0111, 0x0111, 0x0111, 0x010E, 0x010E, + 0x010E, 0x010C, 0x010C, 0x010A, 0x010A, 0x0108, 0x0108, 0x0108, 0x0106, 0x0106, 0x0104, 0x0104, 0x0102, 0x0102, 0x0100, 0x0100, + 0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, + 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00E0, + 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D4, 0x00D3, 0x00D3, 0x00D2, 0x00D0, 0x00D0, + 0x00CF, 0x00CE, 0x00CE, 0x00CC, 0x00CB, 0x00CA, 0x00CA, 0x00C9, 0x00C7, 0x00C7, 0x00C6, 0x00C5, 0x00C4, 0x00C4, 0x00C3, 0x00C1, + 0x0121, 0x0121, 0x0121, 0x0121, 0x0121, 0x0121, 0x0121, 0x0121, 0x0121, 0x0121, 0x0121, 0x0121, 0x0121, 0x0121, 0x011F, 0x011F, + 0x011F, 0x011F, 0x011F, 0x011F, 0x011F, 0x011C, 0x011C, 0x011C, 0x011C, 0x011C, 0x011A, 0x011A, 0x011A, 0x011A, 0x0118, 0x0118, + 0x0118, 0x0118, 0x0115, 0x0115, 0x0115, 0x0113, 0x0113, 0x0113, 0x0111, 0x0111, 0x0111, 0x010E, 0x010E, 0x010E, 0x010C, 0x010C, + 0x010C, 0x010A, 0x010A, 0x0108, 0x0108, 0x0108, 0x0106, 0x0106, 0x0104, 0x0104, 0x0102, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FE, + 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00EF, + 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DE, + 0x00DD, 0x00DB, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 0x00D6, 0x00D6, 0x00D4, 0x00D3, 0x00D3, 0x00D2, 0x00D0, 0x00D0, 0x00CF, + 0x00CE, 0x00CE, 0x00CC, 0x00CB, 0x00CA, 0x00CA, 0x00C9, 0x00C7, 0x00C7, 0x00C6, 0x00C5, 0x00C4, 0x00C4, 0x00C3, 0x00C1, 0x00C0, + 0x011F, 0x011F, 0x011F, 0x011F, 0x011F, 0x011F, 0x011F, 0x011F, 0x011F, 0x011F, 0x011F, 0x011F, 0x011F, 0x011F, 0x011F, 0x011C, + 0x011C, 0x011C, 0x011C, 0x011C, 0x011C, 0x011A, 0x011A, 0x011A, 0x011A, 0x011A, 0x0118, 0x0118, 0x0118, 0x0118, 0x0115, 0x0115, + 0x0115, 0x0115, 0x0113, 0x0113, 0x0113, 0x0113, 0x0111, 0x0111, 0x0111, 0x010E, 0x010E, 0x010E, 0x010C, 0x010C, 0x010A, 0x010A, + 0x010A, 0x0108, 0x0108, 0x0106, 0x0106, 0x0106, 0x0104, 0x0104, 0x0102, 0x0102, 0x0100, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FC, + 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00ED, + 0x00EB, 0x00EB, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DE, 0x00DD, + 0x00DB, 0x00DB, 0x00DA, 0x00D9, 0x00D9, 0x00D7, 0x00D6, 0x00D6, 0x00D4, 0x00D3, 0x00D3, 0x00D2, 0x00D0, 0x00D0, 0x00CF, 0x00CE, + 0x00CE, 0x00CC, 0x00CB, 0x00CA, 0x00CA, 0x00C9, 0x00C7, 0x00C7, 0x00C6, 0x00C5, 0x00C4, 0x00C4, 0x00C3, 0x00C1, 0x00C0, 0x00C0, + 0x011C, 0x011C, 0x011C, 0x011C, 0x011C, 0x011C, 0x011C, 0x011C, 0x011C, 0x011C, 0x011C, 0x011C, 0x011C, 0x011C, 0x011C, 0x011A, + 0x011A, 0x011A, 0x011A, 0x011A, 0x011A, 0x0118, 0x0118, 0x0118, 0x0118, 0x0118, 0x0115, 0x0115, 0x0115, 0x0115, 0x0115, 0x0113, + 0x0113, 0x0113, 0x0111, 0x0111, 0x0111, 0x0111, 0x010E, 0x010E, 0x010E, 0x010C, 0x010C, 0x010C, 0x010A, 0x010A, 0x010A, 0x0108, + 0x0108, 0x0106, 0x0106, 0x0106, 0x0104, 0x0104, 0x0102, 0x0102, 0x0100, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FA, + 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00EB, 0x00EB, + 0x00EA, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E0, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DB, + 0x00DB, 0x00DA, 0x00D9, 0x00D9, 0x00D7, 0x00D6, 0x00D6, 0x00D4, 0x00D3, 0x00D3, 0x00D2, 0x00D0, 0x00D0, 0x00CF, 0x00CE, 0x00CE, + 0x00CC, 0x00CB, 0x00CA, 0x00CA, 0x00C9, 0x00C7, 0x00C7, 0x00C6, 0x00C5, 0x00C4, 0x00C4, 0x00C3, 0x00C1, 0x00C1, 0x00C0, 0x00BF, + 0x011A, 0x011A, 0x011A, 0x011A, 0x011A, 0x011A, 0x011A, 0x011A, 0x011A, 0x011A, 0x011A, 0x011A, 0x011A, 0x011A, 0x011A, 0x0118, + 0x0118, 0x0118, 0x0118, 0x0118, 0x0118, 0x0118, 0x0115, 0x0115, 0x0115, 0x0115, 0x0115, 0x0113, 0x0113, 0x0113, 0x0113, 0x0111, + 0x0111, 0x0111, 0x0111, 0x010E, 0x010E, 0x010E, 0x010C, 0x010C, 0x010C, 0x010A, 0x010A, 0x010A, 0x0108, 0x0108, 0x0108, 0x0106, + 0x0106, 0x0104, 0x0104, 0x0104, 0x0102, 0x0102, 0x0100, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00FA, 0x00F8, + 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EB, 0x00EA, + 0x00E8, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00E0, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DA, + 0x00DA, 0x00D9, 0x00D9, 0x00D7, 0x00D6, 0x00D6, 0x00D4, 0x00D3, 0x00D3, 0x00D2, 0x00D0, 0x00D0, 0x00CF, 0x00CE, 0x00CE, 0x00CC, + 0x00CB, 0x00CA, 0x00CA, 0x00C9, 0x00C7, 0x00C7, 0x00C6, 0x00C5, 0x00C5, 0x00C4, 0x00C3, 0x00C1, 0x00C1, 0x00C0, 0x00BF, 0x00BE, + 0x0118, 0x0118, 0x0118, 0x0118, 0x0118, 0x0118, 0x0118, 0x0118, 0x0118, 0x0118, 0x0118, 0x0118, 0x0118, 0x0118, 0x0118, 0x0115, + 0x0115, 0x0115, 0x0115, 0x0115, 0x0115, 0x0115, 0x0113, 0x0113, 0x0113, 0x0113, 0x0113, 0x0111, 0x0111, 0x0111, 0x0111, 0x010E, + 0x010E, 0x010E, 0x010E, 0x010C, 0x010C, 0x010C, 0x010A, 0x010A, 0x010A, 0x0108, 0x0108, 0x0108, 0x0106, 0x0106, 0x0106, 0x0104, + 0x0104, 0x0104, 0x0102, 0x0102, 0x0100, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F8, 0x00F8, + 0x00F6, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00EA, 0x00E8, + 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DA, 0x00DA, + 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D6, 0x00D4, 0x00D3, 0x00D3, 0x00D2, 0x00D0, 0x00D0, 0x00CF, 0x00CE, 0x00CE, 0x00CC, 0x00CB, + 0x00CA, 0x00CA, 0x00C9, 0x00C7, 0x00C7, 0x00C6, 0x00C5, 0x00C5, 0x00C4, 0x00C3, 0x00C1, 0x00C1, 0x00C0, 0x00BF, 0x00BE, 0x00BE, + 0x0115, 0x0115, 0x0115, 0x0115, 0x0115, 0x0115, 0x0115, 0x0115, 0x0115, 0x0115, 0x0115, 0x0115, 0x0115, 0x0115, 0x0115, 0x0113, + 0x0113, 0x0113, 0x0113, 0x0113, 0x0113, 0x0113, 0x0111, 0x0111, 0x0111, 0x0111, 0x0111, 0x010E, 0x010E, 0x010E, 0x010E, 0x010C, + 0x010C, 0x010C, 0x010C, 0x010A, 0x010A, 0x010A, 0x0108, 0x0108, 0x0108, 0x0106, 0x0106, 0x0106, 0x0104, 0x0104, 0x0104, 0x0102, + 0x0102, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F8, 0x00F8, 0x00F6, 0x00F6, + 0x00F4, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E6, + 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E0, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DB, 0x00DB, 0x00DA, 0x00DA, 0x00D9, + 0x00D7, 0x00D7, 0x00D6, 0x00D4, 0x00D4, 0x00D3, 0x00D2, 0x00D2, 0x00D0, 0x00CF, 0x00CF, 0x00CE, 0x00CC, 0x00CC, 0x00CB, 0x00CA, + 0x00CA, 0x00C9, 0x00C7, 0x00C7, 0x00C6, 0x00C5, 0x00C5, 0x00C4, 0x00C3, 0x00C1, 0x00C1, 0x00C0, 0x00BF, 0x00BF, 0x00BE, 0x00BD, + 0x0113, 0x0113, 0x0113, 0x0113, 0x0113, 0x0113, 0x0113, 0x0113, 0x0113, 0x0113, 0x0113, 0x0113, 0x0113, 0x0113, 0x0113, 0x0111, + 0x0111, 0x0111, 0x0111, 0x0111, 0x0111, 0x0111, 0x010E, 0x010E, 0x010E, 0x010E, 0x010E, 0x010C, 0x010C, 0x010C, 0x010C, 0x010A, + 0x010A, 0x010A, 0x010A, 0x0108, 0x0108, 0x0108, 0x0108, 0x0106, 0x0106, 0x0106, 0x0104, 0x0104, 0x0104, 0x0102, 0x0102, 0x0100, + 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F8, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F4, + 0x00F2, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E6, + 0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00E0, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DB, 0x00DA, 0x00D9, 0x00D9, 0x00D7, + 0x00D7, 0x00D6, 0x00D4, 0x00D4, 0x00D3, 0x00D2, 0x00D2, 0x00D0, 0x00CF, 0x00CF, 0x00CE, 0x00CC, 0x00CC, 0x00CB, 0x00CA, 0x00CA, + 0x00C9, 0x00C7, 0x00C7, 0x00C6, 0x00C5, 0x00C5, 0x00C4, 0x00C3, 0x00C1, 0x00C1, 0x00C0, 0x00BF, 0x00BF, 0x00BE, 0x00BD, 0x00BC, + 0x0111, 0x0111, 0x0111, 0x0111, 0x0111, 0x0111, 0x0111, 0x0111, 0x0111, 0x0111, 0x0111, 0x0111, 0x0111, 0x0111, 0x0111, 0x010E, + 0x010E, 0x010E, 0x010E, 0x010E, 0x010E, 0x010E, 0x010C, 0x010C, 0x010C, 0x010C, 0x010C, 0x010A, 0x010A, 0x010A, 0x010A, 0x0108, + 0x0108, 0x0108, 0x0108, 0x0106, 0x0106, 0x0106, 0x0106, 0x0104, 0x0104, 0x0104, 0x0102, 0x0102, 0x0102, 0x0100, 0x0100, 0x0100, + 0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F8, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F2, + 0x00F0, 0x00F0, 0x00EF, 0x00EF, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E5, + 0x00E3, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D9, 0x00D7, 0x00D6, + 0x00D6, 0x00D4, 0x00D4, 0x00D3, 0x00D2, 0x00D2, 0x00D0, 0x00CF, 0x00CF, 0x00CE, 0x00CC, 0x00CC, 0x00CB, 0x00CA, 0x00CA, 0x00C9, + 0x00C7, 0x00C7, 0x00C6, 0x00C5, 0x00C5, 0x00C4, 0x00C3, 0x00C1, 0x00C1, 0x00C0, 0x00BF, 0x00BF, 0x00BE, 0x00BD, 0x00BC, 0x00BC, + 0x010E, 0x010E, 0x010E, 0x010E, 0x010E, 0x010E, 0x010E, 0x010E, 0x010E, 0x010E, 0x010E, 0x010E, 0x010E, 0x010E, 0x010E, 0x010E, + 0x010C, 0x010C, 0x010C, 0x010C, 0x010C, 0x010C, 0x010A, 0x010A, 0x010A, 0x010A, 0x010A, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, + 0x0106, 0x0106, 0x0106, 0x0104, 0x0104, 0x0104, 0x0104, 0x0102, 0x0102, 0x0102, 0x0100, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FE, + 0x00FC, 0x00FC, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F8, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00F0, + 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E3, 0x00E3, + 0x00E1, 0x00E1, 0x00E0, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DB, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D6, + 0x00D4, 0x00D3, 0x00D3, 0x00D2, 0x00D0, 0x00D0, 0x00CF, 0x00CF, 0x00CE, 0x00CC, 0x00CC, 0x00CB, 0x00CA, 0x00CA, 0x00C9, 0x00C7, + 0x00C7, 0x00C6, 0x00C5, 0x00C5, 0x00C4, 0x00C3, 0x00C1, 0x00C1, 0x00C0, 0x00BF, 0x00BF, 0x00BE, 0x00BD, 0x00BC, 0x00BC, 0x00BB, + 0x010C, 0x010C, 0x010C, 0x010C, 0x010C, 0x010C, 0x010C, 0x010C, 0x010C, 0x010C, 0x010C, 0x010C, 0x010C, 0x010C, 0x010C, 0x010C, + 0x010A, 0x010A, 0x010A, 0x010A, 0x010A, 0x010A, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0106, 0x0106, 0x0106, 0x0106, 0x0106, + 0x0104, 0x0104, 0x0104, 0x0104, 0x0102, 0x0102, 0x0102, 0x0100, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FC, + 0x00FA, 0x00FA, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F6, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00F0, 0x00F0, 0x00EF, + 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E3, 0x00E1, + 0x00E1, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DB, 0x00DA, 0x00D9, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D4, 0x00D4, + 0x00D3, 0x00D3, 0x00D2, 0x00D0, 0x00D0, 0x00CF, 0x00CE, 0x00CE, 0x00CC, 0x00CB, 0x00CB, 0x00CA, 0x00CA, 0x00C9, 0x00C7, 0x00C7, + 0x00C6, 0x00C5, 0x00C4, 0x00C4, 0x00C3, 0x00C1, 0x00C1, 0x00C0, 0x00BF, 0x00BF, 0x00BE, 0x00BD, 0x00BD, 0x00BC, 0x00BB, 0x00BA, + 0x010A, 0x010A, 0x010A, 0x010A, 0x010A, 0x010A, 0x010A, 0x010A, 0x010A, 0x010A, 0x010A, 0x010A, 0x010A, 0x010A, 0x010A, 0x010A, + 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0106, 0x0106, 0x0106, 0x0106, 0x0106, 0x0104, 0x0104, 0x0104, 0x0104, + 0x0102, 0x0102, 0x0102, 0x0102, 0x0100, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FC, 0x00FA, 0x00FA, 0x00FA, + 0x00F8, 0x00F8, 0x00F8, 0x00F6, 0x00F6, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00F0, 0x00F0, 0x00EF, 0x00EF, 0x00ED, + 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E1, 0x00E0, + 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D9, 0x00D7, 0x00D6, 0x00D6, 0x00D4, 0x00D4, 0x00D3, + 0x00D2, 0x00D2, 0x00D0, 0x00D0, 0x00CF, 0x00CE, 0x00CE, 0x00CC, 0x00CB, 0x00CB, 0x00CA, 0x00C9, 0x00C9, 0x00C7, 0x00C6, 0x00C6, + 0x00C5, 0x00C4, 0x00C4, 0x00C3, 0x00C1, 0x00C1, 0x00C0, 0x00BF, 0x00BF, 0x00BE, 0x00BD, 0x00BD, 0x00BC, 0x00BB, 0x00BA, 0x00BA, + 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, + 0x0106, 0x0106, 0x0106, 0x0106, 0x0106, 0x0106, 0x0106, 0x0104, 0x0104, 0x0104, 0x0104, 0x0104, 0x0102, 0x0102, 0x0102, 0x0102, + 0x0100, 0x0100, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FC, 0x00FC, 0x00FA, 0x00FA, 0x00FA, 0x00F8, 0x00F8, + 0x00F6, 0x00F6, 0x00F6, 0x00F4, 0x00F4, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00F0, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00ED, 0x00EB, + 0x00EB, 0x00EA, 0x00EA, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, + 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D6, 0x00D4, 0x00D3, 0x00D3, 0x00D2, + 0x00D2, 0x00D0, 0x00CF, 0x00CF, 0x00CE, 0x00CE, 0x00CC, 0x00CB, 0x00CB, 0x00CA, 0x00C9, 0x00C9, 0x00C7, 0x00C6, 0x00C6, 0x00C5, + 0x00C4, 0x00C4, 0x00C3, 0x00C1, 0x00C1, 0x00C0, 0x00BF, 0x00BF, 0x00BE, 0x00BD, 0x00BD, 0x00BC, 0x00BB, 0x00BA, 0x00BA, 0x00B9, + 0x0106, 0x0106, 0x0106, 0x0106, 0x0106, 0x0106, 0x0106, 0x0106, 0x0106, 0x0106, 0x0106, 0x0106, 0x0106, 0x0106, 0x0106, 0x0106, + 0x0104, 0x0104, 0x0104, 0x0104, 0x0104, 0x0104, 0x0104, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0100, 0x0100, 0x0100, 0x0100, + 0x00FE, 0x00FE, 0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FC, 0x00FC, 0x00FA, 0x00FA, 0x00FA, 0x00F8, 0x00F8, 0x00F8, 0x00F6, 0x00F6, + 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F2, 0x00F2, 0x00F0, 0x00F0, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EB, 0x00EA, + 0x00EA, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00E0, 0x00DE, 0x00DE, + 0x00DD, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D4, 0x00D4, 0x00D3, 0x00D3, 0x00D2, 0x00D0, + 0x00D0, 0x00CF, 0x00CF, 0x00CE, 0x00CC, 0x00CC, 0x00CB, 0x00CB, 0x00CA, 0x00C9, 0x00C9, 0x00C7, 0x00C6, 0x00C6, 0x00C5, 0x00C4, + 0x00C4, 0x00C3, 0x00C1, 0x00C1, 0x00C0, 0x00BF, 0x00BF, 0x00BE, 0x00BD, 0x00BD, 0x00BC, 0x00BB, 0x00BA, 0x00BA, 0x00B9, 0x00B8, + 0x0104, 0x0104, 0x0104, 0x0104, 0x0104, 0x0104, 0x0104, 0x0104, 0x0104, 0x0104, 0x0104, 0x0104, 0x0104, 0x0104, 0x0104, 0x0104, + 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FE, 0x00FE, + 0x00FC, 0x00FC, 0x00FC, 0x00FC, 0x00FA, 0x00FA, 0x00FA, 0x00FA, 0x00F8, 0x00F8, 0x00F8, 0x00F6, 0x00F6, 0x00F6, 0x00F4, 0x00F4, + 0x00F4, 0x00F2, 0x00F2, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00EF, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00EA, 0x00EA, + 0x00E8, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DD, + 0x00DB, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D6, 0x00D4, 0x00D4, 0x00D3, 0x00D2, 0x00D2, 0x00D0, 0x00D0, + 0x00CF, 0x00CE, 0x00CE, 0x00CC, 0x00CC, 0x00CB, 0x00CA, 0x00CA, 0x00C9, 0x00C7, 0x00C7, 0x00C6, 0x00C6, 0x00C5, 0x00C4, 0x00C4, + 0x00C3, 0x00C1, 0x00C1, 0x00C0, 0x00BF, 0x00BF, 0x00BE, 0x00BD, 0x00BC, 0x00BC, 0x00BB, 0x00BA, 0x00BA, 0x00B9, 0x00B8, 0x00B8, + 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, + 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FE, 0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FC, 0x00FC, + 0x00FC, 0x00FA, 0x00FA, 0x00FA, 0x00F8, 0x00F8, 0x00F8, 0x00F8, 0x00F6, 0x00F6, 0x00F6, 0x00F4, 0x00F4, 0x00F4, 0x00F2, 0x00F2, + 0x00F2, 0x00F0, 0x00F0, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00ED, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00EA, 0x00EA, 0x00E8, 0x00E8, + 0x00E6, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00E0, 0x00E0, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DB, + 0x00DA, 0x00DA, 0x00D9, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D6, 0x00D4, 0x00D3, 0x00D3, 0x00D2, 0x00D2, 0x00D0, 0x00CF, 0x00CF, + 0x00CE, 0x00CE, 0x00CC, 0x00CB, 0x00CB, 0x00CA, 0x00CA, 0x00C9, 0x00C7, 0x00C7, 0x00C6, 0x00C5, 0x00C5, 0x00C4, 0x00C3, 0x00C3, + 0x00C1, 0x00C0, 0x00C0, 0x00BF, 0x00BE, 0x00BE, 0x00BD, 0x00BC, 0x00BC, 0x00BB, 0x00BA, 0x00BA, 0x00B9, 0x00B8, 0x00B8, 0x00B7, + 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, + 0x00FE, 0x00FE, 0x00FE, 0x00FE, 0x00FE, 0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FC, 0x00FC, 0x00FC, 0x00FA, 0x00FA, 0x00FA, 0x00FA, + 0x00FA, 0x00F8, 0x00F8, 0x00F8, 0x00F8, 0x00F6, 0x00F6, 0x00F6, 0x00F4, 0x00F4, 0x00F4, 0x00F2, 0x00F2, 0x00F2, 0x00F0, 0x00F0, + 0x00F0, 0x00EF, 0x00EF, 0x00EF, 0x00ED, 0x00ED, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00EA, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E6, + 0x00E5, 0x00E5, 0x00E3, 0x00E3, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DB, 0x00DA, + 0x00DA, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D6, 0x00D4, 0x00D4, 0x00D3, 0x00D3, 0x00D2, 0x00D0, 0x00D0, 0x00CF, 0x00CF, 0x00CE, + 0x00CC, 0x00CC, 0x00CB, 0x00CB, 0x00CA, 0x00C9, 0x00C9, 0x00C7, 0x00C7, 0x00C6, 0x00C5, 0x00C5, 0x00C4, 0x00C3, 0x00C3, 0x00C1, + 0x00C0, 0x00C0, 0x00BF, 0x00BE, 0x00BE, 0x00BD, 0x00BC, 0x00BC, 0x00BB, 0x00BA, 0x00BA, 0x00B9, 0x00B8, 0x00B8, 0x00B7, 0x00B6 +}; +#define kDeltaUsedToBuildTable 32 diff --git a/skia/effects/SkGradientShader.cpp b/skia/effects/SkGradientShader.cpp new file mode 100644 index 0000000..b296c15 --- /dev/null +++ b/skia/effects/SkGradientShader.cpp @@ -0,0 +1,1541 @@ +/* libs/graphics/effects/SkGradientShader.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkGradientShader.h" +#include "SkColorPriv.h" +#include "SkUnitMapper.h" +#include "SkUtils.h" + +/* + ToDo + + - not sure we still need the full Rec struct, now that we're using a cache + - detect const-alpha (but not opaque) in getFlags() +*/ + +/* dither seems to look better, but not stuningly yet, and it slows us down a little + so its not on by default yet. +*/ +#define TEST_GRADIENT_DITHER + +/////////////////////////////////////////////////////////////////////////// + +typedef SkFixed (*TileProc)(SkFixed); + +static SkFixed clamp_tileproc(SkFixed x) +{ + return SkClampMax(x, 0xFFFF); +} + +static SkFixed repeat_tileproc(SkFixed x) +{ + return x & 0xFFFF; +} + +static inline SkFixed mirror_tileproc(SkFixed x) +{ + int s = x << 15 >> 31; + return (x ^ s) & 0xFFFF; +} + +static const TileProc gTileProcs[] = { + clamp_tileproc, + repeat_tileproc, + mirror_tileproc +}; + +////////////////////////////////////////////////////////////////////////////// + +static inline int repeat_6bits(int x) +{ + return x & 63; +} + +static inline int mirror_6bits(int x) +{ +#ifdef SK_CPU_HAS_CONDITIONAL_INSTR + if (x & 64) + x = ~x; + return x & 63; +#else + int s = x << 25 >> 31; + return (x ^ s) & 63; +#endif +} + +static inline int repeat_8bits(int x) +{ + return x & 0xFF; +} + +static inline int mirror_8bits(int x) +{ +#ifdef SK_CPU_HAS_CONDITIONAL_INSTR + if (x & 256) + x = ~x; + return x & 255; +#else + int s = x << 23 >> 31; + return (x ^ s) & 0xFF; +#endif +} + +////////////////////////////////////////////////////////////////////////////// + +class Gradient_Shader : public SkShader { +public: + Gradient_Shader(const SkColor colors[], const SkScalar pos[], + int colorCount, SkShader::TileMode mode, SkUnitMapper* mapper); + virtual ~Gradient_Shader(); + + // overrides + virtual bool setContext(const SkBitmap&, const SkPaint&, const SkMatrix&); + virtual uint32_t getFlags() { return fFlags; } + +protected: + Gradient_Shader(SkFlattenableReadBuffer& ); + SkUnitMapper* fMapper; + SkMatrix fPtsToUnit; // set by subclass + SkMatrix fDstToIndex; + SkMatrix::MapXYProc fDstToIndexProc; + SkPMColor* fARGB32; + TileMode fTileMode; + TileProc fTileProc; + uint16_t fColorCount; + uint8_t fDstToIndexClass; + uint8_t fFlags; + + struct Rec { + SkFixed fPos; // 0...1 + uint32_t fScale; // (1 << 24) / range + }; + Rec* fRecs; + + enum { + kCache16Bits = 6, // seems like enough for visual accuracy + kCache16Count = 1 << kCache16Bits, + kCache32Bits = 8, // pretty much should always be 8 + kCache32Count = 1 << kCache32Bits + }; + virtual void flatten(SkFlattenableWriteBuffer& ); + const uint16_t* getCache16(); + const SkPMColor* getCache32(); + +private: + enum { + kColorStorageCount = 4, // more than this many colors, and we'll use sk_malloc for the space + + kStorageSize = kColorStorageCount * (sizeof(SkColor) + sizeof(SkPMColor) + sizeof(Rec)) + }; + SkColor fStorage[(kStorageSize + 3) >> 2]; + SkColor* fOrigColors; + + uint16_t* fCache16; // working ptr. If this is NULL, we need to recompute the cache values + SkPMColor* fCache32; // working ptr. If this is NULL, we need to recompute the cache values + + uint16_t* fCache16Storage; // storage for fCache16, allocated on demand + SkPMColor* fCache32Storage; // storage for fCache32, allocated on demand + unsigned fCacheAlpha; // the alpha value we used when we computed the cache. larger than 8bits so we can store uninitialized value + + typedef SkShader INHERITED; +}; + +static inline unsigned scalarToU16(SkScalar x) +{ + SkASSERT(x >= 0 && x <= SK_Scalar1); + +#ifdef SK_SCALAR_IS_FLOAT + return (unsigned)(x * 0xFFFF); +#else + return x - (x >> 16); // probably should be x - (x > 0x7FFF) but that is slower +#endif +} + +Gradient_Shader::Gradient_Shader(const SkColor colors[], const SkScalar pos[], int colorCount, + SkShader::TileMode mode, SkUnitMapper* mapper) +{ + SkASSERT(colorCount > 1); + + fCacheAlpha = 256; // init to a value that paint.getAlpha() can't return + + fMapper = mapper; + mapper->safeRef(); + + fCache16 = fCache16Storage = NULL; + fCache32 = fCache32Storage = NULL; + + fColorCount = SkToU16(colorCount); + if (colorCount > kColorStorageCount) + fOrigColors = (SkColor*)sk_malloc_throw((sizeof(SkColor) + sizeof(SkPMColor) + sizeof(Rec)) * colorCount); + else + fOrigColors = fStorage; + memcpy(fOrigColors, colors, colorCount * sizeof(SkColor)); + // our premul colors point to the 2nd half of the array + // these are assigned each time in setContext + fARGB32 = fOrigColors + colorCount; + + SkASSERT((unsigned)mode < SkShader::kTileModeCount); + SkASSERT(SkShader::kTileModeCount == SK_ARRAY_COUNT(gTileProcs)); + fTileMode = mode; + fTileProc = gTileProcs[mode]; + + fRecs = (Rec*)(fARGB32 + colorCount); + if (colorCount > 2) + { + Rec* recs = fRecs; + + recs[0].fPos = 0; + // recs[0].fScale = 0; // unused; + if (pos) + { + /* We need to convert the user's array of relative positions into + fixed-point positions and scale factors. We need these results + to be strictly monotonic (no two values equal or out of order). + Hence this complex loop that just jams a zero for the scale + value if it sees a segment out of order, and it assures that + we start at 0 and end at 1.0 + */ + SkFixed prev = 0; + for (int i = 1; i < colorCount; i++) + { + // force the last value to be 1.0 + SkFixed curr; + if (i == colorCount - 1) + curr = SK_Fixed1; + else + { + curr = SkScalarToFixed(pos[i]); + // pin curr withing range + if (curr < 0) + curr = 0; + else if (curr > SK_Fixed1) + curr = SK_Fixed1; + } + recs[i].fPos = curr; + if (curr > prev) + recs[i].fScale = (1 << 24) / (curr - prev); + else + recs[i].fScale = 0; // ignore this segment + // get ready for the next value + prev = curr; + } + } + else // assume even distribution + { + SkFixed dp = SK_Fixed1 / (colorCount - 1); + SkFixed p = dp; + SkFixed scale = (colorCount - 1) << 8; // (1 << 24) / dp + for (int i = 1; i < colorCount; i++) + { + recs[i].fPos = p; + recs[i].fScale = scale; + p += dp; + } + } + } +} + +Gradient_Shader::Gradient_Shader(SkFlattenableReadBuffer& buffer) : + INHERITED(buffer) +{ + fCacheAlpha = 256; + + fMapper = static_cast<SkUnitMapper*>(buffer.readFlattenable()); + + fCache16 = fCache16Storage = NULL; + fCache32 = fCache32Storage = NULL; + + int colorCount = fColorCount = buffer.readU16(); + if (colorCount > kColorStorageCount) + fOrigColors = (SkColor*)sk_malloc_throw((sizeof(SkColor) + sizeof(SkPMColor) + sizeof(Rec)) * colorCount); + else + fOrigColors = fStorage; + buffer.read(fOrigColors, colorCount * sizeof(SkColor)); + fARGB32 = fOrigColors + colorCount; + + fTileMode = (TileMode)buffer.readU8(); + fTileProc = gTileProcs[fTileMode]; + fRecs = (Rec*)(fARGB32 + colorCount); + if (colorCount > 2) { + Rec* recs = fRecs; + recs[0].fPos = 0; + for (int i = 1; i < colorCount; i++) { + recs[i].fPos = buffer.readS32(); + recs[i].fScale = buffer.readU32(); + } + } + buffer.read(&fPtsToUnit, sizeof(SkMatrix)); +} + +Gradient_Shader::~Gradient_Shader() +{ + if (fCache16Storage) + sk_free(fCache16Storage); + if (fCache32Storage) + sk_free(fCache32Storage); + if (fOrigColors != fStorage) + sk_free(fOrigColors); + fMapper->safeUnref(); +} + +void Gradient_Shader::flatten(SkFlattenableWriteBuffer& buffer) +{ + this->INHERITED::flatten(buffer); + buffer.writeFlattenable(fMapper); + buffer.write16(fColorCount); + buffer.writeMul4(fOrigColors, fColorCount * sizeof(SkColor)); + buffer.write8(fTileMode); + if (fColorCount > 2) { + Rec* recs = fRecs; + for (int i = 1; i < fColorCount; i++) { + buffer.write32(recs[i].fPos); + buffer.write32(recs[i].fScale); + } + } + buffer.writeMul4(&fPtsToUnit, sizeof(SkMatrix)); +} + +bool Gradient_Shader::setContext(const SkBitmap& device, + const SkPaint& paint, + const SkMatrix& matrix) +{ + if (!this->INHERITED::setContext(device, paint, matrix)) + return false; + + const SkMatrix& inverse = this->getTotalInverse(); + + if (!fDstToIndex.setConcat(fPtsToUnit, inverse)) { + return false; + } + + fDstToIndexProc = fDstToIndex.getMapXYProc(); + fDstToIndexClass = (uint8_t)SkShader::ComputeMatrixClass(fDstToIndex); + + // now convert our colors in to PMColors + unsigned paintAlpha = this->getPaintAlpha(); + unsigned colorAlpha = 0xFF; + + for (unsigned i = 0; i < fColorCount; i++) { + SkColor src = fOrigColors[i]; + unsigned sa = SkColorGetA(src); + colorAlpha &= sa; + + // now modulate it by the paint for our resulting ARGB32 array + sa = SkMulDiv255Round(sa, paintAlpha); + fARGB32[i] = SkPreMultiplyARGB(sa, SkColorGetR(src), SkColorGetG(src), + SkColorGetB(src)); + } + + fFlags = this->INHERITED::getFlags(); + if ((colorAlpha & paintAlpha) == 0xFF) { + fFlags |= kOpaqueAlpha_Flag; + } + // we can do span16 as long as our individual colors are opaque, + // regardless of the paint's alpha + if (0xFF == colorAlpha) { + fFlags |= kHasSpan16_Flag; + } + + // if the new alpha differs from the previous time we were called, inval our cache + // this will trigger the cache to be rebuilt. + // we don't care about the first time, since the cache ptrs will already be NULL + if (fCacheAlpha != paintAlpha) { + fCache16 = NULL; // inval the cache + fCache32 = NULL; // inval the cache + fCacheAlpha = paintAlpha; // record the new alpha + } + return true; +} + +static inline int blend8(int a, int b, int scale) +{ + SkASSERT(a == SkToU8(a)); + SkASSERT(b == SkToU8(b)); + SkASSERT(scale >= 0 && scale <= 256); + + return a + ((b - a) * scale >> 8); +} + +static inline uint32_t dot8_blend_packed32(uint32_t s0, uint32_t s1, int blend) +{ +#if 0 + int a = blend8(SkGetPackedA32(s0), SkGetPackedA32(s1), blend); + int r = blend8(SkGetPackedR32(s0), SkGetPackedR32(s1), blend); + int g = blend8(SkGetPackedG32(s0), SkGetPackedG32(s1), blend); + int b = blend8(SkGetPackedB32(s0), SkGetPackedB32(s1), blend); + + return SkPackARGB32(a, r, g, b); +#else + int otherBlend = 256 - blend; + +#if 0 + U32 t0 = (((s0 & 0xFF00FF) * blend + (s1 & 0xFF00FF) * otherBlend) >> 8) & 0xFF00FF; + U32 t1 = (((s0 >> 8) & 0xFF00FF) * blend + ((s1 >> 8) & 0xFF00FF) * otherBlend) & 0xFF00FF00; + SkASSERT((t0 & t1) == 0); + return t0 | t1; +#else + return ((((s0 & 0xFF00FF) * blend + (s1 & 0xFF00FF) * otherBlend) >> 8) & 0xFF00FF) | + ((((s0 >> 8) & 0xFF00FF) * blend + ((s1 >> 8) & 0xFF00FF) * otherBlend) & 0xFF00FF00); +#endif + +#endif +} + +#define Fixed_To_Dot8(x) (((x) + 0x80) >> 8) + +/** We take the original colors, not our premultiplied PMColors, since we can build a 16bit table + as long as the original colors are opaque, even if the paint specifies a non-opaque alpha. +*/ +static void build_16bit_cache(uint16_t cache[], SkColor c0, SkColor c1, int count) +{ + SkASSERT(count > 1); + SkASSERT(SkColorGetA(c0) == 0xFF); + SkASSERT(SkColorGetA(c1) == 0xFF); + + SkFixed r = SkColorGetR(c0); + SkFixed g = SkColorGetG(c0); + SkFixed b = SkColorGetB(c0); + + SkFixed dr = SkIntToFixed(SkColorGetR(c1) - r) / (count - 1); + SkFixed dg = SkIntToFixed(SkColorGetG(c1) - g) / (count - 1); + SkFixed db = SkIntToFixed(SkColorGetB(c1) - b) / (count - 1); + + r = SkIntToFixed(r) + 0x8000; + g = SkIntToFixed(g) + 0x8000; + b = SkIntToFixed(b) + 0x8000; + + do { + unsigned rr = r >> 16; + unsigned gg = g >> 16; + unsigned bb = b >> 16; + cache[0] = SkPackRGB16(SkR32ToR16(rr), SkG32ToG16(gg), SkB32ToB16(bb)); + cache[64] = SkDitherPack888ToRGB16(rr, gg, bb); + cache += 1; + r += dr; + g += dg; + b += db; + } while (--count != 0); +} + +static void build_32bit_cache(SkPMColor cache[], SkPMColor c0, SkPMColor c1, int count) +{ + SkASSERT(count > 1); + + SkFixed a = SkGetPackedA32(c0); + SkFixed r = SkGetPackedR32(c0); + SkFixed g = SkGetPackedG32(c0); + SkFixed b = SkGetPackedB32(c0); + + SkFixed da = SkIntToFixed(SkGetPackedA32(c1) - a) / (count - 1); + SkFixed dr = SkIntToFixed(SkGetPackedR32(c1) - r) / (count - 1); + SkFixed dg = SkIntToFixed(SkGetPackedG32(c1) - g) / (count - 1); + SkFixed db = SkIntToFixed(SkGetPackedB32(c1) - b) / (count - 1); + + a = SkIntToFixed(a) + 0x8000; + r = SkIntToFixed(r) + 0x8000; + g = SkIntToFixed(g) + 0x8000; + b = SkIntToFixed(b) + 0x8000; + + do { + *cache++ = SkPackARGB32(a >> 16, r >> 16, g >> 16, b >> 16); + a += da; + r += dr; + g += dg; + b += db; + } while (--count != 0); +} + +static inline int SkFixedToFFFF(SkFixed x) +{ + SkASSERT((unsigned)x <= SK_Fixed1); + return x - (x >> 16); +} + +static inline U16CPU dot6to16(unsigned x) +{ + SkASSERT(x < 64); + return (x << 10) | (x << 4) | (x >> 2); +} + +const uint16_t* Gradient_Shader::getCache16() +{ + if (fCache16 == NULL) + { + if (fCache16Storage == NULL) // set the storage and our working ptr +#ifdef TEST_GRADIENT_DITHER + fCache16Storage = (uint16_t*)sk_malloc_throw(sizeof(uint16_t) * kCache16Count * 2); +#else + fCache16Storage = (uint16_t*)sk_malloc_throw(sizeof(uint16_t) * kCache16Count); +#endif + fCache16 = fCache16Storage; + if (fColorCount == 2) + build_16bit_cache(fCache16, fOrigColors[0], fOrigColors[1], kCache16Count); + else + { + Rec* rec = fRecs; + int prevIndex = 0; + for (unsigned i = 1; i < fColorCount; i++) + { + int nextIndex = SkFixedToFFFF(rec[i].fPos) >> (16 - kCache16Bits); + SkASSERT(nextIndex < kCache16Count); + + if (nextIndex > prevIndex) + build_16bit_cache(fCache16 + prevIndex, fOrigColors[i-1], fOrigColors[i], nextIndex - prevIndex + 1); + prevIndex = nextIndex; + } + SkASSERT(prevIndex == kCache16Count - 1); + } + + if (fMapper) + { +#ifdef TEST_GRADIENT_DITHER + fCache16Storage = (uint16_t*)sk_malloc_throw(sizeof(uint16_t) * kCache16Count * 2); +#else + fCache16Storage = (uint16_t*)sk_malloc_throw(sizeof(uint16_t) * kCache16Count); +#endif + uint16_t* linear = fCache16; // just computed linear data + uint16_t* mapped = fCache16Storage; // storage for mapped data + SkUnitMapper* map = fMapper; + for (int i = 0; i < 64; i++) + { + int index = map->mapUnit16(dot6to16(i)) >> 10; + mapped[i] = linear[index]; +#ifdef TEST_GRADIENT_DITHER + mapped[i + 64] = linear[index + 64]; +#endif + } + sk_free(fCache16); + fCache16 = fCache16Storage; + } + } + return fCache16; +} + +const SkPMColor* Gradient_Shader::getCache32() +{ + if (fCache32 == NULL) + { + if (fCache32Storage == NULL) // set the storage and our working ptr + fCache32Storage = (SkPMColor*)sk_malloc_throw(sizeof(SkPMColor) * kCache32Count); + + fCache32 = fCache32Storage; + if (fColorCount == 2) + build_32bit_cache(fCache32, fARGB32[0], fARGB32[1], kCache32Count); + else + { + Rec* rec = fRecs; + int prevIndex = 0; + for (unsigned i = 1; i < fColorCount; i++) + { + int nextIndex = SkFixedToFFFF(rec[i].fPos) >> (16 - kCache32Bits); + SkASSERT(nextIndex < kCache32Count); + + if (nextIndex > prevIndex) + build_32bit_cache(fCache32 + prevIndex, fARGB32[i-1], fARGB32[i], nextIndex - prevIndex + 1); + prevIndex = nextIndex; + } + SkASSERT(prevIndex == kCache32Count - 1); + } + + if (fMapper) + { + fCache32Storage = (SkPMColor*)sk_malloc_throw(sizeof(SkPMColor) * kCache32Count); + SkPMColor* linear = fCache32; // just computed linear data + SkPMColor* mapped = fCache32Storage; // storage for mapped data + SkUnitMapper* map = fMapper; + for (int i = 0; i < 256; i++) + mapped[i] = linear[map->mapUnit16((i << 8) | i) >> 8]; + sk_free(fCache32); + fCache32 = fCache32Storage; + } + } + return fCache32; +} + +/////////////////////////////////////////////////////////////////////////// + +static void pts_to_unit_matrix(const SkPoint pts[2], SkMatrix* matrix) +{ + SkVector vec = pts[1] - pts[0]; + SkScalar mag = vec.length(); + SkScalar inv = mag ? SkScalarInvert(mag) : 0; + + vec.scale(inv); + matrix->setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY); + matrix->postTranslate(-pts[0].fX, -pts[0].fY); + matrix->postScale(inv, inv); +} + +/////////////////////////////////////////////////////////////////////////////// + +class Linear_Gradient : public Gradient_Shader { +public: + Linear_Gradient(const SkPoint pts[2], + const SkColor colors[], const SkScalar pos[], int colorCount, + SkShader::TileMode mode, SkUnitMapper* mapper) + : Gradient_Shader(colors, pos, colorCount, mode, mapper) + { + pts_to_unit_matrix(pts, &fPtsToUnit); + } + virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count); + virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count); + virtual bool asABitmap(SkBitmap*, SkMatrix*, TileMode*); + + static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) { + return SkNEW_ARGS(Linear_Gradient, (buffer)); + } + +protected: + Linear_Gradient(SkFlattenableReadBuffer& buffer) : Gradient_Shader(buffer) {}; + virtual Factory getFactory() { return CreateProc; } + +private: + typedef Gradient_Shader INHERITED; +}; + +// Return true if fx, fx+dx, fx+2*dx, ... is always in range +static bool no_need_for_clamp(int fx, int dx, int count) +{ + SkASSERT(count > 0); + return (unsigned)((fx | (fx + (count - 1) * dx)) >> 8) <= 0xFF; +} + +void Linear_Gradient::shadeSpan(int x, int y, SkPMColor dstC[], int count) +{ + SkASSERT(count > 0); + + SkPoint srcPt; + SkMatrix::MapXYProc dstProc = fDstToIndexProc; + TileProc proc = fTileProc; + const SkPMColor* cache = this->getCache32(); + + if (fDstToIndexClass != kPerspective_MatrixClass) + { + dstProc(fDstToIndex, SkIntToScalar(x), SkIntToScalar(y), &srcPt); + SkFixed dx, fx = SkScalarToFixed(srcPt.fX); + + if (fDstToIndexClass == kFixedStepInX_MatrixClass) + { + SkFixed dxStorage[1]; + (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL); + dx = dxStorage[0]; + } + else + { + SkASSERT(fDstToIndexClass == kLinear_MatrixClass); + dx = SkScalarToFixed(fDstToIndex.getScaleX()); + } + + if (SkFixedNearlyZero(dx)) // we're a vertical gradient, so no change in a span + { + unsigned fi = proc(fx); + SkASSERT(fi <= 0xFFFF); + sk_memset32(dstC, cache[fi >> (16 - kCache32Bits)], count); + } + else if (proc == clamp_tileproc) + { +#if 0 + if (no_need_for_clamp(fx, dx, count)) + { + unsigned fi; + while ((count -= 4) >= 0) + { + fi = fx >> 8; SkASSERT(fi <= 0xFF); fx += dx; *dstC++ = cache[fi]; + fi = fx >> 8; SkASSERT(fi <= 0xFF); fx += dx; *dstC++ = cache[fi]; + fi = fx >> 8; SkASSERT(fi <= 0xFF); fx += dx; *dstC++ = cache[fi]; + fi = fx >> 8; SkASSERT(fi <= 0xFF); fx += dx; *dstC++ = cache[fi]; + } + SkASSERT(count <= -1 && count >= -4); + count += 4; + while (--count >= 0) + { + fi = fx >> 8; + SkASSERT(fi <= 0xFF); + fx += dx; + *dstC++ = cache[fi]; + } + } + else +#endif + do { + unsigned fi = SkClampMax(fx >> 8, 0xFF); + SkASSERT(fi <= 0xFF); + fx += dx; + *dstC++ = cache[fi]; + } while (--count != 0); + } + else if (proc == mirror_tileproc) + { + do { + unsigned fi = mirror_8bits(fx >> 8); + SkASSERT(fi <= 0xFF); + fx += dx; + *dstC++ = cache[fi]; + } while (--count != 0); + } + else + { + SkASSERT(proc == repeat_tileproc); + do { + unsigned fi = repeat_8bits(fx >> 8); + SkASSERT(fi <= 0xFF); + fx += dx; + *dstC++ = cache[fi]; + } while (--count != 0); + } + } + else + { + SkScalar dstX = SkIntToScalar(x); + SkScalar dstY = SkIntToScalar(y); + do { + dstProc(fDstToIndex, dstX, dstY, &srcPt); + unsigned fi = proc(SkScalarToFixed(srcPt.fX)); + SkASSERT(fi <= 0xFFFF); + *dstC++ = cache[fi >> (16 - kCache32Bits)]; + dstX += SK_Scalar1; + } while (--count != 0); + } +} + +bool Linear_Gradient::asABitmap(SkBitmap* bitmap, SkMatrix* matrix, + TileMode xy[]) { + if (bitmap) { + bitmap->setConfig(SkBitmap::kARGB_8888_Config, kCache32Count, 1); + bitmap->allocPixels(); // share with shader??? + memcpy(bitmap->getPixels(), this->getCache32(), kCache32Count * 4); + } + if (matrix) { + matrix->setScale(SkIntToScalar(kCache32Count), SK_Scalar1); + matrix->preConcat(fPtsToUnit); + } + if (xy) { + xy[0] = fTileMode; + xy[1] = kClamp_TileMode; + } + return true; +} + +#ifdef TEST_GRADIENT_DITHER +static void dither_memset16(uint16_t dst[], uint16_t value, uint16_t other, int count) +{ + if ((unsigned)dst & 2) + { + *dst++ = value; + count -= 1; + SkTSwap(value, other); + } + + sk_memset32((uint32_t*)dst, (value << 16) | other, count >> 1); + + if (count & 1) + dst[count - 1] = value; +} +#endif + +void Linear_Gradient::shadeSpan16(int x, int y, uint16_t dstC[], int count) +{ + SkASSERT(count > 0); + + SkPoint srcPt; + SkMatrix::MapXYProc dstProc = fDstToIndexProc; + TileProc proc = fTileProc; + const uint16_t* cache = this->getCache16(); +#ifdef TEST_GRADIENT_DITHER + int toggle = ((x ^ y) & 1) << kCache16Bits; +#endif + + if (fDstToIndexClass != kPerspective_MatrixClass) + { + dstProc(fDstToIndex, SkIntToScalar(x), SkIntToScalar(y), &srcPt); + SkFixed dx, fx = SkScalarToFixed(srcPt.fX); + + if (fDstToIndexClass == kFixedStepInX_MatrixClass) + { + SkFixed dxStorage[1]; + (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL); + dx = dxStorage[0]; + } + else + { + SkASSERT(fDstToIndexClass == kLinear_MatrixClass); + dx = SkScalarToFixed(fDstToIndex.getScaleX()); + } + + if (SkFixedNearlyZero(dx)) // we're a vertical gradient, so no change in a span + { + unsigned fi = proc(fx) >> 10; + SkASSERT(fi <= 63); +#ifdef TEST_GRADIENT_DITHER + dither_memset16(dstC, cache[toggle + fi], cache[(toggle ^ (1 << kCache16Bits)) + fi], count); +#else + sk_memset16(dstC, cache[fi], count); +#endif + } + else if (proc == clamp_tileproc) + { + do { + unsigned fi = SkClampMax(fx >> 10, 63); + SkASSERT(fi <= 63); + fx += dx; +#ifdef TEST_GRADIENT_DITHER + *dstC++ = cache[toggle + fi]; + toggle ^= (1 << kCache16Bits); +#else + *dstC++ = cache[fi]; +#endif + } while (--count != 0); + } + else if (proc == mirror_tileproc) + { + do { + unsigned fi = mirror_6bits(fx >> 10); + SkASSERT(fi <= 0x3F); + fx += dx; +#ifdef TEST_GRADIENT_DITHER + *dstC++ = cache[toggle + fi]; + toggle ^= (1 << kCache16Bits); +#else + *dstC++ = cache[fi]; +#endif + } while (--count != 0); + } + else + { + SkASSERT(proc == repeat_tileproc); + do { + unsigned fi = repeat_6bits(fx >> 10); + SkASSERT(fi <= 0x3F); + fx += dx; +#ifdef TEST_GRADIENT_DITHER + *dstC++ = cache[toggle + fi]; + toggle ^= (1 << kCache16Bits); +#else + *dstC++ = cache[fi]; +#endif + } while (--count != 0); + } + } + else + { + SkScalar dstX = SkIntToScalar(x); + SkScalar dstY = SkIntToScalar(y); + do { + dstProc(fDstToIndex, dstX, dstY, &srcPt); + unsigned fi = proc(SkScalarToFixed(srcPt.fX)); + SkASSERT(fi <= 0xFFFF); + + int index = fi >> (16 - kCache16Bits); +#ifdef TEST_GRADIENT_DITHER + *dstC++ = cache[toggle + index]; + toggle ^= (1 << kCache16Bits); +#else + *dstC++ = cache[index]; +#endif + + dstX += SK_Scalar1; + } while (--count != 0); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +#define kSQRT_TABLE_BITS 11 +#define kSQRT_TABLE_SIZE (1 << kSQRT_TABLE_BITS) + +#include "SkRadialGradient_Table.h" + +#if defined(SK_BUILD_FOR_WIN32) && defined(SK_DEBUG) + +#include <stdio.h> + +void SkRadialGradient_BuildTable() +{ + // build it 0..127 x 0..127, so we use 2^15 - 1 in the numerator for our "fixed" table + + FILE* file = ::fopen("SkRadialGradient_Table.h", "w"); + SkASSERT(file); + ::fprintf(file, "static const uint8_t gSqrt8Table[] = {\n"); + + for (int i = 0; i < kSQRT_TABLE_SIZE; i++) + { + if ((i & 15) == 0) + ::fprintf(file, "\t"); + + uint8_t value = SkToU8(SkFixedSqrt(i * SK_Fixed1 / kSQRT_TABLE_SIZE) >> 8); + + ::fprintf(file, "0x%02X", value); + if (i < kSQRT_TABLE_SIZE-1) + ::fprintf(file, ", "); + if ((i & 15) == 15) + ::fprintf(file, "\n"); + } + ::fprintf(file, "};\n"); + ::fclose(file); +} + +#endif + + +static void rad_to_unit_matrix(const SkPoint& center, SkScalar radius, SkMatrix* matrix) +{ + SkScalar inv = SkScalarInvert(radius); + + matrix->setTranslate(-center.fX, -center.fY); + matrix->postScale(inv, inv); +} + +class Radial_Gradient : public Gradient_Shader { +public: + Radial_Gradient(const SkPoint& center, SkScalar radius, + const SkColor colors[], const SkScalar pos[], int colorCount, + SkShader::TileMode mode, SkUnitMapper* mapper) + : Gradient_Shader(colors, pos, colorCount, mode, mapper) + { + // make sure our table is insync with our current #define for kSQRT_TABLE_SIZE + SkASSERT(sizeof(gSqrt8Table) == kSQRT_TABLE_SIZE); + + rad_to_unit_matrix(center, radius, &fPtsToUnit); + } + virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count) + { + SkASSERT(count > 0); + + SkPoint srcPt; + SkMatrix::MapXYProc dstProc = fDstToIndexProc; + TileProc proc = fTileProc; + const SkPMColor* cache = this->getCache32(); + + if (fDstToIndexClass != kPerspective_MatrixClass) + { + dstProc(fDstToIndex, SkIntToScalar(x), SkIntToScalar(y), &srcPt); + SkFixed dx, fx = SkScalarToFixed(srcPt.fX); + SkFixed dy, fy = SkScalarToFixed(srcPt.fY); + + if (fDstToIndexClass == kFixedStepInX_MatrixClass) + { + SkFixed storage[2]; + (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &storage[0], &storage[1]); + dx = storage[0]; + dy = storage[1]; + } + else + { + SkASSERT(fDstToIndexClass == kLinear_MatrixClass); + dx = SkScalarToFixed(fDstToIndex.getScaleX()); + dy = SkScalarToFixed(fDstToIndex.getSkewY()); + } + + if (proc == clamp_tileproc) + { + const uint8_t* sqrt_table = gSqrt8Table; + fx >>= 1; + dx >>= 1; + fy >>= 1; + dy >>= 1; + do { + unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1); + unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1); + fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS); + fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS)); + *dstC++ = cache[sqrt_table[fi] >> (8 - kCache32Bits)]; + fx += dx; + fy += dy; + } while (--count != 0); + } + else if (proc == mirror_tileproc) + { + do { + SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy)); + unsigned fi = mirror_tileproc(dist); + SkASSERT(fi <= 0xFFFF); + *dstC++ = cache[fi >> (16 - kCache32Bits)]; + fx += dx; + fy += dy; + } while (--count != 0); + } + else + { + SkASSERT(proc == repeat_tileproc); + do { + SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy)); + unsigned fi = repeat_tileproc(dist); + SkASSERT(fi <= 0xFFFF); + *dstC++ = cache[fi >> (16 - kCache32Bits)]; + fx += dx; + fy += dy; + } while (--count != 0); + } + } + else // perspective case + { + SkScalar dstX = SkIntToScalar(x); + SkScalar dstY = SkIntToScalar(y); + do { + dstProc(fDstToIndex, dstX, dstY, &srcPt); + unsigned fi = proc(SkScalarToFixed(srcPt.length())); + SkASSERT(fi <= 0xFFFF); + *dstC++ = cache[fi >> (16 - kCache32Bits)]; + dstX += SK_Scalar1; + } while (--count != 0); + } + } + virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count) + { + SkASSERT(count > 0); + + SkPoint srcPt; + SkMatrix::MapXYProc dstProc = fDstToIndexProc; + TileProc proc = fTileProc; + const uint16_t* cache = this->getCache16(); +#ifdef TEST_GRADIENT_DITHER + int toggle = ((x ^ y) & 1) << kCache16Bits; +#endif + + if (fDstToIndexClass != kPerspective_MatrixClass) + { + dstProc(fDstToIndex, SkIntToScalar(x), SkIntToScalar(y), &srcPt); + SkFixed dx, fx = SkScalarToFixed(srcPt.fX); + SkFixed dy, fy = SkScalarToFixed(srcPt.fY); + + if (fDstToIndexClass == kFixedStepInX_MatrixClass) + { + SkFixed storage[2]; + (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &storage[0], &storage[1]); + dx = storage[0]; + dy = storage[1]; + } + else + { + SkASSERT(fDstToIndexClass == kLinear_MatrixClass); + dx = SkScalarToFixed(fDstToIndex.getScaleX()); + dy = SkScalarToFixed(fDstToIndex.getSkewY()); + } + + if (proc == clamp_tileproc) + { + const uint8_t* sqrt_table = gSqrt8Table; + + /* knock these down so we can pin against +- 0x7FFF, which is an immediate load, + rather than 0xFFFF which is slower. This is a compromise, since it reduces our + precision, but that appears to be visually OK. If we decide this is OK for + all of our cases, we could (it seems) put this scale-down into fDstToIndex, + to avoid having to do these extra shifts each time. + */ + fx >>= 1; + dx >>= 1; + fy >>= 1; + dy >>= 1; + if (dy == 0) // might perform this check for the other modes, but the win will be a smaller % of the total + { + fy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1); + fy *= fy; + do { + unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1); + unsigned fi = (xx * xx + fy) >> (14 + 16 - kSQRT_TABLE_BITS); + fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS)); + fx += dx; +#ifdef TEST_GRADIENT_DITHER + *dstC++ = cache[toggle + (sqrt_table[fi] >> (8 - kCache16Bits))]; + toggle ^= (1 << kCache16Bits); +#else + *dstC++ = cache[sqrt_table[fi] >> (8 - kCache16Bits)]; +#endif + } while (--count != 0); + } + else + { + do { + unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1); + unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1); + fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS); + fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS)); + fx += dx; + fy += dy; +#ifdef TEST_GRADIENT_DITHER + *dstC++ = cache[toggle + (sqrt_table[fi] >> (8 - kCache16Bits))]; + toggle ^= (1 << kCache16Bits); +#else + *dstC++ = cache[sqrt_table[fi] >> (8 - kCache16Bits)]; +#endif + } while (--count != 0); + } + } + else if (proc == mirror_tileproc) + { + do { + SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy)); + unsigned fi = mirror_tileproc(dist); + SkASSERT(fi <= 0xFFFF); + fx += dx; + fy += dy; +#ifdef TEST_GRADIENT_DITHER + *dstC++ = cache[toggle + (fi >> (16 - kCache16Bits))]; + toggle ^= (1 << kCache16Bits); +#else + *dstC++ = cache[fi >> (16 - kCache16Bits)]; +#endif + } while (--count != 0); + } + else + { + SkASSERT(proc == repeat_tileproc); + do { + SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy)); + unsigned fi = repeat_tileproc(dist); + SkASSERT(fi <= 0xFFFF); + fx += dx; + fy += dy; +#ifdef TEST_GRADIENT_DITHER + *dstC++ = cache[toggle + (fi >> (16 - kCache16Bits))]; + toggle ^= (1 << kCache16Bits); +#else + *dstC++ = cache[fi >> (16 - kCache16Bits)]; +#endif + } while (--count != 0); + } + } + else // perspective case + { + SkScalar dstX = SkIntToScalar(x); + SkScalar dstY = SkIntToScalar(y); + do { + dstProc(fDstToIndex, dstX, dstY, &srcPt); + unsigned fi = proc(SkScalarToFixed(srcPt.length())); + SkASSERT(fi <= 0xFFFF); + + int index = fi >> (16 - kCache16Bits); +#ifdef TEST_GRADIENT_DITHER + *dstC++ = cache[toggle + index]; + toggle ^= (1 << kCache16Bits); +#else + *dstC++ = cache[index]; +#endif + + dstX += SK_Scalar1; + } while (--count != 0); + } + } + + static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) { + return SkNEW_ARGS(Radial_Gradient, (buffer)); + } + +protected: + Radial_Gradient(SkFlattenableReadBuffer& buffer) : Gradient_Shader(buffer) {}; + virtual Factory getFactory() { return CreateProc; } + +private: + typedef Gradient_Shader INHERITED; +}; + +/////////////////////////////////////////////////////////////////////////////// + +class Sweep_Gradient : public Gradient_Shader { +public: + Sweep_Gradient(SkScalar cx, SkScalar cy, const SkColor colors[], + const SkScalar pos[], int count, SkUnitMapper* mapper) + : Gradient_Shader(colors, pos, count, SkShader::kClamp_TileMode, mapper) + { + fPtsToUnit.setTranslate(-cx, -cy); + } + virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count); + virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count); + + static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) { + return SkNEW_ARGS(Sweep_Gradient, (buffer)); + } + +protected: + Sweep_Gradient(SkFlattenableReadBuffer& buffer) : Gradient_Shader(buffer) {} + + virtual Factory getFactory() { return CreateProc; } + +private: + typedef Gradient_Shader INHERITED; +}; + +#ifdef COMPUTE_SWEEP_TABLE +#define PI 3.14159265 +static bool gSweepTableReady; +static uint8_t gSweepTable[65]; + +/* Our table stores precomputed values for atan: [0...1] -> [0..PI/4] + We scale the results to [0..32] +*/ +static const uint8_t* build_sweep_table() +{ + if (!gSweepTableReady) + { + const int N = 65; + const double DENOM = N - 1; + + for (int i = 0; i < N; i++) + { + double arg = i / DENOM; + double v = atan(arg); + int iv = (int)round(v * DENOM * 2 / PI); +// printf("[%d] atan(%g) = %g %d\n", i, arg, v, iv); + printf("%d, ", iv); + gSweepTable[i] = iv; + } + gSweepTableReady = true; + } + return gSweepTable; +} +#else +static const uint8_t gSweepTable[] = { + 0, 1, 1, 2, 3, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 9, + 10, 11, 11, 12, 12, 13, 13, 14, 15, 15, 16, 16, 17, 17, 18, 18, + 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 25, 26, + 26, 27, 27, 27, 28, 28, 29, 29, 29, 30, 30, 30, 31, 31, 31, 32, + 32 +}; +static const uint8_t* build_sweep_table() { return gSweepTable; } +#endif + +// divide numer/denom, with a bias of 6bits. Assumes numer <= denom +// and denom != 0. Since our table is 6bits big (+1), this is a nice fit. +// Same as (but faster than) SkFixedDiv(numer, denom) >> 10 + +//unsigned div_64(int numer, int denom); +static unsigned div_64(int numer, int denom) +{ + SkASSERT(numer <= denom); + SkASSERT(numer > 0); + SkASSERT(denom > 0); + + int nbits = SkCLZ(numer); + int dbits = SkCLZ(denom); + int bits = 6 - nbits + dbits; + SkASSERT(bits <= 6); + + if (bits < 0) // detect underflow + return 0; + + denom <<= dbits - 1; + numer <<= nbits - 1; + + unsigned result = 0; + + // do the first one + if ((numer -= denom) >= 0) + result = 1; + else + numer += denom; + + // Now fall into our switch statement if there are more bits to compute + if (bits > 0) + { + // make room for the rest of the answer bits + result <<= bits; + switch (bits) { + case 6: + if ((numer = (numer << 1) - denom) >= 0) + result |= 32; + else + numer += denom; + case 5: + if ((numer = (numer << 1) - denom) >= 0) + result |= 16; + else + numer += denom; + case 4: + if ((numer = (numer << 1) - denom) >= 0) + result |= 8; + else + numer += denom; + case 3: + if ((numer = (numer << 1) - denom) >= 0) + result |= 4; + else + numer += denom; + case 2: + if ((numer = (numer << 1) - denom) >= 0) + result |= 2; + else + numer += denom; + case 1: + default: // not strictly need, but makes GCC make better ARM code + if ((numer = (numer << 1) - denom) >= 0) + result |= 1; + else + numer += denom; + } + } + return result; +} + +// Given x,y in the first quadrant, return 0..63 for the angle [0..90] +static unsigned atan_0_90(SkFixed y, SkFixed x) +{ +#ifdef SK_DEBUG + { + static bool gOnce; + if (!gOnce) + { + gOnce = true; + SkASSERT(div_64(55, 55) == 64); + SkASSERT(div_64(128, 256) == 32); + SkASSERT(div_64(2326528, 4685824) == 31); + SkASSERT(div_64(753664, 5210112) == 9); + SkASSERT(div_64(229376, 4882432) == 3); + SkASSERT(div_64(2, 64) == 2); + SkASSERT(div_64(1, 64) == 1); + // test that we handle underflow correctly + SkASSERT(div_64(12345, 0x54321234) == 0); + } + } +#endif + + SkASSERT(y > 0 && x > 0); + const uint8_t* table = build_sweep_table(); + + unsigned result; + bool swap = (x < y); + if (swap) + { + // first part of the atan(v) = PI/2 - atan(1/v) identity + // since our div_64 and table want v <= 1, where v = y/x + SkTSwap<SkFixed>(x, y); + } + + result = div_64(y, x); + +#ifdef SK_DEBUG + { + unsigned result2 = SkDivBits(y, x, 6); + SkASSERT(result2 == result || + (result == 1 && result2 == 0)); + } +#endif + + SkASSERT(result < SK_ARRAY_COUNT(gSweepTable)); + result = table[result]; + + if (swap) + { + // complete the atan(v) = PI/2 - atan(1/v) identity + result = 64 - result; + // pin to 63 + result -= result >> 6; + } + + SkASSERT(result <= 63); + return result; +} + +// returns angle in a circle [0..2PI) -> [0..255] +static unsigned SkATan2_255(SkFixed y, SkFixed x) +{ + if (x == 0) + { + if (y == 0) + return 0; + return y < 0 ? 192 : 64; + } + if (y == 0) + return x < 0 ? 128 : 0; + + /* Find the right quadrant for x,y + Since atan_0_90 only handles the first quadrant, we rotate x,y + appropriately before calling it, and then add the right amount + to account for the real quadrant. + quadrant 0 : add 0 | x > 0 && y > 0 + quadrant 1 : add 64 (90 degrees) | x < 0 && y > 0 + quadrant 2 : add 128 (180 degrees) | x < 0 && y < 0 + quadrant 3 : add 192 (270 degrees) | x > 0 && y < 0 + + map x<0 to (1 << 6) + map y<0 to (3 << 6) + add = map_x ^ map_y + */ + int xsign = x >> 31; + int ysign = y >> 31; + int add = ((-xsign) ^ (ysign & 3)) << 6; + +#ifdef SK_DEBUG + if (0 == add) + SkASSERT(x > 0 && y > 0); + else if (64 == add) + SkASSERT(x < 0 && y > 0); + else if (128 == add) + SkASSERT(x < 0 && y < 0); + else if (192 == add) + SkASSERT(x > 0 && y < 0); + else + SkASSERT(!"bad value for add"); +#endif + + /* This ^ trick makes x, y positive, and the swap<> handles quadrants + where we need to rotate x,y by 90 or -90 + */ + x = (x ^ xsign) - xsign; + y = (y ^ ysign) - ysign; + if (add & 64) // quads 1 or 3 need to swap x,y + SkTSwap<SkFixed>(x, y); + + unsigned result = add + atan_0_90(y, x); + SkASSERT(result < 256); + return result; +} + +void Sweep_Gradient::shadeSpan(int x, int y, SkPMColor dstC[], int count) +{ + SkMatrix::MapXYProc proc = fDstToIndexProc; + const SkMatrix& matrix = fDstToIndex; + const SkPMColor* cache = this->getCache32(); + SkPoint srcPt; + + if (fDstToIndexClass != kPerspective_MatrixClass) + { + proc(matrix, SkIntToScalar(x) + SK_ScalarHalf, + SkIntToScalar(y) + SK_ScalarHalf, &srcPt); + SkFixed dx, fx = SkScalarToFixed(srcPt.fX); + SkFixed dy, fy = SkScalarToFixed(srcPt.fY); + + if (fDstToIndexClass == kFixedStepInX_MatrixClass) + { + SkFixed storage[2]; + (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf, + &storage[0], &storage[1]); + dx = storage[0]; + dy = storage[1]; + } + else + { + SkASSERT(fDstToIndexClass == kLinear_MatrixClass); + dx = SkScalarToFixed(matrix.getScaleX()); + dy = SkScalarToFixed(matrix.getSkewY()); + } + + for (; count > 0; --count) + { + *dstC++ = cache[SkATan2_255(fy, fx)]; + fx += dx; + fy += dy; + } + } + else // perspective case + { + for (int stop = x + count; x < stop; x++) + { + proc(matrix, SkIntToScalar(x) + SK_ScalarHalf, + SkIntToScalar(y) + SK_ScalarHalf, &srcPt); + + int index = SkATan2_255(SkScalarToFixed(srcPt.fY), + SkScalarToFixed(srcPt.fX)); + *dstC++ = cache[index]; + } + } +} + +void Sweep_Gradient::shadeSpan16(int x, int y, uint16_t dstC[], int count) +{ + SkMatrix::MapXYProc proc = fDstToIndexProc; + const SkMatrix& matrix = fDstToIndex; + const uint16_t* cache = this->getCache16(); + int toggle = ((x ^ y) & 1) << kCache16Bits; + SkPoint srcPt; + + if (fDstToIndexClass != kPerspective_MatrixClass) + { + proc(matrix, SkIntToScalar(x) + SK_ScalarHalf, + SkIntToScalar(y) + SK_ScalarHalf, &srcPt); + SkFixed dx, fx = SkScalarToFixed(srcPt.fX); + SkFixed dy, fy = SkScalarToFixed(srcPt.fY); + + if (fDstToIndexClass == kFixedStepInX_MatrixClass) + { + SkFixed storage[2]; + (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf, + &storage[0], &storage[1]); + dx = storage[0]; + dy = storage[1]; + } + else + { + SkASSERT(fDstToIndexClass == kLinear_MatrixClass); + dx = SkScalarToFixed(matrix.getScaleX()); + dy = SkScalarToFixed(matrix.getSkewY()); + } + + for (; count > 0; --count) + { + int index = SkATan2_255(fy, fx) >> (8 - kCache16Bits); + *dstC++ = cache[toggle + index]; + toggle ^= (1 << kCache16Bits); + fx += dx; + fy += dy; + } + } + else // perspective case + { + for (int stop = x + count; x < stop; x++) + { + proc(matrix, SkIntToScalar(x) + SK_ScalarHalf, + SkIntToScalar(y) + SK_ScalarHalf, &srcPt); + + int index = SkATan2_255(SkScalarToFixed(srcPt.fY), + SkScalarToFixed(srcPt.fX)); + index >>= (8 - kCache16Bits); + *dstC++ = cache[toggle + index]; + toggle ^= (1 << kCache16Bits); + } + } +} + +/////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////// + +SkShader* SkGradientShader::CreateLinear( const SkPoint pts[2], + const SkColor colors[], const SkScalar pos[], int colorCount, + SkShader::TileMode mode, SkUnitMapper* mapper) +{ + SkASSERT(pts && colors && colorCount >= 2); + + return SkNEW_ARGS(Linear_Gradient, (pts, colors, pos, colorCount, mode, mapper)); +} + +SkShader* SkGradientShader::CreateRadial( const SkPoint& center, SkScalar radius, + const SkColor colors[], const SkScalar pos[], int colorCount, + SkShader::TileMode mode, SkUnitMapper* mapper) +{ + SkASSERT(radius > 0 && colors && colorCount >= 2); + + return SkNEW_ARGS(Radial_Gradient, (center, radius, colors, pos, colorCount, mode, mapper)); +} + +SkShader* SkGradientShader::CreateSweep(SkScalar cx, SkScalar cy, + const SkColor colors[], + const SkScalar pos[], + int count, SkUnitMapper* mapper) +{ + SkASSERT(colors && count >= 2); + + return SkNEW_ARGS(Sweep_Gradient, (cx, cy, colors, pos, count, mapper)); +} + +static SkFlattenable::Registrar gLinearGradientReg("Linear_Gradient", + Linear_Gradient::CreateProc); + +static SkFlattenable::Registrar gRadialGradientReg("Radial_Gradient", + Radial_Gradient::CreateProc); + +static SkFlattenable::Registrar gSweepGradientReg("Sweep_Gradient", + Sweep_Gradient::CreateProc); + diff --git a/skia/effects/SkKernel33MaskFilter.cpp b/skia/effects/SkKernel33MaskFilter.cpp new file mode 100644 index 0000000..a30ea4a --- /dev/null +++ b/skia/effects/SkKernel33MaskFilter.cpp @@ -0,0 +1,122 @@ +#include "SkKernel33MaskFilter.h" +#include "SkColorPriv.h" + +SkMask::Format SkKernel33ProcMaskFilter::getFormat() +{ + return SkMask::kA8_Format; +} + +bool SkKernel33ProcMaskFilter::filterMask(SkMask* dst, const SkMask& src, const SkMatrix&, SkIPoint* margin) +{ + // margin??? + dst->fImage = NULL; + dst->fBounds = src.fBounds; + dst->fBounds.inset(-1, -1); + dst->fFormat = SkMask::kA8_Format; + + if (NULL == src.fImage) + return true; + + dst->fRowBytes = dst->fBounds.width(); + size_t size = dst->computeImageSize(); + dst->fImage = SkMask::AllocImage(size); + + const int h = src.fBounds.height(); + const int w = src.fBounds.width(); + const int srcRB = src.fRowBytes; + const uint8_t* srcImage = src.fImage; + uint8_t* dstImage = dst->fImage; + + uint8_t* srcRows[3]; + uint8_t storage[3][3]; + + srcRows[0] = storage[0]; + srcRows[1] = storage[1]; + srcRows[2] = storage[2]; + + unsigned scale = fPercent256; + + for (int y = -1; y <= h; y++) + { + uint8_t* dstRow = dstImage; + for (int x = -1; x <= w; x++) + { + memset(storage, 0, sizeof(storage)); + uint8_t* storagePtr = &storage[0][0]; + + for (int ky = y - 1; ky <= y + 1; ky++) + { + const uint8_t* srcRow = srcImage + ky * srcRB; // may be out-of-range + for (int kx = x - 1; kx <= x + 1; kx++) + { + if ((unsigned)ky < (unsigned)h && (unsigned)kx < (unsigned)w) + *storagePtr = srcRow[kx]; + storagePtr++; + } + } + int value = this->computeValue(srcRows); + + if (scale < 256) + value = SkAlphaBlend(value, srcRows[1][1], scale); + *dstRow++ = SkToU8(value); + } + dstImage += dst->fRowBytes; + } + return true; +} + +void SkKernel33ProcMaskFilter::flatten(SkFlattenableWriteBuffer& wb) +{ + this->INHERITED::flatten(wb); + wb.write32(fPercent256); +} + +SkKernel33ProcMaskFilter::SkKernel33ProcMaskFilter(SkFlattenableReadBuffer& rb) + : SkMaskFilter(rb) +{ + fPercent256 = rb.readS32(); +} + +/////////////////////////////////////////////////////////////////////////////// + +uint8_t SkKernel33MaskFilter::computeValue(uint8_t* const* srcRows) +{ + int value = 0; + + for (int i = 0; i < 3; i++) + for (int j = 0; j < 3; j++) + value += fKernel[i][j] * srcRows[i][j]; + + value >>= fShift; + + if (value < 0) + value = 0; + else if (value > 255) + value = 255; + return (uint8_t)value; +} + +void SkKernel33MaskFilter::flatten(SkFlattenableWriteBuffer& wb) +{ + this->INHERITED::flatten(wb); + wb.writeMul4(fKernel, 9 * sizeof(int)); + wb.write32(fShift); +} + +SkFlattenable::Factory SkKernel33MaskFilter::getFactory() +{ + return Create; +} + +SkFlattenable* SkKernel33MaskFilter::Create(SkFlattenableReadBuffer& rb) +{ + return new SkKernel33MaskFilter(rb); +} + +SkKernel33MaskFilter::SkKernel33MaskFilter(SkFlattenableReadBuffer& rb) + : SkKernel33ProcMaskFilter(rb) +{ + rb.read(fKernel, 9 * sizeof(int)); + fShift = rb.readS32(); +} + diff --git a/skia/effects/SkLayerRasterizer.cpp b/skia/effects/SkLayerRasterizer.cpp new file mode 100644 index 0000000..b21f885 --- /dev/null +++ b/skia/effects/SkLayerRasterizer.cpp @@ -0,0 +1,242 @@ +/* libs/graphics/effects/SkLayerRasterizer.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkLayerRasterizer.h" +#include "SkBuffer.h" +#include "SkDraw.h" +#include "SkMask.h" +#include "SkMaskFilter.h" +#include "SkPaint.h" +#include "SkPath.h" +#include "SkRegion.h" +#include "SkXfermode.h" +#include <new> + +struct SkLayerRasterizer_Rec { + SkPaint fPaint; + SkVector fOffset; +}; + +SkLayerRasterizer::SkLayerRasterizer() : fLayers(sizeof(SkLayerRasterizer_Rec)) +{ +} + +SkLayerRasterizer::~SkLayerRasterizer() +{ + SkDeque::Iter iter(fLayers); + SkLayerRasterizer_Rec* rec; + + while ((rec = (SkLayerRasterizer_Rec*)iter.next()) != NULL) + rec->fPaint.~SkPaint(); +} + +void SkLayerRasterizer::addLayer(const SkPaint& paint, SkScalar dx, SkScalar dy) +{ + SkLayerRasterizer_Rec* rec = (SkLayerRasterizer_Rec*)fLayers.push_back(); + + new (&rec->fPaint) SkPaint(paint); + rec->fOffset.set(dx, dy); +} + +static bool compute_bounds(const SkDeque& layers, const SkPath& path, const SkMatrix& matrix, + const SkIRect* clipBounds, SkIRect* bounds) +{ + SkDeque::Iter iter(layers); + SkLayerRasterizer_Rec* rec; + + bounds->set(SK_MaxS32, SK_MaxS32, SK_MinS32, SK_MinS32); + + while ((rec = (SkLayerRasterizer_Rec*)iter.next()) != NULL) + { + const SkPaint& paint = rec->fPaint; + SkPath fillPath, devPath; + const SkPath* p = &path; + + if (paint.getPathEffect() || paint.getStyle() != SkPaint::kFill_Style) + { + paint.getFillPath(path, &fillPath); + p = &fillPath; + } + if (p->isEmpty()) + continue; + + // apply the matrix and offset + { + SkMatrix m = matrix; + m.preTranslate(rec->fOffset.fX, rec->fOffset.fY); + p->transform(m, &devPath); + } + + SkMask mask; + if (!SkDraw::DrawToMask(devPath, clipBounds, paint.getMaskFilter(), &matrix, + &mask, SkMask::kJustComputeBounds_CreateMode)) + return false; + + bounds->join(mask.fBounds); + } + return true; +} + +bool SkLayerRasterizer::onRasterize(const SkPath& path, const SkMatrix& matrix, + const SkIRect* clipBounds, + SkMask* mask, SkMask::CreateMode mode) +{ + if (fLayers.empty()) + return false; + + if (SkMask::kJustRenderImage_CreateMode != mode) + { + if (!compute_bounds(fLayers, path, matrix, clipBounds, &mask->fBounds)) + return false; + } + + if (SkMask::kComputeBoundsAndRenderImage_CreateMode == mode) + { + mask->fFormat = SkMask::kA8_Format; + mask->fRowBytes = SkToU16(mask->fBounds.width()); + mask->fImage = SkMask::AllocImage(mask->computeImageSize()); + memset(mask->fImage, 0, mask->computeImageSize()); + } + + if (SkMask::kJustComputeBounds_CreateMode != mode) + { + SkBitmap device; + SkDraw draw; + SkMatrix translatedMatrix; // this translates us to our local pixels + SkMatrix drawMatrix; // this translates the path by each layer's offset + SkRegion rectClip; + + rectClip.setRect(0, 0, mask->fBounds.width(), mask->fBounds.height()); + + translatedMatrix = matrix; + translatedMatrix.postTranslate(-SkIntToScalar(mask->fBounds.fLeft), + -SkIntToScalar(mask->fBounds.fTop)); + + device.setConfig(SkBitmap::kA8_Config, mask->fBounds.width(), mask->fBounds.height(), mask->fRowBytes); + device.setPixels(mask->fImage); + + draw.fBitmap = &device; + draw.fMatrix = &drawMatrix; + draw.fClip = &rectClip; + // we set the matrixproc in the loop, as the matrix changes each time (potentially) + draw.fBounder = NULL; + + SkDeque::Iter iter(fLayers); + SkLayerRasterizer_Rec* rec; + + while ((rec = (SkLayerRasterizer_Rec*)iter.next()) != NULL) { + drawMatrix = translatedMatrix; + drawMatrix.preTranslate(rec->fOffset.fX, rec->fOffset.fY); + draw.drawPath(path, rec->fPaint); + } + } + return true; +} + +/////////// Routines for flattening ///////////////// + +static void paint_read(SkPaint* paint, SkFlattenableReadBuffer& buffer) +{ + paint->setAntiAlias(buffer.readBool()); + paint->setStyle((SkPaint::Style)buffer.readU8()); + paint->setAlpha(buffer.readU8()); + + if (paint->getStyle() != SkPaint::kFill_Style) + { + paint->setStrokeWidth(buffer.readScalar()); + paint->setStrokeMiter(buffer.readScalar()); + paint->setStrokeCap((SkPaint::Cap)buffer.readU8()); + paint->setStrokeJoin((SkPaint::Join)buffer.readU8()); + } + + paint->setMaskFilter((SkMaskFilter*)buffer.readFlattenable())->safeUnref(); + paint->setPathEffect((SkPathEffect*)buffer.readFlattenable())->safeUnref(); + paint->setRasterizer((SkRasterizer*)buffer.readFlattenable())->safeUnref(); + paint->setXfermode((SkXfermode*)buffer.readFlattenable())->safeUnref(); +} + +static void paint_write(const SkPaint& paint, SkFlattenableWriteBuffer& buffer) +{ + buffer.writeBool(paint.isAntiAlias()); + buffer.write8(paint.getStyle()); + buffer.write8(paint.getAlpha()); + + if (paint.getStyle() != SkPaint::kFill_Style) + { + buffer.writeScalar(paint.getStrokeWidth()); + buffer.writeScalar(paint.getStrokeMiter()); + buffer.write8(paint.getStrokeCap()); + buffer.write8(paint.getStrokeJoin()); + } + + buffer.writeFlattenable(paint.getMaskFilter()); + buffer.writeFlattenable(paint.getPathEffect()); + buffer.writeFlattenable(paint.getRasterizer()); + buffer.writeFlattenable(paint.getXfermode()); +} + +SkLayerRasterizer::SkLayerRasterizer(SkFlattenableReadBuffer& buffer) + : SkRasterizer(buffer), fLayers(sizeof(SkLayerRasterizer_Rec)) +{ + int count = buffer.readS32(); + + for (int i = 0; i < count; i++) + { + SkLayerRasterizer_Rec* rec = (SkLayerRasterizer_Rec*)fLayers.push_back(); + +#if 0 + new (&rec->fPaint) SkPaint(buffer); +#else + new (&rec->fPaint) SkPaint; + paint_read(&rec->fPaint, buffer); +#endif + rec->fOffset.fX = buffer.readScalar(); + rec->fOffset.fY = buffer.readScalar(); + } +} + +void SkLayerRasterizer::flatten(SkFlattenableWriteBuffer& buffer) +{ + this->INHERITED::flatten(buffer); + + buffer.write32(fLayers.count()); + + SkDeque::Iter iter(fLayers); + const SkLayerRasterizer_Rec* rec; + + while ((rec = (const SkLayerRasterizer_Rec*)iter.next()) != NULL) + { +#if 0 + rec->fPaint.flatten(buffer); +#else + paint_write(rec->fPaint, buffer); +#endif + buffer.writeScalar(rec->fOffset.fX); + buffer.writeScalar(rec->fOffset.fY); + } +} + +SkFlattenable* SkLayerRasterizer::CreateProc(SkFlattenableReadBuffer& buffer) +{ + return SkNEW_ARGS(SkLayerRasterizer, (buffer)); +} + +SkFlattenable::Factory SkLayerRasterizer::getFactory() +{ + return CreateProc; +} + diff --git a/skia/effects/SkNinePatch.cpp b/skia/effects/SkNinePatch.cpp new file mode 100644 index 0000000..267c39e --- /dev/null +++ b/skia/effects/SkNinePatch.cpp @@ -0,0 +1,240 @@ +/* +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkNinePatch.h" +#include "SkCanvas.h" +#include "SkShader.h" + +static const uint16_t g3x3Indices[] = { + 0, 5, 1, 0, 4, 5, + 1, 6, 2, 1, 5, 6, + 2, 7, 3, 2, 6, 7, + + 4, 9, 5, 4, 8, 9, + 5, 10, 6, 5, 9, 10, + 6, 11, 7, 6, 10, 11, + + 8, 13, 9, 8, 12, 13, + 9, 14, 10, 9, 13, 14, + 10, 15, 11, 10, 14, 15 +}; + +static int fillIndices(uint16_t indices[], int xCount, int yCount) { + uint16_t* startIndices = indices; + + int n = 0; + for (int y = 0; y < yCount; y++) { + for (int x = 0; x < xCount; x++) { + *indices++ = n; + *indices++ = n + xCount + 2; + *indices++ = n + 1; + + *indices++ = n; + *indices++ = n + xCount + 1; + *indices++ = n + xCount + 2; + + n += 1; + } + n += 1; + } + return indices - startIndices; +} + +static void fillRow(SkPoint verts[], SkPoint texs[], + const SkScalar vy, const SkScalar ty, + const SkRect& bounds, const int32_t xDivs[], int numXDivs, + const SkScalar stretchX, int width) { + SkScalar vx = bounds.fLeft; + verts->set(vx, vy); verts++; + texs->set(0, ty); texs++; + for (int x = 0; x < numXDivs; x++) { + SkScalar tx = SkIntToScalar(xDivs[x]); + if (x & 1) { + vx += stretchX; + } else { + vx += tx; + } + verts->set(vx, vy); verts++; + texs->set(tx, ty); texs++; + } + verts->set(bounds.fRight, vy); verts++; + texs->set(SkIntToScalar(width), ty); texs++; +} + +struct Mesh { + const SkPoint* fVerts; + const SkPoint* fTexs; + const SkColor* fColors; + const uint16_t* fIndices; +}; + +void SkNinePatch::DrawMesh(SkCanvas* canvas, const SkRect& bounds, + const SkBitmap& bitmap, + const int32_t xDivs[], int numXDivs, + const int32_t yDivs[], int numYDivs, + const SkPaint* paint) { + if (bounds.isEmpty() || bitmap.width() == 0 || bitmap.height() == 0) { + return; + } + + // should try a quick-reject test before calling lockPixels <reed> + SkAutoLockPixels alp(bitmap); + // after the lock, it is valid to check getPixels() + if (bitmap.getPixels() == NULL) { + return; + } + + // check for degenerate divs (just an optimization, not required) + { + int i; + int zeros = 0; + for (i = 0; i < numYDivs && yDivs[i] == 0; i++) { + zeros += 1; + } + numYDivs -= zeros; + yDivs += zeros; + for (i = numYDivs - 1; i >= 0 && yDivs[i] == bitmap.height(); --i) { + numYDivs -= 1; + } + } + + Mesh mesh; + + const int numXStretch = (numXDivs + 1) >> 1; + const int numYStretch = (numYDivs + 1) >> 1; + + if (numXStretch < 1 && numYStretch < 1) { + BITMAP_RECT: +// SkDebugf("------ drawasamesh revert to bitmaprect\n"); + canvas->drawBitmapRect(bitmap, NULL, bounds, paint); + return; + } + + if (false) { + int i; + for (i = 0; i < numXDivs; i++) { + SkDebugf("--- xdivs[%d] %d\n", i, xDivs[i]); + } + for (i = 0; i < numYDivs; i++) { + SkDebugf("--- ydivs[%d] %d\n", i, yDivs[i]); + } + } + + SkScalar stretchX = 0, stretchY = 0; + + if (numXStretch > 0) { + int stretchSize = 0; + for (int i = 1; i < numXDivs; i += 2) { + stretchSize += xDivs[i] - xDivs[i-1]; + } + int fixed = bitmap.width() - stretchSize; + stretchX = (bounds.width() - SkIntToScalar(fixed)) / numXStretch; + if (stretchX < 0) { + goto BITMAP_RECT; + } + } + + if (numYStretch > 0) { + int stretchSize = 0; + for (int i = 1; i < numYDivs; i += 2) { + stretchSize += yDivs[i] - yDivs[i-1]; + } + int fixed = bitmap.height() - stretchSize; + stretchY = (bounds.height() - SkIntToScalar(fixed)) / numYStretch; + if (stretchY < 0) { + goto BITMAP_RECT; + } + } + +#if 0 + SkDebugf("---- drawasamesh [%d %d] -> [%g %g] <%d %d> (%g %g)\n", + bitmap.width(), bitmap.height(), + SkScalarToFloat(bounds.width()), SkScalarToFloat(bounds.height()), + numXDivs + 1, numYDivs + 1, + SkScalarToFloat(stretchX), SkScalarToFloat(stretchY)); +#endif + + const int vCount = (numXDivs + 2) * (numYDivs + 2); + // number of celss * 2 (tris per cell) * 3 (verts per tri) + const int indexCount = (numXDivs + 1) * (numYDivs + 1) * 2 * 3; + // allocate 2 times, one for verts, one for texs, plus indices + SkAutoMalloc storage(vCount * sizeof(SkPoint) * 2 + + indexCount * sizeof(uint16_t)); + SkPoint* verts = (SkPoint*)storage.get(); + SkPoint* texs = verts + vCount; + uint16_t* indices = (uint16_t*)(texs + vCount); + + mesh.fVerts = verts; + mesh.fTexs = texs; + mesh.fColors = NULL; + mesh.fIndices = NULL; + + // we use <= for YDivs, since the prebuild indices work for 3x2 and 3x1 too + if (numXDivs == 2 && numYDivs <= 2) { + mesh.fIndices = g3x3Indices; + } else { + int n = fillIndices(indices, numXDivs + 1, numYDivs + 1); + SkASSERT(n == indexCount); + mesh.fIndices = indices; + } + + SkScalar vy = bounds.fTop; + fillRow(verts, texs, vy, 0, bounds, xDivs, numXDivs, + stretchX, bitmap.width()); + verts += numXDivs + 2; + texs += numXDivs + 2; + for (int y = 0; y < numYDivs; y++) { + const SkScalar ty = SkIntToScalar(yDivs[y]); + if (y & 1) { + vy += stretchY; + } else { + vy += ty; + } + fillRow(verts, texs, vy, ty, bounds, xDivs, numXDivs, + stretchX, bitmap.width()); + verts += numXDivs + 2; + texs += numXDivs + 2; + } + fillRow(verts, texs, bounds.fBottom, SkIntToScalar(bitmap.height()), + bounds, xDivs, numXDivs, stretchX, bitmap.width()); + + SkShader* shader = SkShader::CreateBitmapShader(bitmap, + SkShader::kClamp_TileMode, + SkShader::kClamp_TileMode); + SkPaint p; + if (paint) { + p = *paint; + } + p.setShader(shader)->unref(); + canvas->drawVertices(SkCanvas::kTriangles_VertexMode, vCount, + mesh.fVerts, mesh.fTexs, mesh.fColors, NULL, + mesh.fIndices, indexCount, p); +} + +void SkNinePatch::DrawNine(SkCanvas* canvas, const SkRect& bounds, + const SkBitmap& bitmap, const SkIRect& margins, + const SkPaint* paint) { + int32_t xDivs[2]; + int32_t yDivs[2]; + + xDivs[0] = margins.fLeft; + xDivs[1] = bitmap.width() - margins.fRight; + yDivs[0] = margins.fTop; + yDivs[1] = bitmap.height() - margins.fBottom; + + SkNinePatch::DrawMesh(canvas, bounds, bitmap, xDivs, 2, yDivs, 2, paint); +} + diff --git a/skia/effects/SkPaintFlagsDrawFilter.cpp b/skia/effects/SkPaintFlagsDrawFilter.cpp new file mode 100644 index 0000000..ed2df88 --- /dev/null +++ b/skia/effects/SkPaintFlagsDrawFilter.cpp @@ -0,0 +1,22 @@ +#include "SkPaintFlagsDrawFilter.h" +#include "SkPaint.h" + +SkPaintFlagsDrawFilter::SkPaintFlagsDrawFilter(uint32_t clearFlags, + uint32_t setFlags) +{ + fClearFlags = SkToU16(clearFlags & SkPaint::kAllFlags); + fSetFlags = SkToU16(setFlags & SkPaint::kAllFlags); +} + +bool SkPaintFlagsDrawFilter::filter(SkCanvas*, SkPaint* paint, Type) +{ + fPrevFlags = paint->getFlags(); + paint->setFlags((fPrevFlags & ~fClearFlags) | fSetFlags); + return true; +} + +void SkPaintFlagsDrawFilter::restore(SkCanvas*, SkPaint* paint, Type) +{ + paint->setFlags(fPrevFlags); +} + diff --git a/skia/effects/SkPixelXorXfermode.cpp b/skia/effects/SkPixelXorXfermode.cpp new file mode 100644 index 0000000..a5599e2 --- /dev/null +++ b/skia/effects/SkPixelXorXfermode.cpp @@ -0,0 +1,36 @@ +#include "SkPixelXorXfermode.h" +#include "SkColorPriv.h" + +// we always return an opaque color, 'cause I don't know what to do with +// the alpha-component and still return a valid premultiplied color. +SkPMColor SkPixelXorXfermode::xferColor(SkPMColor src, SkPMColor dst) +{ + SkPMColor res = src ^ dst ^ fOpColor; + res |= (SK_A32_MASK << SK_A32_SHIFT); // force it to be opaque + return res; +} + +void SkPixelXorXfermode::flatten(SkFlattenableWriteBuffer& wb) +{ + this->INHERITED::flatten(wb); + wb.write32(fOpColor); +} + +SkPixelXorXfermode::SkPixelXorXfermode(SkFlattenableReadBuffer& rb) + : SkXfermode(rb) +{ + fOpColor = rb.readU32(); +} + +SkFlattenable::Factory SkPixelXorXfermode::getFactory() +{ + return Create; +} + +SkFlattenable* SkPixelXorXfermode::Create(SkFlattenableReadBuffer& rb) +{ + return SkNEW_ARGS(SkPixelXorXfermode, (rb)); +} + + + diff --git a/skia/effects/SkRadialGradient_Table.h b/skia/effects/SkRadialGradient_Table.h new file mode 100644 index 0000000..ed9712f --- /dev/null +++ b/skia/effects/SkRadialGradient_Table.h @@ -0,0 +1,147 @@ +/* libs/graphics/effects/SkRadialGradient_Table.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +static const uint8_t gSqrt8Table[] = { + 0x00, 0x05, 0x08, 0x09, 0x0B, 0x0C, 0x0D, 0x0E, 0x10, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x15, + 0x16, 0x17, 0x18, 0x18, 0x19, 0x19, 0x1A, 0x1B, 0x1B, 0x1C, 0x1C, 0x1D, 0x1D, 0x1E, 0x1E, 0x1F, + 0x20, 0x20, 0x20, 0x21, 0x21, 0x22, 0x22, 0x23, 0x23, 0x24, 0x24, 0x25, 0x25, 0x25, 0x26, 0x26, + 0x27, 0x27, 0x28, 0x28, 0x28, 0x29, 0x29, 0x29, 0x2A, 0x2A, 0x2B, 0x2B, 0x2B, 0x2C, 0x2C, 0x2C, + 0x2D, 0x2D, 0x2D, 0x2E, 0x2E, 0x2E, 0x2F, 0x2F, 0x30, 0x30, 0x30, 0x30, 0x31, 0x31, 0x31, 0x32, + 0x32, 0x32, 0x33, 0x33, 0x33, 0x34, 0x34, 0x34, 0x35, 0x35, 0x35, 0x35, 0x36, 0x36, 0x36, 0x37, + 0x37, 0x37, 0x38, 0x38, 0x38, 0x38, 0x39, 0x39, 0x39, 0x39, 0x3A, 0x3A, 0x3A, 0x3B, 0x3B, 0x3B, + 0x3B, 0x3C, 0x3C, 0x3C, 0x3C, 0x3D, 0x3D, 0x3D, 0x3D, 0x3E, 0x3E, 0x3E, 0x3E, 0x3F, 0x3F, 0x3F, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x41, 0x41, 0x41, 0x41, 0x42, 0x42, 0x42, 0x42, 0x43, 0x43, 0x43, + 0x43, 0x44, 0x44, 0x44, 0x44, 0x45, 0x45, 0x45, 0x45, 0x45, 0x46, 0x46, 0x46, 0x46, 0x47, 0x47, + 0x47, 0x47, 0x48, 0x48, 0x48, 0x48, 0x48, 0x49, 0x49, 0x49, 0x49, 0x49, 0x4A, 0x4A, 0x4A, 0x4A, + 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4C, 0x4C, 0x4C, 0x4C, 0x4C, 0x4D, 0x4D, 0x4D, 0x4D, 0x4D, 0x4E, + 0x4E, 0x4E, 0x4E, 0x4E, 0x4F, 0x4F, 0x4F, 0x4F, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x51, 0x51, + 0x51, 0x51, 0x51, 0x52, 0x52, 0x52, 0x52, 0x52, 0x53, 0x53, 0x53, 0x53, 0x53, 0x54, 0x54, 0x54, + 0x54, 0x54, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x56, 0x56, 0x56, 0x56, 0x56, 0x57, 0x57, 0x57, + 0x57, 0x57, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x5A, 0x5A, + 0x5A, 0x5A, 0x5A, 0x5B, 0x5B, 0x5B, 0x5B, 0x5B, 0x5B, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5D, + 0x5D, 0x5D, 0x5D, 0x5D, 0x5D, 0x5E, 0x5E, 0x5E, 0x5E, 0x5E, 0x5E, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, + 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x62, 0x62, 0x62, + 0x62, 0x62, 0x62, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x65, + 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x67, 0x67, 0x67, 0x67, + 0x67, 0x67, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, + 0x6A, 0x6A, 0x6A, 0x6A, 0x6A, 0x6A, 0x6B, 0x6B, 0x6B, 0x6B, 0x6B, 0x6B, 0x6B, 0x6C, 0x6C, 0x6C, + 0x6C, 0x6C, 0x6C, 0x6C, 0x6D, 0x6D, 0x6D, 0x6D, 0x6D, 0x6D, 0x6D, 0x6E, 0x6E, 0x6E, 0x6E, 0x6E, + 0x6E, 0x6E, 0x6F, 0x6F, 0x6F, 0x6F, 0x6F, 0x6F, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, + 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x73, 0x73, + 0x73, 0x73, 0x73, 0x73, 0x73, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x75, + 0x75, 0x75, 0x75, 0x75, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x77, 0x77, 0x77, 0x77, 0x77, + 0x77, 0x77, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, + 0x79, 0x79, 0x7A, 0x7A, 0x7A, 0x7A, 0x7A, 0x7A, 0x7A, 0x7B, 0x7B, 0x7B, 0x7B, 0x7B, 0x7B, 0x7B, + 0x7B, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C, 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, + 0x7D, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8B, 0x8B, 0x8B, 0x8B, + 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8D, 0x8D, + 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, + 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9B, + 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, + 0x9C, 0x9C, 0x9C, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9E, 0x9E, 0x9E, + 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, + 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, + 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA3, + 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, + 0xA4, 0xA4, 0xA4, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA6, 0xA6, + 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, + 0xA7, 0xA7, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA9, 0xA9, 0xA9, + 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, + 0xAA, 0xAA, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAC, 0xAC, 0xAC, + 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, + 0xAD, 0xAD, 0xAD, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAF, 0xAF, + 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, + 0xB0, 0xB0, 0xB0, 0xB0, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB2, + 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, + 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, + 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB6, 0xB6, 0xB6, 0xB6, + 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, + 0xB7, 0xB7, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB9, 0xB9, + 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, + 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, + 0xBB, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBD, 0xBD, 0xBD, + 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, + 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, + 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC1, 0xC1, 0xC1, + 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, + 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, + 0xC3, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC5, 0xC5, 0xC5, + 0xC5, 0xC5, 0xC5, 0xC5, 0xC5, 0xC5, 0xC5, 0xC5, 0xC5, 0xC5, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, + 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC7, 0xC7, 0xC7, 0xC7, 0xC7, 0xC7, 0xC7, 0xC7, 0xC7, 0xC7, + 0xC7, 0xC7, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC9, + 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xCA, 0xCA, 0xCA, 0xCA, + 0xCA, 0xCA, 0xCA, 0xCA, 0xCA, 0xCA, 0xCA, 0xCA, 0xCB, 0xCB, 0xCB, 0xCB, 0xCB, 0xCB, 0xCB, 0xCB, + 0xCB, 0xCB, 0xCB, 0xCB, 0xCB, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, + 0xCC, 0xCC, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCE, + 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCF, 0xCF, 0xCF, 0xCF, + 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0, + 0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xD1, 0xD1, 0xD1, 0xD1, 0xD1, 0xD1, 0xD1, 0xD1, 0xD1, 0xD1, + 0xD1, 0xD1, 0xD1, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, + 0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD4, 0xD4, 0xD4, + 0xD4, 0xD4, 0xD4, 0xD4, 0xD4, 0xD4, 0xD4, 0xD4, 0xD4, 0xD4, 0xD5, 0xD5, 0xD5, 0xD5, 0xD5, 0xD5, + 0xD5, 0xD5, 0xD5, 0xD5, 0xD5, 0xD5, 0xD5, 0xD5, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, + 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, + 0xD7, 0xD7, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, + 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xDA, 0xDA, + 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, + 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, + 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, + 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDE, 0xDE, 0xDE, 0xDE, 0xDE, 0xDE, 0xDE, 0xDE, 0xDE, 0xDE, 0xDE, + 0xDE, 0xDE, 0xDE, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, + 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE1, + 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE2, 0xE2, 0xE2, + 0xE2, 0xE2, 0xE2, 0xE2, 0xE2, 0xE2, 0xE2, 0xE2, 0xE2, 0xE2, 0xE2, 0xE3, 0xE3, 0xE3, 0xE3, 0xE3, + 0xE3, 0xE3, 0xE3, 0xE3, 0xE3, 0xE3, 0xE3, 0xE3, 0xE3, 0xE4, 0xE4, 0xE4, 0xE4, 0xE4, 0xE4, 0xE4, + 0xE4, 0xE4, 0xE4, 0xE4, 0xE4, 0xE4, 0xE4, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, + 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, + 0xE6, 0xE6, 0xE6, 0xE6, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, + 0xE7, 0xE7, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, + 0xE8, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, + 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEB, 0xEB, + 0xEB, 0xEB, 0xEB, 0xEB, 0xEB, 0xEB, 0xEB, 0xEB, 0xEB, 0xEB, 0xEB, 0xEB, 0xEB, 0xEC, 0xEC, 0xEC, + 0xEC, 0xEC, 0xEC, 0xEC, 0xEC, 0xEC, 0xEC, 0xEC, 0xEC, 0xEC, 0xEC, 0xEC, 0xED, 0xED, 0xED, 0xED, + 0xED, 0xED, 0xED, 0xED, 0xED, 0xED, 0xED, 0xED, 0xED, 0xED, 0xED, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, + 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, + 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1, + 0xF1, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, + 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, + 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 0xF4, 0xF4, 0xF4, 0xF4, 0xF4, 0xF4, 0xF4, 0xF4, 0xF4, 0xF4, 0xF4, + 0xF4, 0xF4, 0xF4, 0xF4, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, + 0xF5, 0xF5, 0xF5, 0xF5, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, + 0xF6, 0xF6, 0xF6, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, + 0xF7, 0xF7, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, + 0xF8, 0xF8, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, + 0xF9, 0xF9, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, + 0xFA, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, + 0xFB, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, + 0xFC, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, + 0xFD, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, + 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; diff --git a/skia/effects/SkShaderExtras.cpp b/skia/effects/SkShaderExtras.cpp new file mode 100644 index 0000000..6bfe517 --- /dev/null +++ b/skia/effects/SkShaderExtras.cpp @@ -0,0 +1,174 @@ +/* libs/graphics/effects/SkShaderExtras.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkShaderExtras.h" +#include "SkColorFilter.h" +#include "SkColorPriv.h" +#include "SkXfermode.h" + +////////////////////////////////////////////////////////////////////////////////////// + +SkComposeShader::SkComposeShader(SkShader* sA, SkShader* sB, SkXfermode* mode) +{ + fShaderA = sA; sA->ref(); + fShaderB = sB; sB->ref(); + // mode may be null + fMode = mode; mode->safeRef(); +} + +SkComposeShader::SkComposeShader(SkFlattenableReadBuffer& buffer) : + INHERITED(buffer) +{ + fShaderA = static_cast<SkShader*>(buffer.readFlattenable()); + fShaderB = static_cast<SkShader*>(buffer.readFlattenable()); + fMode = static_cast<SkXfermode*>(buffer.readFlattenable()); +} + +SkComposeShader::~SkComposeShader() +{ + fMode->safeUnref(); // may be null + fShaderB->unref(); + fShaderA->unref(); +} + +void SkComposeShader::beginSession() +{ + this->INHERITED::beginSession(); + fShaderA->beginSession(); + fShaderB->beginSession(); +} + +void SkComposeShader::endSession() +{ + fShaderA->endSession(); + fShaderB->endSession(); + this->INHERITED::endSession(); +} + +class SkAutoAlphaRestore { +public: + SkAutoAlphaRestore(SkPaint* paint, uint8_t newAlpha) + { + fAlpha = paint->getAlpha(); + fPaint = paint; + paint->setAlpha(newAlpha); + } + ~SkAutoAlphaRestore() + { + fPaint->setAlpha(fAlpha); + } +private: + SkPaint* fPaint; + uint8_t fAlpha; +}; + +void SkComposeShader::flatten(SkFlattenableWriteBuffer& buffer) +{ + this->INHERITED::flatten(buffer); + buffer.writeFlattenable(fShaderA); + buffer.writeFlattenable(fShaderB); + buffer.writeFlattenable(fMode); +} + +/* We call setContext on our two worker shaders. However, we + always let them see opaque alpha, and if the paint really + is translucent, then we apply that after the fact. +*/ +bool SkComposeShader::setContext(const SkBitmap& device, + const SkPaint& paint, + const SkMatrix& matrix) +{ + if (!this->INHERITED::setContext(device, paint, matrix)) + return false; + + // we preconcat our localMatrix (if any) with the device matrix + // before calling our sub-shaders + + SkMatrix tmpM; + + (void)this->getLocalMatrix(&tmpM); + tmpM.setConcat(matrix, tmpM); + + SkAutoAlphaRestore restore(const_cast<SkPaint*>(&paint), 0xFF); + + return fShaderA->setContext(device, paint, tmpM) && + fShaderB->setContext(device, paint, tmpM); +} + +// larger is better (fewer times we have to loop), but we shouldn't +// take up too much stack-space (each element is 4 bytes) +#define TMP_COLOR_COUNT 64 + +void SkComposeShader::shadeSpan(int x, int y, SkPMColor result[], int count) +{ + SkShader* shaderA = fShaderA; + SkShader* shaderB = fShaderB; + SkXfermode* mode = fMode; + unsigned scale = SkAlpha255To256(this->getPaintAlpha()); + + SkPMColor tmp[TMP_COLOR_COUNT]; + + if (NULL == mode) // implied SRC_OVER + { + do { + int n = count; + if (n > TMP_COLOR_COUNT) + n = TMP_COLOR_COUNT; + + shaderA->shadeSpan(x, y, result, n); + shaderB->shadeSpan(x, y, tmp, n); + + if (256 == scale) + { + for (int i = 0; i < n; i++) + result[i] = SkPMSrcOver(tmp[i], result[i]); + } + else + { + for (int i = 0; i < n; i++) + result[i] = SkAlphaMulQ(SkPMSrcOver(tmp[i], result[i]), scale); + } + + result += n; + x += n; + count -= n; + } while (count > 0); + } + else // use mode for the composition + { + do { + int n = count; + if (n > TMP_COLOR_COUNT) + n = TMP_COLOR_COUNT; + + shaderA->shadeSpan(x, y, result, n); + shaderB->shadeSpan(x, y, tmp, n); + mode->xfer32(result, tmp, n, NULL); + + if (256 == scale) + { + for (int i = 0; i < n; i++) + result[i] = SkAlphaMulQ(result[i], scale); + } + + result += n; + x += n; + count -= n; + } while (count > 0); + } +} + diff --git a/skia/effects/SkTransparentShader.cpp b/skia/effects/SkTransparentShader.cpp new file mode 100644 index 0000000..0c08543 --- /dev/null +++ b/skia/effects/SkTransparentShader.cpp @@ -0,0 +1,144 @@ +/* libs/graphics/effects/SkTransparentShader.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkTransparentShader.h" +#include "SkColorPriv.h" + +bool SkTransparentShader::setContext(const SkBitmap& device, + const SkPaint& paint, + const SkMatrix& matrix) +{ + fDevice = &device; + fAlpha = paint.getAlpha(); + + return this->INHERITED::setContext(device, paint, matrix); +} + +uint32_t SkTransparentShader::getFlags() +{ + uint32_t flags = this->INHERITED::getFlags(); + + switch (fDevice->getConfig()) { + case SkBitmap::kRGB_565_Config: + flags |= kHasSpan16_Flag; + if (fAlpha == 255) + flags |= kOpaqueAlpha_Flag; + break; + case SkBitmap::kARGB_8888_Config: + case SkBitmap::kARGB_4444_Config: + if (fAlpha == 255 && fDevice->isOpaque()) + flags |= kOpaqueAlpha_Flag; + break; + default: + break; + } + return flags; +} + +void SkTransparentShader::shadeSpan(int x, int y, SkPMColor span[], int count) +{ + unsigned scale = SkAlpha255To256(fAlpha); + + switch (fDevice->getConfig()) { + case SkBitmap::kARGB_8888_Config: + if (scale == 256) + memcpy(span, fDevice->getAddr32(x, y), count * sizeof(SkPMColor)); + else + { + const SkPMColor* src = fDevice->getAddr32(x, y); + for (int i = count - 1; i >= 0; --i) + span[i] = SkAlphaMulQ(src[i], scale); + } + break; + case SkBitmap::kRGB_565_Config: + { + const uint16_t* src = fDevice->getAddr16(x, y); + if (scale == 256) + { + for (int i = count - 1; i >= 0; --i) + span[i] = SkPixel16ToPixel32(src[i]); + } + else + { + unsigned alpha = fAlpha; + for (int i = count - 1; i >= 0; --i) + { + uint16_t c = src[i]; + unsigned r = SkPacked16ToR32(c); + unsigned g = SkPacked16ToG32(c); + unsigned b = SkPacked16ToB32(c); + + span[i] = SkPackARGB32( alpha, + SkAlphaMul(r, scale), + SkAlphaMul(g, scale), + SkAlphaMul(b, scale)); + } + } + } + break; + case SkBitmap::kARGB_4444_Config: + { + const uint16_t* src = fDevice->getAddr16(x, y); + if (scale == 256) + { + for (int i = count - 1; i >= 0; --i) + span[i] = SkPixel4444ToPixel32(src[i]); + } + else + { + unsigned scale16 = scale >> 4; + for (int i = count - 1; i >= 0; --i) + { + uint32_t c = SkExpand_4444(src[i]) * scale16; + span[i] = SkCompact_8888(c); + } + } + } + break; + case SkBitmap::kIndex8_Config: + SkASSERT(!"index8 not supported as a destination device"); + break; + case SkBitmap::kA8_Config: + { + const uint8_t* src = fDevice->getAddr8(x, y); + if (scale == 256) + { + for (int i = count - 1; i >= 0; --i) + span[i] = SkPackARGB32(src[i], 0, 0, 0); + } + else + { + for (int i = count - 1; i >= 0; --i) + span[i] = SkPackARGB32(SkAlphaMul(src[i], scale), 0, 0, 0); + } + } + break; + case SkBitmap::kA1_Config: + SkASSERT(!"kA1_Config umimplemented at this time"); + break; + default: // to avoid warnings + break; + } +} + +void SkTransparentShader::shadeSpan16(int x, int y, uint16_t span[], int count) +{ + SkASSERT(fDevice->getConfig() == SkBitmap::kRGB_565_Config); + + memcpy(span, fDevice->getAddr16(x, y), count << 1); +} + diff --git a/skia/effects/SkUnitMappers.cpp b/skia/effects/SkUnitMappers.cpp new file mode 100644 index 0000000..0363a2b --- /dev/null +++ b/skia/effects/SkUnitMappers.cpp @@ -0,0 +1,80 @@ +#include "SkUnitMappers.h" + +SkDiscreteMapper::SkDiscreteMapper(int segments) +{ + if (segments < 2) + { + fSegments = 0; + fScale = 0; + } + else + { + if (segments > 0xFFFF) + segments = 0xFFFF; + fSegments = segments; + fScale = SK_Fract1 / (segments - 1); + } +} + +uint16_t SkDiscreteMapper::mapUnit16(uint16_t input) +{ + SkFixed x = input * fSegments >> 16; + x = x * fScale >> 14; + x += x << 15 >> 31; // map 0x10000 to 0xFFFF + return SkToU16(x); +} + +SkDiscreteMapper::SkDiscreteMapper(SkFlattenableReadBuffer& rb) + : SkUnitMapper(rb) +{ + fSegments = rb.readU32(); + fScale = rb.readU32(); +} + +SkFlattenable::Factory SkDiscreteMapper::getFactory() +{ + return Create; +} + +SkFlattenable* SkDiscreteMapper::Create(SkFlattenableReadBuffer& rb) +{ + return SkNEW_ARGS(SkDiscreteMapper, (rb)); +} + +void SkDiscreteMapper::flatten(SkFlattenableWriteBuffer& wb) +{ + this->INHERITED::flatten(wb); + + wb.write32(fSegments); + wb.write32(fScale); +} + +/////////////////////////////////////////////////////////////////////////////// + +uint16_t SkCosineMapper::mapUnit16(uint16_t input) +{ + /* we want to call cosine(input * pi/2) treating input as [0...1) + however, the straight multitply would overflow 32bits since input is + 16bits and pi/2 is 17bits, so we shift down our pi const before we mul + */ + SkFixed rads = (unsigned)(input * (SK_FixedPI >> 2)) >> 15; + SkFixed x = SkFixedCos(rads); + x += x << 15 >> 31; // map 0x10000 to 0xFFFF + return SkToU16(x); +} + +SkCosineMapper::SkCosineMapper(SkFlattenableReadBuffer& rb) + : SkUnitMapper(rb) +{ +} + +SkFlattenable::Factory SkCosineMapper::getFactory() +{ + return Create; +} + +SkFlattenable* SkCosineMapper::Create(SkFlattenableReadBuffer& rb) +{ + return SkNEW_ARGS(SkCosineMapper, (rb)); +} + diff --git a/skia/fix_for_1186198.diff b/skia/fix_for_1186198.diff new file mode 100644 index 0000000..6158f59 --- /dev/null +++ b/skia/fix_for_1186198.diff @@ -0,0 +1,38 @@ +Index: sgl/SkEdge.cpp
+===================================================================
+--- sgl/SkEdge.cpp (revision 42965)
++++ sgl/SkEdge.cpp (working copy)
+@@ -17,6 +17,7 @@
+ + #include "SkEdge.h" + #include "SkFDot6.h" ++#include <limits> + + /* + In setLine, setQuadratic, setCubic, the first thing we do is to convert +@@ -76,8 +77,23 @@
+ + fX = SkFDot6ToFixed(x0 + SkFixedMul(slope, (32 - y0) & 63)); // + SK_Fixed1/2 + fDX = slope; +- fFirstY = SkToS16(top); +- fLastY = SkToS16(bot - 1); ++ fFirstY = (int16_t)(top); // inlined skToS16() ++ if (top != (long)fFirstY) { ++ if (fFirstY < top) { ++ fFirstY = std::numeric_limits<int16_t>::max(); ++ } else { ++ fFirstY = std::numeric_limits<int16_t>::min(); ++ } ++ fX -= fDX * (top - (long)fFirstY); ++ } ++ fLastY = (int16_t)(bot - 1); // inlined SkToS16() ++ if (bot-1 != (long)fLastY) { ++ if (fLastY < bot-1) { ++ fLastY = std::numeric_limits<int16_t>::max(); ++ } else { ++ fLastY = std::numeric_limits<int16_t>::min(); ++ } ++ } + fCurveCount = 0; + fWinding = SkToS8(winding); + fCurveShift = 0; diff --git a/skia/gl/SkGL.cpp b/skia/gl/SkGL.cpp new file mode 100644 index 0000000..e4effba --- /dev/null +++ b/skia/gl/SkGL.cpp @@ -0,0 +1,453 @@ +#include "SkGL.h" +#include "SkColorPriv.h" +#include "SkGeometry.h" +#include "SkPaint.h" +#include "SkPath.h" +#include "SkTemplates.h" +#include "SkXfermode.h" + +//#define TRACE_TEXTURE_CREATION + +/////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_GL_HAS_COLOR4UB +static inline void gl_pmcolor(U8CPU r, U8CPU g, U8CPU b, U8CPU a) { + glColor4ub(r, g, b, a); +} + +void SkGL::SetAlpha(U8CPU alpha) { + glColor4ub(alpha, alpha, alpha, alpha); +} +#else +static inline SkFixed byte2fixed(U8CPU value) { + return (value + (value >> 7)) << 8; +} + +static inline void gl_pmcolor(U8CPU r, U8CPU g, U8CPU b, U8CPU a) { + glColor4x(byte2fixed(r), byte2fixed(g), byte2fixed(b), byte2fixed(a)); +} + +void SkGL::SetAlpha(U8CPU alpha) { + SkFixed fa = byte2fixed(alpha); + glColor4x(fa, fa, fa, fa); +} +#endif + +void SkGL::SetColor(SkColor c) { + SkPMColor pm = SkPreMultiplyColor(c); + gl_pmcolor(SkGetPackedR32(pm), + SkGetPackedG32(pm), + SkGetPackedB32(pm), + SkGetPackedA32(pm)); +} + +static const GLenum gXfermodeCoeff2Blend[] = { + GL_ZERO, + GL_ONE, + GL_SRC_COLOR, + GL_ONE_MINUS_SRC_COLOR, + GL_DST_COLOR, + GL_ONE_MINUS_DST_COLOR, + GL_SRC_ALPHA, + GL_ONE_MINUS_SRC_ALPHA, + GL_DST_ALPHA, + GL_ONE_MINUS_DST_ALPHA, +}; + +void SkGL::SetPaint(const SkPaint& paint, bool isPremul, bool justAlpha) { + if (justAlpha) { + SkGL::SetAlpha(paint.getAlpha()); + } else { + SkGL::SetColor(paint.getColor()); + } + + GLenum sm = GL_ONE; + GLenum dm = GL_ONE_MINUS_SRC_ALPHA; + + SkXfermode* mode = paint.getXfermode(); + SkXfermode::Coeff sc, dc; + if (mode && mode->asCoeff(&sc, &dc)) { + sm = gXfermodeCoeff2Blend[sc]; + dm = gXfermodeCoeff2Blend[dc]; + } + + // hack for text, which is not-premul (afaik) + if (!isPremul) { + if (GL_ONE == sm) { + sm = GL_SRC_ALPHA; + } + } + + glEnable(GL_BLEND); + glBlendFunc(sm, dm); + + if (paint.isDither()) { + glEnable(GL_DITHER); + } else { + glDisable(GL_DITHER); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void SkGL::DumpError(const char caller[]) { + GLenum err = glGetError(); + if (err) { + SkDebugf("---- glGetError(%s) %d\n", caller, err); + } +} + +void SkGL::SetRGBA(uint8_t rgba[], const SkColor src[], int count) { + for (int i = 0; i < count; i++) { + SkPMColor c = SkPreMultiplyColor(*src++); + *rgba++ = SkGetPackedR32(c); + *rgba++ = SkGetPackedG32(c); + *rgba++ = SkGetPackedB32(c); + *rgba++ = SkGetPackedA32(c); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void SkGL::Scissor(const SkIRect& r, int viewportHeight) { + glScissor(r.fLeft, viewportHeight - r.fBottom, r.width(), r.height()); +} + +/////////////////////////////////////////////////////////////////////////////// + +void SkGL::Ortho(float left, float right, float bottom, float top, + float near, float far) { + + float mat[16]; + + bzero(mat, sizeof(mat)); + + mat[0] = 2 / (right - left); + mat[5] = 2 / (top - bottom); + mat[10] = 2 / (near - far); + mat[15] = 1; + + mat[12] = (right + left) / (left - right); + mat[13] = (top + bottom) / (bottom - top); + mat[14] = (far + near) / (near - far); + + glMultMatrixf(mat); +} + +/////////////////////////////////////////////////////////////////////////////// + +static bool canBeTexture(const SkBitmap& bm, GLenum* format, GLenum* type) { + switch (bm.config()) { + case SkBitmap::kARGB_8888_Config: + *format = GL_RGBA; + *type = GL_UNSIGNED_BYTE; + break; + case SkBitmap::kRGB_565_Config: + *format = GL_RGB; + *type = GL_UNSIGNED_SHORT_5_6_5; + break; + case SkBitmap::kARGB_4444_Config: + *format = GL_RGBA; + *type = GL_UNSIGNED_SHORT_4_4_4_4; + break; + case SkBitmap::kIndex8_Config: + // we promote index to argb32 + *format = GL_RGBA; + *type = GL_UNSIGNED_BYTE; + break; + case SkBitmap::kA8_Config: + *format = GL_ALPHA; + *type = GL_UNSIGNED_BYTE; + break; + default: + return false; + } + return true; +} + +size_t SkGL::ComputeTextureMemorySize(const SkBitmap& bitmap) { + int shift = 0; + switch (bitmap.config()) { + case SkBitmap::kARGB_8888_Config: + case SkBitmap::kRGB_565_Config: + case SkBitmap::kARGB_4444_Config: + case SkBitmap::kA8_Config: + // we're good as is + break; + case SkBitmap::kIndex8_Config: + // we promote index to argb32 + shift = 2; + break; + default: + return 0; + } + return bitmap.getSize() << shift; +} + +GLuint SkGL::BindNewTexture(const SkBitmap& origBitmap, SkPoint* max) { + SkBitmap tmpBitmap; + const SkBitmap* bitmap = &origBitmap; + + if (origBitmap.config() == SkBitmap::kIndex8_Config) { + // we promote index to argb32 + origBitmap.copyTo(&tmpBitmap, SkBitmap::kARGB_8888_Config); + bitmap = &tmpBitmap; + } + + GLenum format, type; + if (!canBeTexture(*bitmap, &format, &type)) { + return 0; + } + + SkAutoLockPixels alp(*bitmap); + if (bitmap->getPixels() == NULL) { + return 0; + } + + GLuint textureName; + glGenTextures(1, &textureName); + + glBindTexture(GL_TEXTURE_2D, textureName); + + // express rowbytes as a number of pixels for ow + int ow = bitmap->rowBytesAsPixels(); + int oh = bitmap->height(); + int nw = SkNextPow2(ow); + int nh = SkNextPow2(oh); + + glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel()); + + // check if we need to scale to create power-of-2 dimensions + if (ow != nw || oh != nh) { + glTexImage2D(GL_TEXTURE_2D, 0, format, nw, nh, 0, + format, type, NULL); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, ow, oh, + format, type, bitmap->getPixels()); + } else { + // easy case, the bitmap is already pow2 + glTexImage2D(GL_TEXTURE_2D, 0, format, ow, oh, 0, + format, type, bitmap->getPixels()); + } + +#ifdef TRACE_TEXTURE_CREATION + SkDebugf("--- new texture [%d] size=(%d %d) bpp=%d\n", textureName, ow, oh, + bitmap->bytesPerPixel()); +#endif + + if (max) { + max->fX = SkFixedToScalar(bitmap->width() << (16 - SkNextLog2(nw))); + max->fY = SkFixedToScalar(oh << (16 - SkNextLog2(nh))); + } + return textureName; +} + +static const GLenum gTileMode2GLWrap[] = { + GL_CLAMP_TO_EDGE, + GL_REPEAT, +#if GL_VERSION_ES_CM_1_0 + GL_REPEAT // GLES doesn't support MIRROR +#else + GL_MIRRORED_REPEAT +#endif +}; + +void SkGL::SetTexParams(bool doFilter, + SkShader::TileMode tx, SkShader::TileMode ty) { + SkASSERT((unsigned)tx < SK_ARRAY_COUNT(gTileMode2GLWrap)); + SkASSERT((unsigned)ty < SK_ARRAY_COUNT(gTileMode2GLWrap)); + + GLenum filter = doFilter ? GL_LINEAR : GL_NEAREST; + + SK_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter); + SK_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter); + SK_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, gTileMode2GLWrap[tx]); + SK_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, gTileMode2GLWrap[ty]); +} + +void SkGL::SetTexParamsClamp(bool doFilter) { + GLenum filter = doFilter ? GL_LINEAR : GL_NEAREST; + + SK_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter); + SK_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter); + SK_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + SK_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); +} + +/////////////////////////////////////////////////////////////////////////////// + +void SkGL::DrawVertices(int count, GLenum mode, + const SkGLVertex* SK_RESTRICT vertex, + const SkGLVertex* SK_RESTRICT texCoords, + const uint8_t* SK_RESTRICT colorArray, + const uint16_t* SK_RESTRICT indexArray, + SkGLClipIter* iter) { + SkASSERT(NULL != vertex); + + if (NULL != texCoords) { + glEnable(GL_TEXTURE_2D); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glTexCoordPointer(2, SK_GLType, 0, texCoords); + } else { + glDisable(GL_TEXTURE_2D); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + } + + if (NULL != colorArray) { + glEnableClientState(GL_COLOR_ARRAY); + glColorPointer(4, GL_UNSIGNED_BYTE, 0, colorArray); + glShadeModel(GL_SMOOTH); + } else { + glDisableClientState(GL_COLOR_ARRAY); + glShadeModel(GL_FLAT); + } + + glVertexPointer(2, SK_GLType, 0, vertex); + + if (NULL != indexArray) { + if (iter) { + while (!iter->done()) { + iter->scissor(); + glDrawElements(mode, count, GL_UNSIGNED_SHORT, indexArray); + iter->next(); + } + } else { + glDrawElements(mode, count, GL_UNSIGNED_SHORT, indexArray); + } + } else { + if (iter) { + while (!iter->done()) { + iter->scissor(); + glDrawArrays(mode, 0, count); + iter->next(); + } + } else { + glDrawArrays(mode, 0, count); + } + } +} + +void SkGL::PrepareForFillPath(SkPaint* paint) { + if (paint->getStrokeWidth() <= 0) { + paint->setStrokeWidth(SK_Scalar1); + } +} + +void SkGL::FillPath(const SkPath& path, const SkPaint& paint, bool useTex, + SkGLClipIter* iter) { + SkPaint p(paint); + SkPath fillPath; + + SkGL::PrepareForFillPath(&p); + p.getFillPath(path, &fillPath); + SkGL::DrawPath(fillPath, useTex, iter); +} + +// should return max of all contours, rather than the sum (to save temp RAM) +static int worst_case_edge_count(const SkPath& path) { + int edgeCount = 0; + + SkPath::Iter iter(path, true); + SkPath::Verb verb; + + while ((verb = iter.next(NULL)) != SkPath::kDone_Verb) { + switch (verb) { + case SkPath::kLine_Verb: + edgeCount += 1; + break; + case SkPath::kQuad_Verb: + edgeCount += 8; + break; + case SkPath::kCubic_Verb: + edgeCount += 16; + break; + default: + break; + } + } + return edgeCount; +} + +void SkGL::DrawPath(const SkPath& path, bool useTex, SkGLClipIter* clipIter) { + SkRect bounds; + + path.computeBounds(&bounds, SkPath::kFast_BoundsType); + if (bounds.isEmpty()) { + return; + } + + int maxPts = worst_case_edge_count(path); + // add 1 for center of fan, and 1 for closing edge + SkAutoSTMalloc<32, SkGLVertex> storage(maxPts + 2); + SkGLVertex* base = storage.get(); + SkGLVertex* vert = base; + SkGLVertex* texs = useTex ? base : NULL; + + SkPath::Iter pathIter(path, true); + SkPoint pts[4]; + + bool needEnd = false; + + for (;;) { + switch (pathIter.next(pts)) { + case SkPath::kMove_Verb: + if (needEnd) { + SkGL::DrawVertices(vert - base, GL_TRIANGLE_FAN, + base, texs, NULL, NULL, clipIter); + clipIter->safeRewind(); + vert = base; + } + needEnd = true; + // center of the FAN + vert->setScalars(bounds.centerX(), bounds.centerY()); + vert++; + // add first edge point + vert->setPoint(pts[0]); + vert++; + break; + case SkPath::kLine_Verb: + vert->setPoint(pts[1]); + vert++; + break; + case SkPath::kQuad_Verb: { + const int n = 8; + const SkScalar dt = SK_Scalar1 / n; + SkScalar t = dt; + for (int i = 1; i < n; i++) { + SkPoint loc; + SkEvalQuadAt(pts, t, &loc, NULL); + t += dt; + vert->setPoint(loc); + vert++; + } + vert->setPoint(pts[2]); + vert++; + break; + } + case SkPath::kCubic_Verb: { + const int n = 16; + const SkScalar dt = SK_Scalar1 / n; + SkScalar t = dt; + for (int i = 1; i < n; i++) { + SkPoint loc; + SkEvalCubicAt(pts, t, &loc, NULL, NULL); + t += dt; + vert->setPoint(loc); + vert++; + } + vert->setPoint(pts[3]); + vert++; + break; + } + case SkPath::kClose_Verb: + break; + case SkPath::kDone_Verb: + goto FINISHED; + } + } +FINISHED: + if (needEnd) { + SkGL::DrawVertices(vert - base, GL_TRIANGLE_FAN, base, texs, + NULL, NULL, clipIter); + } +} + diff --git a/skia/gl/SkGL.h b/skia/gl/SkGL.h new file mode 100644 index 0000000..cf3c65e --- /dev/null +++ b/skia/gl/SkGL.h @@ -0,0 +1,307 @@ +#ifndef SkGL_DEFINED +#define SkGL_DEFINED + +#ifdef SK_BUILD_FOR_MAC + #include <OpenGL/gl.h> + #include <OpenGL/glext.h> + #include <AGL/agl.h> + // use FBOs for devices + #define SK_GL_DEVICE_FBO +#elif defined(ANDROID) + #include <GLES/gl.h> + #include <GLES/egl.h> +#endif + +#include "SkColor.h" +#include "SkMatrix.h" +#include "SkShader.h" + +class SkPaint; +class SkPath; + +class SkGLClipIter; + +//#define TRACE_TEXTURE_CREATE + +static inline void* SkGetGLContext() { +#ifdef ANDROID + return (void*)eglGetCurrentContext(); +#else + return NULL; +#endif +} + +/////////////////////////////////////////////////////////////////////////////// + +#if GL_OES_fixed_point && defined(SK_SCALAR_IS_FIXED) + #define SK_GLType GL_FIXED +#else + #define SK_GLType GL_FLOAT +#endif + +#if SK_GLType == GL_FIXED + typedef SkFixed SkGLScalar; + + #define SkIntToGL(n) SkIntToFixed(n) + #define SkScalarToGL(x) SkScalarToFixed(x) + #define SK_GLScalar1 SK_Fixed1 + #define SkGLScalarMul(a, b) SkFixedMul(a, b) + #define MAKE_GL(name) name ## x + + #ifdef SK_SCALAR_IS_FIXED + #define GLSCALAR_IS_SCALAR 1 + #define SkPerspToGL(x) SkFractToFixed(x) + #else + #define GLSCALAR_IS_SCALAR 0 + #define SkPerspToGL(x) SkFractToFloat(x) + #endif +#else + typedef float SkGLScalar; + + #define SkIntToGL(n) (n) + #define SkScalarToGL(x) SkScalarToFloat(x) + #define SK_GLScalar1 (1.f) + #define SkGLScalarMul(a, b) ((a) * (b)) + #define MAKE_GL(name) name ## f + + #ifdef SK_SCALAR_IS_FLOAT + #define GLSCALAR_IS_SCALAR 1 + #define SkPerspToGL(x) (x) + #else + #define GLSCALAR_IS_SCALAR 0 + #define SkPerspToGL(x) SkFractToFloat(x) + #endif +#endif + +#if GL_OES_fixed_point + typedef SkFixed SkGLTextScalar; + #define SK_TextGLType GL_FIXED + + #define SkIntToTextGL(n) SkIntToFixed(n) + #define SkFixedToTextGL(x) (x) + + #define SK_glTexParameteri(target, pname, param) \ + glTexParameterx(target, pname, param) +#else + typedef float SkGLTextScalar; + #define SK_TextGLType SK_GLType + #define SK_GL_HAS_COLOR4UB + + #define SkIntToTextGL(n) SkIntToGL(n) + #define SkFixedToTextGL(x) SkFixedToFloat(x) + + + #define SK_glTexParameteri(target, pname, param) \ + glTexParameteri(target, pname, param) +#endif + +/////////////////////////////////////////////////////////////////////////////// + +// text has its own vertex class, since it may want to be in fixed point (given) +// that it starts with all integers) even when the default vertices are floats +struct SkGLTextVertex { + SkGLTextScalar fX; + SkGLTextScalar fY; + + void setI(int x, int y) { + fX = SkIntToTextGL(x); + fY = SkIntToTextGL(y); + } + + void setX(SkFixed x, SkFixed y) { + fX = SkFixedToTextGL(x); + fY = SkFixedToTextGL(y); + } + + // counter-clockwise fan + void setIRectFan(int l, int t, int r, int b) { + SkGLTextVertex* SK_RESTRICT v = this; + v[0].setI(l, t); + v[1].setI(l, b); + v[2].setI(r, b); + v[3].setI(r, t); + } + + // counter-clockwise fan + void setXRectFan(SkFixed l, SkFixed t, SkFixed r, SkFixed b) { + SkGLTextVertex* SK_RESTRICT v = this; + v[0].setX(l, t); + v[1].setX(l, b); + v[2].setX(r, b); + v[3].setX(r, t); + } +}; + +struct SkGLVertex { + SkGLScalar fX; + SkGLScalar fY; + + void setGL(SkGLScalar x, SkGLScalar y) { + fX = x; + fY = y; + } + + void setScalars(SkScalar x, SkScalar y) { + fX = SkScalarToGL(x); + fY = SkScalarToGL(y); + } + + void setPoint(const SkPoint& pt) { + fX = SkScalarToGL(pt.fX); + fY = SkScalarToGL(pt.fY); + } + + void setPoints(const SkPoint* SK_RESTRICT pts, int count) { + const SkScalar* SK_RESTRICT src = (const SkScalar*)pts; + SkGLScalar* SK_RESTRICT dst = (SkGLScalar*)this; + for (int i = 0; i < count; i++) { + *dst++ = SkScalarToGL(*src++); + *dst++ = SkScalarToGL(*src++); + } + } + + // counter-clockwise fan + void setRectFan(SkScalar l, SkScalar t, SkScalar r, SkScalar b) { + SkGLVertex* v = this; + v[0].setScalars(l, t); + v[1].setScalars(l, b); + v[2].setScalars(r, b); + v[3].setScalars(r, t); + } + + // counter-clockwise fan + void setIRectFan(int l, int t, int r, int b) { + SkGLVertex* v = this; + v[0].setGL(SkIntToGL(l), SkIntToGL(t)); + v[1].setGL(SkIntToGL(l), SkIntToGL(b)); + v[2].setGL(SkIntToGL(r), SkIntToGL(b)); + v[3].setGL(SkIntToGL(r), SkIntToGL(t)); + } + + // counter-clockwise fan + void setRectFan(const SkRect& r) { + this->setRectFan(r.fLeft, r.fTop, r.fRight, r.fBottom); + } + + // counter-clockwise fan + void setIRectFan(const SkIRect& r) { + this->setIRectFan(r.fLeft, r.fTop, r.fRight, r.fBottom); + } +}; + +struct SkGLMatrix { + SkGLScalar fMat[16]; + + void reset() { + bzero(fMat, sizeof(fMat)); + fMat[0] = fMat[5] = fMat[10] = fMat[15] = SK_GLScalar1; + } + + void set(const SkMatrix& m) { + bzero(fMat, sizeof(fMat)); + fMat[0] = SkScalarToGL(m[SkMatrix::kMScaleX]); + fMat[4] = SkScalarToGL(m[SkMatrix::kMSkewX]); + fMat[12] = SkScalarToGL(m[SkMatrix::kMTransX]); + + fMat[1] = SkScalarToGL(m[SkMatrix::kMSkewY]); + fMat[5] = SkScalarToGL(m[SkMatrix::kMScaleY]); + fMat[13] = SkScalarToGL(m[SkMatrix::kMTransY]); + + fMat[3] = SkPerspToGL(m[SkMatrix::kMPersp0]); + fMat[7] = SkPerspToGL(m[SkMatrix::kMPersp1]); + fMat[15] = SkPerspToGL(m[SkMatrix::kMPersp2]); + + fMat[10] = SK_GLScalar1; // z-scale + } +}; + +class SkGL { +public: + static void SetColor(SkColor c); + static void SetAlpha(U8CPU alpha); + static void SetPaint(const SkPaint&, bool isPremul = true, + bool justAlpha = false); + static void SetPaintAlpha(const SkPaint& paint, bool isPremul = true) { + SetPaint(paint, isPremul, true); + } + + static void SetRGBA(uint8_t rgba[], const SkColor src[], int count); + static void DumpError(const char caller[]); + + static void Ortho(float left, float right, float bottom, float top, + float near, float far); + + static inline void Translate(SkScalar dx, SkScalar dy) { + MAKE_GL(glTranslate)(SkScalarToGL(dx), SkScalarToGL(dy), 0); + } + + static inline void Scale(SkScalar sx, SkScalar sy) { + MAKE_GL(glScale)(SkScalarToGL(sx), SkScalarToGL(sy), SK_GLScalar1); + } + + static inline void Rotate(SkScalar angle) { + MAKE_GL(glRotate)(SkScalarToGL(angle), 0, 0, SK_GLScalar1); + } + + static inline void MultMatrix(const SkMatrix& m) { + SkGLMatrix glm; + glm.set(m); + MAKE_GL(glMultMatrix)(glm.fMat); + } + + static inline void LoadMatrix(const SkMatrix& m) { + SkGLMatrix glm; + glm.set(m); + MAKE_GL(glLoadMatrix)(glm.fMat); + } + + static void Scissor(const SkIRect&, int viewportHeight); + + // return the byte size for the associated texture memory. This doesn't + // always == bitmap.getSize(), since on a given port we may have to change + // the format when the bitmap's pixels are copied over to GL + static size_t ComputeTextureMemorySize(const SkBitmap&); + // return 0 on failure + static GLuint BindNewTexture(const SkBitmap&, SkPoint* dimension); + + static void SetTexParams(bool filter, + SkShader::TileMode tx, SkShader::TileMode ty); + static void SetTexParamsClamp(bool filter); + + static void DrawVertices(int count, GLenum mode, + const SkGLVertex* SK_RESTRICT vertex, + const SkGLVertex* SK_RESTRICT texCoords, + const uint8_t* SK_RESTRICT colorArray, + const uint16_t* SK_RESTRICT indexArray, + SkGLClipIter*); + + static void PrepareForFillPath(SkPaint* paint); + static void FillPath(const SkPath& path, const SkPaint& paint, bool useTex, + SkGLClipIter*); + static void DrawPath(const SkPath& path, bool useTex, SkGLClipIter*); +}; + +#include "SkRegion.h" + +class SkGLClipIter : public SkRegion::Iterator { +public: + SkGLClipIter(int viewportHeight) : fViewportHeight(viewportHeight) {} + + // call rewind only if this is non-null + void safeRewind() { + if (this) { + this->rewind(); + } + } + + void scissor() { + SkASSERT(!this->done()); + SkGL::Scissor(this->rect(), fViewportHeight); + } + +private: + const int fViewportHeight; +}; + +#endif + diff --git a/skia/gl/SkGLCanvas.cpp b/skia/gl/SkGLCanvas.cpp new file mode 100644 index 0000000..d9f2201 --- /dev/null +++ b/skia/gl/SkGLCanvas.cpp @@ -0,0 +1,143 @@ +#include "SkGLCanvas.h" +#include "SkGLDevice.h" +#include "SkBlitter.h" +#include "SkDraw.h" +#include "SkDrawProcs.h" +#include "SkGL.h" +#include "SkTemplates.h" +#include "SkUtils.h" +#include "SkXfermode.h" + +#ifdef SK_GL_DEVICE_FBO + #define USE_FBO_DEVICE + #include "SkGLDevice_FBO.h" +#else + #define USE_SWLAYER_DEVICE + #include "SkGLDevice_SWLayer.h" +#endif + +// maximum number of entries in our texture cache (before purging) +#define kTexCountMax_Default 256 +// maximum number of bytes used (by gl) for the texture cache (before purging) +#define kTexSizeMax_Default (4 * 1024 * 1024) + +/////////////////////////////////////////////////////////////////////////////// + +SkGLCanvas::SkGLCanvas() { + glEnable(GL_TEXTURE_2D); + glEnable(GL_SCISSOR_TEST); + glEnableClientState(GL_VERTEX_ARRAY); + + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + + fViewportSize.set(0, 0); +} + +SkGLCanvas::~SkGLCanvas() { + // call this now, while our override of restore() is in effect + this->restoreToCount(1); +} + +/////////////////////////////////////////////////////////////////////////////// + +bool SkGLCanvas::getViewport(SkIPoint* size) const { + if (size) { + *size = fViewportSize; + } + return true; +} + +bool SkGLCanvas::setViewport(int width, int height) { + fViewportSize.set(width, height); + + const bool isOpaque = false; // should this be a parameter to setViewport? + const bool isForLayer = false; // viewport is the base layer + SkDevice* device = this->createDevice(SkBitmap::kARGB_8888_Config, width, + height, isOpaque, isForLayer); + this->setDevice(device)->unref(); + + return true; +} + +SkDevice* SkGLCanvas::createDevice(SkBitmap::Config, int width, int height, + bool isOpaque, bool isForLayer) { + SkBitmap bitmap; + + bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height); + bitmap.setIsOpaque(isOpaque); + +#ifdef USE_FBO_DEVICE + return SkNEW_ARGS(SkGLDevice_FBO, (bitmap, isForLayer)); +#elif defined(USE_SWLAYER_DEVICE) + if (isForLayer) { + bitmap.allocPixels(); + if (!bitmap.isOpaque()) { + bitmap.eraseColor(0); + } + return SkNEW_ARGS(SkGLDevice_SWLayer, (bitmap)); + } else { + return SkNEW_ARGS(SkGLDevice, (bitmap, isForLayer)); + } +#else + return SkNEW_ARGS(SkGLDevice, (bitmap, isForLayer)); +#endif +} + +/////////////////////////////////////////////////////////////////////////////// + +#include "SkTextureCache.h" +#include "SkThread.h" + +static SkMutex gTextureCacheMutex; +static SkTextureCache gTextureCache(kTexCountMax_Default, kTexSizeMax_Default); +static void* gTextureGLContext; + +SkGLDevice::TexCache* SkGLDevice::LockTexCache(const SkBitmap& bitmap, + GLuint* name, SkPoint* size) { + SkAutoMutexAcquire amc(gTextureCacheMutex); + + void* ctx = SkGetGLContext(); + if (gTextureGLContext != ctx) { + gTextureGLContext = ctx; + gTextureCache.zapAllTextures(); + } + + SkTextureCache::Entry* entry = gTextureCache.lock(bitmap); + if (NULL != entry) { + if (name) { + *name = entry->name(); + } + if (size) { + *size = entry->texSize(); + } + } + return (TexCache*)entry; +} + +void SkGLDevice::UnlockTexCache(TexCache* cache) { + SkAutoMutexAcquire amc(gTextureCacheMutex); + gTextureCache.unlock((SkTextureCache::Entry*)cache); +} + +// public exposure of texture cache settings + +size_t SkGLCanvas::GetTextureCacheMaxCount() { + SkAutoMutexAcquire amc(gTextureCacheMutex); + return gTextureCache.getMaxCount(); +} + +size_t SkGLCanvas::GetTextureCacheMaxSize() { + SkAutoMutexAcquire amc(gTextureCacheMutex); + return gTextureCache.getMaxSize(); +} + +void SkGLCanvas::SetTextureCacheMaxCount(size_t count) { + SkAutoMutexAcquire amc(gTextureCacheMutex); + gTextureCache.setMaxCount(count); +} + +void SkGLCanvas::SetTextureCacheMaxSize(size_t size) { + SkAutoMutexAcquire amc(gTextureCacheMutex); + gTextureCache.setMaxSize(size); +} + diff --git a/skia/gl/SkGLDevice.cpp b/skia/gl/SkGLDevice.cpp new file mode 100644 index 0000000..2271b05 --- /dev/null +++ b/skia/gl/SkGLDevice.cpp @@ -0,0 +1,774 @@ +#include "SkGLDevice.h" +#include "SkGL.h" +#include "SkDrawProcs.h" +#include "SkRegion.h" +#include "SkThread.h" + +static void TRACE_DRAW(const char func[], SkGLDevice* device, + const SkDraw& draw) { + // SkDebugf("--- <%s> %p %p\n", func, canvas, draw.fDevice); +} + +struct SkGLDrawProcs : public SkDrawProcs { +public: + void init(const SkRegion* clip, int height) { + fCurrQuad = 0; + fCurrTexture = 0; + fClip = clip; + fViewportHeight = height; + + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glTexCoordPointer(2, SK_TextGLType, 0, fTexs); + glDisableClientState(GL_COLOR_ARRAY); + glVertexPointer(2, SK_TextGLType, 0, fVerts); + + fCtx = SkGetGLContext(); + } + + void* ctx() const { return fCtx; } + GLenum texture() const { return fCurrTexture; } + + void flush() { + if (fCurrQuad && fCurrTexture) { + this->drawQuads(); + } + fCurrQuad = 0; + } + + void addQuad(GLuint texture, int x, int y, const SkGlyph& glyph, + SkFixed left, SkFixed right, SkFixed bottom) { + SkASSERT((size_t)fCurrQuad <= SK_ARRAY_COUNT(fVerts)); + + if (fCurrTexture != texture || fCurrQuad == SK_ARRAY_COUNT(fVerts)) { + if (fCurrQuad && fCurrTexture) { + this->drawQuads(); + } + fCurrQuad = 0; + fCurrTexture = texture; + } + + fVerts[fCurrQuad].setIRectFan(x, y, + x + glyph.fWidth, y + glyph.fHeight); + fTexs[fCurrQuad].setXRectFan(left, 0, right, bottom); + fCurrQuad += 4; + } + + void drawQuads(); + +private: + enum { + MAX_QUADS = 32 + }; + + SkGLTextVertex fVerts[MAX_QUADS * 4]; + SkGLTextVertex fTexs[MAX_QUADS * 4]; + + // these are initialized in setupForText + GLuint fCurrTexture; + int fCurrQuad; + int fViewportHeight; + const SkRegion* fClip; + void* fCtx; +}; + +/////////////////////////////////////////////////////////////////////////////// + +SkGLDevice::SkGLDevice(const SkBitmap& bitmap, bool offscreen) + : SkDevice(bitmap), fClipIter(bitmap.height()) { + fDrawProcs = NULL; +} + +SkGLDevice::~SkGLDevice() { + if (fDrawProcs) { + SkDELETE(fDrawProcs); + } +} + +void SkGLDevice::setMatrixClip(const SkMatrix& matrix, const SkRegion& clip) { + this->INHERITED::setMatrixClip(matrix, clip); + + fGLMatrix.set(matrix); + fMatrix = matrix; + fClip = clip; + fDirty = true; +} + +SkGLDevice::TexOrientation SkGLDevice::bindDeviceAsTexture() { + return kNo_TexOrientation; +} + +void SkGLDevice::gainFocus(SkCanvas* canvas) { + this->INHERITED::gainFocus(canvas); + + const int w = this->width(); + const int h = this->height(); + glViewport(0, 0, w, h); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + SkGL::Ortho(0, w, h, 0, -1, 1); + glMatrixMode(GL_MODELVIEW); + fDirty = true; +} + +SkGLClipIter* SkGLDevice::updateMatrixClip() { + bool useIter = false; + + // first handle the clip + if (fDirty || !fClip.isRect()) { + fClipIter.reset(fClip); + useIter = true; + } else if (fDirty) { + // no iter means caller is not respecting complex clips :( + SkGL::Scissor(fClip.getBounds(), this->height()); + } + // else we're just a rect, and we've already call scissor + + // now handle the matrix + if (fDirty) { + MAKE_GL(glLoadMatrix)(fGLMatrix.fMat); +#if 0 + SkDebugf("--- gldevice update matrix %p %p\n", this, fFBO); + for (int y = 0; y < 4; y++) { + SkDebugf(" [ "); + for (int x = 0; x < 4; x++) { + SkDebugf("%g ", fGLMatrix.fMat[y*4 + x]); + } + SkDebugf("]\n"); + } +#endif + fDirty = false; + } + + return useIter ? &fClipIter : NULL; +} + +/////////////////////////////////////////////////////////////////////////////// + +// must be in the same order as SkXfermode::Coeff in SkXfermode.h +SkGLDevice::AutoPaintShader::AutoPaintShader(SkGLDevice* device, + const SkPaint& paint) { + fDevice = device; + fTexCache = device->setupGLPaintShader(paint); +} + +SkGLDevice::AutoPaintShader::~AutoPaintShader() { + if (fTexCache) { + SkGLDevice::UnlockTexCache(fTexCache); + } +} + +SkGLDevice::TexCache* SkGLDevice::setupGLPaintShader(const SkPaint& paint) { + SkGL::SetPaint(paint); + + SkShader* shader = paint.getShader(); + if (NULL == shader) { + return NULL; + } + + if (!shader->setContext(this->accessBitmap(false), paint, this->matrix())) { + return NULL; + } + + SkBitmap bitmap; + SkMatrix matrix; + SkShader::TileMode tileModes[2]; + if (!shader->asABitmap(&bitmap, &matrix, tileModes)) { + return NULL; + } + + bitmap.lockPixels(); + if (bitmap.getPixels() == NULL) { + return NULL; + } + + // see if we've already cached the bitmap from the shader + SkPoint max; + GLuint name; + TexCache* cache = SkGLDevice::LockTexCache(bitmap, &name, &max); + // the lock has already called glBindTexture for us + SkGL::SetTexParams(paint.isFilterBitmap(), tileModes[0], tileModes[1]); + + // since our texture coords will be in local space, we wack the texture + // matrix to map them back into 0...1 before we load it + SkMatrix localM; + if (shader->getLocalMatrix(&localM)) { + SkMatrix inverse; + if (localM.invert(&inverse)) { + matrix.preConcat(inverse); + } + } + + matrix.postScale(max.fX / bitmap.width(), max.fY / bitmap.height()); + glMatrixMode(GL_TEXTURE); + SkGL::LoadMatrix(matrix); + glMatrixMode(GL_MODELVIEW); + + // since we're going to use a shader/texture, we don't want the color, + // just its alpha + SkGL::SetAlpha(paint.getAlpha()); + // report that we have setup the texture + return cache; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void SkGLDevice::drawPaint(const SkDraw& draw, const SkPaint& paint) { + TRACE_DRAW("coreDrawPaint", this, draw); + + AutoPaintShader shader(this, paint); + SkGLVertex vertex[4]; + const SkGLVertex* texs = shader.useTex() ? vertex : NULL; + + // set vert to be big enough to fill the space, but not super-huge, to we + // don't overflow fixed-point implementations + { + SkRect r; + r.set(this->clip().getBounds()); + SkMatrix inverse; + if (draw.fMatrix->invert(&inverse)) { + inverse.mapRect(&r); + } + vertex->setRectFan(r); + } + + SkGL::DrawVertices(4, GL_TRIANGLE_FAN, vertex, texs, NULL, NULL, + this->updateMatrixClip()); +} + +static const GLenum gPointMode2GL[] = { + GL_POINTS, + GL_LINES, + GL_LINE_STRIP +}; + +void SkGLDevice::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode, + size_t count, const SkPoint pts[], const SkPaint& paint) { + TRACE_DRAW("coreDrawPoints", this, draw); + + SkScalar width = paint.getStrokeWidth(); + if (width < 0) { + return; + } + + /* We should really only use drawverts for hairlines, since gl and skia + treat the thickness differently... + */ + + AutoPaintShader shader(this, paint); + + if (width <= 0) { + width = SK_Scalar1; + } + + if (SkCanvas::kPoints_PointMode == mode) { + glPointSize(SkScalarToFloat(width)); + } else { + glLineWidth(SkScalarToFloat(width)); + } + + const SkGLVertex* verts; + +#if GLSCALAR_IS_SCALAR + verts = (const SkGLVertex*)pts; +#else + SkAutoSTMalloc<32, SkGLVertex> storage(count); + SkGLVertex* v = storage.get(); + + v->setPoints(pts, count); + verts = v; +#endif + + const SkGLVertex* texs = shader.useTex() ? verts : NULL; + + SkGL::DrawVertices(count, gPointMode2GL[mode], verts, texs, NULL, NULL, + this->updateMatrixClip()); +} + +void SkGLDevice::drawRect(const SkDraw& draw, const SkRect& rect, + const SkPaint& paint) { + TRACE_DRAW("coreDrawRect", this, draw); + + if (paint.getStyle() == SkPaint::kStroke_Style) { + return; + } + + if (paint.getStrokeJoin() != SkPaint::kMiter_Join) { + SkPath path; + path.addRect(rect); + this->drawPath(draw, path, paint); + return; + } + + AutoPaintShader shader(this, paint); + + SkGLVertex vertex[4]; + vertex->setRectFan(rect); + const SkGLVertex* texs = shader.useTex() ? vertex : NULL; + + SkGL::DrawVertices(4, GL_TRIANGLE_FAN, vertex, texs, NULL, NULL, + this->updateMatrixClip()); +} + +void SkGLDevice::drawPath(const SkDraw& draw, const SkPath& path, + const SkPaint& paint) { + TRACE_DRAW("coreDrawPath", this, draw); + if (paint.getStyle() == SkPaint::kStroke_Style) { + return; + } + + AutoPaintShader shader(this, paint); + + SkGL::FillPath(path, paint, shader.useTex(), this->updateMatrixClip()); +} + +void SkGLDevice::drawBitmap(const SkDraw& draw, const SkBitmap& bitmap, + const SkMatrix& m, const SkPaint& paint) { + TRACE_DRAW("coreDrawBitmap", this, draw); + + SkAutoLockPixels alp(bitmap); + if (bitmap.getPixels() == NULL) { + return; + } + + SkGLClipIter* iter = this->updateMatrixClip(); + + SkPoint max; + GLenum name; + SkAutoLockTexCache(bitmap, &name, &max); + // the lock has already called glBindTexture for us + SkGL::SetTexParamsClamp(paint.isFilterBitmap()); + + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + SkGL::MultMatrix(m); + + SkGLVertex pts[4], tex[4]; + + pts->setIRectFan(0, 0, bitmap.width(), bitmap.height()); + tex->setRectFan(0, 0, max.fX, max.fY); + + // now draw the mesh + SkGL::SetPaintAlpha(paint); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + + SkGL::DrawVertices(4, GL_TRIANGLE_FAN, pts, tex, NULL, NULL, iter); + + glPopMatrix(); +} + +// move this guy into SkGL, so we can call it from SkGLDevice +static void gl_drawSprite(int x, int y, int w, int h, const SkPoint& max, + const SkPaint& paint, SkGLClipIter* iter) { + SkGL::SetTexParamsClamp(false); + + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + + SkGLVertex pts[4], tex[4]; + + // if h < 0, then the texture is bottom-to-top, but since our projection + // matrix always inverts Y, we have to re-invert our texture coord here + if (h < 0) { + h = -h; + tex->setRectFan(0, max.fY, max.fX, 0); + } else { + tex->setRectFan(0, 0, max.fX, max.fY); + } + pts->setIRectFan(x, y, x + w, y + h); + + SkGL::SetPaintAlpha(paint); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + + // should look to use glDrawTexi() has we do for text... + SkGL::DrawVertices(4, GL_TRIANGLE_FAN, pts, tex, NULL, NULL, iter); + + glPopMatrix(); +} + +void SkGLDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap, + int left, int top, const SkPaint& paint) { + TRACE_DRAW("coreDrawSprite", this, draw); + + SkAutoLockPixels alp(bitmap); + if (bitmap.getPixels() == NULL) { + return; + } + + SkGLClipIter* iter = this->updateMatrixClip(); + + SkPoint max; + GLuint name; + SkAutoLockTexCache(bitmap, &name, &max); + + gl_drawSprite(left, top, bitmap.width(), bitmap.height(), max, paint, iter); +} + +void SkGLDevice::drawDevice(const SkDraw& draw, SkDevice* dev, + int x, int y, const SkPaint& paint) { + TRACE_DRAW("coreDrawDevice", this, draw); + + SkGLDevice::TexOrientation to = ((SkGLDevice*)dev)->bindDeviceAsTexture(); + if (SkGLDevice::kNo_TexOrientation != to) { + SkGLClipIter* iter = this->updateMatrixClip(); + + const SkBitmap& bm = dev->accessBitmap(false); + int w = bm.width(); + int h = bm.height(); + SkPoint max; + + max.set(SkFixedToScalar(w << (16 - SkNextLog2(bm.rowBytesAsPixels()))), + SkFixedToScalar(h << (16 - SkNextLog2(h)))); + + if (SkGLDevice::kBottomToTop_TexOrientation == to) { + h = -h; + } + gl_drawSprite(x, y, w, h, max, paint, iter); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static const GLenum gVertexModeToGL[] = { + GL_TRIANGLES, // kTriangles_VertexMode, + GL_TRIANGLE_STRIP, // kTriangleStrip_VertexMode, + GL_TRIANGLE_FAN // kTriangleFan_VertexMode +}; + +#include "SkShader.h" + +void SkGLDevice::drawVertices(const SkDraw& draw, SkCanvas::VertexMode vmode, + int vertexCount, const SkPoint vertices[], + const SkPoint texs[], const SkColor colors[], + SkXfermode* xmode, + const uint16_t indices[], int indexCount, + const SkPaint& paint) { + + if (false) { + SkRect bounds; + SkIRect ibounds; + + bounds.set(vertices, vertexCount); + bounds.round(&ibounds); + + SkDebugf("---- drawverts: %d pts, texs=%d colors=%d indices=%d bounds [%d %d]\n", + vertexCount, texs!=0, colors!=0, indexCount, ibounds.width(), ibounds.height()); + } + + SkGLClipIter* iter = this->updateMatrixClip(); + + SkGL::SetPaint(paint); + + const SkGLVertex* glVerts; + const SkGLVertex* glTexs = NULL; + +#if GLSCALAR_IS_SCALAR + glVerts = (const SkGLVertex*)vertices; +#else + SkAutoSTMalloc<32, SkGLVertex> storage(vertexCount); + storage.get()->setPoints(vertices, vertexCount); + glVerts = storage.get(); +#endif + + uint8_t* colorArray = NULL; + if (colors) { + colorArray = (uint8_t*)sk_malloc_throw(vertexCount*4); + SkGL::SetRGBA(colorArray, colors, vertexCount); + } + SkAutoFree afca(colorArray); + + SkGLVertex* texArray = NULL; + TexCache* cache = NULL; + + if (texs && paint.getShader()) { + SkShader* shader = paint.getShader(); + + // if (!shader->setContext(this->accessBitmap(), paint, *draw.fMatrix)) { + if (!shader->setContext(*draw.fBitmap, paint, *draw.fMatrix)) { + goto DONE; + } + + SkBitmap bitmap; + SkMatrix matrix; + SkShader::TileMode tileModes[2]; + if (shader->asABitmap(&bitmap, &matrix, tileModes)) { + SkPoint max; + GLuint name; + cache = SkGLDevice::LockTexCache(bitmap, &name, &max); + if (NULL == cache) { + return; + } + + matrix.postScale(max.fX / bitmap.width(), max.fY / bitmap.height()); + glMatrixMode(GL_TEXTURE); + SkGL::LoadMatrix(matrix); + glMatrixMode(GL_MODELVIEW); + +#if GLSCALAR_IS_SCALAR + glTexs = (const SkGLVertex*)texs; +#else + texArray = (SkGLVertex*)sk_malloc_throw(vertexCount * sizeof(SkGLVertex)); + texArray->setPoints(texs, vertexCount); + glTexs = texArray; +#endif + + SkGL::SetPaintAlpha(paint); + SkGL::SetTexParams(paint.isFilterBitmap(), + tileModes[0], tileModes[1]); + } + } +DONE: + SkAutoFree aftex(texArray); + + SkGL::DrawVertices(indices ? indexCount : vertexCount, + gVertexModeToGL[vmode], + glVerts, glTexs, colorArray, indices, iter); + + if (cache) { + SkGLDevice::UnlockTexCache(cache); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +#include "SkGlyphCache.h" +#include "SkGLTextCache.h" + +static void SkGL_GlyphCacheAuxProc(void* data) { + SkGLTextCache* cache = (SkGLTextCache*)data; + + SkDebugf("-------------- delete text texture cache, ctx=%p\n", + cache->getCtx()); + SkDELETE((SkGLTextCache*)data); +} + +#ifdef SK_SCALAR_IS_FIXED +#define SkDiv16ToScalar(numer, denom) (SkIntToFixed(numer) / (denom)) +#else +#define SkDiv16ToScalar(numer, denom) SkScalarDiv(numer, denom) +#endif + +// stolen from SkDraw.cpp - D1G_NoBounder_RectClip +static void SkGL_Draw1Glyph(const SkDraw1Glyph& state, const SkGlyph& glyph, + int x, int y) { + SkASSERT(glyph.fWidth > 0 && glyph.fHeight > 0); + + SkGLDrawProcs* procs = (SkGLDrawProcs*)state.fDraw->fProcs; + + x += glyph.fLeft; + y += glyph.fTop; + + // check if we're clipped out (nothing to draw) + SkIRect bounds; + bounds.set(x, y, x + glyph.fWidth, y + glyph.fHeight); + if (!SkIRect::Intersects(state.fClip->getBounds(), bounds)) { + return; + } + + // now dig up our texture cache + + SkGlyphCache* gcache = state.fCache; + void* auxData; + SkGLTextCache* textCache = NULL; + + if (gcache->getAuxProcData(SkGL_GlyphCacheAuxProc, &auxData)) { + textCache = (SkGLTextCache*)auxData; + if (textCache->getCtx() != procs->ctx()) { + SkDebugf("------- textcache: old ctx %p new ctx %p\n", + textCache->getCtx(), procs->ctx()); + SkDELETE(textCache); + textCache = NULL; + } + } + if (NULL == textCache) { + // need to create one + textCache = SkNEW(SkGLTextCache); + gcache->setAuxProc(SkGL_GlyphCacheAuxProc, textCache); + } + + int offset; + SkGLTextCache::Strike* strike = textCache->findGlyph(glyph, &offset); + if (NULL == strike) { + // make sure the glyph has an image + uint8_t* aa = (uint8_t*)glyph.fImage; + if (NULL == aa) { + aa = (uint8_t*)gcache->findImage(glyph); + if (NULL == aa) { + return; // can't rasterize glyph + } + } + strike = textCache->addGlyphAndBind(glyph, aa, &offset); + if (NULL == strike) { + // too big to cache, need to draw as is... + return; + } + } + + const int shiftW = strike->widthShift(); + const int shiftH = strike->heightShift(); + + SkFixed left = offset << (16 - shiftW); + SkFixed right = (offset + glyph.fWidth) << (16 - shiftW); + SkFixed bottom = glyph.fHeight << (16 - shiftH); + + procs->addQuad(strike->texture(), x, y, glyph, left, right, bottom); +} + +#if 1 +// matches the orientation used in SkGL::setRectFan. Too bad we can't rely on +// QUADS in android's GL +static const uint8_t gQuadIndices[] = { + 0, 1, 2, 0, 2, 3, + 4, 5, 6, 4, 6, 7, + 8, 9, 10, 8, 10, 11, + 12, 13, 14, 12, 14, 15, + 16, 17, 18, 16, 18, 19, + 20, 21, 22, 20, 22, 23, + 24, 25, 26, 24, 26, 27, + 28, 29, 30, 28, 30, 31, + 32, 33, 34, 32, 34, 35, + 36, 37, 38, 36, 38, 39, + 40, 41, 42, 40, 42, 43, + 44, 45, 46, 44, 46, 47, + 48, 49, 50, 48, 50, 51, + 52, 53, 54, 52, 54, 55, + 56, 57, 58, 56, 58, 59, + 60, 61, 62, 60, 62, 63, + 64, 65, 66, 64, 66, 67, + 68, 69, 70, 68, 70, 71, + 72, 73, 74, 72, 74, 75, + 76, 77, 78, 76, 78, 79, + 80, 81, 82, 80, 82, 83, + 84, 85, 86, 84, 86, 87, + 88, 89, 90, 88, 90, 91, + 92, 93, 94, 92, 94, 95, + 96, 97, 98, 96, 98, 99, + 100, 101, 102, 100, 102, 103, + 104, 105, 106, 104, 106, 107, + 108, 109, 110, 108, 110, 111, + 112, 113, 114, 112, 114, 115, + 116, 117, 118, 116, 118, 119, + 120, 121, 122, 120, 122, 123, + 124, 125, 126, 124, 126, 127 +}; +#else +static void generateQuadIndices(int n) { + int index = 0; + for (int i = 0; i < n; i++) { + SkDebugf(" %3d, %3d, %3d, %3d, %3d, %3d,\n", + index, index + 1, index + 2, index, index + 2, index + 3); + index += 4; + } +} +#endif + +void SkGLDrawProcs::drawQuads() { + SkASSERT(SK_ARRAY_COUNT(gQuadIndices) == MAX_QUADS * 6); + + glBindTexture(GL_TEXTURE_2D, fCurrTexture); + +#if 0 + static bool gOnce; + if (!gOnce) { + generateQuadIndices(MAX_QUADS); + gOnce = true; + } +#endif + + // convert from quad vertex count to triangle vertex count + // 6/4 * n == n + (n >> 1) since n is always a multiple of 4 + SkASSERT((fCurrQuad & 3) == 0); + int count = fCurrQuad + (fCurrQuad >> 1); + + if (fClip->isComplex()) { + SkGLClipIter iter(fViewportHeight); + iter.reset(*fClip); + while (!iter.done()) { + iter.scissor(); + glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_BYTE, gQuadIndices); + iter.next(); + } + } else { + glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_BYTE, gQuadIndices); + } +} + +void SkGLDevice::setupForText(SkDraw* draw, const SkPaint& paint) { + // we handle complex clips in the SkDraw common code, so we don't check + // for it here + this->updateMatrixClip(); + + SkGL::SetPaint(paint, false); + + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + + // deferred allocation + if (NULL == fDrawProcs) { + fDrawProcs = SkNEW(SkGLDrawProcs); + fDrawProcs->fD1GProc = SkGL_Draw1Glyph; + } + + // init our (and GL's) state + fDrawProcs->init(draw->fClip, this->height()); + // assign to the caller's SkDraw + draw->fProcs = fDrawProcs; + + glEnable(GL_TEXTURE_2D); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glShadeModel(GL_FLAT); +} + +void SkGLDevice::drawText(const SkDraw& draw, const void* text, + size_t byteLength, SkScalar x, SkScalar y, + const SkPaint& paint) { + /* Currently, perspective text is draw via paths, invoked directly by + SkDraw. This can't work for us, since the bitmap that our draw points + to has no pixels, so we just abort if we're in perspective. + + Better fix would be to... + - have a callback inside draw to handle path drawing + - option to have draw call the font cache, which we could patch (?) + */ + if (draw.fMatrix->getType() & SkMatrix::kPerspective_Mask) { + return; + } + + SkDraw myDraw(draw); + this->setupForText(&myDraw, paint); + this->INHERITED::drawText(myDraw, text, byteLength, x, y, paint); + fDrawProcs->flush(); + glPopMatrix(); // GL_MODELVIEW +} + +void SkGLDevice::drawPosText(const SkDraw& draw, const void* text, + size_t byteLength, const SkScalar pos[], + SkScalar constY, int scalarsPerPos, + const SkPaint& paint) { + if (draw.fMatrix->getType() & SkMatrix::kPerspective_Mask) { + return; + } + + SkDraw myDraw(draw); + this->setupForText(&myDraw, paint); + this->INHERITED::drawPosText(myDraw, text, byteLength, pos, constY, + scalarsPerPos, paint); + fDrawProcs->flush(); + glPopMatrix(); // GL_MODELVIEW +} + +void SkGLDevice::drawTextOnPath(const SkDraw& draw, const void* text, + size_t byteLength, const SkPath& path, + const SkMatrix* m, const SkPaint& paint) { + // not supported yet +} + diff --git a/skia/gl/SkGLDevice.h b/skia/gl/SkGLDevice.h new file mode 100644 index 0000000..9e86f3f --- /dev/null +++ b/skia/gl/SkGLDevice.h @@ -0,0 +1,121 @@ +#ifndef SkGLDevice_DEFINED +#define SkGLDevice_DEFINED + +#include "SkDevice.h" +#include "SkGL.h" +#include "SkRegion.h" + +struct SkGLDrawProcs; + +class SkGLDevice : public SkDevice { +public: + SkGLDevice(const SkBitmap& bitmap, bool offscreen); + virtual ~SkGLDevice(); + + enum TexOrientation { + kNo_TexOrientation, + kTopToBottom_TexOrientation, + kBottomToTop_TexOrientation + }; + + /** Called when this device is no longer a candidate for a render target, + but will instead be used as a texture to be drawn. Be sure to call + the base impl if you override, as it will compute size and max. + */ + virtual TexOrientation bindDeviceAsTexture(); + + // returns true if complex + SkGLClipIter* updateMatrixClip(); + // call to set the clip to the specified rect + void scissor(const SkIRect&); + + // overrides from SkDevice + virtual void gainFocus(SkCanvas*); + virtual void setMatrixClip(const SkMatrix& matrix, const SkRegion& clip); + + virtual void drawPaint(const SkDraw&, const SkPaint& paint); + virtual void drawPoints(const SkDraw&, SkCanvas::PointMode mode, size_t count, + const SkPoint[], const SkPaint& paint); + virtual void drawRect(const SkDraw&, const SkRect& r, + const SkPaint& paint); + virtual void drawPath(const SkDraw&, const SkPath& path, + const SkPaint& paint); + virtual void drawBitmap(const SkDraw&, const SkBitmap& bitmap, + const SkMatrix& matrix, const SkPaint& paint); + virtual void drawSprite(const SkDraw&, const SkBitmap& bitmap, + int x, int y, const SkPaint& paint); + virtual void drawText(const SkDraw&, const void* text, size_t len, + SkScalar x, SkScalar y, const SkPaint& paint); + virtual void drawPosText(const SkDraw&, const void* text, size_t len, + const SkScalar pos[], SkScalar constY, + int scalarsPerPos, const SkPaint& paint); + virtual void drawTextOnPath(const SkDraw&, const void* text, size_t len, + const SkPath& path, const SkMatrix* matrix, + const SkPaint& paint); + virtual void drawVertices(const SkDraw&, SkCanvas::VertexMode, int vertexCount, + const SkPoint verts[], const SkPoint texs[], + const SkColor colors[], SkXfermode* xmode, + const uint16_t indices[], int indexCount, + const SkPaint& paint); + virtual void drawDevice(const SkDraw&, SkDevice*, int x, int y, + const SkPaint&); + +protected: + /** Return the current glmatrix, from a previous call to setMatrixClip */ + const SkMatrix& matrix() const { return fMatrix; } + /** Return the current clip, from a previous call to setMatrixClip */ + const SkRegion& clip() const { return fClip; } + +private: + SkGLMatrix fGLMatrix; + SkMatrix fMatrix; + SkRegion fClip; + bool fDirty; + + SkGLClipIter fClipIter; + SkGLDrawProcs* fDrawProcs; + + void setupForText(SkDraw* draw, const SkPaint& paint); + + // global texture cache methods + class TexCache; + static TexCache* LockTexCache(const SkBitmap&, GLuint* name, + SkPoint* size); + static void UnlockTexCache(TexCache*); + class SkAutoLockTexCache { + public: + SkAutoLockTexCache(const SkBitmap& bitmap, GLuint* name, + SkPoint* size) { + fTex = SkGLDevice::LockTexCache(bitmap, name, size); + } + ~SkAutoLockTexCache() { + if (fTex) { + SkGLDevice::UnlockTexCache(fTex); + } + } + TexCache* get() const { return fTex; } + private: + TexCache* fTex; + }; + friend class SkAutoTexCache; + + // returns cache if the texture is bound for the shader + TexCache* setupGLPaintShader(const SkPaint& paint); + + class AutoPaintShader { + public: + AutoPaintShader(SkGLDevice*, const SkPaint& paint); + ~AutoPaintShader(); + + bool useTex() const { return fTexCache != 0; } + private: + SkGLDevice* fDevice; + TexCache* fTexCache; + }; + friend class AutoPaintShader; + + typedef SkDevice INHERITED; +}; + +#endif + diff --git a/skia/gl/SkGLDevice_FBO.cpp b/skia/gl/SkGLDevice_FBO.cpp new file mode 100644 index 0000000..552d619 --- /dev/null +++ b/skia/gl/SkGLDevice_FBO.cpp @@ -0,0 +1,57 @@ +#include "SkGLDevice_FBO.h" +#include "SkRegion.h" + +SkGLDevice_FBO::SkGLDevice_FBO(const SkBitmap& bitmap, bool offscreen) + : SkGLDevice(bitmap, offscreen) { + fFBO = 0; + fTextureID = 0; + + if (offscreen) { + int nw = SkNextPow2(bitmap.rowBytesAsPixels()); + int nh = SkNextPow2(bitmap.height()); + + glGenFramebuffersEXT(1, &fFBO); + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fFBO); + + glGenTextures(1, &fTextureID); + glBindTexture(GL_TEXTURE_2D, fTextureID); + SkGL::SetTexParamsClamp(false); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, nw, nh, 0, + GL_RGBA, GL_UNSIGNED_BYTE, NULL); + + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, + GL_TEXTURE_2D, fTextureID, 0); + GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); + if (status != GL_FRAMEBUFFER_COMPLETE_EXT) { + SkDebugf("-- glCheckFramebufferStatusEXT %x\n", status); + } + + // now reset back to "normal" drawing target + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + } +} + +SkGLDevice_FBO::~SkGLDevice_FBO() { + if (fTextureID) { + glDeleteTextures(1, &fTextureID); + } + if (fFBO) { + glDeleteFramebuffersEXT(1, &fFBO); + } +} + +SkGLDevice::TexOrientation SkGLDevice_FBO::bindDeviceAsTexture() { + if (fTextureID) { + glBindTexture(GL_TEXTURE_2D, fTextureID); + return kBottomToTop_TexOrientation; + } + return kNo_TexOrientation; +} + +void SkGLDevice_FBO::gainFocus(SkCanvas* canvas) { + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fFBO); + + // now we're ready for the viewport and projection matrix + this->INHERITED::gainFocus(canvas); +} + diff --git a/skia/gl/SkGLDevice_FBO.h b/skia/gl/SkGLDevice_FBO.h new file mode 100644 index 0000000..d695ff0 --- /dev/null +++ b/skia/gl/SkGLDevice_FBO.h @@ -0,0 +1,23 @@ +#ifndef SkGLDevice_FBO_DEFINED +#define SkGLDevice_FBO_DEFINED + +#include "SkGLDevice.h" + +class SkGLDevice_FBO : public SkGLDevice { +public: + SkGLDevice_FBO(const SkBitmap& bitmap, bool offscreen); + virtual ~SkGLDevice_FBO(); + + // overrides from SkGLDevice + virtual void gainFocus(SkCanvas*); + virtual TexOrientation bindDeviceAsTexture(); + +private: + GLuint fFBO; + GLuint fTextureID; + + typedef SkGLDevice INHERITED; +}; + +#endif + diff --git a/skia/gl/SkGLDevice_SWLayer.cpp b/skia/gl/SkGLDevice_SWLayer.cpp new file mode 100644 index 0000000..4b75d4c --- /dev/null +++ b/skia/gl/SkGLDevice_SWLayer.cpp @@ -0,0 +1,91 @@ +#include "SkGLDevice_SWLayer.h" +#include "SkRegion.h" + +SkGLDevice_SWLayer::SkGLDevice_SWLayer(const SkBitmap& bitmap) + : SkGLDevice(bitmap, true) { + fTextureID = 0; + + SkASSERT(bitmap.getPixels()); +} + +SkGLDevice_SWLayer::~SkGLDevice_SWLayer() { + if (fTextureID) { + glDeleteTextures(1, &fTextureID); + } +} + +SkGLDevice::TexOrientation SkGLDevice_SWLayer::bindDeviceAsTexture() { + const SkBitmap& bitmap = this->accessBitmap(false); + + if (0 == fTextureID) { + fTextureID = SkGL::BindNewTexture(bitmap, NULL); + } + return kTopToBottom_TexOrientation; +} + +/////////////////////////////////////////////////////////////////////////////// + +#include "SkDraw.h" + +void SkGLDevice_SWLayer::drawPaint(const SkDraw& draw, const SkPaint& paint) { + draw.drawPaint(paint); +} + +void SkGLDevice_SWLayer::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode, size_t count, + const SkPoint pts[], const SkPaint& paint) { + draw.drawPoints(mode, count, pts, paint); +} + +void SkGLDevice_SWLayer::drawRect(const SkDraw& draw, const SkRect& r, + const SkPaint& paint) { + draw.drawRect(r, paint); +} + +void SkGLDevice_SWLayer::drawPath(const SkDraw& draw, const SkPath& path, + const SkPaint& paint) { + draw.drawPath(path, paint); +} + +void SkGLDevice_SWLayer::drawBitmap(const SkDraw& draw, const SkBitmap& bitmap, + const SkMatrix& matrix, const SkPaint& paint) { + draw.drawBitmap(bitmap, matrix, paint); +} + +void SkGLDevice_SWLayer::drawSprite(const SkDraw& draw, const SkBitmap& bitmap, + int x, int y, const SkPaint& paint) { + draw.drawSprite(bitmap, x, y, paint); +} + +void SkGLDevice_SWLayer::drawText(const SkDraw& draw, const void* text, size_t len, + SkScalar x, SkScalar y, const SkPaint& paint) { + draw.drawText((const char*)text, len, x, y, paint); +} + +void SkGLDevice_SWLayer::drawPosText(const SkDraw& draw, const void* text, size_t len, + const SkScalar xpos[], SkScalar y, + int scalarsPerPos, const SkPaint& paint) { + draw.drawPosText((const char*)text, len, xpos, y, scalarsPerPos, paint); +} + +void SkGLDevice_SWLayer::drawTextOnPath(const SkDraw& draw, const void* text, + size_t len, const SkPath& path, + const SkMatrix* matrix, + const SkPaint& paint) { + draw.drawTextOnPath((const char*)text, len, path, matrix, paint); +} + +void SkGLDevice_SWLayer::drawVertices(const SkDraw& draw, SkCanvas::VertexMode vmode, + int vertexCount, + const SkPoint verts[], const SkPoint textures[], + const SkColor colors[], SkXfermode* xmode, + const uint16_t indices[], int indexCount, + const SkPaint& paint) { + draw.drawVertices(vmode, vertexCount, verts, textures, colors, xmode, + indices, indexCount, paint); +} + +void SkGLDevice_SWLayer::drawDevice(const SkDraw& draw, SkDevice* dev, + int x, int y, const SkPaint& paint) { + this->SkDevice::drawDevice(draw, dev, x, y, paint); +} + diff --git a/skia/gl/SkGLDevice_SWLayer.h b/skia/gl/SkGLDevice_SWLayer.h new file mode 100644 index 0000000..7e61370 --- /dev/null +++ b/skia/gl/SkGLDevice_SWLayer.h @@ -0,0 +1,49 @@ +#ifndef SkGLDevice_SWLayer_DEFINED +#define SkGLDevice_SWLayer_DEFINED + +#include "SkGLDevice.h" + +class SkGLDevice_SWLayer : public SkGLDevice { +public: + SkGLDevice_SWLayer(const SkBitmap& bitmap); + virtual ~SkGLDevice_SWLayer(); + + // overrides from SkGLDevice + virtual TexOrientation bindDeviceAsTexture(); + + // overrides from SkDevice + virtual void drawPaint(const SkDraw&, const SkPaint& paint); + virtual void drawPoints(const SkDraw&, SkCanvas::PointMode mode, size_t count, + const SkPoint[], const SkPaint& paint); + virtual void drawRect(const SkDraw&, const SkRect& r, + const SkPaint& paint); + virtual void drawPath(const SkDraw&, const SkPath& path, + const SkPaint& paint); + virtual void drawBitmap(const SkDraw&, const SkBitmap& bitmap, + const SkMatrix& matrix, const SkPaint& paint); + virtual void drawSprite(const SkDraw&, const SkBitmap& bitmap, + int x, int y, const SkPaint& paint); + virtual void drawText(const SkDraw&, const void* text, size_t len, + SkScalar x, SkScalar y, const SkPaint& paint); + virtual void drawPosText(const SkDraw&, const void* text, size_t len, + const SkScalar pos[], SkScalar constY, + int scalarsPerPos, const SkPaint& paint); + virtual void drawTextOnPath(const SkDraw&, const void* text, size_t len, + const SkPath& path, const SkMatrix* matrix, + const SkPaint& paint); + virtual void drawVertices(const SkDraw&, SkCanvas::VertexMode, int vertexCount, + const SkPoint verts[], const SkPoint texs[], + const SkColor colors[], SkXfermode* xmode, + const uint16_t indices[], int indexCount, + const SkPaint& paint); + virtual void drawDevice(const SkDraw&, SkDevice*, int x, int y, + const SkPaint&); + +private: + GLuint fTextureID; + + typedef SkGLDevice INHERITED; +}; + +#endif + diff --git a/skia/gl/SkGLTextCache.cpp b/skia/gl/SkGLTextCache.cpp new file mode 100644 index 0000000..25f822e --- /dev/null +++ b/skia/gl/SkGLTextCache.cpp @@ -0,0 +1,201 @@ +#include "SkGLTextCache.h" +#include "SkScalerContext.h" +#include "SkTSearch.h" + +const GLenum gTextTextureFormat = GL_ALPHA; +const GLenum gTextTextureType = GL_UNSIGNED_BYTE; + +SkGLTextCache::Strike::Strike(Strike* next, int width, int height) { + fStrikeWidth = SkNextPow2(SkMax32(kMinStrikeWidth, width)); + fStrikeHeight = SkNextPow2(height); + fGlyphCount = 0; + fNextFreeOffsetX = 0; + fNext = next; + + fStrikeWidthShift = SkNextLog2(fStrikeWidth); + fStrikeHeightShift = SkNextLog2(fStrikeHeight); + + if (next) { + SkASSERT(next->fStrikeHeight == fStrikeHeight); + } + + // create an empty texture to receive glyphs + fTexName = 0; + glGenTextures(1, &fTexName); + glBindTexture(GL_TEXTURE_2D, fTexName); + glTexImage2D(GL_TEXTURE_2D, 0, gTextTextureFormat, + fStrikeWidth, fStrikeHeight, 0, + gTextTextureFormat, gTextTextureType, NULL); + + SK_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + SK_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + SK_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + SK_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); +} + +SkGLTextCache::Strike::~Strike() { + if (fTexName != 0) { + glDeleteTextures(1, &fTexName); + } +} + +SkGLTextCache::Strike* +SkGLTextCache::Strike::findGlyph(const SkGlyph& glyph, int* offset) { + Strike* strike = this; + SkDEBUGCODE(const int height = SkNextPow2(glyph.fHeight);) + + do { + SkASSERT(height == strike->fStrikeHeight); + + int index = SkTSearch(strike->fGlyphIDArray, strike->fGlyphCount, + glyph.fID, sizeof(strike->fGlyphIDArray[0])); + if (index >= 0) { + if (offset) { + *offset = strike->fGlyphOffsetX[index]; + } + return strike; + } + strike = strike->fNext; + } while (NULL != strike); + return NULL; +} + +static void make_a_whole(void* buffer, int index, int count, size_t elemSize) { + SkASSERT(index >= 0 && index <= count); + size_t offset = index * elemSize; + memmove((char*)buffer + offset + elemSize, + (const char*)buffer + offset, + (count - index) * elemSize); +} + +SkGLTextCache::Strike* +SkGLTextCache::Strike::addGlyphAndBind(const SkGlyph& glyph, + const uint8_t image[], int* offset) { +#ifdef SK_DEBUG + SkASSERT(this->findGlyph(glyph, NULL) == NULL); + const int height = SkNextPow2(glyph.fHeight); + SkASSERT(height <= fStrikeHeight && height > (fStrikeHeight >> 1)); +#endif + + int rowBytes = glyph.rowBytes(); + SkASSERT(rowBytes >= glyph.fWidth); + + Strike* strike; + if (fGlyphCount == kMaxGlyphCount || + fNextFreeOffsetX + rowBytes >= fStrikeWidth) { + // this will bind the next texture for us + SkDebugf("--- extend strike %p\n", this); + strike = SkNEW_ARGS(Strike, (this, rowBytes, glyph.fHeight)); + } else { + glBindTexture(GL_TEXTURE_2D, fTexName); + strike = this; + } + + uint32_t* idArray = strike->fGlyphIDArray; + uint16_t* offsetArray = strike->fGlyphOffsetX; + const int glyphCount = strike->fGlyphCount; + + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glTexSubImage2D(GL_TEXTURE_2D, 0, strike->fNextFreeOffsetX, 0, rowBytes, + glyph.fHeight, gTextTextureFormat, gTextTextureType, + image); + + // need to insert the offset + int index = SkTSearch(idArray, glyphCount, glyph.fID, sizeof(idArray[0])); + SkASSERT(index < 0); + index = ~index; // this is where we should insert it + make_a_whole(idArray, index, glyphCount, sizeof(idArray)); + make_a_whole(offsetArray, index, glyphCount, sizeof(offsetArray[0])); + idArray[index] = glyph.fID; + offsetArray[index] = strike->fNextFreeOffsetX; + if (offset) { + *offset = strike->fNextFreeOffsetX; + } + +#if 0 + SkDebugf("--- strike %p glyph %x [%d %d] offset %d count %d\n", + strike, glyph.fID, glyph.fWidth, glyph.fHeight, + strike->fNextFreeOffsetX, glyphCount + 1); +#endif + + // now update our header + strike->fGlyphCount = glyphCount + 1; + strike->fNextFreeOffsetX += glyph.fWidth; + return strike; +} + +/////////////////////////////////////////////////////////////////////////////// + +SkGLTextCache::SkGLTextCache() { + fCtx = SkGetGLContext(); + bzero(fStrikeList, sizeof(fStrikeList)); +} + +SkGLTextCache::~SkGLTextCache() { + SkDebugf("--- delete textcache %p\n", this); + + // if true, we need to not call glDeleteTexture, since they will have + // already gone out of scope + bool zap = SkGetGLContext() != fCtx; + + for (size_t i = 0; i < SK_ARRAY_COUNT(fStrikeList); i++) { + Strike* strike = fStrikeList[i]; + while (strike != NULL) { + Strike* next = strike->fNext; + if (zap) { + strike->zapTexture(); + } + SkDELETE(strike); + strike = next; + } + } +} + +SkGLTextCache::Strike* SkGLTextCache::findGlyph(const SkGlyph& glyph, + int* offset) { + if (SkGetGLContext() != fCtx) { + SkDebugf("====== stale context for text texture\n"); + } + + SkASSERT(glyph.fWidth != 0); + SkASSERT(glyph.fHeight != 0); + + size_t index = SkNextLog2(glyph.fHeight); + if (index >= SK_ARRAY_COUNT(fStrikeList)) { + // too big for us to cache; + return NULL; + } + + Strike* strike = fStrikeList[index]; + if (strike) { + strike = strike->findGlyph(glyph, offset); + } + return strike; +} + +SkGLTextCache::Strike* SkGLTextCache::addGlyphAndBind(const SkGlyph& glyph, + const uint8_t image[], int* offset) { + if (SkGetGLContext() != fCtx) { + SkDebugf("====== stale context for text texture\n"); + } + + SkASSERT(image != NULL); + SkASSERT(glyph.fWidth != 0); + SkASSERT(glyph.fHeight != 0); + + size_t index = SkNextLog2(glyph.fHeight); + if (index >= SK_ARRAY_COUNT(fStrikeList)) { + // too big for us to cache; + return NULL; + } + + Strike* strike = fStrikeList[index]; + if (NULL == strike) { + strike = SkNEW_ARGS(Strike, (NULL, glyph.rowBytes(), glyph.fHeight)); +// SkDebugf("--- create strike [%d] %p cache %p\n", index, strike, this); + } + strike = strike->addGlyphAndBind(glyph, image, offset); + fStrikeList[index] = strike; + return strike; +} + diff --git a/skia/gl/SkGLTextCache.h b/skia/gl/SkGLTextCache.h new file mode 100644 index 0000000..386b274 --- /dev/null +++ b/skia/gl/SkGLTextCache.h @@ -0,0 +1,83 @@ +#ifndef SkGLTextCache_DEFINED +#define SkGLTextCache_DEFINED + +#include "SkGL.h" + +class SkGlyph; + +class SkGLTextCache { +public: + SkGLTextCache(); + ~SkGLTextCache(); + + void* getCtx() const { return fCtx; } + + class Strike { + public: + int width() const { return fStrikeWidth; } + int height() const { return fStrikeHeight; } + GLuint texture() const { return fTexName; } + int widthShift() const { return fStrikeWidthShift; } + int heightShift() const { return fStrikeHeightShift; } + + // call this to force us to ignore the texture name in our destructor + // only call it right before our destructor + void zapTexture() { fTexName = 0; } + + private: + // if next is non-null, its height must match our height + Strike(Strike* next, int width, int height); + ~Strike(); + + Strike* findGlyph(const SkGlyph&, int* offset); + Strike* addGlyphAndBind(const SkGlyph&, const uint8_t*, int* offset); + + enum { + kMinStrikeWidth = 1024, + kMaxGlyphCount = 256 + }; + + Strike* fNext; + GLuint fTexName; + uint32_t fGlyphIDArray[kMaxGlyphCount]; // stores glyphIDs + uint16_t fGlyphOffsetX[kMaxGlyphCount]; // stores x-offsets + uint16_t fGlyphCount; + uint16_t fNextFreeOffsetX; + uint16_t fStrikeWidth; + uint16_t fStrikeHeight; + uint8_t fStrikeWidthShift; // pow2(fStrikeWidth) + uint8_t fStrikeHeightShift; // pow2(fStrikeHeight) + + friend class SkGLTextCache; + }; + + /** If found, returns the exact strike containing it (there may be more than + one with a given height), and sets offset to the offset for that glyph + (if not null). Does NOT bind the texture. + If not found, returns null and ignores offset param. + */ + Strike* findGlyph(const SkGlyph&, int* offset); + + /** Adds the specified glyph to this list of strikes, returning the new + head of the list. If offset is not null, it is set to the offset + for this glyph within the strike. The associated texture is bound + to the gl context. + */ + Strike* addGlyphAndBind(const SkGlyph&, const uint8_t image[], int* offset); + +private: + void* fCtx; + + enum { + // greater than this we won't cache + kMaxGlyphHeightShift = 9, + + kMaxGlyphHeight = 1 << kMaxGlyphHeightShift, + kMaxStrikeListCount = kMaxGlyphHeightShift + 1 + }; + + // heads of the N families, one for each pow2 height + Strike* fStrikeList[kMaxStrikeListCount]; +}; + +#endif diff --git a/skia/gl/SkTextureCache.cpp b/skia/gl/SkTextureCache.cpp new file mode 100644 index 0000000..6d578fe --- /dev/null +++ b/skia/gl/SkTextureCache.cpp @@ -0,0 +1,362 @@ +#include "SkTextureCache.h" + +//#define TRACE_HASH_HITS + +SkTextureCache::Entry::Entry(const SkBitmap& bitmap) + : fName(0), fKey(bitmap), fPrev(NULL), fNext(NULL) { + + fMemSize = SkGL::ComputeTextureMemorySize(bitmap); + fLockCount = 0; +} + +SkTextureCache::Entry::~Entry() { + if (fName != 0) { + glDeleteTextures(1, &fName); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +SkTextureCache::SkTextureCache(size_t countMax, size_t sizeMax) + : fHead(NULL), fTail(NULL), + fTexCountMax(countMax), fTexSizeMax(sizeMax), + fTexCount(0), fTexSize(0) { + + bzero(fHash, sizeof(fHash)); + this->validate(); +} + +SkTextureCache::~SkTextureCache() { +#ifdef SK_DEBUG + Entry* entry = fHead; + while (entry) { + SkASSERT(entry->lockCount() == 0); + entry = entry->fNext; + } +#endif + this->validate(); +} + +/////////////////////////////////////////////////////////////////////////////// + +int SkTextureCache::findInSorted(const Key& key) const { + int count = fSorted.count(); + if (count == 0) { + return ~0; + } + + Entry** sorted = fSorted.begin(); + int lo = 0; + int hi = count - 1; + while (lo < hi) { + int mid = (hi + lo) >> 1; + if (sorted[mid]->getKey() < key) { + lo = mid + 1; + } else { + hi = mid; + } + } + + // hi is now our best guess + const Entry* entry = sorted[hi]; + if (entry->getKey() == key) { + return hi; + } + + // return where to insert it + if (entry->getKey() < key) { + hi += 1; + } + return ~hi; // we twiddle to indicate not-found +} + +#ifdef TRACE_HASH_HITS +static int gHashHits; +static int gSortedHits; +#endif + +SkTextureCache::Entry* SkTextureCache::find(const Key& key, int* insert) const { + int count = fSorted.count(); + if (count == 0) { + *insert = 0; + return NULL; + } + + // check the hash first + int hashIndex = key.getHashIndex(); + Entry* entry = fHash[hashIndex]; + if (NULL != entry && entry->getKey() == key) { +#ifdef TRACE_HASH_HITS + gHashHits += 1; +#endif + return entry; + } + + int index = this->findInSorted(key); + if (index >= 0) { +#ifdef TRACE_HASH_HITS + gSortedHits += 1; +#endif + entry = fSorted[index]; + fHash[hashIndex] = entry; + return entry; + } + + // ~index is where to insert the entry + *insert = ~index; + return NULL; +} + +SkTextureCache::Entry* SkTextureCache::lock(const SkBitmap& bitmap) { + this->validate(); + + // call this before we call find(), so we don't reorder after find() and + // invalidate our index + this->purgeIfNecessary(SkGL::ComputeTextureMemorySize(bitmap)); + + Key key(bitmap); + int index; + Entry* entry = this->find(key, &index); + + if (NULL == entry) { + entry = SkNEW_ARGS(Entry, (bitmap)); + + entry->fName = SkGL::BindNewTexture(bitmap, &entry->fTexSize); + if (0 == entry->fName) { + SkDELETE(entry); + return NULL; + } + fHash[key.getHashIndex()] = entry; + *fSorted.insert(index) = entry; + + fTexCount += 1; + fTexSize += entry->memSize(); + } else { + // detach from our llist + Entry* prev = entry->fPrev; + Entry* next = entry->fNext; + if (prev) { + prev->fNext = next; + } else { + SkASSERT(fHead == entry); + fHead = next; + } + if (next) { + next->fPrev = prev; + } else { + SkASSERT(fTail == entry); + fTail = prev; + } + // now bind the texture + glBindTexture(GL_TEXTURE_2D, entry->fName); + } + + // add to head of llist for LRU + entry->fPrev = NULL; + entry->fNext = fHead; + if (NULL != fHead) { + SkASSERT(NULL == fHead->fPrev); + fHead->fPrev = entry; + } + fHead = entry; + if (NULL == fTail) { + fTail = entry; + } + + this->validate(); + entry->lock(); + +#ifdef TRACE_HASH_HITS + SkDebugf("---- texture cache hash=%d sorted=%d\n", gHashHits, gSortedHits); +#endif + return entry; +} + +void SkTextureCache::unlock(Entry* entry) { + this->validate(); + +#ifdef SK_DEBUG + SkASSERT(entry); + int index = this->findInSorted(entry->getKey()); + SkASSERT(fSorted[index] == entry); +#endif + + SkASSERT(entry->fLockCount > 0); + entry->unlock(); +} + +void SkTextureCache::purgeIfNecessary(size_t extraSize) { + this->validate(); + + size_t countMax = fTexCountMax; + size_t sizeMax = fTexSizeMax; + + // take extraSize into account, but watch for underflow of size_t + if (extraSize > sizeMax) { + sizeMax = 0; + } else { + sizeMax -= extraSize; + } + + Entry* entry = fTail; + while (entry) { + if (fTexCount <= countMax && fTexSize <= sizeMax) { + break; + } + + Entry* prev = entry->fPrev; + // don't purge an entry that is locked + if (entry->isLocked()) { + entry = prev; + continue; + } + + fTexCount -= 1; + fTexSize -= entry->memSize(); + + // remove from our sorted and hash arrays + int index = this->findInSorted(entry->getKey()); + SkASSERT(index >= 0); + fSorted.remove(index); + index = entry->getKey().getHashIndex(); + if (entry == fHash[index]) { + fHash[index] = NULL; + } + + // now detach it from our llist + Entry* next = entry->fNext; + if (prev) { + prev->fNext = next; + } else { + fHead = next; + } + if (next) { + next->fPrev = prev; + } else { + fTail = prev; + } + + // now delete it + SkDebugf("---- purge texture cache %d size=%d\n", + entry->name(), entry->memSize()); + SkDELETE(entry); + + // keep going + entry = prev; + } + + this->validate(); +} + +void SkTextureCache::setMaxCount(size_t count) { + if (fTexCountMax != count) { + fTexCountMax = count; + this->purgeIfNecessary(0); + } +} + +void SkTextureCache::setMaxSize(size_t size) { + if (fTexSizeMax != size) { + fTexSizeMax = size; + this->purgeIfNecessary(0); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void SkTextureCache::zapAllTextures() { + SkDebugf("---- zapAllTextures\n"); + + this->validate(); + + Entry* entry = fHead; + while (entry) { + Entry* next = entry->fNext; + entry->zapName(); + SkDELETE(entry); + entry = next; + } + + fSorted.reset(); + bzero(fHash, sizeof(fHash)); + + fTexCount = 0; + fTexSize = 0; + + fTail = fHead = NULL; + + this->validate(); +} + +/////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_DEBUG +void SkTextureCache::validate() const { + if (0 == fTexCount) { + SkASSERT(0 == fTexSize); + SkASSERT(NULL == fHead); + SkASSERT(NULL == fTail); + return; + } + + SkASSERT(fTexSize); // do we allow a zero-sized texture? + SkASSERT(fHead); + SkASSERT(fTail); + + SkASSERT(NULL == fHead->fPrev); + SkASSERT(NULL == fTail->fNext); + if (1 == fTexCount) { + SkASSERT(fHead == fTail); + } + + const Entry* entry = fHead; + size_t count = 0; + size_t size = 0; + size_t i; + + while (entry != NULL) { + SkASSERT(count < fTexCount); + SkASSERT(size < fTexSize); + size += entry->memSize(); + count += 1; + if (NULL == entry->fNext) { + SkASSERT(fTail == entry); + } + entry = entry->fNext; + } + SkASSERT(count == fTexCount); + SkASSERT(size == fTexSize); + + count = 0; + size = 0; + entry = fTail; + while (entry != NULL) { + SkASSERT(count < fTexCount); + SkASSERT(size < fTexSize); + size += entry->memSize(); + count += 1; + if (NULL == entry->fPrev) { + SkASSERT(fHead == entry); + } + entry = entry->fPrev; + } + SkASSERT(count == fTexCount); + SkASSERT(size == fTexSize); + + SkASSERT(count == (size_t)fSorted.count()); + for (i = 1; i < count; i++) { + SkASSERT(fSorted[i-1]->getKey() < fSorted[i]->getKey()); + } + + for (i = 0; i < kHashCount; i++) { + if (fHash[i]) { + size_t index = fHash[i]->getKey().getHashIndex(); + SkASSERT(index == i); + index = fSorted.find(fHash[i]); + SkASSERT((size_t)index < count); + } + } +} +#endif + + diff --git a/skia/gl/SkTextureCache.h b/skia/gl/SkTextureCache.h new file mode 100644 index 0000000..e4ffc78 --- /dev/null +++ b/skia/gl/SkTextureCache.h @@ -0,0 +1,160 @@ +#ifndef SkTextureCache_DEFINED +#define SkTextureCache_DEFINED + +#include "SkBitmap.h" +#include "SkPoint.h" +#include "SkGL.h" +#include "SkTDArray.h" + +class SkTextureCache { +public: + SkTextureCache(size_t maxCount, size_t maxSize); + ~SkTextureCache(); + + size_t getMaxCount() { return fTexCountMax; } + size_t getMaxSize() { return fTexSizeMax; } + + void setMaxCount(size_t count); + void setMaxSize(size_t size); + + /** Call this if the context has changed behind our backs, and the cache + needs to abandon all of its existing textures. This saves us from using + or deleting a texture created from a different context + */ + void zapAllTextures(); + + static int HashMask() { return kHashMask; } + + class Key { + public: + Key(const SkBitmap& bm) { + fGenID = bm.getGenerationID(); + fOffset = bm.pixelRefOffset(); + fWH = (bm.width() << 16) | bm.height(); + this->computeHash(); + } + + int getHashIndex() const { return fHashIndex; } + + friend bool operator==(const Key& a, const Key& b) { + return a.fHash == b.fHash && + a.fGenID == b.fGenID && + a.fOffset == b.fOffset && + a.fWH == b.fWH; + } + + friend bool operator<(const Key& a, const Key& b) { + if (a.fHash < b.fHash) { + return true; + } else if (a.fHash > b.fHash) { + return false; + } + + if (a.fGenID < b.fGenID) { + return true; + } else if (a.fGenID > b.fGenID) { + return false; + } + + if (a.fOffset < b.fOffset) { + return true; + } else if (a.fOffset > b.fOffset) { + return false; + } + + return a.fWH < b.fWH; + } + + private: + void computeHash() { + uint32_t hash = fGenID ^ fOffset ^ fWH; + fHash = hash; + hash ^= hash >> 16; + fHashIndex = hash & SkTextureCache::HashMask(); + } + + uint32_t fHash; // computed from the other fields + uint32_t fGenID; + size_t fOffset; + uint32_t fWH; + // for indexing into the texturecache's fHash + int fHashIndex; + }; + + class Entry { + public: + GLuint name() const { return fName; } + SkPoint texSize() const { return fTexSize; } + size_t memSize() const { return fMemSize; } + const Key& getKey() const { return fKey; } + + // call this to clear the texture name, in case the context has changed + // in which case we should't reference or delete this texture in GL + void zapName() { fName = 0; } + + private: + Entry(const SkBitmap& bitmap); + ~Entry(); + + int lockCount() const { return fLockCount; } + bool isLocked() const { return fLockCount > 0; } + + void lock() { fLockCount += 1; } + void unlock() { + SkASSERT(fLockCount > 0); + fLockCount -= 1; + } + + private: + GLuint fName; + SkPoint fTexSize; + Key fKey; + size_t fMemSize; + int fLockCount; + + Entry* fPrev; + Entry* fNext; + + friend class SkTextureCache; + }; + + Entry* lock(const SkBitmap&); + void unlock(Entry*); + +private: + void purgeIfNecessary(size_t extraSize); + +#ifdef SK_DEBUG + void validate() const; +#else + void validate() const {} +#endif + + Entry* fHead; + Entry* fTail; + + // limits for the cache + size_t fTexCountMax; + size_t fTexSizeMax; + + // current values for the cache + size_t fTexCount; + size_t fTexSize; + + enum { + kHashBits = 6, + kHashCount = 1 << kHashBits, + kHashMask = kHashCount - 1 + }; + mutable Entry* fHash[kHashCount]; + SkTDArray<Entry*> fSorted; + + /* If we find the key, return the entry and ignore index. If we don't, + return NULL and set index to the place to insert the entry in fSorted + */ + Entry* find(const Key&, int* index) const; + // returns index or <0 if not found. Does NOT update hash + int findInSorted(const Key& key) const; +}; + +#endif diff --git a/skia/images/SkBitmap_RLEPixels.h b/skia/images/SkBitmap_RLEPixels.h new file mode 100644 index 0000000..c83bc69 --- /dev/null +++ b/skia/images/SkBitmap_RLEPixels.h @@ -0,0 +1,19 @@ +#ifndef SkBitmap_RLEPixels_DEFINED +#define SkBitmap_RLEPixels_DEFINED + +#include "SkChunkAlloc.h" + +class SkBitmap_RLEPixels { +public: + SkBitmap_RLEPixels(int width, int height); + ~SkBitmap_RLEPixels(); + + uint8_t* yptrs() const { return fYPtrs; } + uint8_t* allocChunk(size_t chunk); + +private: + SkChunkAlloc fChunk; + uint8_t** fYPtrs; +}; + +#endif diff --git a/skia/images/SkCreateRLEPixelRef.cpp b/skia/images/SkCreateRLEPixelRef.cpp new file mode 100644 index 0000000..5756237 --- /dev/null +++ b/skia/images/SkCreateRLEPixelRef.cpp @@ -0,0 +1,120 @@ +#include "SkChunkAlloc.h" +#include "SkPackBits.h" +#include "SkBitmap.h" +#include "SkPixelRef.h" + +class RLEPixelRef : public SkPixelRef { +public: + RLEPixelRef(SkBitmap::RLEPixels* rlep, SkColorTable* ctable); + virtual ~RLEPixelRef(); + +protected: + // overrides from SkPixelRef + virtual void* onLockPixels(SkColorTable**); + virtual void onUnlockPixels(); + +private: + SkBitmap::RLEPixels* fRLEPixels; + SkColorTable* fCTable; +}; + +RLEPixelRef::RLEPixelRef(SkBitmap::RLEPixels* rlep, SkColorTable* ctable) + : SkPixelRef(NULL) { + fRLEPixels = rlep; // we now own this ptr + fCTable = ctable; + ctable->safeRef(); +} + +RLEPixelRef::~RLEPixelRef() { + SkDELETE(fRLEPixels); + fCTable->safeUnref(); +} + +void* RLEPixelRef::onLockPixels(SkColorTable** ct) { + *ct = fCTable; + return fRLEPixels; +} + +void RLEPixelRef::onUnlockPixels() { + // nothing to do +} + +///////////////////////////////////////////////////////////////////////////// + +class ChunkRLEPixels : public SkBitmap::RLEPixels { +public: + ChunkRLEPixels(int width, int height, size_t chunkSize) + : SkBitmap::RLEPixels(width, height), fStorage(chunkSize) { + } + + SkChunkAlloc fStorage; +}; + +SkPixelRef* SkCreateRLEPixelRef(const SkBitmap& src); +SkPixelRef* SkCreateRLEPixelRef(const SkBitmap& src) { + + if (SkBitmap::kIndex8_Config != src.config() && + SkBitmap::kA8_Config != src.config()) { + return NULL; + } + + size_t maxPacked = SkPackBits::ComputeMaxSize8(src.width()); + + // estimate the rle size based on the original size + size_t size = src.getSize() >> 3; + if (size < maxPacked) { + size = maxPacked; + } + + ChunkRLEPixels* rlePixels = SkNEW_ARGS(ChunkRLEPixels, + (src.width(), src.height(), size)); + + uint8_t* dstRow = NULL; + size_t free = 0; + size_t totalPacked = 0; + + for (int y = 0; y < src.height(); y++) { + const uint8_t* srcRow = src.getAddr8(0, y); + + if (free < maxPacked) { + dstRow = (uint8_t*)rlePixels->fStorage.allocThrow(size); + free = size; + } + size_t packedSize = SkPackBits::Pack8(srcRow, src.width(), dstRow); + SkASSERT(packedSize <= free); + rlePixels->setPackedAtY(y, dstRow); + + dstRow += packedSize; + free -= packedSize; + + totalPacked += packedSize; + } + +//#ifdef SK_DEBUG +#if 0 + // test + uint8_t* buffer = new uint8_t[src.width()]; + for (int y = 0; y < src.height(); y++) { + const uint8_t* srcRow = src.getAddr8(0, y); + SkPackBits::Unpack8(buffer, 0, src.width(), rlePixels->packedAtY(y)); + int n = memcmp(buffer, srcRow, src.width()); + if (n) { + SkDebugf("----- memcmp returned %d on line %d\n", n, y); + } + SkASSERT(n == 0); + } + delete[] buffer; + + size_t totalAlloc = src.height() * sizeof(uint8_t*) + totalPacked; + + SkDebugf("--- RLE: orig [%d %d] %d, rle %d %d savings %g\n", + src.width(), src.height(), src.getSize(), + src.height() * sizeof(uint8_t*), totalPacked, + (float)totalAlloc / src.getSize()); + +#endif + + // transfer ownership of rlePixels to our pixelref + return SkNEW_ARGS(RLEPixelRef, (rlePixels, src.getColorTable())); +} + diff --git a/skia/images/SkImageDecoder.cpp b/skia/images/SkImageDecoder.cpp new file mode 100644 index 0000000..3579e3c --- /dev/null +++ b/skia/images/SkImageDecoder.cpp @@ -0,0 +1,157 @@ +/* libs/graphics/images/SkImageDecoder.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkImageDecoder.h" +#include "SkStream.h" +#include "SkTemplates.h" + +static SkBitmap::Config gDeviceConfig = SkBitmap::kNo_Config; + +SkBitmap::Config SkImageDecoder::GetDeviceConfig() +{ + return gDeviceConfig; +} + +void SkImageDecoder::SetDeviceConfig(SkBitmap::Config config) +{ + gDeviceConfig = config; +} + +/////////////////////////////////////////////////////////////////////////////// + +SkImageDecoder::SkImageDecoder() + : fPeeker(NULL), fChooser(NULL), fAllocator(NULL), fSampleSize(1), + fDitherImage(true) { +} + +SkImageDecoder::~SkImageDecoder() { + fPeeker->safeUnref(); + fChooser->safeUnref(); + fAllocator->safeUnref(); +} + +SkImageDecoder::Format SkImageDecoder::getFormat() const { + return kUnknown_Format; +} + +SkImageDecoder::Peeker* SkImageDecoder::setPeeker(Peeker* peeker) { + SkRefCnt_SafeAssign(fPeeker, peeker); + return peeker; +} + +SkImageDecoder::Chooser* SkImageDecoder::setChooser(Chooser* chooser) { + SkRefCnt_SafeAssign(fChooser, chooser); + return chooser; +} + +SkBitmap::Allocator* SkImageDecoder::setAllocator(SkBitmap::Allocator* alloc) { + SkRefCnt_SafeAssign(fAllocator, alloc); + return alloc; +} + +void SkImageDecoder::setSampleSize(int size) { + if (size < 1) { + size = 1; + } + fSampleSize = size; +} + +bool SkImageDecoder::chooseFromOneChoice(SkBitmap::Config config, int width, + int height) const { + Chooser* chooser = fChooser; + + if (NULL == chooser) { // no chooser, we just say YES to decoding :) + return true; + } + chooser->begin(1); + chooser->inspect(0, config, width, height); + return chooser->choose() == 0; +} + +bool SkImageDecoder::allocPixelRef(SkBitmap* bitmap, + SkColorTable* ctable) const { + return bitmap->allocPixels(fAllocator, ctable); +} + +/////////////////////////////////////////////////////////////////////////////// + +bool SkImageDecoder::DecodeFile(const char file[], SkBitmap* bm, + SkBitmap::Config pref, Mode mode) { + SkASSERT(file); + SkASSERT(bm); + + SkFILEStream stream(file); + return stream.isValid() && + SkImageDecoder::DecodeStream(&stream, bm, pref, mode); +} + +bool SkImageDecoder::DecodeMemory(const void* buffer, size_t size, SkBitmap* bm, + SkBitmap::Config pref, Mode mode) { + if (0 == size) { + return false; + } + SkASSERT(buffer); + + SkMemoryStream stream(buffer, size); + return SkImageDecoder::DecodeStream(&stream, bm, pref, mode); +} + +bool SkImageDecoder::DecodeStream(SkStream* stream, SkBitmap* bm, + SkBitmap::Config pref, Mode mode) { + SkASSERT(stream); + SkASSERT(bm); + + SkImageDecoder* codec = SkImageDecoder::Factory(stream); + if (NULL != codec) { + SkBitmap tmp; + + SkAutoTDelete<SkImageDecoder> ad(codec); + + if (codec->onDecode(stream, &tmp, pref, mode)) { + /* We operate on a tmp bitmap until we know we succeed. This way + we're sure we don't change the caller's bitmap and then later + return false. Returning false must mean that their parameter + is unchanged. + */ + bm->swap(tmp); + return true; + } + } + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_SUPPORT_IMAGE_ENCODE + +SkImageEncoder::~SkImageEncoder() {} + +bool SkImageEncoder::encodeStream(SkWStream* stream, const SkBitmap& bm, + int quality) { + quality = SkMin32(100, SkMax32(0, quality)); + return this->onEncode(stream, bm, quality); +} + +bool SkImageEncoder::encodeFile(const char file[], const SkBitmap& bm, + int quality) { + quality = SkMin32(100, SkMax32(0, quality)); + SkFILEWStream stream(file); + return this->onEncode(&stream, bm, quality); +} + +#endif + diff --git a/skia/images/SkImageDecoder_libbmp.cpp b/skia/images/SkImageDecoder_libbmp.cpp new file mode 100644 index 0000000..0de760f --- /dev/null +++ b/skia/images/SkImageDecoder_libbmp.cpp @@ -0,0 +1,147 @@ +/* + * Copyright 2007, Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "bmpdecoderhelper.h" +#include "SkImageDecoder.h" +#include "SkScaledBitmapSampler.h" +#include "SkStream.h" +#include "SkColorPriv.h" +#include "SkTDArray.h" + +class SkBMPImageDecoder : public SkImageDecoder { +public: + SkBMPImageDecoder() {} + + virtual Format getFormat() const { + return kBMP_Format; + } + +protected: + virtual bool onDecode(SkStream* stream, SkBitmap* bm, + SkBitmap::Config pref, Mode mode); +}; + +SkImageDecoder* SkImageDecoder_BMP_Factory(SkStream*); +SkImageDecoder* SkImageDecoder_BMP_Factory(SkStream* stream) { + static const char kBmpMagic[] = { 'B', 'M' }; + + size_t len = stream->getLength(); + char buffer[sizeof(kBmpMagic)]; + + if (len > sizeof(kBmpMagic) && + stream->read(buffer, sizeof(kBmpMagic)) == sizeof(kBmpMagic) && + !memcmp(buffer, kBmpMagic, sizeof(kBmpMagic))) { + return SkNEW(SkBMPImageDecoder); + } + return NULL; +} + +/////////////////////////////////////////////////////////////////////////////// + +class SkBmpDecoderCallback : public image_codec::BmpDecoderCallback { +public: + // we don't copy the bitmap, just remember the pointer + SkBmpDecoderCallback(bool justBounds) : fJustBounds(justBounds) {} + + // override from BmpDecoderCallback + virtual uint8* SetSize(int width, int height) { + fWidth = width; + fHeight = height; + if (fJustBounds) { + return NULL; + } + + fRGB.setCount(width * height * 3); // 3 == r, g, b + return fRGB.begin(); + } + + int width() const { return fWidth; } + int height() const { return fHeight; } + uint8_t* rgb() const { return fRGB.begin(); } + +private: + SkTDArray<uint8_t> fRGB; + int fWidth; + int fHeight; + bool fJustBounds; +}; + +bool SkBMPImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, + SkBitmap::Config prefConfig, Mode mode) { + + size_t length = stream->getLength(); + SkAutoMalloc storage(length); + + if (stream->read(storage.get(), length) != length) { + return false; + } + + const bool justBounds = SkImageDecoder::kDecodeBounds_Mode == mode; + SkBmpDecoderCallback callback(justBounds); + + // Now decode the BMP into callback's rgb() array [r,g,b, r,g,b, ...] + { + image_codec::BmpDecoderHelper helper; + const int max_pixels = 16383*16383; // max width*height + if (!helper.DecodeImage((const char*)storage.get(), length, + max_pixels, &callback)) { + return false; + } + } + + // we don't need this anymore, so free it now (before we try to allocate + // the bitmap's pixels) rather than waiting for its destructor + storage.free(); + + int width = callback.width(); + int height = callback.height(); + SkBitmap::Config config = SkBitmap::kARGB_8888_Config; + + // only accept prefConfig if it makes sense for us + if (SkBitmap::kARGB_4444_Config == prefConfig || + SkBitmap::kRGB_565_Config == config) { + config = prefConfig; + } + + SkScaledBitmapSampler sampler(width, height, getSampleSize()); + + bm->setConfig(config, sampler.scaledWidth(), sampler.scaledHeight()); + bm->setIsOpaque(true); + if (justBounds) { + return true; + } + + if (!this->allocPixelRef(bm, NULL)) { + return false; + } + + SkAutoLockPixels alp(*bm); + + if (!sampler.begin(bm, SkScaledBitmapSampler::kRGB, getDitherImage())) { + return false; + } + + const int srcRowBytes = width * 3; + const int dstHeight = sampler.scaledHeight(); + const uint8_t* srcRow = callback.rgb(); + + srcRow += sampler.srcY0() * srcRowBytes; + for (int y = 0; y < dstHeight; y++) { + sampler.next(srcRow); + srcRow += sampler.srcDY() * srcRowBytes; + } + return true; +} diff --git a/skia/images/SkImageDecoder_libgif.cpp b/skia/images/SkImageDecoder_libgif.cpp new file mode 100644 index 0000000..3b5e420 --- /dev/null +++ b/skia/images/SkImageDecoder_libgif.cpp @@ -0,0 +1,331 @@ +/* libs/graphics/images/SkImageDecoder_libgif.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkImageDecoder.h" +#include "SkColor.h" +#include "SkColorPriv.h" +#include "SkStream.h" +#include "SkTemplates.h" +#include "SkPackBits.h" + +#include "gif_lib.h" + +class SkGIFImageDecoder : public SkImageDecoder { +public: + virtual Format getFormat() const { + return kGIF_Format; + } + +protected: + virtual bool onDecode(SkStream* stream, SkBitmap* bm, + SkBitmap::Config pref, Mode mode); +}; + +static const uint8_t gStartingIterlaceYValue[] = { + 0, 4, 2, 1 +}; +static const uint8_t gDeltaIterlaceYValue[] = { + 8, 8, 4, 2 +}; + +/* Implement the GIF interlace algorithm in an iterator. + 1) grab every 8th line beginning at 0 + 2) grab every 8th line beginning at 4 + 3) grab every 4th line beginning at 2 + 4) grab every 2nd line beginning at 1 +*/ +class GifInterlaceIter { +public: + GifInterlaceIter(int height) : fHeight(height) { + fStartYPtr = gStartingIterlaceYValue; + fDeltaYPtr = gDeltaIterlaceYValue; + + fCurrY = *fStartYPtr++; + fDeltaY = *fDeltaYPtr++; + } + + int currY() const { + SkASSERT(fStartYPtr); + SkASSERT(fDeltaYPtr); + return fCurrY; + } + + void next() { + SkASSERT(fStartYPtr); + SkASSERT(fDeltaYPtr); + + int y = fCurrY + fDeltaY; + // We went from an if statement to a while loop so that we iterate + // through fStartYPtr until a valid row is found. This is so that images + // that are smaller than 5x5 will not trash memory. + while (y >= fHeight) { + if (gStartingIterlaceYValue + + SK_ARRAY_COUNT(gStartingIterlaceYValue) == fStartYPtr) { + // we done + SkDEBUGCODE(fStartYPtr = NULL;) + SkDEBUGCODE(fDeltaYPtr = NULL;) + y = 0; + } else { + y = *fStartYPtr++; + fDeltaY = *fDeltaYPtr++; + } + } + fCurrY = y; + } + +private: + const int fHeight; + int fCurrY; + int fDeltaY; + const uint8_t* fStartYPtr; + const uint8_t* fDeltaYPtr; +}; + +/////////////////////////////////////////////////////////////////////////////// + +//#define GIF_STAMP "GIF" /* First chars in file - GIF stamp. */ +//#define GIF_STAMP_LEN (sizeof(GIF_STAMP) - 1) + +static int DecodeCallBackProc(GifFileType* fileType, GifByteType* out, + int size) { + SkStream* stream = (SkStream*) fileType->UserData; + return (int) stream->read(out, size); +} + +void CheckFreeExtension(SavedImage* Image) { + if (Image->ExtensionBlocks) { + FreeExtension(Image); + } +} + +// return NULL on failure +static const ColorMapObject* find_colormap(const GifFileType* gif) { + const ColorMapObject* cmap = gif->SColorMap; + if (NULL == cmap) { + cmap = gif->Image.ColorMap; + } + // some sanity checks + if ((unsigned)cmap->ColorCount > 256 || + cmap->ColorCount != (1 << cmap->BitsPerPixel)) { + cmap = NULL; + } + return cmap; +} + +// return -1 if not found (i.e. we're completely opaque) +static int find_transpIndex(const SavedImage& image, int colorCount) { + int transpIndex = -1; + for (int i = 0; i < image.ExtensionBlockCount; ++i) { + const ExtensionBlock* eb = image.ExtensionBlocks + i; + if (eb->Function == 0xF9 && eb->ByteCount == 4) { + if (eb->Bytes[0] & 1) { + transpIndex = (unsigned char)eb->Bytes[3]; + // check for valid transpIndex + if (transpIndex >= colorCount) { + transpIndex = -1; + } + break; + } + } + } + return transpIndex; +} + +bool SkGIFImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* bm, + SkBitmap::Config prefConfig, Mode mode) { + GifFileType* gif = DGifOpen(sk_stream, DecodeCallBackProc); + if (NULL == gif) { + return false; + } + + SkAutoTCallIProc<GifFileType, DGifCloseFile> acp(gif); + + SavedImage temp_save; + temp_save.ExtensionBlocks=NULL; + temp_save.ExtensionBlockCount=0; + SkAutoTCallVProc<SavedImage, CheckFreeExtension> acp2(&temp_save); + + int width, height; + GifRecordType recType; + GifByteType *extData; + + do { + if (DGifGetRecordType(gif, &recType) == GIF_ERROR) { + return false; + } + + switch (recType) { + case IMAGE_DESC_RECORD_TYPE: { + if (DGifGetImageDesc(gif) == GIF_ERROR) { + return false; + } + + if (gif->ImageCount < 1) { // sanity check + return false; + } + + width = gif->SWidth; + height = gif->SHeight; + if (width <= 0 || height <= 0 || + !this->chooseFromOneChoice(SkBitmap::kIndex8_Config, + width, height)) { + return false; + } + + bm->setConfig(SkBitmap::kIndex8_Config, width, height); + if (SkImageDecoder::kDecodeBounds_Mode == mode) + return true; + + SavedImage* image = &gif->SavedImages[gif->ImageCount-1]; + const GifImageDesc& desc = image->ImageDesc; + + // check for valid descriptor + if ( (desc.Top | desc.Left) < 0 || + desc.Left + desc.Width > width || + desc.Top + desc.Height > height) { + return false; + } + + // now we decode the colortable + int colorCount = 0; + { + const ColorMapObject* cmap = find_colormap(gif); + if (NULL == cmap) { + return false; + } + + colorCount = cmap->ColorCount; + SkColorTable* ctable = SkNEW_ARGS(SkColorTable, (colorCount)); + SkPMColor* colorPtr = ctable->lockColors(); + for (int index = 0; index < colorCount; index++) + colorPtr[index] = SkPackARGB32(0xFF, + cmap->Colors[index].Red, + cmap->Colors[index].Green, + cmap->Colors[index].Blue); + + int transpIndex = find_transpIndex(temp_save, colorCount); + if (transpIndex < 0) + ctable->setFlags(ctable->getFlags() | SkColorTable::kColorsAreOpaque_Flag); + else + colorPtr[transpIndex] = 0; // ram in a transparent SkPMColor + ctable->unlockColors(true); + + SkAutoUnref aurts(ctable); + if (!this->allocPixelRef(bm, ctable)) { + return false; + } + } + + SkAutoLockPixels alp(*bm); + + // time to decode the scanlines + // + uint8_t* scanline = bm->getAddr8(0, 0); + const int rowBytes = bm->rowBytes(); + const int innerWidth = desc.Width; + const int innerHeight = desc.Height; + + // abort if either inner dimension is <= 0 + if (innerWidth <= 0 || innerHeight <= 0) { + return false; + } + + // are we only a subset of the total bounds? + if ((desc.Top | desc.Left) > 0 || + innerWidth < width || innerHeight < height) + { + uint8_t fill = (uint8_t)gif->SBackGroundColor; + // check for valid fill index/color + if (fill >= (unsigned)colorCount) { + fill = 0; + } + memset(scanline, gif->SBackGroundColor, bm->getSize()); + // bump our starting address + scanline += desc.Top * rowBytes + desc.Left; + } + + // now decode each scanline + if (gif->Image.Interlace) + { + GifInterlaceIter iter(innerHeight); + for (int y = 0; y < innerHeight; y++) + { + uint8_t* row = scanline + iter.currY() * rowBytes; + if (DGifGetLine(gif, row, innerWidth) == GIF_ERROR) { + return false; + } + iter.next(); + } + } + else + { + // easy, non-interlace case + for (int y = 0; y < innerHeight; y++) { + if (DGifGetLine(gif, scanline, innerWidth) == GIF_ERROR) { + return false; + } + scanline += rowBytes; + } + } + goto DONE; + } break; + + case EXTENSION_RECORD_TYPE: + if (DGifGetExtension(gif, &temp_save.Function, + &extData) == GIF_ERROR) { + return false; + } + + while (extData != NULL) { + /* Create an extension block with our data */ + if (AddExtensionBlock(&temp_save, extData[0], + &extData[1]) == GIF_ERROR) { + return false; + } + if (DGifGetExtensionNext(gif, &extData) == GIF_ERROR) { + return false; + } + temp_save.Function = 0; + } + break; + + case TERMINATE_RECORD_TYPE: + break; + + default: /* Should be trapped by DGifGetRecordType */ + break; + } + } while (recType != TERMINATE_RECORD_TYPE); + +DONE: + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +SkImageDecoder* SkImageDecoder_GIF_Factory(SkStream* stream) { + char buf[GIF_STAMP_LEN]; + if (stream->read(buf, GIF_STAMP_LEN) == GIF_STAMP_LEN) { + if (memcmp(GIF_STAMP, buf, GIF_STAMP_LEN) == 0 || + memcmp(GIF87_STAMP, buf, GIF_STAMP_LEN) == 0 || + memcmp(GIF89_STAMP, buf, GIF_STAMP_LEN) == 0) { + return SkNEW(SkGIFImageDecoder); + } + } + return NULL; +} + diff --git a/skia/images/SkImageDecoder_libico.cpp b/skia/images/SkImageDecoder_libico.cpp new file mode 100644 index 0000000..8b1be08 --- /dev/null +++ b/skia/images/SkImageDecoder_libico.cpp @@ -0,0 +1,372 @@ +/* libs/graphics/images/SkImageDecoder_libico.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkImageDecoder.h" +#include "SkStream.h" +#include "SkColorPriv.h" +#include "SkTypes.h" + +class SkICOImageDecoder : public SkImageDecoder { +public: + SkICOImageDecoder(); + + virtual Format getFormat() const { + return kICO_Format; + } + +protected: + virtual bool onDecode(SkStream* stream, SkBitmap* bm, + SkBitmap::Config pref, Mode); +}; + +///////////////////////////////////////////////////////////////////////////////////////// + +//read bytes starting from the begin-th index in the buffer +//read in Intel order, and return an integer + +#define readByte(buffer,begin) buffer[begin] +#define read2Bytes(buffer,begin) buffer[begin]+(buffer[begin+1]<<8) +#define read4Bytes(buffer,begin) buffer[begin]+(buffer[begin+1]<<8)+(buffer[begin+2]<<16)+(buffer[begin+3]<<24) + +///////////////////////////////////////////////////////////////////////////////////////// + +SkImageDecoder* SkImageDecoder_ICO_Factory(SkStream*); +SkImageDecoder* SkImageDecoder_ICO_Factory(SkStream* stream) +{ + //i'm going to check if we basically have 0,0,1,0 (reserved = 0, type = 1) + //is that required and sufficient? + SkAutoMalloc autoMal(4); + unsigned char* buf = (unsigned char*)autoMal.get(); + stream->read((void*)buf, 4); + int reserved = read2Bytes(buf, 0); + int type = read2Bytes(buf, 2); + if (reserved != 0 || type != 1) //it's not an ico + return NULL; + return SkNEW(SkICOImageDecoder); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +SkICOImageDecoder::SkICOImageDecoder() +{ +} + +//helpers - my function pointer will call one of these, depending on the bitCount, each time through the inner loop +static void editPixelBit1(const int pixelNo, const unsigned char* buf, + const int xorOffset, int& x, int y, const int w, + SkBitmap* bm, int alphaByte, int m, int shift, SkPMColor* colors); +static void editPixelBit4(const int pixelNo, const unsigned char* buf, + const int xorOffset, int& x, int y, const int w, + SkBitmap* bm, int alphaByte, int m, int shift, SkPMColor* colors); +static void editPixelBit8(const int pixelNo, const unsigned char* buf, + const int xorOffset, int& x, int y, const int w, + SkBitmap* bm, int alphaByte, int m, int shift, SkPMColor* colors); +static void editPixelBit24(const int pixelNo, const unsigned char* buf, + const int xorOffset, int& x, int y, const int w, + SkBitmap* bm, int alphaByte, int m, int shift, SkPMColor* colors); +static void editPixelBit32(const int pixelNo, const unsigned char* buf, + const int xorOffset, int& x, int y, const int w, + SkBitmap* bm, int alphaByte, int m, int shift, SkPMColor* colors); + +bool SkICOImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, + SkBitmap::Config pref, Mode mode) +{ + size_t length = stream->read(NULL, 0); + SkAutoMalloc autoMal(length); + unsigned char* buf = (unsigned char*)autoMal.get(); + if (stream->read((void*)buf, length) != length) { + return false; + } + + //these should always be the same - should i use for error checking? - what about files that have some + //incorrect values, but still decode properly? + int reserved = read2Bytes(buf, 0); // 0 + int type = read2Bytes(buf, 2); // 1 + if (reserved != 0 || type != 1) + return false; + int count = read2Bytes(buf, 4); + + //need to at least have enough space to hold the initial table of info + if (length < (size_t)(6 + count*16)) + return false; + + int choice; + Chooser* chooser = this->getChooser(); + //FIXME:if no chooser, consider providing the largest color image + //what are the odds that the largest image would be monochrome? + if (NULL == chooser) { + choice = 0; + } else { + chooser->begin(count); + for (int i = 0; i < count; i++) + { + //need to find out the config, width, and height from the stream + int width = readByte(buf, 6 + i*16); + int height = readByte(buf, 7 + i*16); + int offset = read4Bytes(buf, 18 + i*16); + int bitCount = read2Bytes(buf, offset+14); + SkBitmap::Config c; + //currently only provide ARGB_8888_, but maybe we want kIndex8_Config for 1 and 4, and possibly 8? + //or maybe we'll determine this based on the provided config + switch (bitCount) + { + case 1: + case 4: + // In reality, at least for the moment, these will be decoded into kARGB_8888 bitmaps. + // However, this will be used to distinguish between the lower quality 1bpp and 4 bpp + // images and the higher quality images. + c = SkBitmap::kIndex8_Config; + break; + case 8: + case 24: + case 32: + c = SkBitmap::kARGB_8888_Config; + break; + default: + SkDEBUGF(("Image with %ibpp not supported\n", bitCount)); + continue; + } + chooser->inspect(i, c, width, height); + } + choice = chooser->choose(); + } + + //you never know what the chooser is going to supply + if (choice >= count || choice < 0) + return false; + + //skip ahead to the correct header + //commented out lines are not used, but if i switch to other read method, need to know how many to skip + //otherwise, they could be used for error checking + int w = readByte(buf, 6 + choice*16); + int h = readByte(buf, 7 + choice*16); + int colorCount = readByte(buf, 8 + choice*16); + //int reservedToo = readByte(buf, 9 + choice*16); //0 + //int planes = read2Bytes(buf, 10 + choice*16); //1 - but often 0 + //int fakeBitCount = read2Bytes(buf, 12 + choice*16); //should be real - usually 0 + int size = read4Bytes(buf, 14 + choice*16); //matters? + int offset = read4Bytes(buf, 18 + choice*16); + if ((size_t)(offset + size) > length) + return false; + //int infoSize = read4Bytes(buf, offset); //40 + //int width = read4Bytes(buf, offset+4); //should == w + //int height = read4Bytes(buf, offset+8); //should == 2*h + //int planesToo = read2Bytes(buf, offset+12); //should == 1 (does it?) + int bitCount = read2Bytes(buf, offset+14); + + void (*placePixel)(const int pixelNo, const unsigned char* buf, + const int xorOffset, int& x, int y, const int w, + SkBitmap* bm, int alphaByte, int m, int shift, SkPMColor* colors) = NULL; + switch (bitCount) + { + case 1: + placePixel = &editPixelBit1; + colorCount = 2; + break; + case 4: + placePixel = &editPixelBit4; + colorCount = 16; + break; + case 8: + placePixel = &editPixelBit8; + colorCount = 256; + break; + case 24: + placePixel = &editPixelBit24; + colorCount = 0; + break; + case 32: + placePixel = &editPixelBit32; + colorCount = 0; + break; + default: + SkDEBUGF(("Decoding %ibpp is unimplemented\n", bitCount)); + return false; + } + + //these should all be zero, but perhaps are not - need to check + //int compression = read4Bytes(buf, offset+16); //0 + //int imageSize = read4Bytes(buf, offset+20); //0 - sometimes has a value + //int xPixels = read4Bytes(buf, offset+24); //0 + //int yPixels = read4Bytes(buf, offset+28); //0 + //int colorsUsed = read4Bytes(buf, offset+32) //0 - might have an actual value though + //int colorsImportant = read4Bytes(buf, offset+36); //0 + + int begin = offset + 40; + //this array represents the colortable + //if i allow other types of bitmaps, it may actually be used as a part of the bitmap + SkPMColor* colors = NULL; + int blue, green, red; + if (colorCount) + { + colors = new SkPMColor[colorCount]; + for (int j = 0; j < colorCount; j++) + { + //should this be a function - maybe a #define? + blue = readByte(buf, begin + 4*j); + green = readByte(buf, begin + 4*j + 1); + red = readByte(buf, begin + 4*j + 2); + colors[j] = SkPackARGB32(0xFF, red & 0xFF, green & 0xFF, blue & 0xFF); + } + } + int bitWidth = w*bitCount; + int test = bitWidth & 0x1F; + int mask = -(((test >> 4) | (test >> 3) | (test >> 2) | (test >> 1) | test) & 0x1); //either 0xFFFFFFFF or 0 + int lineBitWidth = (bitWidth & 0xFFFFFFE0) + (0x20 & mask); + int lineWidth = lineBitWidth/bitCount; + + int xorOffset = begin + colorCount*4; //beginning of the color bitmap + //other read method means we will just be here already + int andOffset = xorOffset + ((lineWidth*h*bitCount) >> 3); + + /*int */test = w & 0x1F; //the low 5 bits - we are rounding up to the next 32 (2^5) + /*int */mask = -(((test >> 4) | (test >> 3) | (test >> 2) | (test >> 1) | test) & 0x1); //either 0xFFFFFFFF or 0 + int andLineWidth = (w & 0xFFFFFFE0) + (0x20 & mask); + //if we allow different Configs, everything is the same til here + //change the config, and use different address getter, and place index vs color, and add the color table + //FIXME: what is the tradeoff in size? + //if the andbitmap (mask) is all zeroes, then we can easily do an index bitmap + //however, with small images with large colortables, maybe it's better to still do argb_8888 + //default rowBytes is w << 2 for kARGB_8888 + //i'm adding one - it's only truly necessary in the case that w is odd and we are four bit + //so we can go off the end of the drawn bitmap + //FIXME: need to test with an odd width bitmap that is 4bit + bm->setConfig(SkBitmap::kARGB_8888_Config, w, h, (w << 2) + (0x1 & w & (bitCount >> 2))); + + if (SkImageDecoder::kDecodeBounds_Mode == mode) { + delete[] colors; + return true; + } + + if (!this->allocPixelRef(bm, NULL)) + { + delete[] colors; + return false; + } + + SkAutoLockPixels alp(*bm); + + for (int y = 0; y < h; y++) + { + for (int x = 0; x < w; x++) + { + //U32* address = bm->getAddr32(x, y); + + //check the alpha bit first, but pass it along to the function to figure out how to deal with it + int andPixelNo = andLineWidth*(h-y-1)+x; + //only need to get a new alphaByte when x %8 == 0 + //but that introduces an if and a mod - probably much slower + //that's ok, it's just a read of an array, not a stream + int alphaByte = readByte(buf, andOffset + (andPixelNo >> 3)); + int shift = 7 - (andPixelNo & 0x7); + int m = 1 << shift; + + int pixelNo = lineWidth*(h-y-1)+x; + placePixel(pixelNo, buf, xorOffset, x, y, w, bm, alphaByte, m, shift, colors); + + } + } + + delete [] colors; + //ensure we haven't read off the end? + //of course this doesn't help us if the andOffset was a lie... + //return andOffset + (andLineWidth >> 3) <= length; + return true; +} //onDecode + +//function to place the pixel, determined by the bitCount +static void editPixelBit1(const int pixelNo, const unsigned char* buf, + const int xorOffset, int& x, int y, const int w, + SkBitmap* bm, int alphaByte, int m, int shift, SkPMColor* colors) +{ + // note that this should be the same as/similar to the AND bitmap + SkPMColor* address = bm->getAddr32(x,y); + int byte = readByte(buf, xorOffset + (pixelNo >> 3)); + int colorBit; + int alphaBit; + int i = x + 8; + while (x < i) + { + + colorBit = (byte & m) >> shift; + alphaBit = (alphaByte & m) >> shift; + *address = (alphaBit-1)&(colors[colorBit]); + x++; + // setup for the next pixel + address = address + 1; + m = m >> 1; + shift -= 1; + } + x--; +} +static void editPixelBit4(const int pixelNo, const unsigned char* buf, + const int xorOffset, int& x, int y, const int w, + SkBitmap* bm, int alphaByte, int m, int shift, SkPMColor* colors) +{ + SkPMColor* address = bm->getAddr32(x, y); + int byte = readByte(buf, xorOffset + (pixelNo >> 1)); + int pixel = (byte >> 4) & 0xF; + int alphaBit = (alphaByte & m) >> shift; + *address = (alphaBit-1)&(colors[pixel]); + x++; + //if w is odd, x may be the same as w, which means we are writing to an unused portion of the bitmap + //but that's okay, since i've added an extra rowByte for just this purpose + address = address + 1; + pixel = byte & 0xF; + m = m >> 1; + alphaBit = (alphaByte & m) >> (shift-1); + //speed up trick here + *address = (alphaBit-1)&(colors[pixel]); +} + +static void editPixelBit8(const int pixelNo, const unsigned char* buf, + const int xorOffset, int& x, int y, const int w, + SkBitmap* bm, int alphaByte, int m, int shift, SkPMColor* colors) +{ + SkPMColor* address = bm->getAddr32(x, y); + int pixel = readByte(buf, xorOffset + pixelNo); + int alphaBit = (alphaByte & m) >> shift; + *address = (alphaBit-1)&(colors[pixel]); +} + +static void editPixelBit24(const int pixelNo, const unsigned char* buf, + const int xorOffset, int& x, int y, const int w, + SkBitmap* bm, int alphaByte, int m, int shift, SkPMColor* colors) +{ + SkPMColor* address = bm->getAddr32(x, y); + int blue = readByte(buf, xorOffset + 3*pixelNo); + int green = readByte(buf, xorOffset + 3*pixelNo + 1); + int red = readByte(buf, xorOffset + 3*pixelNo + 2); + int alphaBit = (alphaByte & m) >> shift; + //alphaBit == 1 => alpha = 0 + int alpha = (alphaBit-1) & 0xFF; + *address = SkPackARGB32(alpha, red & alpha, green & alpha, blue & alpha); +} + +static void editPixelBit32(const int pixelNo, const unsigned char* buf, + const int xorOffset, int& x, int y, const int w, + SkBitmap* bm, int alphaByte, int m, int shift, SkPMColor* colors) +{ + SkPMColor* address = bm->getAddr32(x, y); + int blue = readByte(buf, xorOffset + 4*pixelNo); + int green = readByte(buf, xorOffset + 4*pixelNo + 1); + int red = readByte(buf, xorOffset + 4*pixelNo + 2); + int alphaBit = (alphaByte & m) >> shift; + int alpha = readByte(buf, xorOffset + 4*pixelNo + 3) & ((alphaBit-1)&0xFF); + *address = SkPackARGB32(alpha, red & alpha, green & alpha, blue & alpha); +} + diff --git a/skia/images/SkImageDecoder_libjpeg.cpp b/skia/images/SkImageDecoder_libjpeg.cpp new file mode 100644 index 0000000..0d8da7ef --- /dev/null +++ b/skia/images/SkImageDecoder_libjpeg.cpp @@ -0,0 +1,654 @@ +/* + * Copyright 2007, Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "SkImageDecoder.h" +#include "SkColorPriv.h" +#include "SkDither.h" +#include "SkScaledBitmapSampler.h" +#include "SkStream.h" +#include "SkTemplates.h" +#include "SkUtils.h" + +#include <stdio.h> +extern "C" { + #include "jpeglib.h" +} + +// this enables timing code to report milliseconds for an encode +//#define TIME_ENCODE +//#define TIME_DECODE + +// this enables our rgb->yuv code, which is faster than libjpeg on ARM +// disable for the moment, as we have some glitches when width != multiple of 4 +#define WE_CONVERT_TO_YUV + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + +class SkJPEGImageDecoder : public SkImageDecoder { +public: + virtual Format getFormat() const { + return kJPEG_Format; + } + +protected: + virtual bool onDecode(SkStream* stream, SkBitmap* bm, + SkBitmap::Config pref, Mode); +}; + +SkImageDecoder* SkImageDecoder_JPEG_Factory(SkStream* stream) { + // !!! unimplemented; rely on PNG test first for now + return SkNEW(SkJPEGImageDecoder); +} + +////////////////////////////////////////////////////////////////////////// + +#include "SkTime.h" + +class AutoTimeMillis { +public: + AutoTimeMillis(const char label[]) : fLabel(label) { + if (!fLabel) { + fLabel = ""; + } + fNow = SkTime::GetMSecs(); + } + ~AutoTimeMillis() { + SkDebugf("---- Time (ms): %s %d\n", fLabel, SkTime::GetMSecs() - fNow); + } +private: + const char* fLabel; + SkMSec fNow; +}; + +/* our source struct for directing jpeg to our stream object +*/ +struct sk_source_mgr : jpeg_source_mgr { + sk_source_mgr(SkStream* stream); + + SkStream* fStream; + + enum { + kBufferSize = 1024 + }; + char fBuffer[kBufferSize]; +}; + +/* Automatically clean up after throwing an exception */ +class JPEGAutoClean { +public: + JPEGAutoClean(): cinfo_ptr(NULL) {} + ~JPEGAutoClean() { + if (cinfo_ptr) { + jpeg_destroy_decompress(cinfo_ptr); + } + } + void set(jpeg_decompress_struct* info) { + cinfo_ptr = info; + } +private: + jpeg_decompress_struct* cinfo_ptr; +}; + +static void sk_init_source(j_decompress_ptr cinfo) { + sk_source_mgr* src = (sk_source_mgr*)cinfo->src; + src->next_input_byte = (const JOCTET*)src->fBuffer; + src->bytes_in_buffer = 0; +} + +static boolean sk_fill_input_buffer(j_decompress_ptr cinfo) { + sk_source_mgr* src = (sk_source_mgr*)cinfo->src; + size_t bytes = src->fStream->read(src->fBuffer, sk_source_mgr::kBufferSize); + // note that JPEG is happy with less than the full read, + // as long as the result is non-zero + if (bytes == 0) { + cinfo->err->error_exit((j_common_ptr)cinfo); + return FALSE; + } + + src->next_input_byte = (const JOCTET*)src->fBuffer; + src->bytes_in_buffer = bytes; + return TRUE; +} + +static void sk_skip_input_data(j_decompress_ptr cinfo, long num_bytes) { + SkASSERT(num_bytes > 0); + + sk_source_mgr* src = (sk_source_mgr*)cinfo->src; + + long skip = num_bytes - src->bytes_in_buffer; + + if (skip > 0) { + size_t bytes = src->fStream->read(NULL, skip); + if (bytes != (size_t)skip) { + cinfo->err->error_exit((j_common_ptr)cinfo); + } + src->next_input_byte = (const JOCTET*)src->fBuffer; + src->bytes_in_buffer = 0; + } else { + src->next_input_byte += num_bytes; + src->bytes_in_buffer -= num_bytes; + } +} + +static boolean sk_resync_to_restart(j_decompress_ptr cinfo, int desired) { + sk_source_mgr* src = (sk_source_mgr*)cinfo->src; + + // what is the desired param for??? + + if (!src->fStream->rewind()) { + printf("------------- sk_resync_to_restart: stream->rewind() failed\n"); + cinfo->err->error_exit((j_common_ptr)cinfo); + return FALSE; + } + return TRUE; +} + +static void sk_term_source(j_decompress_ptr /*cinfo*/) {} + +sk_source_mgr::sk_source_mgr(SkStream* stream) + : fStream(stream) { + init_source = sk_init_source; + fill_input_buffer = sk_fill_input_buffer; + skip_input_data = sk_skip_input_data; + resync_to_restart = sk_resync_to_restart; + term_source = sk_term_source; +} + +#include <setjmp.h> + +struct sk_error_mgr : jpeg_error_mgr { + jmp_buf fJmpBuf; +}; + +static void sk_error_exit(j_common_ptr cinfo) { + sk_error_mgr* error = (sk_error_mgr*)cinfo->err; + + (*error->output_message) (cinfo); + + /* Let the memory manager delete any temp files before we die */ + jpeg_destroy(cinfo); + + longjmp(error->fJmpBuf, -1); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void skip_src_rows(jpeg_decompress_struct* cinfo, void* buffer, + int count) { + for (int i = 0; i < count; i++) { + JSAMPLE* rowptr = (JSAMPLE*)buffer; + int row_count = jpeg_read_scanlines(cinfo, &rowptr, 1); + SkASSERT(row_count == 1); + } +} + +bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, + SkBitmap::Config prefConfig, Mode mode) { +#ifdef TIME_DECODE + AutoTimeMillis atm("JPEG Decode"); +#endif + + SkAutoMalloc srcStorage; + JPEGAutoClean autoClean; + + jpeg_decompress_struct cinfo; + sk_error_mgr sk_err; + sk_source_mgr sk_stream(stream); + + cinfo.err = jpeg_std_error(&sk_err); + sk_err.error_exit = sk_error_exit; + + // All objects need to be instantiated before this setjmp call so that + // they will be cleaned up properly if an error occurs. + if (setjmp(sk_err.fJmpBuf)) { + return false; + } + + jpeg_create_decompress(&cinfo); + autoClean.set(&cinfo); + + //jpeg_stdio_src(&cinfo, file); + cinfo.src = &sk_stream; + + jpeg_read_header(&cinfo, true); + + /* Try to fulfill the requested sampleSize. Since jpeg can do it (when it + can) much faster that we, just use their num/denom api to approximate + the size. + */ + int sampleSize = this->getSampleSize(); + + cinfo.dct_method = JDCT_IFAST; + cinfo.scale_num = 1; + cinfo.scale_denom = sampleSize; + + /* image_width and image_height are the original dimensions, available + after jpeg_read_header(). To see the scaled dimensions, we have to call + jpeg_start_decompress(), and then read output_width and output_height. + */ + jpeg_start_decompress(&cinfo); + + /* If we need to better match the request, we might examine the image and + output dimensions, and determine if the downsampling jpeg provided is + not sufficient. If so, we can recompute a modified sampleSize value to + make up the difference. + + To skip this additional scaling, just set sampleSize = 1; below. + */ + sampleSize = sampleSize * cinfo.output_width / cinfo.image_width; + + // check for supported formats + bool isRGB; // as opposed to gray8 + if (3 == cinfo.num_components && JCS_RGB == cinfo.out_color_space) { + isRGB = true; + } else if (1 == cinfo.num_components && + JCS_GRAYSCALE == cinfo.out_color_space) { + isRGB = false; // could use Index8 config if we want... + } else { + SkDEBUGF(("SkJPEGImageDecoder: unsupported jpeg colorspace %d with %d components\n", + cinfo.jpeg_color_space, cinfo.num_components)); + return false; + } + + SkBitmap::Config config = prefConfig; + // if no user preference, see what the device recommends + if (config == SkBitmap::kNo_Config) + config = SkImageDecoder::GetDeviceConfig(); + + // only these make sense for jpegs + if (config != SkBitmap::kARGB_8888_Config && + config != SkBitmap::kARGB_4444_Config && + config != SkBitmap::kRGB_565_Config) { + config = SkBitmap::kARGB_8888_Config; + } + + // should we allow the Chooser (if present) to pick a config for us??? + if (!this->chooseFromOneChoice(config, cinfo.output_width, + cinfo.output_height)) { + return false; + } + + SkScaledBitmapSampler sampler(cinfo.output_width, cinfo.output_height, + sampleSize); + + bm->setConfig(config, sampler.scaledWidth(), sampler.scaledHeight()); + // jpegs are always opauqe (i.e. have no per-pixel alpha) + bm->setIsOpaque(true); + + if (SkImageDecoder::kDecodeBounds_Mode == mode) { + return true; + } + if (!this->allocPixelRef(bm, NULL)) { + return false; + } + + SkAutoLockPixels alp(*bm); + + if (!sampler.begin(bm, + isRGB ? SkScaledBitmapSampler::kRGB : + SkScaledBitmapSampler::kGray, + this->getDitherImage())) { + return false; + } + + uint8_t* srcRow = (uint8_t*)srcStorage.alloc(cinfo.output_width * 3); + + skip_src_rows(&cinfo, srcRow, sampler.srcY0()); + for (int y = 0;; y++) { + JSAMPLE* rowptr = (JSAMPLE*)srcRow; + int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1); + SkASSERT(row_count == 1); + + sampler.next(srcRow); + if (bm->height() - 1 == y) { + break; + } + skip_src_rows(&cinfo, srcRow, sampler.srcDY() - 1); + } + + // ??? If I don't do this, I get an error from finish_decompress + skip_src_rows(&cinfo, srcRow, cinfo.output_height - cinfo.output_scanline); + + jpeg_finish_decompress(&cinfo); + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_SUPPORT_IMAGE_ENCODE + +#include "SkColorPriv.h" + +// taken from jcolor.c in libjpeg +#if 0 // 16bit - precise but slow + #define CYR 19595 // 0.299 + #define CYG 38470 // 0.587 + #define CYB 7471 // 0.114 + + #define CUR -11059 // -0.16874 + #define CUG -21709 // -0.33126 + #define CUB 32768 // 0.5 + + #define CVR 32768 // 0.5 + #define CVG -27439 // -0.41869 + #define CVB -5329 // -0.08131 + + #define CSHIFT 16 +#else // 8bit - fast, slightly less precise + #define CYR 77 // 0.299 + #define CYG 150 // 0.587 + #define CYB 29 // 0.114 + + #define CUR -43 // -0.16874 + #define CUG -85 // -0.33126 + #define CUB 128 // 0.5 + + #define CVR 128 // 0.5 + #define CVG -107 // -0.41869 + #define CVB -21 // -0.08131 + + #define CSHIFT 8 +#endif + +static void rgb2yuv_32(uint8_t dst[], SkPMColor c) { + int r = SkGetPackedR32(c); + int g = SkGetPackedG32(c); + int b = SkGetPackedB32(c); + + int y = ( CYR*r + CYG*g + CYB*b ) >> CSHIFT; + int u = ( CUR*r + CUG*g + CUB*b ) >> CSHIFT; + int v = ( CVR*r + CVG*g + CVB*b ) >> CSHIFT; + + dst[0] = SkToU8(y); + dst[1] = SkToU8(u + 128); + dst[2] = SkToU8(v + 128); +} + +static void rgb2yuv_4444(uint8_t dst[], U16CPU c) { + int r = SkGetPackedR4444(c); + int g = SkGetPackedG4444(c); + int b = SkGetPackedB4444(c); + + int y = ( CYR*r + CYG*g + CYB*b ) >> (CSHIFT - 4); + int u = ( CUR*r + CUG*g + CUB*b ) >> (CSHIFT - 4); + int v = ( CVR*r + CVG*g + CVB*b ) >> (CSHIFT - 4); + + dst[0] = SkToU8(y); + dst[1] = SkToU8(u + 128); + dst[2] = SkToU8(v + 128); +} + +static void rgb2yuv_16(uint8_t dst[], U16CPU c) { + int r = SkGetPackedR16(c); + int g = SkGetPackedG16(c); + int b = SkGetPackedB16(c); + + int y = ( 2*CYR*r + CYG*g + 2*CYB*b ) >> (CSHIFT - 2); + int u = ( 2*CUR*r + CUG*g + 2*CUB*b ) >> (CSHIFT - 2); + int v = ( 2*CVR*r + CVG*g + 2*CVB*b ) >> (CSHIFT - 2); + + dst[0] = SkToU8(y); + dst[1] = SkToU8(u + 128); + dst[2] = SkToU8(v + 128); +} + +/////////////////////////////////////////////////////////////////////////////// + +typedef void (*WriteScanline)(uint8_t* SK_RESTRICT dst, + const void* SK_RESTRICT src, int width, + const SkPMColor* SK_RESTRICT ctable); + +static void Write_32_YUV(uint8_t* SK_RESTRICT dst, + const void* SK_RESTRICT srcRow, int width, + const SkPMColor*) { + const uint32_t* SK_RESTRICT src = (const uint32_t*)srcRow; + while (--width >= 0) { +#ifdef WE_CONVERT_TO_YUV + rgb2yuv_32(dst, *src++); +#else + uint32_t c = *src++; + dst[0] = SkGetPackedR32(c); + dst[1] = SkGetPackedG32(c); + dst[2] = SkGetPackedB32(c); +#endif + dst += 3; + } +} + +static void Write_4444_YUV(uint8_t* SK_RESTRICT dst, + const void* SK_RESTRICT srcRow, int width, + const SkPMColor*) { + const SkPMColor16* SK_RESTRICT src = (const SkPMColor16*)srcRow; + while (--width >= 0) { +#ifdef WE_CONVERT_TO_YUV + rgb2yuv_4444(dst, *src++); +#else + SkPMColor16 c = *src++; + dst[0] = SkPacked4444ToR32(c); + dst[1] = SkPacked4444ToG32(c); + dst[2] = SkPacked4444ToB32(c); +#endif + dst += 3; + } +} + +static void Write_16_YUV(uint8_t* SK_RESTRICT dst, + const void* SK_RESTRICT srcRow, int width, + const SkPMColor*) { + const uint16_t* SK_RESTRICT src = (const uint16_t*)srcRow; + while (--width >= 0) { +#ifdef WE_CONVERT_TO_YUV + rgb2yuv_16(dst, *src++); +#else + uint16_t c = *src++; + dst[0] = SkPacked16ToR32(c); + dst[1] = SkPacked16ToG32(c); + dst[2] = SkPacked16ToB32(c); +#endif + dst += 3; + } +} + +static void Write_Index_YUV(uint8_t* SK_RESTRICT dst, + const void* SK_RESTRICT srcRow, int width, + const SkPMColor* SK_RESTRICT ctable) { + const uint8_t* SK_RESTRICT src = (const uint8_t*)srcRow; + while (--width >= 0) { +#ifdef WE_CONVERT_TO_YUV + rgb2yuv_32(dst, ctable[*src++]); +#else + uint32_t c = ctable[*src++]; + dst[0] = SkGetPackedR32(c); + dst[1] = SkGetPackedG32(c); + dst[2] = SkGetPackedB32(c); +#endif + dst += 3; + } +} + +static WriteScanline ChooseWriter(const SkBitmap& bm) { + switch (bm.config()) { + case SkBitmap::kARGB_8888_Config: + return Write_32_YUV; + case SkBitmap::kRGB_565_Config: + return Write_16_YUV; + case SkBitmap::kARGB_4444_Config: + return Write_4444_YUV; + case SkBitmap::kIndex8_Config: + return Write_Index_YUV; + default: + return NULL; + } +} + +struct sk_destination_mgr : jpeg_destination_mgr { + sk_destination_mgr(SkWStream* stream); + + SkWStream* fStream; + + enum { + kBufferSize = 1024 + }; + uint8_t fBuffer[kBufferSize]; +}; + +static void sk_init_destination(j_compress_ptr cinfo) { + sk_destination_mgr* dest = (sk_destination_mgr*)cinfo->dest; + + dest->next_output_byte = dest->fBuffer; + dest->free_in_buffer = sk_destination_mgr::kBufferSize; +} + +static boolean sk_empty_output_buffer(j_compress_ptr cinfo) { + sk_destination_mgr* dest = (sk_destination_mgr*)cinfo->dest; + +// if (!dest->fStream->write(dest->fBuffer, sk_destination_mgr::kBufferSize - dest->free_in_buffer)) + if (!dest->fStream->write(dest->fBuffer, sk_destination_mgr::kBufferSize)) { + sk_throw(); + } + // ERREXIT(cinfo, JERR_FILE_WRITE); + + dest->next_output_byte = dest->fBuffer; + dest->free_in_buffer = sk_destination_mgr::kBufferSize; + return TRUE; +} + +static void sk_term_destination (j_compress_ptr cinfo) { + sk_destination_mgr* dest = (sk_destination_mgr*)cinfo->dest; + + size_t size = sk_destination_mgr::kBufferSize - dest->free_in_buffer; + if (size > 0) { + if (!dest->fStream->write(dest->fBuffer, size)) { + sk_throw(); + } + } + dest->fStream->flush(); +} + +sk_destination_mgr::sk_destination_mgr(SkWStream* stream) + : fStream(stream) { + this->init_destination = sk_init_destination; + this->empty_output_buffer = sk_empty_output_buffer; + this->term_destination = sk_term_destination; +} + +class SkAutoLockColors : public SkNoncopyable { +public: + SkAutoLockColors(const SkBitmap& bm) { + fCTable = bm.getColorTable(); + fColors = fCTable ? fCTable->lockColors() : NULL; + } + ~SkAutoLockColors() { + if (fCTable) { + fCTable->unlockColors(false); + } + } + const SkPMColor* colors() const { return fColors; } +private: + SkColorTable* fCTable; + const SkPMColor* fColors; +}; + +class SkJPEGImageEncoder : public SkImageEncoder { +protected: + virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) { +#ifdef TIME_ENCODE + AutoTimeMillis atm("JPEG Encode"); +#endif + + const WriteScanline writer = ChooseWriter(bm); + if (NULL == writer) { + return false; + } + + SkAutoLockPixels alp(bm); + if (NULL == bm.getPixels()) { + return false; + } + + jpeg_compress_struct cinfo; + sk_error_mgr sk_err; + sk_destination_mgr sk_wstream(stream); + + cinfo.err = jpeg_std_error(&sk_err); + sk_err.error_exit = sk_error_exit; + if (setjmp(sk_err.fJmpBuf)) { + return false; + } + jpeg_create_compress(&cinfo); + + cinfo.dest = &sk_wstream; + cinfo.image_width = bm.width(); + cinfo.image_height = bm.height(); + cinfo.input_components = 3; +#ifdef WE_CONVERT_TO_YUV + cinfo.in_color_space = JCS_YCbCr; +#else + cinfo.in_color_space = JCS_RGB; +#endif + cinfo.input_gamma = 1; + + jpeg_set_defaults(&cinfo); + jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */); + cinfo.dct_method = JDCT_IFAST; + + jpeg_start_compress(&cinfo, TRUE); + + const int width = bm.width(); + SkAutoMalloc oneRow(width * 3); + uint8_t* oneRowP = (uint8_t*)oneRow.get(); + + SkAutoLockColors alc(bm); + const SkPMColor* colors = alc.colors(); + const void* srcRow = bm.getPixels(); + + while (cinfo.next_scanline < cinfo.image_height) { + JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */ + + writer(oneRowP, srcRow, width, colors); + row_pointer[0] = oneRowP; + (void) jpeg_write_scanlines(&cinfo, row_pointer, 1); + srcRow = (const void*)((const char*)srcRow + bm.rowBytes()); + } + + jpeg_finish_compress(&cinfo); + jpeg_destroy_compress(&cinfo); + + return true; + } +}; + +SkImageEncoder* SkImageEncoder_JPEG_Factory(); +SkImageEncoder* SkImageEncoder_JPEG_Factory() { + return SkNEW(SkJPEGImageEncoder); +} + +#endif /* SK_SUPPORT_IMAGE_ENCODE */ + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + +#ifdef SK_DEBUG + +void SkImageDecoder::UnitTest() { + SkBitmap bm; + + (void)SkImageDecoder::DecodeFile("logo.jpg", &bm); +} + +#endif diff --git a/skia/images/SkImageDecoder_libpng.cpp b/skia/images/SkImageDecoder_libpng.cpp new file mode 100644 index 0000000..4378ca9 --- /dev/null +++ b/skia/images/SkImageDecoder_libpng.cpp @@ -0,0 +1,643 @@ +/* libs/graphics/images/SkImageDecoder_libpng.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkImageDecoder.h" +#include "SkColor.h" +#include "SkColorPriv.h" +#include "SkDither.h" +#include "SkMath.h" +#include "SkScaledBitmapSampler.h" +#include "SkStream.h" +#include "SkTemplates.h" +#include "SkUtils.h" + +extern "C" { +#include "png.h" +} + +class SkPNGImageDecoder : public SkImageDecoder { +public: + virtual Format getFormat() const { + return kPNG_Format; + } + +protected: + virtual bool onDecode(SkStream* stream, SkBitmap* bm, + SkBitmap::Config pref, Mode); +}; + +#ifndef png_jmpbuf +# define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf) +#endif + +#define PNG_BYTES_TO_CHECK 4 + +/* Automatically clean up after throwing an exception */ +struct PNGAutoClean { + PNGAutoClean(png_structp p, png_infop i): png_ptr(p), info_ptr(i) {} + ~PNGAutoClean() { + png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL); + } +private: + png_structp png_ptr; + png_infop info_ptr; +}; + +SkImageDecoder* SkImageDecoder_PNG_Factory(SkStream* stream) { + char buf[PNG_BYTES_TO_CHECK]; + if (stream->read(buf, PNG_BYTES_TO_CHECK) == PNG_BYTES_TO_CHECK && + !png_sig_cmp((png_bytep) buf, (png_size_t)0, PNG_BYTES_TO_CHECK)) { + return SkNEW(SkPNGImageDecoder); + } + return NULL; +} + +static void sk_read_fn(png_structp png_ptr, png_bytep data, png_size_t length) { + SkStream* sk_stream = (SkStream*) png_ptr->io_ptr; + size_t bytes = sk_stream->read(data, length); + if (bytes != length) { + png_error(png_ptr, "Read Error!"); + } +} + +static int sk_read_user_chunk(png_structp png_ptr, png_unknown_chunkp chunk) { + SkImageDecoder::Peeker* peeker = + (SkImageDecoder::Peeker*)png_get_user_chunk_ptr(png_ptr); + // peek() returning true means continue decoding + return peeker->peek((const char*)chunk->name, chunk->data, chunk->size) ? + 1 : -1; +} + +static void sk_error_fn(png_structp png_ptr, png_const_charp msg) { + SkDebugf("------ png error %s\n", msg); + longjmp(png_jmpbuf(png_ptr), 1); +} + +static void skip_src_rows(png_structp png_ptr, uint8_t storage[], int count) { + for (int i = 0; i < count; i++) { + uint8_t* tmp = storage; + png_read_rows(png_ptr, &tmp, png_bytepp_NULL, 1); + } +} + +static bool pos_le(int value, int max) { + return value > 0 && value <= max; +} + +bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap, + SkBitmap::Config prefConfig, Mode mode) { +// SkAutoTrace apr("SkPNGImageDecoder::onDecode"); + + /* Create and initialize the png_struct with the desired error handler + * functions. If you want to use the default stderr and longjump method, + * you can supply NULL for the last three parameters. We also supply the + * the compiler header file version, so that we know if the application + * was compiled with a compatible version of the library. */ + png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, + NULL, sk_error_fn, NULL); + // png_voidp user_error_ptr, user_error_fn, user_warning_fn); + if (png_ptr == NULL) { + return false; + } + + /* Allocate/initialize the memory for image information. */ + png_infop info_ptr = png_create_info_struct(png_ptr); + if (info_ptr == NULL) { + png_destroy_read_struct(&png_ptr, png_infopp_NULL, png_infopp_NULL); + return false; + } + + PNGAutoClean autoClean(png_ptr, info_ptr); + + /* Set error handling if you are using the setjmp/longjmp method (this is + * the normal method of doing things with libpng). REQUIRED unless you + * set up your own error handlers in the png_create_read_struct() earlier. + */ + if (setjmp(png_jmpbuf(png_ptr))) { + return false; + } + + /* If you are using replacement read functions, instead of calling + * png_init_io() here you would call: + */ + png_set_read_fn(png_ptr, (void *)sk_stream, sk_read_fn); + /* where user_io_ptr is a structure you want available to the callbacks */ + /* If we have already read some of the signature */ +// png_set_sig_bytes(png_ptr, 0 /* sig_read */ ); + + // hookup our peeker so we can see any user-chunks the caller may be interested in + png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_ALWAYS, (png_byte*)"", 0); + if (this->getPeeker()) { + png_set_read_user_chunk_fn(png_ptr, (png_voidp)this->getPeeker(), sk_read_user_chunk); + } + + /* The call to png_read_info() gives us all of the information from the + * PNG file before the first IDAT (image data chunk). */ + png_read_info(png_ptr, info_ptr); + png_uint_32 origWidth, origHeight; + int bit_depth, color_type, interlace_type; + png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bit_depth, &color_type, + &interlace_type, int_p_NULL, int_p_NULL); + + SkBitmap::Config config; + bool hasAlpha = false; + bool doDither = this->getDitherImage(); + + // check for sBIT chunk data, in case we should disable dithering because + // our data is not truely 8bits per component + if (doDither) { +#if 0 + SkDebugf("----- sBIT %d %d %d %d\n", info_ptr->sig_bit.red, + info_ptr->sig_bit.green, info_ptr->sig_bit.blue, + info_ptr->sig_bit.alpha); +#endif + // 0 seems to indicate no information available + if (pos_le(info_ptr->sig_bit.red, SK_R16_BITS) && + pos_le(info_ptr->sig_bit.green, SK_G16_BITS) && + pos_le(info_ptr->sig_bit.blue, SK_B16_BITS)) { + doDither = false; + } + } + + if (color_type == PNG_COLOR_TYPE_PALETTE) { + config = SkBitmap::kIndex8_Config; // defer sniffing for hasAlpha + } else { + png_color_16p transColor; + + png_get_tRNS(png_ptr, info_ptr, NULL, NULL, &transColor); + + if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) || + PNG_COLOR_TYPE_RGB_ALPHA == color_type || + PNG_COLOR_TYPE_GRAY_ALPHA == color_type) { + hasAlpha = true; + config = SkBitmap::kARGB_8888_Config; + } else { // we get to choose the config + config = prefConfig; + if (config == SkBitmap::kNo_Config) { + config = SkImageDecoder::GetDeviceConfig(); + } + if (config != SkBitmap::kRGB_565_Config && + config != SkBitmap::kARGB_4444_Config) { + config = SkBitmap::kARGB_8888_Config; + } + } + } + + if (!this->chooseFromOneChoice(config, origWidth, origHeight)) { + return false; + } + + const int sampleSize = this->getSampleSize(); + SkScaledBitmapSampler sampler(origWidth, origHeight, sampleSize); + + decodedBitmap->setConfig(config, sampler.scaledWidth(), + sampler.scaledHeight(), 0); + if (SkImageDecoder::kDecodeBounds_Mode == mode) { + return true; + } + + // from here down we are concerned with colortables and pixels + + /* tell libpng to strip 16 bit/color files down to 8 bits/color */ + if (bit_depth == 16) { + png_set_strip_16(png_ptr); + } + /* Extract multiple pixels with bit depths of 1, 2, and 4 from a single + * byte into separate bytes (useful for paletted and grayscale images). */ + if (bit_depth < 8) { + png_set_packing(png_ptr); + } + /* Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel */ + if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) { + png_set_gray_1_2_4_to_8(png_ptr); + } + + /* Make a grayscale image into RGB. */ + if (color_type == PNG_COLOR_TYPE_GRAY || + color_type == PNG_COLOR_TYPE_GRAY_ALPHA) { + png_set_gray_to_rgb(png_ptr); + } + + // we track if we actually see a non-opaque pixels, since sometimes a PNG sets its colortype + // to |= PNG_COLOR_MASK_ALPHA, but all of its pixels are in fact opaque. We care, since we + // draw lots faster if we can flag the bitmap has being opaque + bool reallyHasAlpha = false; + + SkColorTable* colorTable = NULL; + + if (color_type == PNG_COLOR_TYPE_PALETTE) { + int num_palette; + png_colorp palette; + png_bytep trans; + int num_trans; + + png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette); + + /* BUGGY IMAGE WORKAROUND + + We hit some images (e.g. fruit_.png) who contain bytes that are == colortable_count + which is a problem since we use the byte as an index. To work around this we grow + the colortable by 1 (if its < 256) and duplicate the last color into that slot. + */ + int colorCount = num_palette + (num_palette < 256); + + colorTable = SkNEW_ARGS(SkColorTable, (colorCount)); + + SkPMColor* colorPtr = colorTable->lockColors(); + if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { + png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL); + hasAlpha = (num_trans > 0); + } else { + num_trans = 0; + colorTable->setFlags(colorTable->getFlags() | SkColorTable::kColorsAreOpaque_Flag); + } + // check for bad images that might make us crash + if (num_trans > num_palette) { + num_trans = num_palette; + } + + int index = 0; + int transLessThanFF = 0; + + for (; index < num_trans; index++) { + transLessThanFF |= (int)*trans - 0xFF; + *colorPtr++ = SkPreMultiplyARGB(*trans++, palette->red, palette->green, palette->blue); + palette++; + } + reallyHasAlpha |= (transLessThanFF < 0); + + for (; index < num_palette; index++) { + *colorPtr++ = SkPackARGB32(0xFF, palette->red, palette->green, palette->blue); + palette++; + } + + // see BUGGY IMAGE WORKAROUND comment above + if (num_palette < 256) { + *colorPtr = colorPtr[-1]; + } + colorTable->unlockColors(true); + } + + SkAutoUnref aur(colorTable); + + if (!this->allocPixelRef(decodedBitmap, colorTable)) { + delete colorTable; + return false; + } + + SkAutoLockPixels alp(*decodedBitmap); + + /* swap the RGBA or GA data to ARGB or AG (or BGRA to ABGR) */ +// if (color_type == PNG_COLOR_TYPE_RGB_ALPHA) +// ; // png_set_swap_alpha(png_ptr); + + /* swap bytes of 16 bit files to least significant byte first */ + // png_set_swap(png_ptr); + + /* Add filler (or alpha) byte (before/after each RGB triplet) */ + if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_GRAY) { + png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER); + } + + /* Turn on interlace handling. REQUIRED if you are not using + * png_read_image(). To see how to handle interlacing passes, + * see the png_read_row() method below: + */ + const int number_passes = interlace_type != PNG_INTERLACE_NONE ? + png_set_interlace_handling(png_ptr) : 1; + + /* Optional call to gamma correct and add the background to the palette + * and update info structure. REQUIRED if you are expecting libpng to + * update the palette for you (ie you selected such a transform above). + */ + png_read_update_info(png_ptr, info_ptr); + + if (SkBitmap::kIndex8_Config == config && 1 == sampleSize) { + for (int i = 0; i < number_passes; i++) { + for (png_uint_32 y = 0; y < origHeight; y++) { + uint8_t* bmRow = decodedBitmap->getAddr8(0, y); + png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1); + } + } + } else { + SkScaledBitmapSampler::SrcConfig sc; + int srcBytesPerPixel = 4; + + if (SkBitmap::kIndex8_Config == config) { + sc = SkScaledBitmapSampler::kIndex; + srcBytesPerPixel = 1; + } else if (hasAlpha) { + sc = SkScaledBitmapSampler::kRGBA; + } else { + sc = SkScaledBitmapSampler::kRGBX; + } + + SkAutoMalloc storage(origWidth * srcBytesPerPixel); + const int height = decodedBitmap->height(); + + for (int i = 0; i < number_passes; i++) { + if (!sampler.begin(decodedBitmap, sc, doDither)) { + return false; + } + + uint8_t* srcRow = (uint8_t*)storage.get(); + skip_src_rows(png_ptr, srcRow, sampler.srcY0()); + + for (int y = 0; y < height; y++) { + uint8_t* tmp = srcRow; + png_read_rows(png_ptr, &tmp, png_bytepp_NULL, 1); + reallyHasAlpha |= sampler.next(srcRow); + if (y < height - 1) { + skip_src_rows(png_ptr, srcRow, sampler.srcDY() - 1); + } + } + + // skip the rest of the rows (if any) + png_uint_32 read = (height - 1) * sampler.srcDY() + + sampler.srcY0() + 1; + SkASSERT(read <= origHeight); + skip_src_rows(png_ptr, srcRow, origHeight - read); + } + + if (hasAlpha && !reallyHasAlpha) { + SkDEBUGF(("Image doesn't really have alpha [%d %d]\n", + origWidth, origHeight)); + } + } + + /* read rest of file, and get additional chunks in info_ptr - REQUIRED */ + png_read_end(png_ptr, info_ptr); + + decodedBitmap->setIsOpaque(!reallyHasAlpha); + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_SUPPORT_IMAGE_ENCODE + +#include "SkColorPriv.h" +#include "SkUnPreMultiply.h" + +static void sk_write_fn(png_structp png_ptr, png_bytep data, png_size_t len) { + SkWStream* sk_stream = (SkWStream*)png_ptr->io_ptr; + if (!sk_stream->write(data, len)) { + png_error(png_ptr, "sk_write_fn Error!"); + } +} + +typedef void (*transform_scanline_proc)(const char* SK_RESTRICT src, + int width, char* SK_RESTRICT dst); + +static void transform_scanline_565(const char* SK_RESTRICT src, int width, + char* SK_RESTRICT dst) { + const uint16_t* SK_RESTRICT srcP = (const uint16_t*)src; + for (int i = 0; i < width; i++) { + unsigned c = *srcP++; + *dst++ = SkPacked16ToR32(c); + *dst++ = SkPacked16ToG32(c); + *dst++ = SkPacked16ToB32(c); + } +} + +static void transform_scanline_888(const char* SK_RESTRICT src, int width, + char* SK_RESTRICT dst) { + const SkPMColor* SK_RESTRICT srcP = (const SkPMColor*)src; + for (int i = 0; i < width; i++) { + SkPMColor c = *srcP++; + *dst++ = SkGetPackedR32(c); + *dst++ = SkGetPackedG32(c); + *dst++ = SkGetPackedB32(c); + } +} + +static void transform_scanline_444(const char* SK_RESTRICT src, int width, + char* SK_RESTRICT dst) { + const SkPMColor16* SK_RESTRICT srcP = (const SkPMColor16*)src; + for (int i = 0; i < width; i++) { + SkPMColor16 c = *srcP++; + *dst++ = SkPacked4444ToR32(c); + *dst++ = SkPacked4444ToG32(c); + *dst++ = SkPacked4444ToB32(c); + } +} + +static void transform_scanline_8888(const char* SK_RESTRICT src, int width, + char* SK_RESTRICT dst) { + const SkPMColor* SK_RESTRICT srcP = (const SkPMColor*)src; + const SkUnPreMultiply::Scale* SK_RESTRICT table = + SkUnPreMultiply::GetScaleTable(); + + for (int i = 0; i < width; i++) { + SkPMColor c = *srcP++; + unsigned a = SkGetPackedA32(c); + unsigned r = SkGetPackedR32(c); + unsigned g = SkGetPackedG32(c); + unsigned b = SkGetPackedB32(c); + + if (0 != a && 255 != a) { + SkUnPreMultiply::Scale scale = table[a]; + r = SkUnPreMultiply::ApplyScale(scale, r); + g = SkUnPreMultiply::ApplyScale(scale, g); + b = SkUnPreMultiply::ApplyScale(scale, b); + } + *dst++ = r; + *dst++ = g; + *dst++ = b; + *dst++ = a; + } +} + +static void transform_scanline_4444(const char* SK_RESTRICT src, int width, + char* SK_RESTRICT dst) { + const SkPMColor16* SK_RESTRICT srcP = (const SkPMColor16*)src; + const SkUnPreMultiply::Scale* SK_RESTRICT table = + SkUnPreMultiply::GetScaleTable(); + + for (int i = 0; i < width; i++) { + SkPMColor16 c = *srcP++; + unsigned a = SkPacked4444ToA32(c); + unsigned r = SkPacked4444ToR32(c); + unsigned g = SkPacked4444ToG32(c); + unsigned b = SkPacked4444ToB32(c); + + if (0 != a && 255 != a) { + SkUnPreMultiply::Scale scale = table[a]; + r = SkUnPreMultiply::ApplyScale(scale, r); + g = SkUnPreMultiply::ApplyScale(scale, g); + b = SkUnPreMultiply::ApplyScale(scale, b); + } + *dst++ = r; + *dst++ = g; + *dst++ = b; + *dst++ = a; + } +} + +static transform_scanline_proc choose_proc(SkBitmap::Config config, + bool hasAlpha) { + static const struct { + SkBitmap::Config fConfig; + bool fHasAlpha; + transform_scanline_proc fProc; + } gMap[] = { + { SkBitmap::kRGB_565_Config, false, transform_scanline_565 }, + { SkBitmap::kARGB_8888_Config, false, transform_scanline_888 }, + { SkBitmap::kARGB_8888_Config, true, transform_scanline_8888 }, + { SkBitmap::kARGB_4444_Config, false, transform_scanline_444 }, + { SkBitmap::kARGB_4444_Config, true, transform_scanline_4444 }, + }; + + for (int i = SK_ARRAY_COUNT(gMap) - 1; i >= 0; --i) { + if (gMap[i].fConfig == config && gMap[i].fHasAlpha == hasAlpha) { + return gMap[i].fProc; + } + } + sk_throw(); + return NULL; +} + +class SkPNGImageEncoder : public SkImageEncoder { +protected: + virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality); +}; + +bool SkPNGImageEncoder::onEncode(SkWStream* stream, const SkBitmap& bitmap, + int /*quality*/) { + SkBitmap::Config config = bitmap.getConfig(); + + const bool hasAlpha = !bitmap.isOpaque(); + png_color_8 sig_bit; + + switch (config) { + case SkBitmap::kARGB_8888_Config: + sig_bit.red = 8; + sig_bit.green = 8; + sig_bit.blue = 8; + sig_bit.alpha = hasAlpha ? 8 : 0; + break; + case SkBitmap::kARGB_4444_Config: + sig_bit.red = 4; + sig_bit.green = 4; + sig_bit.blue = 4; + sig_bit.alpha = hasAlpha ? 4 : 0; + break; + case SkBitmap::kRGB_565_Config: + sig_bit.red = 5; + sig_bit.green = 6; + sig_bit.blue = 5; + sig_bit.alpha = 0; + break; + default: + SkDEBUGF(("SkPNGImageEncoder::onEncode can't encode %d config\n", + config)); + return false; + } + + SkAutoLockPixels alp(bitmap); + if (NULL == bitmap.getPixels()) { + return false; + } + + png_structp png_ptr; + png_infop info_ptr; + + png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, sk_error_fn, + NULL); + if (NULL == png_ptr) { + return false; + } + + info_ptr = png_create_info_struct(png_ptr); + if (NULL == info_ptr) { + png_destroy_write_struct(&png_ptr, png_infopp_NULL); + return false; + } + + /* Set error handling. REQUIRED if you aren't supplying your own + * error handling functions in the png_create_write_struct() call. + */ + if (setjmp(png_jmpbuf(png_ptr))) { + png_destroy_write_struct(&png_ptr, &info_ptr); + return false; + } + + png_set_write_fn(png_ptr, (void*)stream, sk_write_fn, png_flush_ptr_NULL); + + /* Set the image information here. Width and height are up to 2^31, + * bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on + * the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY, + * PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB, + * or PNG_COLOR_TYPE_RGB_ALPHA. interlace is either PNG_INTERLACE_NONE or + * PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST + * currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED + */ + + png_set_IHDR(png_ptr, info_ptr, bitmap.width(), bitmap.height(), 8, + hasAlpha ? PNG_COLOR_TYPE_RGB_ALPHA : PNG_COLOR_TYPE_RGB, + PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, + PNG_FILTER_TYPE_BASE); + +#if 0 // need to support this some day <reed> + /* set the palette if there is one. REQUIRED for indexed-color images */ + palette = (png_colorp)png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH + * png_sizeof (png_color)); + /* ... set palette colors ... */ + png_set_PLTE(png_ptr, info_ptr, palette, PNG_MAX_PALETTE_LENGTH); + /* You must not free palette here, because png_set_PLTE only makes a link to + the palette that you malloced. Wait until you are about to destroy + the png structure. */ +#endif + + png_set_sBIT(png_ptr, info_ptr, &sig_bit); + png_write_info(png_ptr, info_ptr); + + const char* srcImage = (const char*)bitmap.getPixels(); + SkAutoSMalloc<1024> rowStorage(bitmap.width() << 2); + char* storage = (char*)rowStorage.get(); + transform_scanline_proc proc = choose_proc(config, hasAlpha); + + for (int y = 0; y < bitmap.height(); y++) { + png_bytep row_ptr = (png_bytep)storage; + proc(srcImage, bitmap.width(), storage); + png_write_rows(png_ptr, &row_ptr, 1); + srcImage += bitmap.rowBytes(); + } + + png_write_end(png_ptr, info_ptr); + +#if 0 + /* If you png_malloced a palette, free it here (don't free info_ptr->palette, + as recommended in versions 1.0.5m and earlier of this example; if + libpng mallocs info_ptr->palette, libpng will free it). If you + allocated it with malloc() instead of png_malloc(), use free() instead + of png_free(). */ + png_free(png_ptr, palette); +#endif + + /* clean up after the write, and free any memory allocated */ + png_destroy_write_struct(&png_ptr, &info_ptr); + return true; +} + +SkImageEncoder* SkImageEncoder_PNG_Factory(); +SkImageEncoder* SkImageEncoder_PNG_Factory() { + return SkNEW(SkPNGImageEncoder); +} + +#endif /* SK_SUPPORT_IMAGE_ENCODE */ + diff --git a/skia/images/SkImageDecoder_libpvjpeg.cpp b/skia/images/SkImageDecoder_libpvjpeg.cpp new file mode 100644 index 0000000..9177741 --- /dev/null +++ b/skia/images/SkImageDecoder_libpvjpeg.cpp @@ -0,0 +1,206 @@ +#include "SkImageDecoder.h" +#include "SkColor.h" +#include "SkColorPriv.h" +#include "SkDither.h" +#include "SkMath.h" +#include "SkStream.h" +#include "SkTemplates.h" +#include "SkUtils.h" + +extern void ValidateHeap(); + +class SkPVJPEGImageDecoder : public SkImageDecoder { +protected: + virtual bool onDecode(SkStream* stream, SkBitmap* bm, + SkBitmap::Config pref, Mode); + +private: + enum { + STORAGE_SIZE = 8 * 1024 + }; + char fStorage[STORAGE_SIZE]; +}; + +SkImageDecoder* SkImageDecoder_PVJPEG_Factory(SkStream* stream) +{ + return SkNEW(SkPVJPEGImageDecoder); +} + +#include "pvjpgdecoderinterface.h" +#include "pvjpgdecoder_factory.h" + +class AutoPVDelete { +public: + AutoPVDelete(PVJpgDecoderInterface* codec) : fCodec(codec) {} + ~AutoPVDelete() { + fCodec->Reset(); + PVJpgDecoderFactory::DeletePVJpgDecoder(fCodec); + } +private: + PVJpgDecoderInterface* fCodec; +}; + +class MyObserver : public MPVJpegDecObserver { +public: + MyObserver() : fCount(0) {} + ~MyObserver() { + if (fCount != 0) { + SkDebugf("--- pvjpeg left %d allocations\n", fCount); + } + } + + virtual void allocateBuffer(uint8* &buffer, int32 buffersize) { + ++fCount; + // we double the allocation to work around bug when height is odd + buffer = (uint8*)sk_malloc_throw(buffersize << 1); + SkDebugf("--- pvjpeg alloc [%d] %d addr=%p\n", fCount, buffersize, buffer); + } + + virtual void deallocateBuffer(uint8 *buffer) { + SkDebugf("--- pvjpeg free [%d] addr=%p\n", fCount, buffer); + --fCount; + sk_free(buffer); + } + +private: + int fCount; +}; + +static void check_status(TPvJpgDecStatus status) { + if (TPVJPGDEC_SUCCESS != status) { + SkDEBUGF(("--- pvjpeg status %d\n", status)); + } +} + +static bool getFrame(PVJpgDecoderInterface* codec, SkBitmap* bitmap, + SkBitmap::Config prefConfig, SkImageDecoder::Mode mode) { + TPvJpgDecInfo info; + TPvJpgDecStatus status = codec->GetInfo(&info); + if (status != TPVJPGDEC_SUCCESS) + return false; + + int width = info.iWidth[0]; + int height = info.iHeight[0]; + + bitmap->setConfig(SkBitmap::kRGB_565_Config, width, height); + bitmap->setIsOpaque(true); + + if (SkImageDecoder::kDecodeBounds_Mode == mode) { + return true; + } + + SkASSERT(info.iNumComponent == 3); + + TPvJpgDecOutputFmt format; + format.iColorFormat = TPV_COLORFMT_RGB16; + format.iCropped.topLeftX = 0; + format.iCropped.topLeftY = 0; + format.iCropped.bottomRightX = width - 1; + format.iCropped.bottomRightY = height - 1; + format.iOutputPitch = bitmap->rowBytes() >> 1; + status = codec->SetOutput(&format); + if (status != TPVJPGDEC_SUCCESS) { + SkDebugf("--- PV SetOutput failed %d\n", status); + return false; + } + + TPvJpgDecFrame frame; + uint8* ptrs[3]; + int32 widths[3], heights[3]; + bzero(ptrs, sizeof(ptrs)); + frame.ptr = ptrs; + frame.iWidth = widths; + frame.iHeight = heights; + + status = codec->GetFrame(&frame); + if (status != TPVJPGDEC_SUCCESS) { + SkDebugf("--- PV GetFrame failed %d\n", status); + return false; + } + + bitmap->allocPixels(); + memcpy(bitmap->getPixels(), ptrs[0], bitmap->getSize()); + return true; +} + +class OsclCleanupper { +public: + OsclCleanupper() { + OsclBase::Init(); + OsclErrorTrap::Init(); + OsclMem::Init(); + } + ~OsclCleanupper() { + OsclMem::Cleanup(); + OsclErrorTrap::Cleanup(); + OsclBase::Cleanup(); + } +}; + +bool SkPVJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* decodedBitmap, + SkBitmap::Config prefConfig, Mode mode) +{ + // do I need this guy? + OsclCleanupper oc; + + PVJpgDecoderInterface* codec = PVJpgDecoderFactory::CreatePVJpgDecoder(); + TPvJpgDecStatus status = codec->Init(); + check_status(status); + + MyObserver observer; // must create before autopvdelete + AutoPVDelete ad(codec); + + status = codec->SetObserver(&observer); + check_status(status); + + char* storage = fStorage; + int32 bytesInStorage = 0; + for (;;) + { + int32 bytesRead = stream->read(storage + bytesInStorage, + STORAGE_SIZE - bytesInStorage); + if (bytesRead <= 0) { + SkDEBUGF(("SkPVJPEGImageDecoder: stream read returned %d\n", bytesRead)); + return false; + } + + // update bytesInStorage to account for the read() + bytesInStorage += bytesRead; + SkASSERT(bytesInStorage <= STORAGE_SIZE); + + // now call Decode to eat some of the bytes + int32 consumed = bytesInStorage; + status = codec->Decode((uint8*)storage, &consumed); + + SkASSERT(bytesInStorage >= consumed); + bytesInStorage -= consumed; + // now bytesInStorage is the remaining unread bytes + if (bytesInStorage > 0) { // slide the leftovers to the beginning + SkASSERT(storage == fStorage); + SkASSERT(consumed >= 0 && bytesInStorage >= 0); + SkASSERT((size_t)(consumed + bytesInStorage) <= sizeof(fStorage)); + SkASSERT(sizeof(fStorage) == STORAGE_SIZE); + // SkDebugf("-- memmov srcOffset=%d, numBytes=%d\n", consumed, bytesInStorage); + memmove(storage, storage + consumed, bytesInStorage); + } + + switch (status) { + case TPVJPGDEC_SUCCESS: + SkDEBUGF(("SkPVJPEGImageDecoder::Decode returned success?\n");) + return false; + case TPVJPGDEC_FRAME_READY: + case TPVJPGDEC_DONE: + return getFrame(codec, decodedBitmap, prefConfig, mode); + case TPVJPGDEC_FAIL: + case TPVJPGDEC_INVALID_MEMORY: + case TPVJPGDEC_INVALID_PARAMS: + case TPVJPGDEC_NO_IMAGE_DATA: + SkDEBUGF(("SkPVJPEGImageDecoder: failed to decode err=%d\n", status);) + return false; + case TPVJPGDEC_WAITING_FOR_INPUT: + break; // loop around and eat more from the stream + } + } + return false; +} + diff --git a/skia/images/SkImageDecoder_wbmp.cpp b/skia/images/SkImageDecoder_wbmp.cpp new file mode 100644 index 0000000..d2fea75 --- /dev/null +++ b/skia/images/SkImageDecoder_wbmp.cpp @@ -0,0 +1,167 @@ +/** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkImageDecoder.h" +#include "SkColor.h" +#include "SkColorPriv.h" +#include "SkMath.h" +#include "SkStream.h" +#include "SkTemplates.h" +#include "SkUtils.h" + +class SkWBMPImageDecoder : public SkImageDecoder { +public: + virtual Format getFormat() const { + return kWBMP_Format; + } + +protected: + virtual bool onDecode(SkStream* stream, SkBitmap* bm, + SkBitmap::Config pref, Mode); +}; + +static bool read_byte(SkStream* stream, uint8_t* data) +{ + return stream->read(data, 1) == 1; +} + +static bool read_mbf(SkStream* stream, int* value) +{ + int n = 0; + uint8_t data; + do { + if (!read_byte(stream, &data)) { + return false; + } + n = (n << 7) | (data & 0x7F); + } while (data & 0x80); + + *value = n; + return true; +} + +struct wbmp_head { + int fWidth; + int fHeight; + + bool init(SkStream* stream) + { + uint8_t data; + + if (!read_byte(stream, &data) || data != 0) { // unknown type + return false; + } + if (!read_byte(stream, &data) || (data & 0x9F)) { // skip fixed header + return false; + } + if (!read_mbf(stream, &fWidth) || (unsigned)fWidth > 0xFFFF) { + return false; + } + if (!read_mbf(stream, &fHeight) || (unsigned)fHeight > 0xFFFF) { + return false; + } + return fWidth != 0 && fHeight != 0; + } +}; + +SkImageDecoder* SkImageDecoder_WBMP_Factory(SkStream* stream) +{ + wbmp_head head; + + if (head.init(stream)) { + return SkNEW(SkWBMPImageDecoder); + } + return NULL; +} + +static void expand_bits_to_bytes(uint8_t dst[], const uint8_t src[], int bits) +{ + int bytes = bits >> 3; + + for (int i = 0; i < bytes; i++) { + unsigned mask = *src++; + dst[0] = (mask >> 7) & 1; + dst[1] = (mask >> 6) & 1; + dst[2] = (mask >> 5) & 1; + dst[3] = (mask >> 4) & 1; + dst[4] = (mask >> 3) & 1; + dst[5] = (mask >> 2) & 1; + dst[6] = (mask >> 1) & 1; + dst[7] = (mask >> 0) & 1; + dst += 8; + } + + bits &= 7; + if (bits > 0) { + unsigned mask = *src; + do { + *dst++ = (mask >> 7) & 1;; + mask <<= 1; + } while (--bits != 0); + } +} + +#define SkAlign8(x) (((x) + 7) & ~7) + +bool SkWBMPImageDecoder::onDecode(SkStream* stream, SkBitmap* decodedBitmap, + SkBitmap::Config prefConfig, Mode mode) +{ + wbmp_head head; + + if (!head.init(stream)) { + return false; + } + + int width = head.fWidth; + int height = head.fHeight; + + // assign these directly, in case we return kDimensions_Result + decodedBitmap->setConfig(SkBitmap::kIndex8_Config, width, height); + decodedBitmap->setIsOpaque(true); + + if (SkImageDecoder::kDecodeBounds_Mode == mode) + return true; + + const SkPMColor colors[] = { SK_ColorBLACK, SK_ColorWHITE }; + SkColorTable* ct = SkNEW_ARGS(SkColorTable, (colors, 2)); + SkAutoUnref aur(ct); + + if (!this->allocPixelRef(decodedBitmap, ct)) { + return false; + } + + SkAutoLockPixels alp(*decodedBitmap); + + uint8_t* dst = decodedBitmap->getAddr8(0, 0); + // store the 1-bit valuess at the end of our pixels, so we won't stomp + // on them before we're read them. Just trying to avoid a temp allocation + size_t srcRB = SkAlign8(width) >> 3; + size_t srcSize = height * srcRB; + uint8_t* src = dst + decodedBitmap->getSize() - srcSize; + if (stream->read(src, srcSize) != srcSize) { + return false; + } + + for (int y = 0; y < height; y++) + { + expand_bits_to_bytes(dst, src, width); + dst += decodedBitmap->rowBytes(); + src += srcRB; + } + + return true; +} + diff --git a/skia/images/SkImageRef.cpp b/skia/images/SkImageRef.cpp new file mode 100644 index 0000000..05960a2 --- /dev/null +++ b/skia/images/SkImageRef.cpp @@ -0,0 +1,157 @@ +#include "SkImageRef.h" +#include "SkBitmap.h" +#include "SkFlattenable.h" +#include "SkImageDecoder.h" +#include "SkStream.h" +#include "SkTemplates.h" +#include "SkThread.h" + +// can't be static, as SkImageRef_Pool needs to see it +SkMutex gImageRefMutex; + +/////////////////////////////////////////////////////////////////////////////// + +SkImageRef::SkImageRef(SkStream* stream, SkBitmap::Config config, + int sampleSize) + : SkPixelRef(&gImageRefMutex), fErrorInDecoding(false) { + SkASSERT(stream); + SkASSERT(1 == stream->getRefCnt()); + + fStream = stream; + fConfig = config; + fSampleSize = sampleSize; + fPrev = fNext = NULL; + +#ifdef DUMP_IMAGEREF_LIFECYCLE + SkDebugf("add ImageRef %p [%d] data=%d\n", + this, config, (int)stream->getLength()); +#endif +} + +SkImageRef::~SkImageRef() { + SkASSERT(&gImageRefMutex == this->mutex()); + +#ifdef DUMP_IMAGEREF_LIFECYCLE + SkDebugf("delete ImageRef %p [%d] data=%d\n", + this, fConfig, (int)fStream->getLength()); +#endif + + delete fStream; +} + +bool SkImageRef::getInfo(SkBitmap* bitmap) { + SkAutoMutexAcquire ac(gImageRefMutex); + + if (!this->prepareBitmap(SkImageDecoder::kDecodeBounds_Mode)) { + return false; + } + + SkASSERT(SkBitmap::kNo_Config != fBitmap.config()); + if (bitmap) { + bitmap->setConfig(fBitmap.config(), fBitmap.width(), fBitmap.height()); + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool SkImageRef::onDecode(SkImageDecoder* codec, SkStream* stream, + SkBitmap* bitmap, SkBitmap::Config config, + SkImageDecoder::Mode mode) { + return codec->onDecode(stream, bitmap, config, mode); +} + +bool SkImageRef::prepareBitmap(SkImageDecoder::Mode mode) { + SkASSERT(&gImageRefMutex == this->mutex()); + + if (fErrorInDecoding) { + return false; + } + + if (NULL != fBitmap.getPixels() || + (SkBitmap::kNo_Config != fBitmap.config() && + SkImageDecoder::kDecodeBounds_Mode == mode)) { + return true; + } + + SkASSERT(fBitmap.getPixels() == NULL); + + fStream->rewind(); + + SkImageDecoder* codec = SkImageDecoder::Factory(fStream); + if (codec) { + SkAutoTDelete<SkImageDecoder> ad(codec); + + codec->setSampleSize(fSampleSize); + if (this->onDecode(codec, fStream, &fBitmap, fConfig, mode)) { + return true; + } + } + +#ifdef DUMP_IMAGEREF_LIFECYCLE + if (NULL == codec) { + SkDebugf("--- ImageRef: <%s> failed to find codec\n", fName.c_str()); + } else { + SkDebugf("--- ImageRef: <%s> failed in codec for %d mode\n", + fName.c_str(), mode); + } +#endif + fErrorInDecoding = true; + fBitmap.reset(); + return false; +} + +void* SkImageRef::onLockPixels(SkColorTable** ct) { + SkASSERT(&gImageRefMutex == this->mutex()); + + if (NULL == fBitmap.getPixels()) { + (void)this->prepareBitmap(SkImageDecoder::kDecodePixels_Mode); + } + + if (ct) { + *ct = fBitmap.getColorTable(); + } + return fBitmap.getPixels(); +} + +void SkImageRef::onUnlockPixels() { + // we're already have the mutex locked + SkASSERT(&gImageRefMutex == this->mutex()); +} + +size_t SkImageRef::ramUsed() const { + size_t size = 0; + + if (fBitmap.getPixels()) { + size = fBitmap.getSize(); + if (fBitmap.getColorTable()) { + size += fBitmap.getColorTable()->count() * sizeof(SkPMColor); + } + } + return size; +} + +/////////////////////////////////////////////////////////////////////////////// + +SkImageRef::SkImageRef(SkFlattenableReadBuffer& buffer) + : INHERITED(buffer, &gImageRefMutex), fErrorInDecoding(false) { + fConfig = (SkBitmap::Config)buffer.readU8(); + fSampleSize = buffer.readU8(); + size_t length = buffer.readU32(); + fStream = SkNEW_ARGS(SkMemoryStream, (length)); + buffer.read((void*)fStream->getMemoryBase(), length); + + fPrev = fNext = NULL; +} + +void SkImageRef::flatten(SkFlattenableWriteBuffer& buffer) const { + this->INHERITED::flatten(buffer); + + buffer.write8(fConfig); + buffer.write8(fSampleSize); + size_t length = fStream->getLength(); + buffer.write32(length); + fStream->rewind(); + buffer.readFromStream(fStream, length); +} + diff --git a/skia/images/SkImageRefPool.cpp b/skia/images/SkImageRefPool.cpp new file mode 100644 index 0000000..fa3ef8a --- /dev/null +++ b/skia/images/SkImageRefPool.cpp @@ -0,0 +1,186 @@ +#include "SkImageRefPool.h" +#include "SkImageRef.h" +#include "SkThread.h" + +SkImageRefPool::SkImageRefPool() { + fRAMBudget = 0; // means no explicit limit + fRAMUsed = 0; + fCount = 0; + fHead = fTail = NULL; +} + +SkImageRefPool::~SkImageRefPool() { + // SkASSERT(NULL == fHead); +} + +void SkImageRefPool::setRAMBudget(size_t size) { + if (fRAMBudget != size) { + fRAMBudget = size; + this->purgeIfNeeded(); + } +} + +void SkImageRefPool::justAddedPixels(SkImageRef* ref) { +#ifdef DUMP_IMAGEREF_LIFECYCLE + SkDebugf("=== ImagePool: add pixels %s [%d %d %d] bytes=%d heap=%d\n", + ref->fName.c_str(), + ref->fBitmap.width(), ref->fBitmap.height(), + ref->fBitmap.bytesPerPixel(), + ref->fBitmap.getSize(), (int)fRAMUsed); +#endif + fRAMUsed += ref->ramUsed(); + this->purgeIfNeeded(); +} + +void SkImageRefPool::canLosePixels(SkImageRef* ref) { + // the refs near fHead have recently been released (used) + // if we purge, we purge from the tail + this->detach(ref); + this->addToHead(ref); + this->purgeIfNeeded(); +} + +void SkImageRefPool::purgeIfNeeded() { + // do nothing if we have a zero-budget (i.e. unlimited) + if (fRAMBudget != 0) { + this->setRAMUsed(fRAMBudget); + } +} + +void SkImageRefPool::setRAMUsed(size_t limit) { + SkImageRef* ref = fTail; + + while (NULL != ref && fRAMUsed > limit) { + // only purge it if its pixels are unlocked + if (0 == ref->getLockCount() && ref->fBitmap.getPixels()) { + size_t size = ref->ramUsed(); + SkASSERT(size <= fRAMUsed); + fRAMUsed -= size; + +#ifdef DUMP_IMAGEREF_LIFECYCLE + SkDebugf("=== ImagePool: purge %s [%d %d %d] bytes=%d heap=%d\n", + ref->fName.c_str(), + ref->fBitmap.width(), ref->fBitmap.height(), + ref->fBitmap.bytesPerPixel(), + (int)size, (int)fRAMUsed); +#endif + + // remember the bitmap config (don't call reset), + // just clear the pixel memory + ref->fBitmap.setPixels(NULL); + SkASSERT(NULL == ref->fBitmap.getPixels()); + } + ref = ref->fPrev; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void SkImageRefPool::addToHead(SkImageRef* ref) { + ref->fNext = fHead; + ref->fPrev = NULL; + + if (fHead) { + SkASSERT(NULL == fHead->fPrev); + fHead->fPrev = ref; + } + fHead = ref; + + if (NULL == fTail) { + fTail = ref; + } + fCount += 1; + SkASSERT(computeCount() == fCount); + + fRAMUsed += ref->ramUsed(); +} + +void SkImageRefPool::addToTail(SkImageRef* ref) { + ref->fNext = NULL; + ref->fPrev = fTail; + + if (fTail) { + SkASSERT(NULL == fTail->fNext); + fTail->fNext = ref; + } + fTail = ref; + + if (NULL == fHead) { + fHead = ref; + } + fCount += 1; + SkASSERT(computeCount() == fCount); + + fRAMUsed += ref->ramUsed(); +} + +void SkImageRefPool::detach(SkImageRef* ref) { + SkASSERT(fCount > 0); + + if (fHead == ref) { + fHead = ref->fNext; + } + if (fTail == ref) { + fTail = ref->fPrev; + } + if (ref->fPrev) { + ref->fPrev->fNext = ref->fNext; + } + if (ref->fNext) { + ref->fNext->fPrev = ref->fPrev; + } + + ref->fNext = ref->fPrev = NULL; + + fCount -= 1; + SkASSERT(computeCount() == fCount); + + SkASSERT(fRAMUsed >= ref->ramUsed()); + fRAMUsed -= ref->ramUsed(); +} + +int SkImageRefPool::computeCount() const { + SkImageRef* ref = fHead; + int count = 0; + + while (ref != NULL) { + count += 1; + ref = ref->fNext; + } + +#ifdef SK_DEBUG + ref = fTail; + int count2 = 0; + + while (ref != NULL) { + count2 += 1; + ref = ref->fPrev; + } + SkASSERT(count2 == count); +#endif + + return count; +} + +/////////////////////////////////////////////////////////////////////////////// + +#include "SkStream.h" + +void SkImageRefPool::dump() const { +#if defined(SK_DEBUG) || defined(DUMP_IMAGEREF_LIFECYCLE) + SkDebugf("ImagePool dump: bugdet: %d used: %d count: %d\n", + (int)fRAMBudget, (int)fRAMUsed, fCount); + + SkImageRef* ref = fHead; + + while (ref != NULL) { + SkDebugf(" [%3d %3d %d] ram=%d data=%d locks=%d %s\n", ref->fBitmap.width(), + ref->fBitmap.height(), ref->fBitmap.config(), + ref->ramUsed(), (int)ref->fStream->getLength(), + ref->getLockCount(), ref->fName.c_str()); + + ref = ref->fNext; + } +#endif +} + diff --git a/skia/images/SkImageRefPool.h b/skia/images/SkImageRefPool.h new file mode 100644 index 0000000..b2eb7b3 --- /dev/null +++ b/skia/images/SkImageRefPool.h @@ -0,0 +1,43 @@ +#ifndef SkImageRefPool_DEFINED +#define SkImageRefPool_DEFINED + +#include "SkTypes.h" + +class SkImageRef; +class SkImageRef_GlobalPool; + +class SkImageRefPool { +public: + SkImageRefPool(); + ~SkImageRefPool(); + + size_t getRAMBudget() const { return fRAMBudget; } + void setRAMBudget(size_t); + + size_t getRAMUsed() const { return fRAMUsed; } + void setRAMUsed(size_t limit); + + void addToHead(SkImageRef*); + void addToTail(SkImageRef*); + void detach(SkImageRef*); + + void dump() const; + +private: + size_t fRAMBudget; + size_t fRAMUsed; + + int fCount; + SkImageRef* fHead, *fTail; + + int computeCount() const; + + friend class SkImageRef_GlobalPool; + + void justAddedPixels(SkImageRef*); + void canLosePixels(SkImageRef*); + void purgeIfNeeded(); +}; + +#endif + diff --git a/skia/images/SkImageRef_GlobalPool.cpp b/skia/images/SkImageRef_GlobalPool.cpp new file mode 100644 index 0000000..1f0bc43 --- /dev/null +++ b/skia/images/SkImageRef_GlobalPool.cpp @@ -0,0 +1,83 @@ +#include "SkImageRef_GlobalPool.h" +#include "SkImageRefPool.h" +#include "SkThread.h" + +extern SkMutex gImageRefMutex; + +static SkImageRefPool gGlobalImageRefPool; + +SkImageRef_GlobalPool::SkImageRef_GlobalPool(SkStream* stream, + SkBitmap::Config config, + int sampleSize) + : SkImageRef(stream, config, sampleSize) { + this->mutex()->acquire(); + gGlobalImageRefPool.addToHead(this); + this->mutex()->release(); +} + +SkImageRef_GlobalPool::~SkImageRef_GlobalPool() { + this->mutex()->acquire(); + gGlobalImageRefPool.detach(this); + this->mutex()->release(); +} + +bool SkImageRef_GlobalPool::onDecode(SkImageDecoder* codec, SkStream* stream, + SkBitmap* bitmap, SkBitmap::Config config, + SkImageDecoder::Mode mode) { + if (!this->INHERITED::onDecode(codec, stream, bitmap, config, mode)) { + return false; + } + if (mode == SkImageDecoder::kDecodePixels_Mode) { + gGlobalImageRefPool.justAddedPixels(this); + } + return true; +} + +void SkImageRef_GlobalPool::onUnlockPixels() { + this->INHERITED::onUnlockPixels(); + + gGlobalImageRefPool.canLosePixels(this); +} + +SkImageRef_GlobalPool::SkImageRef_GlobalPool(SkFlattenableReadBuffer& buffer) + : INHERITED(buffer) { + this->mutex()->acquire(); + gGlobalImageRefPool.addToHead(this); + this->mutex()->release(); +} + +SkPixelRef* SkImageRef_GlobalPool::Create(SkFlattenableReadBuffer& buffer) { + return SkNEW_ARGS(SkImageRef_GlobalPool, (buffer)); +} + +static SkPixelRef::Registrar::Registrar reg("SkImageRef_GlobalPool", + SkImageRef_GlobalPool::Create); + +/////////////////////////////////////////////////////////////////////////////// +// global imagerefpool wrappers + +size_t SkImageRef_GlobalPool::GetRAMBudget() { + SkAutoMutexAcquire ac(gImageRefMutex); + return gGlobalImageRefPool.getRAMBudget(); +} + +void SkImageRef_GlobalPool::SetRAMBudget(size_t size) { + SkAutoMutexAcquire ac(gImageRefMutex); + gGlobalImageRefPool.setRAMBudget(size); +} + +size_t SkImageRef_GlobalPool::GetRAMUsed() { + SkAutoMutexAcquire ac(gImageRefMutex); + return gGlobalImageRefPool.getRAMUsed(); +} + +void SkImageRef_GlobalPool::SetRAMUsed(size_t usage) { + SkAutoMutexAcquire ac(gImageRefMutex); + gGlobalImageRefPool.setRAMUsed(usage); +} + +void SkImageRef_GlobalPool::DumpPool() { + SkAutoMutexAcquire ac(gImageRefMutex); + gGlobalImageRefPool.dump(); +} + diff --git a/skia/images/SkMMapStream.cpp b/skia/images/SkMMapStream.cpp new file mode 100644 index 0000000..2aee945 --- /dev/null +++ b/skia/images/SkMMapStream.cpp @@ -0,0 +1,63 @@ +#include "SkMMapStream.h" + +#include <unistd.h> +#include <sys/mman.h> +#include <fcntl.h> +#include <errno.h> + +SkMMAPStream::SkMMAPStream(const char filename[]) +{ + fFildes = -1; // initialize to failure case + + int fildes = open(filename, O_RDONLY); + if (fildes < 0) + { + SkDEBUGF(("---- failed to open(%s) for mmap stream error=%d\n", filename, errno)); + return; + } + + off_t size = lseek(fildes, 0, SEEK_END); // find the file size + if (size == -1) + { + SkDEBUGF(("---- failed to lseek(%s) for mmap stream error=%d\n", filename, errno)); + close(fildes); + return; + } + (void)lseek(fildes, 0, SEEK_SET); // restore file offset to beginning + + void* addr = mmap(NULL, size, PROT_READ, MAP_SHARED, fildes, 0); + if (MAP_FAILED == addr) + { + SkDEBUGF(("---- failed to mmap(%s) for mmap stream error=%d\n", filename, errno)); + close(fildes); + return; + } + + this->INHERITED::setMemory(addr, size); + + fFildes = fildes; + fAddr = addr; + fSize = size; +} + +SkMMAPStream::~SkMMAPStream() +{ + this->closeMMap(); +} + +void SkMMAPStream::setMemory(const void* data, size_t length) +{ + this->closeMMap(); + this->INHERITED::setMemory(data, length); +} + +void SkMMAPStream::closeMMap() +{ + if (fFildes >= 0) + { + munmap(fAddr, fSize); + close(fFildes); + fFildes = -1; + } +} + diff --git a/skia/images/SkMovie.cpp b/skia/images/SkMovie.cpp new file mode 100644 index 0000000..4b8f16a --- /dev/null +++ b/skia/images/SkMovie.cpp @@ -0,0 +1,97 @@ +#include "SkMovie.h" +#include "SkCanvas.h" +#include "SkPaint.h" + +// We should never see this in normal operation since our time values are +// 0-based. So we use it as a sentinal. +#define UNINITIALIZED_MSEC ((SkMSec)-1) + +SkMovie::SkMovie() +{ + fInfo.fDuration = UNINITIALIZED_MSEC; // uninitialized + fCurrTime = UNINITIALIZED_MSEC; // uninitialized + fNeedBitmap = true; +} + +void SkMovie::ensureInfo() +{ + if (fInfo.fDuration == UNINITIALIZED_MSEC && !this->onGetInfo(&fInfo)) + memset(&fInfo, 0, sizeof(fInfo)); // failure +} + +SkMSec SkMovie::duration() +{ + this->ensureInfo(); + return fInfo.fDuration; +} + +int SkMovie::width() +{ + this->ensureInfo(); + return fInfo.fWidth; +} + +int SkMovie::height() +{ + this->ensureInfo(); + return fInfo.fHeight; +} + +int SkMovie::isOpaque() +{ + this->ensureInfo(); + return fInfo.fIsOpaque; +} + +bool SkMovie::setTime(SkMSec time) +{ + SkMSec dur = this->duration(); + if (time > dur) + time = dur; + + bool changed = false; + if (time != fCurrTime) + { + fCurrTime = time; + changed = this->onSetTime(time); + fNeedBitmap |= changed; + } + return changed; +} + +const SkBitmap& SkMovie::bitmap() +{ + if (fCurrTime == UNINITIALIZED_MSEC) // uninitialized + this->setTime(0); + + if (fNeedBitmap) + { + if (!this->onGetBitmap(&fBitmap)) // failure + fBitmap.reset(); + fNeedBitmap = false; + } + return fBitmap; +} + +//////////////////////////////////////////////////////////////////// + +#include "SkStream.h" + +SkMovie* SkMovie::DecodeFile(const char path[]) +{ + SkMovie* movie = NULL; + + // since the movie might hold onto the stream + // we dynamically allocate it and then unref() + SkFILEStream* stream = new SkFILEStream(path); + if (stream->isValid()) + movie = SkMovie::DecodeStream(stream); +#ifdef SK_DEBUG + else + SkDebugf("Movie file not found <%s>\n", path); +#endif + stream->unref(); + + return movie; +} + diff --git a/skia/images/SkMovie_gif.cpp b/skia/images/SkMovie_gif.cpp new file mode 100644 index 0000000..907ea82 --- /dev/null +++ b/skia/images/SkMovie_gif.cpp @@ -0,0 +1,227 @@ +/* libs/graphics/images/SkImageDecoder_libgif.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkMovie.h" +#include "SkColor.h" +#include "SkColorPriv.h" +#include "SkStream.h" +#include "SkTemplates.h" + +#include "gif_lib.h" + +class SkGIFMovie : public SkMovie { +public: + // we do NOT hold onto the stream, so it is OK if it is on the + // stack of the caller + SkGIFMovie(SkStream* stream); + virtual ~SkGIFMovie(); + +protected: + virtual bool onGetInfo(Info*); + virtual bool onSetTime(SkMSec); + virtual bool onGetBitmap(SkBitmap*); + +private: + GifFileType* fGIF; + SavedImage* fCurrSavedImage; +}; + +SkMovie* SkMovie_GIF_StreamFactory(SkStream* stream) { + char buf[GIF_STAMP_LEN]; + if (stream->read(buf, GIF_STAMP_LEN) == GIF_STAMP_LEN) { + if (memcmp(GIF_STAMP, buf, GIF_STAMP_LEN) == 0 || + memcmp(GIF87_STAMP, buf, GIF_STAMP_LEN) == 0 || + memcmp(GIF89_STAMP, buf, GIF_STAMP_LEN) == 0) { + stream->rewind(); + return SkNEW_ARGS(SkGIFMovie, (stream)); + } + } + return NULL; +} + +SkMovie* SkMovie_GIF_MemoryFactory(const void* data, size_t length) { + if (length > GIF_STAMP_LEN && !memcmp(GIF_STAMP, data, GIF_STAMP_LEN)) { + SkMemoryStream stream(data, length); + return SkNEW_ARGS(SkGIFMovie, (&stream)); + } + return NULL; +} + +static int Decode(GifFileType* fileType, GifByteType* out, int size) { + SkStream* stream = (SkStream*) fileType->UserData; + return (int) stream->read(out, size); +} + +SkGIFMovie::SkGIFMovie(SkStream* stream) +{ + fGIF = DGifOpen( stream, Decode ); + if (NULL == fGIF) + return; + + if (DGifSlurp(fGIF) != GIF_OK) + { + DGifCloseFile(fGIF); + fGIF = NULL; + } + fCurrSavedImage = NULL; +} + +SkGIFMovie::~SkGIFMovie() +{ + if (fGIF) + DGifCloseFile(fGIF); +} + +static SkMSec savedimage_duration(const SavedImage* image) +{ + for (int j = 0; j < image->ExtensionBlockCount; j++) + { + if (image->ExtensionBlocks[j].Function == GRAPHICS_EXT_FUNC_CODE) + { + int size = image->ExtensionBlocks[j].ByteCount; + SkASSERT(size >= 4); + const uint8_t* b = (const uint8_t*)image->ExtensionBlocks[j].Bytes; + return ((b[2] << 8) | b[1]) * 10; + } + } + return 0; +} + +bool SkGIFMovie::onGetInfo(Info* info) +{ + if (NULL == fGIF) + return false; + + SkMSec dur = 0; + for (int i = 0; i < fGIF->ImageCount; i++) + dur += savedimage_duration(&fGIF->SavedImages[i]); + + info->fDuration = dur; + info->fWidth = fGIF->SWidth; + info->fHeight = fGIF->SHeight; + info->fIsOpaque = false; // how to compute? + return true; +} + +bool SkGIFMovie::onSetTime(SkMSec time) +{ + if (NULL == fGIF) + return false; + + SkMSec dur = 0; + for (int i = 0; i < fGIF->ImageCount; i++) + { + dur += savedimage_duration(&fGIF->SavedImages[i]); + if (dur >= time) + { + SavedImage* prev = fCurrSavedImage; + fCurrSavedImage = &fGIF->SavedImages[i]; + return prev != fCurrSavedImage; + } + } + fCurrSavedImage = &fGIF->SavedImages[fGIF->ImageCount - 1]; + return true; +} + +bool SkGIFMovie::onGetBitmap(SkBitmap* bm) +{ + GifFileType* gif = fGIF; + if (NULL == gif) + return false; + + // should we check for the Image cmap or the global (SColorMap) first? <reed> + ColorMapObject* cmap = gif->SColorMap; + if (cmap == NULL) + cmap = gif->Image.ColorMap; + + if (cmap == NULL || gif->ImageCount < 1 || cmap->ColorCount != (1 << cmap->BitsPerPixel)) + { + SkASSERT(!"bad colortable setup"); + return false; + } + + SavedImage* gif_image = fCurrSavedImage; + const int width = gif->SWidth; + const int height = gif->SHeight; + SkBitmap::Config config = SkBitmap::kIndex8_Config; + + SkColorTable* colorTable = SkNEW_ARGS(SkColorTable, (cmap->ColorCount)); + bm->setConfig(config, width, height, 0); + bm->allocPixels(colorTable); + colorTable->unref(); + + int transparent = -1; + for (int i = 0; i < gif_image->ExtensionBlockCount; ++i) { + ExtensionBlock* eb = gif_image->ExtensionBlocks + i; + if (eb->Function == 0xF9 && + eb->ByteCount == 4) { + bool has_transparency = ((eb->Bytes[0] & 1) == 1); + if (has_transparency) { + transparent = (unsigned char)eb->Bytes[3]; + } + } + } + + SkPMColor* colorPtr = colorTable->lockColors(); + + if (transparent >= 0) + memset(colorPtr, 0, cmap->ColorCount * 4); + else + colorTable->setFlags(colorTable->getFlags() | SkColorTable::kColorsAreOpaque_Flag); + + for (int index = 0; index < cmap->ColorCount; index++) + { + if (transparent != index) + colorPtr[index] = SkPackARGB32(0xFF, cmap->Colors[index].Red, + cmap->Colors[index].Green, cmap->Colors[index].Blue); + } + colorTable->unlockColors(true); + + unsigned char* in = (unsigned char*)gif_image->RasterBits; + unsigned char* out = bm->getAddr8(0, 0); + if (gif->Image.Interlace) { + + // deinterlace + int row; + // group 1 - every 8th row, starting with row 0 + for (row = 0; row < height; row += 8) { + memcpy(out + width * row, in, width); + in += width; + } + + // group 2 - every 8th row, starting with row 4 + for (row = 4; row < height; row += 8) { + memcpy(out + width * row, in, width); + in += width; + } + + // group 3 - every 4th row, starting with row 2 + for (row = 2; row < height; row += 4) { + memcpy(out + width * row, in, width); + in += width; + } + + for (row = 1; row < height; row += 2) { + memcpy(out + width * row, in, width); + in += width; + } + + } else { + memcpy(out, in, width * height); + } + return true; +} diff --git a/skia/images/SkScaledBitmapSampler.cpp b/skia/images/SkScaledBitmapSampler.cpp new file mode 100644 index 0000000..529be61 --- /dev/null +++ b/skia/images/SkScaledBitmapSampler.cpp @@ -0,0 +1,338 @@ +/* + * Copyright 2007, Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "SkScaledBitmapSampler.h" +#include "SkBitmap.h" +#include "SkColorPriv.h" +#include "SkDither.h" + +// 8888 + +static bool Sample_Gray_D8888(void* SK_RESTRICT dstRow, + const uint8_t* SK_RESTRICT src, + int width, int deltaSrc, int) { + SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow; + for (int x = 0; x < width; x++) { + dst[x] = SkPackARGB32(0xFF, src[0], src[0], src[0]); + src += deltaSrc; + } + return false; +} + +static bool Sample_RGBx_D8888(void* SK_RESTRICT dstRow, + const uint8_t* SK_RESTRICT src, + int width, int deltaSrc, int) { + SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow; + for (int x = 0; x < width; x++) { + dst[x] = SkPackARGB32(0xFF, src[0], src[1], src[2]); + src += deltaSrc; + } + return false; +} + +static bool Sample_RGBA_D8888(void* SK_RESTRICT dstRow, + const uint8_t* SK_RESTRICT src, + int width, int deltaSrc, int) { + SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow; + unsigned alphaMask = 0xFF; + for (int x = 0; x < width; x++) { + unsigned alpha = src[3]; + dst[x] = SkPreMultiplyARGB(alpha, src[0], src[1], src[2]); + src += deltaSrc; + alphaMask &= alpha; + } + return alphaMask != 0xFF; +} + +// 565 + +static bool Sample_Gray_D565(void* SK_RESTRICT dstRow, + const uint8_t* SK_RESTRICT src, + int width, int deltaSrc, int) { + uint16_t* SK_RESTRICT dst = (uint16_t*)dstRow; + for (int x = 0; x < width; x++) { + dst[x] = SkPack888ToRGB16(src[0], src[0], src[0]); + src += deltaSrc; + } + return false; +} + +static bool Sample_Gray_D565_D(void* SK_RESTRICT dstRow, + const uint8_t* SK_RESTRICT src, + int width, int deltaSrc, int y) { + uint16_t* SK_RESTRICT dst = (uint16_t*)dstRow; + DITHER_565_SCAN(y); + for (int x = 0; x < width; x++) { + dst[x] = SkDitherRGBTo565(src[0], src[0], src[0], DITHER_VALUE(x)); + src += deltaSrc; + } + return false; +} + +static bool Sample_RGBx_D565(void* SK_RESTRICT dstRow, + const uint8_t* SK_RESTRICT src, + int width, int deltaSrc, int) { + uint16_t* SK_RESTRICT dst = (uint16_t*)dstRow; + for (int x = 0; x < width; x++) { + dst[x] = SkPack888ToRGB16(src[0], src[1], src[2]); + src += deltaSrc; + } + return false; +} + +static bool Sample_RGBx_D565_D(void* SK_RESTRICT dstRow, + const uint8_t* SK_RESTRICT src, + int width, int deltaSrc, int y) { + uint16_t* SK_RESTRICT dst = (uint16_t*)dstRow; + DITHER_565_SCAN(y); + for (int x = 0; x < width; x++) { + dst[x] = SkDitherRGBTo565(src[0], src[1], src[2], DITHER_VALUE(x)); + src += deltaSrc; + } + return false; +} + +// 4444 + +static bool Sample_Gray_D4444(void* SK_RESTRICT dstRow, + const uint8_t* SK_RESTRICT src, + int width, int deltaSrc, int) { + SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow; + for (int x = 0; x < width; x++) { + unsigned gray = src[0] >> 4; + dst[x] = SkPackARGB4444(0xF, gray, gray, gray); + src += deltaSrc; + } + return false; +} + +static bool Sample_Gray_D4444_D(void* SK_RESTRICT dstRow, + const uint8_t* SK_RESTRICT src, + int width, int deltaSrc, int y) { + SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow; + DITHER_4444_SCAN(y); + for (int x = 0; x < width; x++) { + dst[x] = SkDitherARGB32To4444(0xFF, src[0], src[0], src[0], + DITHER_VALUE(x)); + src += deltaSrc; + } + return false; +} + +static bool Sample_RGBx_D4444(void* SK_RESTRICT dstRow, + const uint8_t* SK_RESTRICT src, + int width, int deltaSrc, int) { + SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow; + for (int x = 0; x < width; x++) { + dst[x] = SkPackARGB4444(0xF, src[0] >> 4, src[1] >> 4, src[2] >> 4); + src += deltaSrc; + } + return false; +} + +static bool Sample_RGBx_D4444_D(void* SK_RESTRICT dstRow, + const uint8_t* SK_RESTRICT src, + int width, int deltaSrc, int y) { + SkPMColor16* dst = (SkPMColor16*)dstRow; + DITHER_4444_SCAN(y); + + for (int x = 0; x < width; x++) { + dst[x] = SkDitherARGB32To4444(0xFF, src[0], src[1], src[2], + DITHER_VALUE(x)); + src += deltaSrc; + } + return false; +} + +static bool Sample_RGBA_D4444(void* SK_RESTRICT dstRow, + const uint8_t* SK_RESTRICT src, + int width, int deltaSrc, int) { + SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow; + unsigned alphaMask = 0xFF; + + for (int x = 0; x < width; x++) { + unsigned alpha = src[3]; + SkPMColor c = SkPreMultiplyARGB(alpha, src[0], src[1], src[2]); + dst[x] = SkPixel32ToPixel4444(c); + src += deltaSrc; + alphaMask &= alpha; + } + return alphaMask != 0xFF; +} + +static bool Sample_RGBA_D4444_D(void* SK_RESTRICT dstRow, + const uint8_t* SK_RESTRICT src, + int width, int deltaSrc, int y) { + SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow; + unsigned alphaMask = 0xFF; + DITHER_4444_SCAN(y); + + for (int x = 0; x < width; x++) { + unsigned alpha = src[3]; + SkPMColor c = SkPreMultiplyARGB(alpha, src[0], src[1], src[2]); + dst[x] = SkDitherARGB32To4444(c, DITHER_VALUE(x)); + src += deltaSrc; + alphaMask &= alpha; + } + return alphaMask != 0xFF; +} + +// Index + +static bool Sample_Index_DI(void* SK_RESTRICT dstRow, + const uint8_t* SK_RESTRICT src, + int width, int deltaSrc, int) { + if (1 == deltaSrc) { + memcpy(dstRow, src, width); + } else { + uint8_t* SK_RESTRICT dst = (uint8_t*)dstRow; + for (int x = 0; x < width; x++) { + dst[x] = src[0]; + src += deltaSrc; + } + } + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +#include "SkScaledBitmapSampler.h" + +SkScaledBitmapSampler::SkScaledBitmapSampler(int width, int height, + int sampleSize) { + if (width <= 0 || height <= 0) { + sk_throw(); + } + + if (sampleSize <= 1) { + fScaledWidth = width; + fScaledHeight = height; + fX0 = fY0 = 0; + fDX = fDY = 1; + return; + } + + int dx = SkMin32(sampleSize, width); + int dy = SkMin32(sampleSize, height); + + fScaledWidth = width / dx; + fScaledHeight = height / dy; + + SkASSERT(fScaledWidth > 0); + SkASSERT(fScaledHeight > 0); + + fX0 = dx >> 1; + fY0 = dy >> 1; + + SkASSERT(fX0 >= 0 && fX0 < width); + SkASSERT(fY0 >= 0 && fY0 < height); + + fDX = dx; + fDY = dy; + + SkASSERT(fDX > 0 && (fX0 + fDX * (fScaledWidth - 1)) < width); + SkASSERT(fDY > 0 && (fY0 + fDY * (fScaledHeight - 1)) < height); + + fRowProc = NULL; +} + +bool SkScaledBitmapSampler::begin(SkBitmap* dst, SrcConfig sc, bool dither) { + static const RowProc gProcs[] = { + // 8888 (no dither distinction) + Sample_Gray_D8888, Sample_Gray_D8888, + Sample_RGBx_D8888, Sample_RGBx_D8888, + Sample_RGBA_D8888, Sample_RGBA_D8888, + NULL, NULL, + // 565 (no alpha distinction) + Sample_Gray_D565, Sample_Gray_D565_D, + Sample_RGBx_D565, Sample_RGBx_D565_D, + Sample_RGBx_D565, Sample_RGBx_D565_D, + NULL, NULL, + // 4444 + Sample_Gray_D4444, Sample_Gray_D4444_D, + Sample_RGBx_D4444, Sample_RGBx_D4444_D, + Sample_RGBA_D4444, Sample_RGBA_D4444_D, + NULL, NULL, + // Index8 + NULL, NULL, + NULL, NULL, + NULL, NULL, + Sample_Index_DI, Sample_Index_DI, + }; + + + int index = 0; + if (dither) { + index += 1; + } + switch (sc) { + case SkScaledBitmapSampler::kGray: + fSrcPixelSize = 1; + index += 0; + break; + case SkScaledBitmapSampler::kRGB: + fSrcPixelSize = 3; + index += 2; + break; + case SkScaledBitmapSampler::kRGBX: + fSrcPixelSize = 4; + index += 2; + break; + case SkScaledBitmapSampler::kRGBA: + fSrcPixelSize = 4; + index += 4; + break; + case SkScaledBitmapSampler::kIndex: + fSrcPixelSize = 1; + index += 6; + break; + default: + return false; + } + + switch (dst->config()) { + case SkBitmap::kARGB_8888_Config: + index += 0; + break; + case SkBitmap::kRGB_565_Config: + index += 8; + break; + case SkBitmap::kARGB_4444_Config: + index += 16; + break; + case SkBitmap::kIndex8_Config: + index += 24; + break; + default: + return false; + } + + fRowProc = gProcs[index]; + fDstRow = (char*)dst->getPixels(); + fDstRowBytes = dst->rowBytes(); + fCurrY = 0; + return fRowProc != NULL; +} + +bool SkScaledBitmapSampler::next(const uint8_t* SK_RESTRICT src) { + SkASSERT((unsigned)fCurrY < (unsigned)fScaledHeight); + + bool hadAlpha = fRowProc(fDstRow, src + fX0 * fSrcPixelSize, fScaledWidth, + fDX * fSrcPixelSize, fCurrY); + fDstRow += fDstRowBytes; + fCurrY += 1; + return hadAlpha; +} diff --git a/skia/images/SkScaledBitmapSampler.h b/skia/images/SkScaledBitmapSampler.h new file mode 100644 index 0000000..0bb9924 --- /dev/null +++ b/skia/images/SkScaledBitmapSampler.h @@ -0,0 +1,55 @@ +#ifndef SkScaledBitmapSampler_DEFINED +#define SkScaledBitmapSampler_DEFINED + +#include "SkTypes.h" + +class SkBitmap; + +class SkScaledBitmapSampler { +public: + SkScaledBitmapSampler(int origWidth, int origHeight, int cellSize); + + int scaledWidth() const { return fScaledWidth; } + int scaledHeight() const { return fScaledHeight; } + + int srcY0() const { return fY0; } + int srcDY() const { return fDY; } + + enum SrcConfig { + kGray, // 1 byte per pixel + kIndex, // 1 byte per pixel + kRGB, // 3 bytes per pixel + kRGBX, // 4 byes per pixel (ignore 4th) + kRGBA // 4 bytes per pixel + }; + + // Given a dst bitmap (with pixels already allocated) and a src-config, + // prepares iterator to process the src colors and write them into dst. + // Returns false if the request cannot be fulfulled. + bool begin(SkBitmap* dst, SrcConfig sc, bool doDither); + // call with row of src pixels, for y = 0...scaledHeight-1. + // returns true if the row had non-opaque alpha in it + bool next(const uint8_t* SK_RESTRICT src); + +private: + int fScaledWidth; + int fScaledHeight; + + int fX0; // first X coord to sample + int fY0; // first Y coord (scanline) to sample + int fDX; // step between X samples + int fDY; // step between Y samples + + typedef bool (*RowProc)(void* SK_RESTRICT dstRow, + const uint8_t* SK_RESTRICT src, + int width, int deltaSrc, int y); + + // setup state + char* fDstRow; // points into bitmap's pixels + int fDstRowBytes; + int fCurrY; // used for dithering + int fSrcPixelSize; // 1, 3, 4 + RowProc fRowProc; +}; + +#endif diff --git a/skia/images/SkStream.cpp b/skia/images/SkStream.cpp new file mode 100644 index 0000000..5c1eebe --- /dev/null +++ b/skia/images/SkStream.cpp @@ -0,0 +1,792 @@ +/* libs/graphics/images/SkStream.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkStream.h" +#include "SkFixed.h" +#include "SkString.h" +#include "SkOSFile.h" + +SkStream::~SkStream() {} + +const char* SkStream::getFileName() +{ + // override in subclass if you represent a file + return NULL; +} + +const void* SkStream::getMemoryBase() +{ + // override in subclass if you represent a memory block + return NULL; +} + +size_t SkStream::skip(size_t size) +{ + /* Check for size == 0, and just return 0. If we passed that + to read(), it would interpret it as a request for the entire + size of the stream. + */ + return size ? this->read(NULL, size) : 0; +} + +int8_t SkStream::readS8() { + int8_t value; + size_t len = this->read(&value, 1); + SkASSERT(1 == len); + return value; +} + +int16_t SkStream::readS16() { + int16_t value; + size_t len = this->read(&value, 2); + SkASSERT(2 == len); + return value; +} + +int32_t SkStream::readS32() { + int32_t value; + size_t len = this->read(&value, 4); + SkASSERT(4 == len); + return value; +} + +SkScalar SkStream::readScalar() { + SkScalar value; + size_t len = this->read(&value, sizeof(SkScalar)); + SkASSERT(sizeof(SkScalar) == len); + return value; +} + +size_t SkStream::readPackedUInt() { + uint8_t byte; + if (!this->read(&byte, 1)) { + return 0; + } + if (byte != 0xFF) { + return byte; + } + + uint16_t word; + if (!this->read(&word, 2)) { + return 0; + } + if (word != 0xFFFF) { + return word; + } + + uint32_t quad; + if (!this->read(&quad, 4)) { + return 0; + } + return quad; +} + +////////////////////////////////////////////////////////////////////////////////////// + +SkWStream::~SkWStream() +{ +} + +void SkWStream::newline() +{ + this->write("\n", 1); +} + +void SkWStream::flush() +{ +} + +bool SkWStream::writeText(const char text[]) +{ + SkASSERT(text); + return this->write(text, strlen(text)); +} + +bool SkWStream::writeDecAsText(int32_t dec) +{ + SkString tmp; + tmp.appendS32(dec); + return this->write(tmp.c_str(), tmp.size()); +} + +bool SkWStream::writeHexAsText(uint32_t hex, int digits) +{ + SkString tmp; + tmp.appendHex(hex, digits); + return this->write(tmp.c_str(), tmp.size()); +} + +bool SkWStream::writeScalarAsText(SkScalar value) +{ + SkString tmp; + tmp.appendScalar(value); + return this->write(tmp.c_str(), tmp.size()); +} + +bool SkWStream::write8(U8CPU value) { + uint8_t v = SkToU8(value); + return this->write(&v, 1); +} + +bool SkWStream::write16(U16CPU value) { + uint16_t v = SkToU16(value); + return this->write(&v, 2); +} + +bool SkWStream::write32(uint32_t value) { + return this->write(&value, 4); +} + +bool SkWStream::writeScalar(SkScalar value) { + return this->write(&value, sizeof(value)); +} + +bool SkWStream::writePackedUInt(size_t value) { + if (value < 0xFF) { + return this->write8(value); + } else if (value < 0xFFFF) { + return this->write8(0xFF) && this->write16(value); + } else { + return this->write16(0xFFFF) && this->write32(value); + } +} + +bool SkWStream::writeStream(SkStream* stream, size_t length) { + char scratch[1024]; + const size_t MAX = sizeof(scratch); + + while (length != 0) { + size_t n = length; + if (n > MAX) { + n = MAX; + } + stream->read(scratch, n); + if (!this->write(scratch, n)) { + return false; + } + length -= n; + } + return true; +} + +//////////////////////////////////////////////////////////////////////////// + +SkFILEStream::SkFILEStream(const char file[]) : fName(file) +{ +#ifdef SK_BUILD_FOR_BREW + if (SkStrEndsWith(fName.c_str(), ".xml")) + fName.writable_str()[fName.size()-3] = 'b'; +#endif + + fFILE = file ? sk_fopen(fName.c_str(), kRead_SkFILE_Flag) : NULL; +} + +SkFILEStream::~SkFILEStream() +{ + if (fFILE) + sk_fclose(fFILE); +} + +void SkFILEStream::setPath(const char path[]) +{ + fName.set(path); +#ifdef SK_BUILD_FOR_BREW + if (SkStrEndsWith(fName.c_str(), ".xml")) + fName.writable_str()[fName.size()-3] = 'b'; +#endif + + if (fFILE) + { + sk_fclose(fFILE); + fFILE = NULL; + } + if (path) + fFILE = sk_fopen(fName.c_str(), kRead_SkFILE_Flag); +} + +const char* SkFILEStream::getFileName() +{ + return fName.c_str(); +} + +bool SkFILEStream::rewind() +{ + if (fFILE) + { + if (sk_frewind(fFILE)) + return true; + // we hit an error + sk_fclose(fFILE); + fFILE = NULL; + } + return false; +} + +size_t SkFILEStream::read(void* buffer, size_t size) +{ + if (fFILE) + { + if (buffer == NULL && size == 0) // special signature, they want the total size + return sk_fgetsize(fFILE); + else + return sk_fread(buffer, size, fFILE); + } + return 0; +} + +//////////////////////////////////////////////////////////////////////// + +SkMemoryStream::SkMemoryStream() +{ + fWeOwnTheData = false; + this->setMemory(NULL, 0); +} + +SkMemoryStream::SkMemoryStream(size_t size) { + fWeOwnTheData = true; + fOffset = 0; + fSize = size; + fSrc = sk_malloc_throw(size); +} + +SkMemoryStream::SkMemoryStream(const void* src, size_t size, bool copyData) +{ + fWeOwnTheData = false; + this->setMemory(src, size, copyData); +} + +SkMemoryStream::~SkMemoryStream() +{ + if (fWeOwnTheData) + sk_free((void*)fSrc); +} + +void SkMemoryStream::setMemory(const void* src, size_t size, bool copyData) +{ + if (fWeOwnTheData) + sk_free((void*)fSrc); + + fSize = size; + fOffset = 0; + fWeOwnTheData = copyData; + + if (copyData) + { + void* copy = sk_malloc_throw(size); + memcpy(copy, src, size); + src = copy; + } + fSrc = src; +} + +void SkMemoryStream::skipToAlign4() +{ + // cast to remove unary-minus warning + fOffset += -(int)fOffset & 0x03; +} + +bool SkMemoryStream::rewind() +{ + fOffset = 0; + return true; +} + +size_t SkMemoryStream::read(void* buffer, size_t size) +{ + if (buffer == NULL && size == 0) // special signature, they want the total size + return fSize; + + // if buffer is NULL, seek ahead by size + + if (size == 0) + return 0; + if (size > fSize - fOffset) + size = fSize - fOffset; + if (buffer) { + memcpy(buffer, (const char*)fSrc + fOffset, size); + } + fOffset += size; + return size; +} + +const void* SkMemoryStream::getMemoryBase() +{ + return fSrc; +} + +const void* SkMemoryStream::getAtPos() +{ + return (const char*)fSrc + fOffset; +} + +size_t SkMemoryStream::seek(size_t offset) +{ + if (offset > fSize) + offset = fSize; + fOffset = offset; + return offset; +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////// + +SkBufferStream::SkBufferStream(SkStream& proxy, size_t bufferSize) + : fProxy(proxy) +{ + SkASSERT(&proxy != NULL); + this->init(NULL, bufferSize); +} + +SkBufferStream::SkBufferStream(SkStream& proxy, void* buffer, size_t bufferSize) + : fProxy(proxy) +{ + SkASSERT(&proxy != NULL); + SkASSERT(buffer == NULL || bufferSize != 0); // init(addr, 0) makes no sense, we must know how big their buffer is + + this->init(buffer, bufferSize); +} + +void SkBufferStream::init(void* buffer, size_t bufferSize) +{ + if (bufferSize == 0) + bufferSize = kDefaultBufferSize; + + fOrigBufferSize = bufferSize; + fBufferSize = bufferSize; + fBufferOffset = bufferSize; // to trigger a reload on the first read() + + if (buffer == NULL) + { + fBuffer = (char*)sk_malloc_throw(fBufferSize); + fWeOwnTheBuffer = true; + } + else + { + fBuffer = (char*)buffer; + fWeOwnTheBuffer = false; + } +} + +SkBufferStream::~SkBufferStream() +{ + if (fWeOwnTheBuffer) + sk_free(fBuffer); +} + +bool SkBufferStream::rewind() +{ + fBufferOffset = fBufferSize = fOrigBufferSize; + return fProxy.rewind(); +} + +const char* SkBufferStream::getFileName() +{ + return fProxy.getFileName(); +} + +#ifdef SK_DEBUG +// #define SK_TRACE_BUFFERSTREAM +#endif + +size_t SkBufferStream::read(void* buffer, size_t size) +{ +#ifdef SK_TRACE_BUFFERSTREAM + SkDebugf("Request %d", size); +#endif + + if (buffer == NULL && size == 0) + return fProxy.read(buffer, size); // requesting total size + + if (buffer == NULL || size == 0) + { + fBufferOffset += size; + return fProxy.read(buffer, size); + } + + size_t s = size; + size_t actuallyRead = 0; + + // flush what we can from our fBuffer + if (fBufferOffset < fBufferSize) + { + if (s > fBufferSize - fBufferOffset) + s = fBufferSize - fBufferOffset; + memcpy(buffer, fBuffer + fBufferOffset, s); +#ifdef SK_TRACE_BUFFERSTREAM + SkDebugf(" flush %d", s); +#endif + size -= s; + fBufferOffset += s; + buffer = (char*)buffer + s; + actuallyRead = s; + } + + // check if there is more to read + if (size) + { + SkASSERT(fBufferOffset >= fBufferSize); // need to refill our fBuffer + + if (size < fBufferSize) // lets try to read more than the request + { + s = fProxy.read(fBuffer, fBufferSize); +#ifdef SK_TRACE_BUFFERSTREAM + SkDebugf(" read %d into fBuffer", s); +#endif + if (size > s) // they asked for too much + size = s; + if (size) + { + memcpy(buffer, fBuffer, size); + actuallyRead += size; +#ifdef SK_TRACE_BUFFERSTREAM + SkDebugf(" memcpy %d into dst", size); +#endif + } + + fBufferOffset = size; + fBufferSize = s; // record the (possibly smaller) size for the buffer + } + else // just do a direct read + { + actuallyRead += fProxy.read(buffer, size); +#ifdef SK_TRACE_BUFFERSTREAM + SkDebugf(" direct read %d", size); +#endif + } + } +#ifdef SK_TRACE_BUFFERSTREAM + SkDebugf("\n"); +#endif + return actuallyRead; +} + +const void* SkBufferStream::getMemoryBase() +{ + return fProxy.getMemoryBase(); +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////////// + +SkFILEWStream::SkFILEWStream(const char path[]) +{ + fFILE = sk_fopen(path, kWrite_SkFILE_Flag); +} + +SkFILEWStream::~SkFILEWStream() +{ + if (fFILE) + sk_fclose(fFILE); +} + +bool SkFILEWStream::write(const void* buffer, size_t size) +{ + if (fFILE == NULL) + return false; + + if (sk_fwrite(buffer, size, fFILE) != size) + { + SkDEBUGCODE(SkDebugf("SkFILEWStream failed writing %d bytes\n", size);) + sk_fclose(fFILE); + fFILE = NULL; + return false; + } + return true; +} + +void SkFILEWStream::flush() +{ + if (fFILE) + sk_fflush(fFILE); +} + +//////////////////////////////////////////////////////////////////////// + +SkMemoryWStream::SkMemoryWStream(void* buffer, size_t size) + : fBuffer((char*)buffer), fMaxLength(size), fBytesWritten(0) +{ +} + +bool SkMemoryWStream::write(const void* buffer, size_t size) +{ + size = SkMin32(size, fMaxLength - fBytesWritten); + if (size > 0) + { + memcpy(fBuffer + fBytesWritten, buffer, size); + fBytesWritten += size; + return true; + } + return false; +} + +//////////////////////////////////////////////////////////////////////// + +#define SkDynamicMemoryWStream_MinBlockSize 256 + +struct SkDynamicMemoryWStream::Block { + Block* fNext; + char* fCurr; + char* fStop; + + const char* start() const { return (const char*)(this + 1); } + char* start() { return (char*)(this + 1); } + size_t avail() const { return fStop - fCurr; } + size_t written() const { return fCurr - this->start(); } + + void init(size_t size) + { + fNext = NULL; + fCurr = this->start(); + fStop = this->start() + size; + } + + const void* append(const void* data, size_t size) + { + SkASSERT((size_t)(fStop - fCurr) >= size); + memcpy(fCurr, data, size); + fCurr += size; + return (const void*)((const char*)data + size); + } +}; + +SkDynamicMemoryWStream::SkDynamicMemoryWStream() : fHead(NULL), fTail(NULL), fBytesWritten(0), fCopyToCache(NULL) +{ +} + +SkDynamicMemoryWStream::~SkDynamicMemoryWStream() +{ + reset(); +} + +const char* SkDynamicMemoryWStream::detach() +{ + const char* result = getStream(); + fCopyToCache = NULL; + return result; +} + +void SkDynamicMemoryWStream::reset() +{ + sk_free(fCopyToCache); + Block* block = fHead; + + while (block != NULL) { + Block* next = block->fNext; + sk_free(block); + block = next; + } + fHead = fTail = NULL; + fBytesWritten = 0; + fCopyToCache = NULL; +} + +bool SkDynamicMemoryWStream::write(const void* buffer, size_t count) +{ + if (count > 0) { + + if (fCopyToCache) { + sk_free(fCopyToCache); + fCopyToCache = NULL; + } + fBytesWritten += count; + + size_t size; + + if (fTail != NULL && fTail->avail() > 0) { + size = SkMin32(fTail->avail(), count); + buffer = fTail->append(buffer, size); + SkASSERT(count >= size); + count -= size; + if (count == 0) + return true; + } + + size = SkMax32(count, SkDynamicMemoryWStream_MinBlockSize); + Block* block = (Block*)sk_malloc_throw(sizeof(Block) + size); + block->init(size); + block->append(buffer, count); + + if (fTail != NULL) + fTail->fNext = block; + else + fHead = fTail = block; + fTail = block; + } + return true; +} + +bool SkDynamicMemoryWStream::write(const void* buffer, size_t offset, size_t count) +{ + if (offset + count > fBytesWritten) + return false; // test does not partially modify + Block* block = fHead; + while (block != NULL) { + size_t size = block->written(); + if (offset < size) { + size_t part = offset + count > size ? size - offset : count; + memcpy(block->start() + offset, buffer, part); + if (count <= part) + return true; + count -= part; + buffer = (const void*) ((char* ) buffer + part); + } + offset = offset > size ? offset - size : 0; + block = block->fNext; + } + return false; +} + +bool SkDynamicMemoryWStream::read(void* buffer, size_t offset, size_t count) +{ + if (offset + count > fBytesWritten) + return false; // test does not partially modify + Block* block = fHead; + while (block != NULL) { + size_t size = block->written(); + if (offset < size) { + size_t part = offset + count > size ? size - offset : count; + memcpy(buffer, block->start() + offset, part); + if (count <= part) + return true; + count -= part; + buffer = (void*) ((char* ) buffer + part); + } + offset = offset > size ? offset - size : 0; + block = block->fNext; + } + return false; +} + +void SkDynamicMemoryWStream::copyTo(void* dst) const +{ + Block* block = fHead; + + while (block != NULL) { + size_t size = block->written(); + memcpy(dst, block->start(), size); + dst = (void*)((char*)dst + size); + block = block->fNext; + } +} + +const char* SkDynamicMemoryWStream::getStream() const +{ + if (fCopyToCache == NULL) { + fCopyToCache = (char*)sk_malloc_throw(fBytesWritten); + this->copyTo(fCopyToCache); + } + return fCopyToCache; +} + +void SkDynamicMemoryWStream::padToAlign4() +{ + // cast to remove unary-minus warning + int padBytes = -(int)fBytesWritten & 0x03; + if (padBytes == 0) + return; + int zero = 0; + write(&zero, padBytes); +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////// + +void SkDebugWStream::newline() +{ +#ifdef SK_DEBUG + SkDebugf("\n"); +#endif +} + +bool SkDebugWStream::write(const void* buffer, size_t size) +{ +#ifdef SK_DEBUG + char* s = new char[size+1]; + memcpy(s, buffer, size); + s[size] = 0; + SkDebugf("%s", s); + delete[] s; +#endif + return true; +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_DEBUG + +#include "SkRandom.h" + +void SkWStream::UnitTest() +{ +#ifdef SK_SUPPORT_UNITTEST + { + static const char s[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + char copy[sizeof(s)]; + SkRandom rand; + + for (int i = 0; i < 65; i++) + { + char* copyPtr = copy; + SkMemoryStream mem(s, sizeof(s)); + SkBufferStream buff(mem, i); + + do { + copyPtr += buff.read(copyPtr, rand.nextU() & 15); + } while (copyPtr < copy + sizeof(s)); + SkASSERT(copyPtr == copy + sizeof(s)); + SkASSERT(memcmp(s, copy, sizeof(s)) == 0); + } + } + { + SkDebugWStream s; + + s.writeText("testing wstream helpers\n"); + s.writeText("compare: 0 "); s.writeDecAsText(0); s.newline(); + s.writeText("compare: 591 "); s.writeDecAsText(591); s.newline(); + s.writeText("compare: -9125 "); s.writeDecAsText(-9125); s.newline(); + s.writeText("compare: 0 "); s.writeHexAsText(0, 0); s.newline(); + s.writeText("compare: 03FA "); s.writeHexAsText(0x3FA, 4); s.newline(); + s.writeText("compare: DEADBEEF "); s.writeHexAsText(0xDEADBEEF, 4); s.newline(); + s.writeText("compare: 0 "); s.writeScalarAsText(SkIntToScalar(0)); s.newline(); + s.writeText("compare: 27 "); s.writeScalarAsText(SkIntToScalar(27)); s.newline(); + s.writeText("compare: -119 "); s.writeScalarAsText(SkIntToScalar(-119)); s.newline(); + s.writeText("compare: 851.3333 "); s.writeScalarAsText(SkIntToScalar(851) + SK_Scalar1/3); s.newline(); + s.writeText("compare: -0.08 "); s.writeScalarAsText(-SK_Scalar1*8/100); s.newline(); + } + + { + SkDynamicMemoryWStream ds; + const char s[] = "abcdefghijklmnopqrstuvwxyz"; + int i; + for (i = 0; i < 100; i++) { + bool result = ds.write(s, 26); + SkASSERT(result); + } + SkASSERT(ds.getOffset() == 100 * 26); + char* dst = new char[100 * 26 + 1]; + dst[100*26] = '*'; + ds.copyTo(dst); + SkASSERT(dst[100*26] == '*'); + // char* p = dst; + for (i = 0; i < 100; i++) + SkASSERT(memcmp(&dst[i * 26], s, 26) == 0); + SkASSERT(memcmp(dst, ds.getStream(), 100*26) == 0); + delete[] dst; + } +#endif +} + +#endif diff --git a/skia/images/bmpdecoderhelper.cpp b/skia/images/bmpdecoderhelper.cpp new file mode 100644 index 0000000..e390731 --- /dev/null +++ b/skia/images/bmpdecoderhelper.cpp @@ -0,0 +1,376 @@ +/* + * Copyright 2007, Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// Author: cevans@google.com (Chris Evans) + +#include "bmpdecoderhelper.h" + +namespace image_codec { + +static const int kBmpHeaderSize = 14; +static const int kBmpInfoSize = 40; +static const int kBmpOS2InfoSize = 12; +static const int kMaxDim = SHRT_MAX / 2; + +bool BmpDecoderHelper::DecodeImage(const char* p, + int len, + int max_pixels, + BmpDecoderCallback* callback) { + data_ = reinterpret_cast<const uint8*>(p); + pos_ = 0; + len_ = len; + inverted_ = true; + // Parse the header structure. + if (len < kBmpHeaderSize + 4) { + return false; + } + GetShort(); // Signature. + GetInt(); // Size. + GetInt(); // Reserved. + int offset = GetInt(); + // Parse the info structure. + int infoSize = GetInt(); + if (infoSize != kBmpOS2InfoSize && infoSize < kBmpInfoSize) { + return false; + } + int cols = 0; + int comp = 0; + int colLen = 4; + if (infoSize >= kBmpInfoSize) { + if (len < kBmpHeaderSize + kBmpInfoSize) { + return false; + } + width_ = GetInt(); + height_ = GetInt(); + GetShort(); // Planes. + bpp_ = GetShort(); + comp = GetInt(); + GetInt(); // Size. + GetInt(); // XPPM. + GetInt(); // YPPM. + cols = GetInt(); + GetInt(); // Important colours. + } else { + if (len < kBmpHeaderSize + kBmpOS2InfoSize) { + return false; + } + colLen = 3; + width_ = GetShort(); + height_ = GetShort(); + GetShort(); // Planes. + bpp_ = GetShort(); + } + if (height_ < 0) { + height_ = -height_; + inverted_ = false; + } + if (width_ <= 0 || width_ > kMaxDim || height_ <= 0 || height_ > kMaxDim) { + return false; + } + if (width_ * height_ > max_pixels) { + return false; + } + if (cols < 0 || cols > 256) { + return false; + } + // Allocate then read in the colour map. + if (cols == 0 && bpp_ <= 8) { + cols = 1 << bpp_; + } + if (bpp_ <= 8 || cols > 0) { + uint8* colBuf = new uint8[256 * 3]; + memset(colBuf, '\0', 256 * 3); + colTab_.reset(colBuf); + } + if (cols > 0) { + if (pos_ + (cols * colLen) > len_) { + return false; + } + for (int i = 0; i < cols; ++i) { + int base = i * 3; + colTab_[base + 2] = GetByte(); + colTab_[base + 1] = GetByte(); + colTab_[base] = GetByte(); + if (colLen == 4) { + GetByte(); + } + } + } + // Read in the compression data if necessary. + redBits_ = 0x7c00; + greenBits_ = 0x03e0; + blueBits_ = 0x001f; + bool rle = false; + if (comp == 1 || comp == 2) { + rle = true; + } else if (comp == 3) { + if (pos_ + 12 > len_) { + return false; + } + redBits_ = GetInt() & 0xffff; + greenBits_ = GetInt() & 0xffff; + blueBits_ = GetInt() & 0xffff; + } + redShiftRight_ = CalcShiftRight(redBits_); + greenShiftRight_ = CalcShiftRight(greenBits_); + blueShiftRight_ = CalcShiftRight(blueBits_); + redShiftLeft_ = CalcShiftLeft(redBits_); + greenShiftLeft_ = CalcShiftLeft(greenBits_); + blueShiftLeft_ = CalcShiftLeft(blueBits_); + rowPad_ = 0; + pixelPad_ = 0; + int rowLen; + if (bpp_ == 32) { + rowLen = width_ * 4; + pixelPad_ = 1; + } else if (bpp_ == 24) { + rowLen = width_ * 3; + } else if (bpp_ == 16) { + rowLen = width_ * 2; + } else if (bpp_ == 8) { + rowLen = width_; + } else if (bpp_ == 4) { + rowLen = width_ / 2; + if (width_ & 1) { + rowLen++; + } + } else if (bpp_ == 1) { + rowLen = width_ / 8; + if (width_ & 7) { + rowLen++; + } + } else { + return false; + } + // Round the rowLen up to a multiple of 4. + if (rowLen % 4 != 0) { + rowPad_ = 4 - (rowLen % 4); + rowLen += rowPad_; + } + + if (offset > 0 && offset > pos_ && offset < len_) { + pos_ = offset; + } + // Deliberately off-by-one; a load of BMPs seem to have their last byte + // missing. + if (!rle && (pos_ + (rowLen * height_) > len_ + 1)) { + return false; + } + + output_ = callback->SetSize(width_, height_); + if (NULL == output_) { + return true; // meaning we succeeded, but they want us to stop now + } + + if (rle && (bpp_ == 4 || bpp_ == 8)) { + DoRLEDecode(); + } else { + DoStandardDecode(); + } + return true; +} + +void BmpDecoderHelper::DoRLEDecode() { + static const uint8 RLE_ESCAPE = 0; + static const uint8 RLE_EOL = 0; + static const uint8 RLE_EOF = 1; + static const uint8 RLE_DELTA = 2; + int x = 0; + int y = height_ - 1; + while (pos_ < len_ - 1) { + uint8 cmd = GetByte(); + if (cmd != RLE_ESCAPE) { + uint8 pixels = GetByte(); + int num = 0; + uint8 col = pixels; + while (cmd-- && x < width_) { + if (bpp_ == 4) { + if (num & 1) { + col = pixels & 0xf; + } else { + col = pixels >> 4; + } + } + PutPixel(x++, y, col); + num++; + } + } else { + cmd = GetByte(); + if (cmd == RLE_EOF) { + return; + } else if (cmd == RLE_EOL) { + x = 0; + y--; + if (y < 0) { + return; + } + } else if (cmd == RLE_DELTA) { + if (pos_ < len_ - 1) { + uint8 dx = GetByte(); + uint8 dy = GetByte(); + x += dx; + if (x > width_) { + x = width_; + } + y -= dy; + if (y < 0) { + return; + } + } + } else { + int num = 0; + int bytesRead = 0; + uint8 val = 0; + while (cmd-- && pos_ < len_) { + if (bpp_ == 8 || !(num & 1)) { + val = GetByte(); + bytesRead++; + } + uint8 col = val; + if (bpp_ == 4) { + if (num & 1) { + col = col & 0xf; + } else { + col >>= 4; + } + } + if (x < width_) { + PutPixel(x++, y, col); + } + num++; + } + // All pixel runs must be an even number of bytes - skip a byte if we + // read an odd number. + if ((bytesRead & 1) && pos_ < len_) { + GetByte(); + } + } + } + } +} + +void BmpDecoderHelper::PutPixel(int x, int y, uint8 col) { + CHECK(x >= 0 && x < width_); + CHECK(y >= 0 && y < height_); + if (!inverted_) { + y = height_ - (y + 1); + } + + int base = ((y * width_) + x) * 3; + int colBase = col * 3; + output_[base] = colTab_[colBase]; + output_[base + 1] = colTab_[colBase + 1]; + output_[base + 2] = colTab_[colBase + 2]; +} + +void BmpDecoderHelper::DoStandardDecode() { + int row = 0; + uint8 currVal = 0; + for (int h = height_ - 1; h >= 0; h--, row++) { + int realH = h; + if (!inverted_) { + realH = height_ - (h + 1); + } + uint8* line = output_ + (3 * width_ * realH); + for (int w = 0; w < width_; w++) { + if (bpp_ >= 24) { + line[2] = GetByte(); + line[1] = GetByte(); + line[0] = GetByte(); + } else if (bpp_ == 16) { + uint32 val = GetShort(); + line[0] = ((val & redBits_) >> redShiftRight_) << redShiftLeft_; + line[1] = ((val & greenBits_) >> greenShiftRight_) << greenShiftLeft_; + line[2] = ((val & blueBits_) >> blueShiftRight_) << blueShiftLeft_; + } else if (bpp_ <= 8) { + uint8 col; + if (bpp_ == 8) { + col = GetByte(); + } else if (bpp_ == 4) { + if ((w % 2) == 0) { + currVal = GetByte(); + col = currVal >> 4; + } else { + col = currVal & 0xf; + } + } else { + if ((w % 8) == 0) { + currVal = GetByte(); + } + int bit = w & 7; + col = ((currVal >> (7 - bit)) & 1); + } + int base = col * 3; + line[0] = colTab_[base]; + line[1] = colTab_[base + 1]; + line[2] = colTab_[base + 2]; + } + line += 3; + for (int i = 0; i < pixelPad_; ++i) { + GetByte(); + } + } + for (int i = 0; i < rowPad_; ++i) { + GetByte(); + } + } +} + +int BmpDecoderHelper::GetInt() { + uint8 b1 = GetByte(); + uint8 b2 = GetByte(); + uint8 b3 = GetByte(); + uint8 b4 = GetByte(); + return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24); +} + +int BmpDecoderHelper::GetShort() { + uint8 b1 = GetByte(); + uint8 b2 = GetByte(); + return b1 | (b2 << 8); +} + +uint8 BmpDecoderHelper::GetByte() { + CHECK(pos_ >= 0 && pos_ <= len_); + // We deliberately allow this off-by-one access to cater for BMPs with their + // last byte missing. + if (pos_ == len_) { + return 0; + } + return data_[pos_++]; +} + +int BmpDecoderHelper::CalcShiftRight(uint32 mask) { + int ret = 0; + while (mask != 0 && !(mask & 1)) { + mask >>= 1; + ret++; + } + return ret; +} + +int BmpDecoderHelper::CalcShiftLeft(uint32 mask) { + int ret = 0; + while (mask != 0 && !(mask & 1)) { + mask >>= 1; + } + while (mask != 0 && !(mask & 0x80)) { + mask <<= 1; + ret++; + } + return ret; +} + +} // namespace image_codec diff --git a/skia/images/bmpdecoderhelper.h b/skia/images/bmpdecoderhelper.h new file mode 100644 index 0000000..7da1326 --- /dev/null +++ b/skia/images/bmpdecoderhelper.h @@ -0,0 +1,122 @@ +/* + * Copyright 2007, Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef IMAGE_CODEC_BMPDECODERHELPER_H__ +#define IMAGE_CODEC_BMPDECODERHELPER_H__ + +/////////////////////////////////////////////////////////////////////////////// +// Some glue code that should be removed soon. + +#include "SkTypes.h" +#include <limits.h> +#define DISALLOW_EVIL_CONSTRUCTORS(name) +#define CHECK(predicate) SkASSERT(predicate) +typedef uint8_t uint8; +typedef uint32_t uint32; + +template <typename T> class scoped_array { +private: + T* ptr_; + scoped_array(scoped_array const&); + scoped_array& operator=(const scoped_array&); + +public: + explicit scoped_array(T* p = 0) : ptr_(p) {} + ~scoped_array() { + delete[] ptr_; + } + + void reset(T* p = 0) { + if (p != ptr_) { + delete[] ptr_; + ptr_ = p; + } + } + + T& operator[](int i) const { + return ptr_[i]; + } +}; + +/////////////////////////////////////////////////////////////////////////////// + +namespace image_codec { + +class BmpDecoderCallback { + public: + BmpDecoderCallback() { } + virtual ~BmpDecoderCallback() {} + + /** + * This is called once for an image. It is passed the width and height and + * should return the address of a buffer that is large enough to store + * all of the resulting pixels (widht * height * 3 bytes). If it returns NULL, + * then the decoder will abort, but return true, as the caller has received + * valid dimensions. + */ + virtual uint8* SetSize(int width, int height) = 0; + + private: + DISALLOW_EVIL_CONSTRUCTORS(BmpDecoderCallback); +}; + +class BmpDecoderHelper { + public: + BmpDecoderHelper() { } + ~BmpDecoderHelper() { } + bool DecodeImage(const char* data, + int len, + int max_pixels, + BmpDecoderCallback* callback); + + private: + DISALLOW_EVIL_CONSTRUCTORS(BmpDecoderHelper); + + void DoRLEDecode(); + void DoStandardDecode(); + void PutPixel(int x, int y, uint8 col); + + int GetInt(); + int GetShort(); + uint8 GetByte(); + int CalcShiftRight(uint32 mask); + int CalcShiftLeft(uint32 mask); + + const uint8* data_; + int pos_; + int len_; + int width_; + int height_; + int bpp_; + int pixelPad_; + int rowPad_; + scoped_array<uint8> colTab_; + uint32 redBits_; + uint32 greenBits_; + uint32 blueBits_; + int redShiftRight_; + int greenShiftRight_; + int blueShiftRight_; + int redShiftLeft_; + int greenShiftLeft_; + int blueShiftLeft_; + uint8* output_; + bool inverted_; +}; + +} // namespace + +#endif diff --git a/skia/images/fpdfemb.h b/skia/images/fpdfemb.h new file mode 100644 index 0000000..3c77116 --- /dev/null +++ b/skia/images/fpdfemb.h @@ -0,0 +1,1765 @@ +// FPDFEMB.H - Header file for FPDFEMB SDK +// Copyright (c) 2007-2008 Foxit Software Company, All Right Reserved. + +// Date: 2008-04-07 + +// Embedded platforms have many different aspects from desktop platforms, +// among them, the followings are most important for PDF processing: +// +// 1. Embedded platforms have only limited memory, and there is no virtual memory. +// PDF is a very complicated format, processing PDF may consumes quite +// large amount of memory, even for some smaller PDFs. And, in order to +// increase the performance of PDF parsing and rendering, cache memory +// is often used. For some big PDFs with many pages, the cache may grow +// while user browing through pages, eventually, for some PDFs, the memory +// on the device may run out. +// +// FPDFEMB SDK allows graceful out-of-memory (OOM) handling by returning +// OOM error code for all functions that may involve memory allocation. +// When an application detects OOM situation, it can do one of the followings: +// +// a) Give user some prompt and quit the application or close the document; +// b) Or better, try to recover from the error. Sometimes OOM can be caused +// by ever-growing cache. For example, when user browses a 1000-page +// document, let's say OOM happen at page #300. In this case, the +// application might close the whole document (cache will be gone with +// it), reopen the document, then go directly to page #300. It's likely +// the process will go through this time. This is called "OOM recovery". +// If OOM happens again during a recovery, then, it's not possible to finish +// the process, the application must quit of close the document. +// +// 2. Embedded platforms has only limited computing power. Since some PDFs +// can be very complicated and require a lot of processing to be displayed, +// it may take a lot of time for the process to finish. This may cause +// some problem with user experience, especially for devices like mobile +// phones, when an application may need to be put on hold any time. Therefore, +// it's important to break lengthy process into small steps which can be +// stopped or resumed at any time. We call this kind of process as +// "progressive process". +// +// FPDFEMB SDK allows progressive page parsing and rendering, the most time- +// consuming part of PDF processing. +// +// IMPORTANT: +// FPDFEMB module is not intended to run in multi-threaded environment. + +// Components inside FPDFEMB: +// * Library Memory Management +// * Document Operations +// * Page Basic Operations +// * Page Parsing +// * Page Rendering +// * Coordination Conversion +// * Text Search +// * Text Information +// * Device Independant Bitmap +// * Custom Font Handler and CJK Support +// * Bookmark Information +// * Hyperlink Information +// * Graphic Output + +#ifndef _FPDFEMB_H_ +#define _FPDFEMB_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +// Standard return type for many FPDFEMB functions: FPDFERR_SUCCESS for success, otherwise error code +typedef int FPDFEMB_RESULT; + +// Standard boolean type: 0 for false, non-zero for true +typedef int FPDFEMB_BOOL; + +// Unicode character. FPDFEMB uses UTF16LE format for unicode string. +typedef unsigned short FPDFEMB_WCHAR; + +// Error codes +#define FPDFERR_SUCCESS 0 +#define FPDFERR_MEMORY 1 // Out of memory +#define FPDFERR_ERROR 2 // Error of any kind, without specific reason +#define FPDFERR_PASSWORD 3 // Incorrect password +#define FPDFERR_FORMAT 4 // Not PDF format +#define FPDFERR_FILE 5 // File access error +#define FPDFERR_PARAM 6 // Parameter error +#define FPDFERR_STATUS 7 // Not in correct status +#define FPDFERR_TOBECONTINUED 8 // To be continued +#define FPDFERR_NOTFOUND 9 // Search result not found + +/******************************************************************************************** +**** +**** Library Memory Management +**** +********************************************************************************************/ + +// Structure: FPDFEMB_MEMMGR +// Including interfaces implemented by host application, providing memory allocation +// facilities. All members are required. +// A memory manager structure is required to be valid during the entire period +// when an application using FPDFEMB module. +// +// IMPORTANT NOTE: using of this interface is strongly not recommended, because +// FPDFEMB now internally use FPDFEMB_MEMMGR_EX interface, which allows more +// advanced memory management. This interface is retained for backward compatibility +// only, and maybe discontinued in the future. +// +struct FPDFEMB_MEMMGR { + // Interface: Alloc + // Allocate a memory block + // Parameters: + // pMgr - Pointer to the memory manager. + // size - Number of bytes for the memory block. + // Return Value: + // The pointer to allocated memory block. NULL if no memory available. + // Comments: + // In order to handle OOM situation, application can use longjmp() inside + // implementation of this function. If underlying memory manager fails to + // allocate enough memory, then application can use longjmp() to jump to + // OOM handling codes. + // + void* (*Alloc)(struct FPDFEMB_MEMMGR* pMgr, unsigned int size); + + // Interface: AllocNL + // Allocate a memory block, without leaving + // Parameters: + // pMgr - Pointer to the memory manager. + // size - Number of bytes for the memory block. + // Return Value: + // The pointer to allocated memory block. NULL if no memory available. + // Comments: + // Implementation MUST return NULL if no memory available, no exception + // or longjmp() can be used. + // + void* (*AllocNL)(struct FPDFEMB_MEMMGR* pMgr, unsigned int size); + + // Interfce: Realloc + // Reallocate a memory block + // Parameters: + // pMgr - Pointer to the memory manager. + // pointer - An existing memory block, or NULL. + // new_size - New size (number of bytes) of the memory block. Can be zero. + // Return value: + // The pointer of reallocated memory block, it could be a new block, or just + // the previous block with size modified. + // Comments: + // If an existing memory block specified, the data in the memory block will + // be copied to the new block, if reallocated. + // + // In order to handle OOM situation, application can use longjmp() inside + // implementation of this function. If underlying memory manager fails to + // allocate enough memory, then application can use longjmp() to jump to + // OOM handling codes. + // + void* (*Realloc)(struct FPDFEMB_MEMMGR* pMgr, void* pointer, unsigned int new_size); + + // Interface: Free + // Free a memory block + // Parameters: + // pMgr - Pointer to the memory manager. + // pointer - An existing memory block. + // Return Value: + // None. + // + void (*Free)(struct FPDFEMB_MEMMGR* pMgr, void* pointer); +}; + +// Function: FPDFEMB_Init +// Initialize the FPDFEMB module +// Parameters: +// mem_mgr - Pointer to memory manager structure +// Return Value: +// Error code, or FPDFERR_SUCCESS for success. +// Comments: +// This function will allocate necessary internal data structure for +// the whole module to operate. +FPDFEMB_RESULT FPDFEMB_Init(FPDFEMB_MEMMGR* mem_mgr); + +typedef void (*FPDFEMB_FIXED_OOM_HANDLER)(void* memory, int size); + +// Function: FPDFEMB_InitFixedMemory +// Initialize the FPDFEMB module, providing a fixed memory heap +// Parameters: +// memory - Pointer to a pre-allocated memory block +// size - Number of bytes in the memory block +// oom_handler - Pointer to a function which will be called when OOM happens. Can be +// NULL if application doesn't want to be notified om OOM. +// Return Value: +// Error code, or FPDFERR_SUCCESS for success. +// Comments: +// In many embedded system, memory usage are predetermined. The application +// is assigned with fixed size of available memory, then it can pre-allocate +// a memory block with maximum size and pass to this function to initialize +// FPDFEMB module. In this case, FPDFEMB won't need any additional memory +// allocation. +// +// In case the pre-allocated memory has run out, the "oom_proc" callback +// function will be called to notify the application that an OOM recovery +// procedure needs to be performed. +// +FPDFEMB_RESULT FPDFEMB_InitFixedMemory(void* memory, int size, FPDFEMB_FIXED_OOM_HANDLER oom_handler); + +// Memory Management Flags +#define FPDFEMB_NONLEAVE 1 +#define FPDFEMB_MOVABLE 2 +#define FPDFEMB_DISCARDABLE 4 + +// Structure: FPDFEMB_MEMMGR_EX +// This is an extended version of memory manager interface, allowing advanced +// memory management, including movable and discardable memory blocks. +// +// Use this interface with FPDFEMB_InitExt function. +// +struct FPDFEMB_MEMMGR_EX { + // Interface: Alloc + // Allocate a memory block + // Parameters: + // pMgr - Pointer to the memory manager. + // size - Number of bytes for the memory block. + // flags - A combination of flags defined above. + // Return Value: + // The pointer to allocated memory block. NULL if no memory available. + // If FPDFEMB_MOVABLE flag is used, implementation should return a handle + // to the memory block, if it supports movable block allocation. + // Comments: + // The implementation should not do any action if no memory available, + // just return NULL. OOM handling can be done in OOM_Handler interface. + // + void* (*Alloc)(struct FPDFEMB_MEMMGR_EX* pMgr, unsigned int size, int flags); + + // Interface: OOM_Handler + // OOM (out-of-memory) situation handler + // Parameters: + // pMgr - Pointer to the memory manager. + // Return Value: + // None. + // Comments: + // In order to handle OOM situation, application can use longjmp() inside + // implementation of this function. + // + void (*OOM_Handler)(struct FPDFEMB_MEMMGR_EX* pMgr); + + // Interfce: Realloc + // Reallocate a memory block + // Parameters: + // pMgr - Pointer to the memory manager. + // pointer - Pointer to an existing memory block, or handle to a movable + // block. Can not be NULL. + // new_size - New size (number of bytes) of the memory block. Can not be zero. + // Return value: + // The pointer of reallocated memory block, it could be a new block, or just + // the previous block with size modified. + // If FPDFEMB_MOVABLE flag is used, implementation should return a handle + // to the memory block, if it supports movable block allocation. + // Comments: + // If an existing memory block specified, the data in the memory block should + // be copied to the new block, if reallocated. + // + // The implementation should not do any action if no memory available, + // just return NULL. OOM handling can be done in OOM_Handler interface. + // + void* (*Realloc)(struct FPDFEMB_MEMMGR_EX* pMgr, void* pointer, unsigned int new_size, int flags); + + // Interface: Lock + // Lock a movable memory block + // Parameters: + // pMgr - Pointer to the memory manager. + // handle - Handle to movable memory block, returned by Alloc or Realloc. + // Return Value: + // The pointer of the memory block. NULL if the block was discarded. + // Comments: + // This interface is optional, if implementation doesn't support movable memory + // block, then this interface can be set to NULL. + // + void* (*Lock)(struct FPDFEMB_MEMMGR_EX* pMgr, void* handle); + + // Interface: Unlock + // Unlock a locked movable memory block + // Parameters: + // pMgr - Pointer to the memory manager. + // handle - Handle to movable memory block, returned by Alloc or Realloc. + // Return Value: + // None. + // Comments: + // This interface is optional, if implementation doesn't support movable memory + // block, then this interface can be set to NULL. + // + void (*Unlock)(struct FPDFEMB_MEMMGR_EX* pMgr, void* handle); + + // Interface: Free + // Free a memory block + // Parameters: + // pMgr - Pointer to the memory manager. + // pointer - Pointer to an existing memory block, or handle to a movable block. + // Return Value: + // None. + // + void (*Free)(struct FPDFEMB_MEMMGR_EX* pMgr, void* pointer, int flags); + + void* user; // A user pointer, used by the application +}; + +// Function: FPDFEMB_LoadJbig2Decoder +// Function: FPDFEMB_LoadJpeg2000Decoder +// Enable JBIG2 or JPEG2000 image decoder +// Parameters: +// None. +// Return Value: +// None. +// Comments: +// If you want to display JBIG2 or JPEG2000 encoded images, you need to call +// these functions after FPDFEMB initialized. +// +// Calling these functions will increase code size by about 200K-400K bytes. +// Also JPEG2000 decoder may not be available on some platforms. +// +void FPDFEMB_LoadJbig2Decoder(); +void FPDFEMB_LoadJpeg2000Decoder(); + +// Function: FPDFEMB_InitEx +// Initialize the FPDFEMB module with the extended memory manager +// Parameters: +// mem_mgr - Pointer to memory manager structure +// Return Value: +// Error code, or FPDFERR_SUCCESS for success. +// Comments: +// This function will allocate necessary internal data structure for +// the whole module to operate. +FPDFEMB_RESULT FPDFEMB_InitEx(FPDFEMB_MEMMGR_EX* mem_mgr); + +// Function: FPDFEMB_Exit +// Stop using FPDFEMB module and release all resources +// Parameters: +// None. +// Return Value: +// None. +// Comments: +// All loaded documents and pages will become invalid after this call. +// +// This function is useful for OOM recovery: when your application hits +// an OOM situation, calling this function will clear all memory allocated +// by FPDFEMB module, then you can call one of the initialization functions, +// reopen the document and recovery from OOM. +// +void FPDFEMB_Exit(); + +// Function: FPDFEMB_AllocMemory +// Allocate memory +// Parameters: +// size - Number of bytes +// Return Value: +// The allocated buffer pointer. NULL for out of memory. +// +void* FPDFEMB_AllocMemory(unsigned int size); + +// Function: FPDFEMB_FreeMemory +// Free allocated memory +// Parameters: +// pointer - Pointer returned by FPDFEMB_AllocMemory +// Return Value: +// None. +// +void FPDFEMB_FreeMemory(void* pointer); + +// Function: FPDFEMB_FreeCaches +// Free all expendable caches used by FPDFEMB in order to save memory +// Parameters: +// None. +// Return Value: +// None. +// Comments: +// When an application memory manager runs out of memory, before an OOM situation +// is raised, the application can try this +// +void FPDFEMB_FreeCaches(); + +/******************************************************************************************** +**** +**** Document Operations +**** +********************************************************************************************/ + +// Structure: FPDFEMB_FILE_ACCESS +// Describe the way to access a file (readonly). +struct FPDFEMB_FILE_ACCESS { + // Inteface: GetSize + // Get total size of the file + // Parameters: + // file - Pointer to this file access structure + // Return Value: + // File size, in bytes. Implementation can return 0 for any error. + // + unsigned int (*GetSize)(struct FPDFEMB_FILE_ACCESS* file); + + // Interface: ReadBlock + // Read a data block from the file + // Parameters: + // file - Pointer to this file access structure + // buffer - Pointer to a buffer receiving read data + // offset - Byte offset for the block, from beginning of the file + // size - Number of bytes for the block. + // Return Value: + // Error code, or FPDFERR_SUCCESS for success. + // + FPDFEMB_RESULT (*ReadBlock)(struct FPDFEMB_FILE_ACCESS* file, void* buffer, + unsigned int offset, unsigned int size); + + void* user; // A user pointer, used by the application +}; + +// Structure: FPDFEMB_PAUSE +// An interface for pausing a progressive process. +struct FPDFEMB_PAUSE { + // Interface: NeedPauseNow + // Check if we need to pause a progressive proccess now + // Parameters: + // pause - Pointer to the pause structure + // Return Value: + // Non-zero for pause now, 0 for continue. + // Comments: + // Typically implementation of this interface compares the current system tick + // with the previous one, if the time elapsed exceeds certain threshold, then + // the implementation returns TRUE, indicating a pause is needed. + // + FPDFEMB_BOOL (*NeedPauseNow)(struct FPDFEMB_PAUSE* pause); + + void* user; // A user pointer, used by the application +}; + +typedef void* FPDFEMB_DOCUMENT; + +// Function: FPDFEMB_StartLoadDocument +// Start loading a PDF document +// Parameters: +// file - Pointer to file access structure. +// This structure must be kept valid as long as the document is open. +// password - Pointer to a zero-terminated byte string, for the password. +// Or NULL for no password. +// document - Receiving the document handle +// pause - A callback mechanism allowing the document loading process +// to be paused before it's finished. This can be NULL if you +// don't want to pause. +// Return Value: +// FPDFERR_SUCCESS: document successfully loaded. +// FPDFERR_TOBECONTINUED: The document loading can't be finished now. +// See comments below. +// FPDFERR_PASSWORD: incorrect password. +// FPDFERR_FORMAT: not a PDF or corrupted PDF. +// FPDFERR_FILE: file access error. +// FPDFERR_MEMORY: out of memory. +// Comments: +// Document loading is a progressive process. It might take a long time to +// load a document, especiall when a file is corrupted, FPDFEMB will try to +// recover the document contents by scanning the whole file. If "pause" parameter +// is provided, this function may return FPDFERR_TOBECONTINUED any time during +// the document loading. +// +// When FPDFERR_TOBECONTINUED is returned, the "document" parameter will +// still receive a valid document handle, however, no further operations can +// be performed on the document, except the "FPDFEMB_ContineLoadDocument" function +// call, which resume the document loading. +// +FPDFEMB_RESULT FPDFEMB_StartLoadDocument(FPDFEMB_FILE_ACCESS* file, const char* password, + FPDFEMB_DOCUMENT* document, FPDFEMB_PAUSE* pause); + +// Function: FPDFEMB_ContinueLoadDocument +// Continue loading a PDF document +// Parameters: +// document - Document handle returned by FPDFEMB_StartLoadDocument function +// pause - A callback mechanism allowing the document loading process +// to be paused before it's finished. This can be NULL if you +// don't want to pause. +// Return Value: +// FPDFERR_SUCCESS: document successfully loaded. +// FPDFERR_TOBECONTINUED: The document loading can't be finished now. +// Further call to this function is needed. +// FPDFERR_PASSWORD: incorrect password. +// FPDFERR_FORMAT: not a PDF or corrupted PDF. +// FPDFERR_FILE: file access error. +// FPDFERR_MEMORY: out of memory. +// FPDFERR_STATUS: document already loaded. +// FPDFERR_PARAM: invalid parameter (like NULL document handle) +// +FPDFEMB_RESULT FPDFEMB_ContinueLoadDocument(FPDFEMB_DOCUMENT document, FPDFEMB_PAUSE* pause); + +// Function: FPDFEMB_CloseDocument +// Close a PDF document and free all associated resources +// Parameters: +// document - Document handle +// Return Value: +// Error code. FPDFERR_SUCCESS for success. +// +FPDFEMB_RESULT FPDFEMB_CloseDocument(FPDFEMB_DOCUMENT document); + +// Function: Get page count +// Get number of pages in the document +// Parameters: +// document - Document handle +// Return Value: +// Number of pages. +// +int FPDFEMB_GetPageCount(FPDFEMB_DOCUMENT document); + +// Function: FPDFEMB_SetFileBufferSize +// Set size of internal buffer used to read from source file. +// Parameters: +// size - Number of bytes +// Return Value: +// None. +// Comments: +// Currently FPDFEMB uses 512 bytes as default buffer size. The new buffer size +// takes effect next time you call FPDFEMB_StartLoadDocument. +// +void FPDFEMB_SetFileBufferSize(int size); + +/******************************************************************************************** +**** +**** Page Basic Operations +**** +********************************************************************************************/ + +typedef void* FPDFEMB_PAGE; + +// Function: FPDFEMB_LoadPage +// Load a page +// Parameters: +// document - Document handle +// index - Page index, starting from zero +// page - Receiving the loaded page handler +// Return Value: +// Error code, or FPDFERR_SUCCESS for success. +// +FPDFEMB_RESULT FPDFEMB_LoadPage(FPDFEMB_DOCUMENT document, int index, FPDFEMB_PAGE* page); + +// Function: FPDFEMB_ClosePage +// Close a page and release all related resources +// Parameters: +// page - Page handle +// Return Value: +// Error code, or FPDFERR_SUCCESS for success. +// +FPDFEMB_RESULT FPDFEMB_ClosePage(FPDFEMB_PAGE page); + +// Function: FPDFEMB_GetPageSize +// Get size of a page +// Parameters: +// page - Page handle +// width - Receiving page width, in hundredth of points +// height - Receiving page height, in hundredth of points +// Return Value: +// Error code, or FPDFERR_SUCCESS for success +// +FPDFEMB_RESULT FPDFEMB_GetPageSize(FPDFEMB_PAGE page, int* width, int* height); + +// Structure: FPDFEMB_RECT +// Rectangle area in device or page coordination system +// +struct FPDFEMB_RECT +{ + // For device system, coordinations are measured in pixels; + // For page system, coordinations are measured in hundredth of points. + int left; + int top; + int right; + int bottom; +}; + +// Function: FPDFEMB_GetPageBBox +// Get displayable area (bounding box) of a page +// Parameters: +// page - Page handle +// rect - Pointer to a structure receiving the rectangle +// Return Value: +// Error code, or FPDFERR_SUCCESS for success +// +FPDFEMB_RESULT FPDFEMB_GetPageBBox(FPDFEMB_PAGE page, FPDFEMB_RECT* rect); + +/******************************************************************************************** +**** +**** Page Parsing +**** +********************************************************************************************/ + +// Function: FPDFEMB_StartParse +// Start parsing a page, so it can get rendered or searched +// Parameters: +// page - Page handle +// text_only - flag for parsing texts only (used for searching) +// pause - A structure that can pause the parsing process. +// Or NULL if you don't want to pause the process. +// Return Value: +// FPDFERR_SUCCESS: parsing successfully finished; +// FPDFERR_TOBECONTINUED: parsing started successfully, but not finished; +// FPDFERR_STATUS: page already parsed, or parsing already started. +// Other return value: error code. +// Comments: +// Parsing is a progressive process. This function starts the parsing process, +// and may return before parsing is finished, if a pause structure is provided. +// +// Application should call FPDFEMB_ContinueParse repeatedly to finish the parsing +// when return value is FPDFERR_TOBECONTINUED. +// +// There can be only one parsing procedure active for a page, and if a page +// has already been parsed, you can't start a parsing again. +// +FPDFEMB_RESULT FPDFEMB_StartParse(FPDFEMB_PAGE page, FPDFEMB_BOOL text_only, + FPDFEMB_PAUSE* pause); + +// Function: FPDFEMB_ContinueParse +// Continue the page parsing +// Parameters: +// page - Page handle +// pause - A structure that can pause the parsing process. +// Or NULL if you don't want to pause the process. +// Return Value: +// FPDFERR_SUCCESS: parsing successfully finished; +// FPDFERR_TOBECONTINUED: parsing performed successfully, but not finished; +// FPDFERR_STATUS: page already parsed (or parsing not started). +// Other return value: error code. +// Comments: +// FPDFEMB_StartParse should be called before on the page. +// +// Application should call FPDFEMB_ContinueParse repeatedly to finish the parsing +// when return value is FPDFERR_TOBECONTINUED. +// +FPDFEMB_RESULT FPDFEMB_ContinueParse(FPDFEMB_PAGE page, FPDFEMB_PAUSE* pause); + +// Function: FPDFEMB_GetParseProgress +// Get an estimated parsing progress in percentage +// Parameters: +// page - Page handle +// Return Value: +// An integer between 0 and 100 (inclusive) indicating the parsing progress. +// The result is just a rough estimation. +// +int FPDFEMB_GetParseProgress(FPDFEMB_PAGE page); + +/******************************************************************************************** +**** +**** Page Rendering +**** +********************************************************************************************/ + +typedef void* FPDFEMB_BITMAP; + +// Function: FPDFEMB_StartQuickDraw +// Start drawing a quick preview of a page +// Parameters: +// dib - DIB handle, as the rendering device +// page - Page handle. The page has to be parsed first. +// start_x - Left pixel position of the display area in the device coordination +// start_y - Top pixel position of the display area in the device coordination +// size_x - Horizontal size (in pixels) for displaying the page +// size_y - Vertical size (in pixels) for displaying the page +// rotate - Page orientation: 0 (normal), 1 (rotated 90 degrees clockwise), +// 2 (rotated 180 degrees), 3 (rotated 90 degrees counter-clockwise). +// flags - Reserved, must be zero. +// pause - Pointer to a structure that can pause the rendering process. +// Can be NULL if no pausing is needed. +// Return Value: +// FPDFERR_SUCCESS: quickdraw successly finished; +// FPDFERR_TOBECONTINUED: quickdraw started successfully, but not finished. +// FPDFEMB_ContinueQuickDraw needs to be called to finish the quickdraw; +// FPDFERR_STATUS: quickdraw already in progress, or page not parsed; +// Other return value: error code. +// Comments: +// It's often useful to present user a quick preview of a page, right after the +// page is parsed. This preview renders only a limited set of easy features in the +// page, so it'll be rather quick to finish this process. +// +FPDFEMB_RESULT FPDFEMB_StartQuickDraw(FPDFEMB_BITMAP dib, FPDFEMB_PAGE page, + int start_x, int start_y, int size_x, int size_y, int rotate, + int flags, FPDFEMB_PAUSE* pause); + +// Function: FPDFEMB_ContinueQuickDraw +// Continue a quick draw processing +// Parameters: +// page - Page handle. The page has to be parsed first. +// pause - Pointer to a structure that can pause the rendering process. +// Can be NULL if no pausing is needed. +// Return Value: +// FPDFERR_SUCCESS: quickdraw successly finished; +// FPDFERR_TOBECONTINUED: quickdraw started successfully, but not finished. +// more calls to this function needed to finish the quickdraw; +// FPDFERR_STATUS: quickdraw not started yet; +// Other return value: error code. +// +FPDFEMB_RESULT FPDFEMB_ContinueQuickDraw(FPDFEMB_PAGE page, FPDFEMB_PAUSE* pause); + +#define FPDFEMB_ANNOT 0x01 // Set if annotations are to be rendered +#define FPDFEMB_LCD_TEXT 0x02 // Set if using text rendering optimized for LCD display +#define FPDFEMB_BGR_STRIPE 0x04 // Set if the device is using BGR LCD stripe + +// Function: FPDFEMB_StartRender +// Start rendering of a page. +// Parameter: +// dib - DIB handle, as the rendering device +// page - Page handle. The page has to be parsed first. +// start_x - Left pixel position of the display area in the device coordination +// start_y - Top pixel position of the display area in the device coordination +// size_x - Horizontal size (in pixels) for displaying the page +// size_y - Vertical size (in pixels) for displaying the page +// rotate - Page orientation: 0 (normal), 1 (rotated 90 degrees clockwise), +// 2 (rotated 180 degrees), 3 (rotated 90 degrees counter-clockwise). +// flags - 0 for normal display, or combination of flags defined above +// clip - Pointer to clip rectangle (in DIB device coordinations), +// or NULL if no clipping needed. +// pause - Pointer to a structure that can pause the rendering process. +// Can be NULL if no pausing is needed. +// Return Value: +// FPDFERR_SUCCESS: rendering successfully finished; +// FPDFERR_TOBECONTINUED: rendering started successfully, but not finished; +// Other return value: error code. +// Comments: +// Rendering is a progressive process. This function starts the rendering process, +// and may return before rendering is finished, if a pause structure is provided. +// +// Application should call FPDFEMB_ContinueRender repeatedly to finish the rendering +// when return value is FPDFERR_TOBECONTINUED. +// +// There can be only one rendering procedure for a page at any time. And rendering +// can be started over and over again for the same page. If a page rendering is already +// active, starting another one will cancel the previous rendering. +// +// Rendering of a page doesn't draw the page background, therefore, you usually need +// to draw the background in the DIB yourself. +// +FPDFEMB_RESULT FPDFEMB_StartRender(FPDFEMB_BITMAP dib, FPDFEMB_PAGE page, + int start_x, int start_y, int size_x, int size_y, int rotate, int flags, + FPDFEMB_RECT* clip, FPDFEMB_PAUSE* pause); + +// Function: FPDFEMB_ContinueRender +// Continue the page rendering +// Parameters: +// page - Page handle +// pause - Pointer to a structure that can pause the rendering process. +// Can be NULL if no pausing is needed. +// Return Value: +// FPDFERR_SUCCESS: rendering successfully finished. +// FPDFERR_TOBECONTINUED: rendering needs to be continued; +// Other return value: error code. +// Comments: +// This function may return any time when the pause interface indicates +// a pause is needed. Application can call FPDFEMB_ContinueRender any number +// of times, until FPDFERR_TOBECONTINUED is not returned. +// +FPDFEMB_RESULT FPDFEMB_ContinueRender(FPDFEMB_PAGE page, FPDFEMB_PAUSE* pause); + +// Function: FPDFEMB_GetRenderProgress +// Get an estimated rendering progress in percentage +// Parameters: +// page - Page handle +// Return Value: +// An integer between 0 and 100 (inclusive) indicating the rendering progress. +// The result is just a rough estimation. +// If the rendering just finished, this function will return 0. +// +int FPDFEMB_GetRenderProgress(FPDFEMB_PAGE page); + +// Function: FPDFEMB_SetHalftoneLimit +// Set pixel count limit for using halftone when display image +// Parameter: +// limit - Number of pixels for the limit +// Return Value: +// None. +// Comments: +// By default, FPDFEMB displays all bitmaps using downsamping, which means +// if the image is shrinked onto screen, only part of pixels will be picked +// and displayed. This saves a lot of calculation, especially for big images +// with millions of pixels. However the display quality can be bad. In order to +// reach a balance between performance and quality, application can use this +// function to set a limit, if number of pixels in an image is more than this +// limit, then FPDFEMB will use downsampling for quick drawing, otherwise, if +// the image has less pixels, FPDFEMB will use halftoning for better quality. +// +void FPDFEMB_SetHalftoneLimit(int limit); + +/******************************************************************************************** +**** +**** Coordination Conversion +**** +********************************************************************************************/ + +// Structure: FPDFEMB_POINT +// A point in device or page coordination system +// +struct FPDFEMB_POINT +{ + // For device system, coordinations are measured in pixels; + // For page system, coordinations are measured hundredth of points. + int x; + int y; +}; + +// Function: FPDFEMB_DeviceToPagePoint, FPDFEMB_DeviceToPageRect +// Convert the device coordinations of a point or a rectangle to page coordinations. +// Parameters: +// page - Handle to the page. Returned by FPDFEMB_LoadPage function. +// start_x - Left pixel position of the display area in the device coordination +// start_y - Top pixel position of the display area in the device coordination +// size_x - Horizontal size (in pixels) for displaying the page +// size_y - Vertical size (in pixels) for displaying the page +// rotate - Page orientation: 0 (normal), 1 (rotated 90 degrees clockwise), +// 2 (rotated 180 degrees), 3 (rotated 90 degrees counter-clockwise). +// point - A point structure with device coordinations upon the call, +// also receiving the result page coordinations. +// rect - A rectangle structure with device coordinations upon the call, +// also receiving the result page coordinations. +// Return value: +// None. +// Comments: +// The page coordination system has its origin at left-bottom corner of the page, +// with X axis goes along the bottom side to the right, and Y axis goes along the +// left side upward. No matter how you zoom, scroll, or rotate a page, a particular +// element (like text or image) on the page should always have the same coordination +// values in the page coordination system. +// +// The device coordination system is device dependant. For bitmap device, its origin +// is at left-top corner of the window. You must make sure the start_x, start_y, size_x, +// size_y and rotate parameters have exactly same values as you used in +// FPDFEMB_StartRender() function call. +// +// For rectangle conversion, the result rectangle is always "normalized", meaning for +// page coordinations, left is always smaller than right, bottom is smaller than top. +// +void FPDFEMB_DeviceToPagePoint(FPDFEMB_PAGE page, + int start_x, int start_y, int size_x, int size_y, int rotate, + FPDFEMB_POINT* point); + +void FPDFEMB_DeviceToPageRect(FPDFEMB_PAGE page, + int start_x, int start_y, int size_x, int size_y, int rotate, + FPDFEMB_RECT* rect); + +// Function: FPDFEMB_PageToDevicePoint, FPDFEMB_PageToDeviceRect +// Convert the page coordinations of a point or a rectangle to device coordinations. +// Parameters: +// page - Handle to the page. Returned by FPDFEMB_LoadPage function. +// start_x - Left pixel position of the display area in the device coordination +// start_y - Top pixel position of the display area in the device coordination +// size_x - Horizontal size (in pixels) for displaying the page +// size_y - Vertical size (in pixels) for displaying the page +// rotate - Page orientation: 0 (normal), 1 (rotated 90 degrees clockwise), +// 2 (rotated 180 degrees), 3 (rotated 90 degrees counter-clockwise). +// point - A point structure with page coordinations upon the call, +// also receiving the result device coordinations. +// rect - A rectangle structure with page coordinations upon the call, +// also receiving the result device coordinations. +// Return value: +// None +// Comments: +// For rectangle conversion, the result rectangle is always "normalized", meaning for +// device coordinations, left is always smaller than right, top is smaller than bottom. +// +void FPDFEMB_PageToDevicePoint(FPDFEMB_PAGE page, + int start_x, int start_y, int size_x, int size_y, int rotate, + FPDFEMB_POINT* point); + +void FPDFEMB_PageToDeviceRect(FPDFEMB_PAGE page, + int start_x, int start_y, int size_x, int size_y, int rotate, + FPDFEMB_RECT* rect); + +/******************************************************************************************** +**** +**** Text Search +**** +********************************************************************************************/ + +// Search flags for FPDFEMB_FindFirst function +#define FPDFEMB_MATCHCASE 1 // whether matching case +#define FPDFEMB_MATCHWHOLEWORD 2 // whether matching whole word +#define FPDFEMB_CONSECUTIVE 4 // whether matching consecutively (for example, "CC" will + // match twice in "CCC"). + +// Function: FPDFEMB_FindFirst +// Find first occurance of a pattern string in a page +// Parameters: +// page - Page handle. +// pattern - A zero-terminated unicode string to be found. +// from_last - Whether we start from the end of page +// flags - Search flags, see above defined constants +// Return Value: +// Error code, or FPDFERR_SUCCESS for success. +// Is not found, FPDFERR_NOTFOUND is returned. +// Comments: +// A page must be parsed first before it can be searched. +// There can be only one search in progress for a page. A new search will +// cancel the previous one. +// +// IMPORTANT: this function is now obsolete and kept for back compatibility +// only, please use FPDFEMB_FindFrom function below. +// +FPDFEMB_RESULT FPDFEMB_FindFirst(FPDFEMB_PAGE page, const FPDFEMB_WCHAR* pattern, + FPDFEMB_BOOL from_last, unsigned int flags); + +// Function: FPDFEMB_FindFrom +// Find first occurance of a pattern string in a page, from a particular position +// Parameters: +// page - Page handle. +// pattern - A zero-terminated unicode string to be found. +// pos - The position, returned from FPDFEMB_GetSearchPos. +// Or 0 from the beginning of page, -1 from the end of page. +// flags - Search flags, see above defined constants +// Return Value: +// Error code, or FPDFERR_SUCCESS for success. +// Is not found, FPDFERR_NOTFOUND is returned. +// Comments: +// A page must be parsed first before it can be searched. +// There can be only one search in progress for a page. A new search will +// cancel the previous one. +// +FPDFEMB_RESULT FPDFEMB_FindFrom(FPDFEMB_PAGE page, const FPDFEMB_WCHAR* pattern, + int pos, unsigned int flags); + +// Function: FPDFEMB_FindNext +// Find next occurance of a search +// Parameters: +// page - Page handle. +// FPDFEMB_FindFirst must be called for this page first. +// Return Value: +// Error code, or FPDFERR_SUCCESS for success. +// Is not found, FPDFERR_NOTFOUND is returned. +// +FPDFEMB_RESULT FPDFEMB_FindNext(FPDFEMB_PAGE page); + +// Function: FPDFEMB_FindPrev +// Find previous occurance of a search +// Parameters: +// page - Page handle. +// FPDFEMB_FindFirst must be called for this page first. +// Return Value: +// Error code, or FPDFERR_SUCCESS for success. +// Is not found, FPDFERR_NOTFOUND is returned. +// +FPDFEMB_RESULT FPDFEMB_FindPrev(FPDFEMB_PAGE page); + +// Function: FPDFEMB_CountFoundRects +// Get number of rectangles for last found result +// Parameters: +// page - Page handle. +// Return Value: +// Number of rectangles for last found result. 0 for not found or failure. +// +int FPDFEMB_CountFoundRects(FPDFEMB_PAGE page); + +// Function: FPDFEMB_GetFoundRect +// Get a particular found rectangle +// Parameters: +// page - Page handle. +// index - Zero-based index for the rectangle. +// rect - Receiving the result rectangle, in hundredth of points +// Return Value: +// Error code, or FPDFERR_SUCCESS for success. +// Comments: +// Application should always call FPDFEMB_CountFoundRects first to get +// number of rectangles, then use this function to get each rectangle. +// +// The returned rectangle uses page coordination system. +// +FPDFEMB_RESULT FPDFEMB_GetFoundRect(FPDFEMB_PAGE page, int index, FPDFEMB_RECT* rect); + +// Function: FPDFEMB_GetSearchPos +// Return position of current search result +// Parameters: +// page - Page handle. +// Return Value: +// Zero based character index for the current search result. -1 if not found. +// +int FPDFEMB_GetSearchPos(FPDFEMB_PAGE page); + +// Function: FPDFEMB_QuickSearch +// Search a pattern in a page quickly, without the page to be parsed +// Parameters: +// document - Document handle returned by FPDFEMB_StartLoadDocument function +// page_index - Zero-based index of the page +// pattern - A zero-terminated unicode string to be found. +// case_sensitive - Non-zero for case-sensitive searching, zero for case-insensitive +// Return Value: +// FPDFERR_SUCCESS if pattern found, FPDFERR_NOTFOUND if pattern not found. +// Otherwise error code is returned. +// Comments: +// This function does a rough and quick search in a page, before the page is loaded. +// The quick search will not generate an exact result saying where the pattern is +// found, and, it might be possible if a quick search result is "pattern found", and +// a real search for the same pattern, in the same page, will result in "not found". +// +// However, if quick search doesn't find a pattern in a page, then we can be sure the +// pattern won't be found in this page when we do a real search. So, this function is +// very useful when we search in a multiple-page document, and we want to quickly skip +// those pages in which the pattern can't possibly be found. +// +FPDFEMB_RESULT FPDFEMB_QuickSearch(FPDFEMB_DOCUMENT document, int page_index, + const FPDFEMB_WCHAR* pattern, int case_sensitive); + +/******************************************************************************************** +**** +**** Text Information +**** +********************************************************************************************/ + +// Function: FPDFEMB_GetCharCount +// Get number of characters in the page +// Parameters: +// page - Page handle +// count - Receiving number of characters +// Return Value: +// Error code, or FPDFERR_SUCCESS for success. +// +FPDFEMB_RESULT FPDFEMB_GetCharCount(FPDFEMB_PAGE page, int* count); + +// Structure: FPDFEMB_CHAR_INFO +// Character information. +struct FPDFEMB_CHAR_INFO { + int unicode; // Unicode for the character. 0 if not available. + // Space and new line charcters (U+0020 and U+000A) may be generated + // according to the text formatting. + FPDFEMB_POINT origin; // X/Y position for the character origin, in hundredth of points + FPDFEMB_RECT bbox; // Bounding box of the character, in hundredth of points + // Maybe an empty box (left == right or top == bottom). +}; + +// Function: FPDFEMB_GetCharInfo +// Get character information +// Parameters: +// page - Page handle +// index - Character index, starting from zero +// char_info - Receiving the character info +// Return Value: +// Error code, or FPDFERR_SUCCESS for success +// Comments: +// Application must call FPDFEMB_GetCharCount first before it can call this function +// for any particular characters. +// +FPDFEMB_RESULT FPDFEMB_GetCharInfo(FPDFEMB_PAGE page, int index, FPDFEMB_CHAR_INFO* char_info); + +// Function: FPDFEMB_GetCharIndexAtPos() +// Get index of character nearest to a certain position on the page +// Parameters: +// page - Page handle +// x - X position in PDF page coordination system +// y - Y position in PDF page coordination system +// index - Pointer to an integer receiving zero-based character index. +// Return Value: +// Error code, or FPDFERR_SUCCESS for success +// Comments: +// This function finds the character that's nearest to the particular page position. +// If there is no character, the output index will be -1. +// +FPDFEMB_RESULT FPDFEMB_GetCharIndexAtPos(FPDFEMB_PAGE page, double x, double y, int* index); + +/******************************************************************************************** +**** +**** Device Independant Bitmap +**** +********************************************************************************************/ + +#define FPDFDIB_BGR 1 // 3 bytes per pixel, byte order: Blue, Green, Red +#define FPDFDIB_BGRx 2 // 4 bytes per pixel, byte order: Blue, Green, Red, not used +#define FPDFDIB_BGRA 3 // 4 bytes per pixel, byte order: Blue, Green, Red, Alpha +#define FPDFDIB_GRAY 4 // 1 byte per pixel (grayscale) + +// Function: FPDFEMB_CreateDIB +// Create a DIB (Device Independant Bitmap) +// Parameters: +// width - Width pixels; +// height - Height pixels; +// format - Format type. See FPDFDIB_xxx constants +// buffer - External buffer provided for the DIB, +// or NULL if new buffer is to be allocated. +// stride - Number of bytes for each scan line, for external buffer only. +// If not specified, 4-byte alignment assumed. +// dib - Receiving the created DIB handle +// Return Value: +// Error code, or FPDFERR_SUCCESS for success +// Comments: +// If "buffer" parameter is not NULL, then the provided buffer must conform +// to standard DIB format (see comments of FPDFEMB_GetDIBData function below). +// +// This function doesn't initialize the pixels inside the DIB buffer. So if you +// want to use the DIB to display a PDF page, you usually need to initialize +// the DIB to white background by youself. +// +FPDFEMB_RESULT FPDFEMB_CreateDIB(int width, int height, int format, + void* buffer, int stride, FPDFEMB_BITMAP* dib); + +// Function: FPDFEMB_DestroyDIB +// Destroy a DIB +// Parameters: +// dib - DIB handle +// Return Value: +// Error code, or FPDFERR_SUCCESS for success +// Comments: +// If external buffer is used (specified in "buffer" parameter when calling +// FPDFEMB_CreateDIB), the buffer will not be destroyed. +// +FPDFEMB_RESULT FPDFEMB_DestroyDIB(FPDFEMB_BITMAP dib); + +// Function: FPDFEMB_GetDIBWidth +// Get width (in pixels) of a DIB +// Parameters: +// dib - DIB handle +// Return Value: +// DIB width in pixels. +// +int FPDFEMB_GetDIBWidth(FPDFEMB_BITMAP dib); + +// Function: FPDFEMB_GetDIBHeight +// Get height (in pixels) of a DIB +// Parameters: +// dib - DIB handle +// Return Value: +// DIB height in pixels. +// +int FPDFEMB_GetDIBHeight(FPDFEMB_BITMAP dib); + +// Function: FPDFEMB_GetDIBData +// Get data pointer to a DIB +// Parameters: +// dib - DIB handle +// Return Value: +// Pointer to the DIB data. +// Comments: +// DIB data are organized in scanlines, from top down. +// +void* FPDFEMB_GetDIBData(FPDFEMB_BITMAP dib); + +// Function: FPDFEMB_GetDIBStride +// Get scan line stride of a DIB +// Parameters: +// dib - DIB handle +// Return Value: +// Number of bytes occupied by a scanline +// +int FPDFEMB_GetDIBStride(FPDFEMB_BITMAP dib); + +// Function: FPDFEMB_GetRotatedDIB +// Swap X/Y dimensions of a DIB to generate a rotated new DIB +// Parameters: +// dib - DIB handle +// flip_x - Whether flip pixels on the destination X dimension (left/right) +// flip_y - Whether flip pixels on the destination Y dimension (up/down) +// result_dib - Receiving the result DIB handle +// Return Value: +// Error code, or FPDFERR_SUCCESS for success +// +FPDFEMB_RESULT FPDFEMB_GetRotatedDIB(FPDFEMB_BITMAP dib, + FPDFEMB_BOOL bFlipX, FPDFEMB_BOOL bFlipY, + FPDFEMB_BITMAP* result_dib); + +// Function: FPDFEMB_StretchDIB +// Stretch a source DIB into another destination DIB +// Parameters: +// dest_dib - The destination DIB handle +// dest_left - Left position in the destination DIB +// dest_top - Top position in the destination DIB +// dest_width - Destination width, in pixels. Can be negative for horizontal flipping +// dest_height - Destination height, in pixels. Can be negative for vertical flipping +// clip - Destination clipping rectangle, or NULL for no clipping. +// The coordinations are measured in destination bitmap. +// src_dib - Source DIB handle. +// interpol - Whether we use interpolation to improve the result quality +// Return Value: +// Error code, or FPDFERR_SUCCESS for success +// +FPDFEMB_RESULT FPDFEMB_StretchDIB(FPDFEMB_BITMAP dest_dib, int dest_left, int dest_top, + int dest_width, int dest_height, FPDFEMB_RECT* clip_rect, + FPDFEMB_BITMAP src_dib, FPDFEMB_BOOL interpol); + +// Function: FPDFEMB_TransformDIB +// Transform a source DIB into another destination DIB +// Parameters: +// dest_dib - The destination DIB handle +// clip - Destination clipping rectangle, or NULL for no clipping. +// The coordinations are measured in destination bitmap. +// src_dib - Source DIB handle. +// x - X coordination of the dest origin +// y - Y coordination of the dest origin +// xx - X distance of the dest X vector +// yx - Y distance of the dest X vector +// xy - X distance of the dest Y vector +// yy - Y distance of the dest Y vector +// interpol - Whether we use interpolation to improve the result quality +// Return Value: +// Error code, or FPDFERR_SUCCESS for success +// Comments: +// All coordinations and distances are measured in destination bitmap system. +// +// This function places the bottom-left pixel of the image at the destination +// origin, then the bottom sideline along the destination X vector, and left +// sideline along the destination Y vector. +// +FPDFEMB_RESULT FPDFEMB_TransformDIB(FPDFEMB_BITMAP dest_dib, FPDFEMB_RECT* clip_rect, + FPDFEMB_BITMAP src_dib, int x, int y, int xx, int yx, + int xy, int yy, FPDFEMB_BOOL interpol); + +/******************************************************************************************** +**** +**** Custom Font Handler and CJK Support +**** +********************************************************************************************/ + +// FPDFEMB comes with standard fonts for Latin characters. If your device is targeted to +// Eastern Asian markets, then system fonts must be provided and registered with FPDFEMB. +// Depending on your device configuration, those system fonts might be in TrueType or Type1 +// format, or some other non-standard compact format. For the first case, you should register +// a font mapper so FPDFEMB can pick the right font file, and for the second case, you +// should register a glyph provider so FPDFEMB can get glyph bitmap for each character. + +#define FPDFEMB_CHARSET_DEFAULT 0 +#define FPDFEMB_CHARSET_GB 936 +#define FPDFEMB_CHARSET_BIG5 950 +#define FPDFEMB_CHARSET_JIS 932 +#define FPDFEMB_CHARSET_KOREA 949 +#define FPDFEMB_CHARSET_UNICODE 1200 + +#define FPDFEMB_FONT_FIXEDPITCH 1 +#define FPDFEMB_FONT_SERIF 2 +#define FPDFEMB_FONT_SYMBOLIC 4 +#define FPDFEMB_FONT_SCRIPT 8 +#define FPDFEMB_FONT_NONSYMBOLIC 32 +#define FPDFEMB_FONT_ITALIC 64 +#define FPDFEMB_FONT_ALLCAP 0x10000 +#define FPDFEMB_FONT_SMALLCAP 0x20000 +#define FPDFEMB_FONT_FORCEBOLD 0x40000 + +// Structure: FPDFEMB_FONT_MAPPER +// Defines interface for system font mapper. +// +struct FPDFEMB_FONT_MAPPER +{ + // Interface: MapFont + // Find font file path for a particular PDF font + // Parameters: + // mapper - Pointer to the FPDFEMB_FONT_MAPPER structure + // name - Font name + // charset - Charset ID (see above FPDFEMB_CHARSET_xxx constants) + // flags - Font flags (see above FPDFEMB_FONT_xxx constants) + // weight - Weight of the font. Range from 100 to 900. 400 is normal, + // 700 is bold. + // path - Receiving the full file path. The buffer size is 512 bytes. + // face_index - Receiving an zero-based index value for the font face, if the + // mapped font file is a "collection" (meaning a number of faces + // are stored in the same file). If the font file is not a + // collection, the index value should be zero. + // Return Value: + // Non-zero for success, 0 for failure. + // + FPDFEMB_BOOL (*MapFont)(struct FPDFEMB_FONT_MAPPER* mapper, const char* name, int charset, + unsigned int flags, int weight, + char* path, int* face_index); + + void* user; // A user pointer, used by the application +}; + +// Function: FPDFEMB_SetFontMapper +// Use a system font mapper (typically for Chinese/Japanese/Korean charsets) +// Parameters: +// mapper - Pointer to FPDFEMB_FONT_MAPPER structure. +// Return Value: +// Error code, or FPDFERR_SUCCESS for success +// Comments: +// This function is used with devices that come with one or more system fonts, +// and those fonts are in standard TT or T1 format. +// +FPDFEMB_RESULT FPDFEMB_SetFontMapper(FPDFEMB_FONT_MAPPER* mapper); + +// Structure: FPDFEMB_GLYPH_PROVIDER +// Interface for providing glyph bitmap of non-latin characters. +// This is usually used for embedded devices with Chinese/Japanese/Korean +// fonts installed, but those fonts are not in TrueType or Type1 format. +// +struct FPDFEMB_GLYPH_PROVIDER +{ + // Interface: MapFont + // Return an internal handle for a font + // Parameters: + // provider - Pointer to this structure + // name - Font name + // charset - Charset ID (see above FPDFEMB_CHARSET_xxx constants) + // flags - Font flags (see above FPDFEMB_FONT_xxx constants) + // weight - Weight of the font. Range from 100 to 900. 400 is normal, + // 700 is bold. + // Return Value: + // An internal handle to the mapped font. If the embedded device supports + // multiple fonts, then this value can serve as an identifier to differentiate + // among them. If the device supports only one font, then implementation of + // this function can simply return NULL. + // + void* (*MapFont)(struct FPDFEMB_GLYPH_PROVIDER* provider, const char* name, int charset, + unsigned int flags, int weight); + // Interface: GetGlyphBBox + // Get glyph bounding box + // Parameters: + // provider - Pointer to this structure + // font - Internal handle to the font. Returned by MapFont interface. + // unicode - Unicode of the character + // CID - Adobe CID for this character. Or zero if not available. + // bbox - Receiving the result bounding box. See comments below. + // Return Value: + // None. + // Comments: + // The bounding box is measure in a glyph coordination system, in which the + // origin is set to character origin, and unit is set to one-thousandth of + // "em size" (representing the font size). + // + // In most CJK fonts, all CJK characters (except some symbols or western + // characters) have same glyph bounding box: + // left = 0, right = 1000, bottom = -220, top = 780. + // + // It's OK to return a box that's larger than the actual glyph box. + // + void (*GetGlyphBBox)(struct FPDFEMB_GLYPH_PROVIDER* provider, void* font, + FPDFEMB_WCHAR unicode, unsigned short CID, + FPDFEMB_RECT* bbox); + + // Interface: GetGlyphBitmap + // Get bitmap of a glyph + // Parameters: + // provider - Pointer to this structure + // font - Internal handle to the font. Returned by MapFont interface. + // unicode - Unicode of the character + // CID - Adobe CID for this character. Or zero if not available. + // font_width - Width of the font em square, measured in device units. + // font_height - Height of the font em square, measured in device units. + // left - Receiving the left offset, from the character origin, of the + // result glyph bitmap. Positive value will move the bitmap to + // the right side, negative to the left. + // top - Receiving the top offset, from the character origin, of the + // result glyph bitmap. Positive value will move the bitmap upward, + // negative downward. + // bitmap_width - Receiving number of width pixels in the result bitmap + // bitmap_height - Receiving number of height pixels in the result bitmap + // buffer - Receiving a data buffer pointer, allocated by the implementation. + // See comments below. + // stride - Receiving number of bytes per scanline, in the data buffer. + // pdf_width - Width of the character specified in PDF. It is measured in one- + // thousandth of the font width. It can be 0 if width not specified + // in PDF. See comments below. + // Return Value: + // Non-zero for success. 0 for failure. In this case the glyph can not be displayed. + // Comments: + // The buffer should be allocated by implemenation. And it must be allocated + // using FPDFEMB_AllocMemory function. The result buffer will be destroyed by + // FPDFEMB SDK, so implementation should not destroy it. + // + // The implementation should write "coverage" data into allocated buffer, one byte + // for each pixel, from top scanline to bottom scanline, within each scan line, + // from left pixel to right. Coverage 0 means the pixel is outside the glyph, + // coverage 255 means the pixel is inside the glyph. + // + // The "pdf_width" parameter can be used to scale the character in system font + // horizontally to match the font width specified in PDF. For example, if we have + // a PDF file which requires a character in half-width (pdf_width is 500), but + // in the system font the character has full-width (1000), then the glyph provider + // implementation should scale the font horizontally to half of its original width. + // + FPDFEMB_BOOL (*GetGlyphBitmap)(struct FPDFEMB_GLYPH_PROVIDER* provider, void* font, + FPDFEMB_WCHAR unicode, unsigned short CID, + double font_width, double font_height, int* left, int* top, + int* bitmap_width, int* bitmap_height, + void** buffer, int* stride, int pdf_width); + + void* user; // A user pointer, used by the application +}; + +// Function: FPDFEMB_SetGlyphProvider +// Make use of a glyph provider: generating glyph bitmap for non-Latin characters +// Parameters: +// provider - Pointer to the glyph provider structure. +// This structure must stay valid throughout usage of FPDFEMB module. +// Return Value: +// None. +// Comments: +// FPDFEMB embeds some standard fonts for Latin characters and symbols, like +// Times, Courier and Helvetica (Arial). For non-Latin characters, however, +// FPDFEMB has to ask glyph provide for help. +// +// If an embedded device carries fonts for non-Latin character sets, especially +// those for CJK markets, then the application can implement a glyph provider, +// allowing PDFs using non-embedded CJK fonts to be properly displayed. +// +void FPDFEMB_SetGlyphProvider(FPDFEMB_GLYPH_PROVIDER* provider); + +// Function: FPDFEMB_LoadCMap_GB +// Function: FPDFEMB_LoadCMap_GB_Ext +// Function: FPDFEMB_LoadCMap_CNS +// Function: FPDFEMB_LoadCMap_Korean +// Function: FPDFEMB_LoadCMap_Japan +// Function: FPDFEMB_LoadCMap_Japan_Ext +// Make use of character encoding maps embedded with FPDFEMB +// Parameters: +// None. +// Return Value: +// None. +// Comments: +// These functions add character encoding data to your application. Each call +// will increase the code size of your application. Total data size for all +// character sets is 151K bytes. +void FPDFEMB_LoadCMap_GB(); +void FPDFEMB_LoadCMap_GB_Ext(); // Load full code table for GB +void FPDFEMB_LoadCMap_CNS(); +void FPDFEMB_LoadCMap_Korea(); +void FPDFEMB_LoadCMap_Japan(); +void FPDFEMB_LoadCMap_Japan_Ext(); // Load full code table for Japan + +/******************************************************************************************** +**** +**** Document Information +**** +********************************************************************************************/ + +// Function: PDFEMB_GetDocInfoString +// Get information string about the document, like creator, modifcation date, etc. +// Parameters: +// document - Handle to the document +// key - A byte string for the information key. Currently can be one of the followings: +// "Title", "Author", "Subject", "Keywords", "Creator", "Producer", "CreationDate", +// "ModDate", or some custom information key, if supported by the PDF file. +// buffer - A buffer allocated by the application, or NULL. +// bufsize - [IN/OUT] A pointer to a number indicating the buffer size (number of bytes), +// before this function call. After return, this place will store +// number of bytes used by the output (including terminator). +// Return Value: +// Error code, or FPDFERR_SUCCESS for success +// Comments: +// The string is output in Unicode, using UTF-16LE format. It's terminated by +// two consecutive zero bytes. +// +// If the "buffer" parameter is NULL, then the "bufsize" parameter will receive +// number of bytes required to store the string (including the two-byte terminator). +// +FPDFEMB_RESULT FPDFEMB_GetDocInfoString(FPDFEMB_DOCUMENT document, const char* key, void* buffer, unsigned int* bufsize); + +/******************************************************************************************** +**** +**** Action (Destination) Information +**** +********************************************************************************************/ + +typedef void* FPDFEMB_ACTION; + +// Action types supported by FPDFEMB +#define FPDFEMB_DEST_NONE 0 // No or unsupported destination +#define FPDFEMB_DEST_PAGE 1 // A page inside the same document +#define FPDFEMB_DEST_DOC 2 // An external PDF document +#define FPDFEMB_DEST_URL 3 // An external URL +#define FPDFEMB_ACTION_LAUNCH 4 // Launch an external file or command + +// Zoom mode for destination +#define FPDFEMB_ZOOM_NONE 0 // Zoom mode not specified +#define FPDFEMB_ZOOM_FACTOR 1 // Specific zoom factor is used +#define FPDFEMB_ZOOM_FITPAGE 2 // Fit the whole page on screen +#define FPDFEMB_ZOOM_FITWIDTH 3 // Fit width of the page on screen +#define FPDFEMB_ZOOM_FITHEIGHT 4 // Fit height of the page on screen +#define FPDFEMB_ZOOM_FITRECT 5 // Fit a particular rectangle on screen +#define FPDFEMB_ZOOM_FITCONTENT 6 // Fit whole content of page on screen +#define FPDFEMB_ZOOM_FITCONTENTW 7 // Fit content width of page on screen +#define FPDFEMB_ZOOM_FITCONTENTH 8 // Fit content height of page on screen + +// Data structure for page destination +struct FPDFEMB_PAGEDEST +{ + int page_index; // Zero based index for the page + int zoom_mode; // See FPDFEMB_ZOOM_xxx definition above + int zoom_factor; // For FPDFEMB_ZOOM_FACTOR only: the zoom factor (in percentage) + FPDFEMB_RECT position; // Specify position inside the page. Depends on the zoom mode, + // different members of the rectangle are used: + // FPDFEMB_ZOOM_NONE: left, top + // FPDFEMB_ZOOM_FACTOR: left, top + // FPDFEMB_ZOOM_FITPAGE: none + // FPDFEMB_ZOOM_FITWIDTH: top + // FPDFEMB_ZOOM_FITHEIGHT: left + // FPDFEMB_ZOOM_FITRECT: left, top, bottom, right + // FPDFEMB_ZOOM_FITCONTENT: none + // FPDFEMB_ZOOM_FITCONTENTW: top + // FPDFEMB_ZOOM_FITCONTENTH: left +}; + +// Data structure for document destination +struct FPDFEMB_DOCDEST +{ + FPDFEMB_PAGEDEST page_data; // page data + char* file_name; // The file name, encoded in original charset (maybe MBCS) +}; + +// Data structure for URL destination +struct FPDFEMB_URLDEST +{ + char* url; // URL encoded in 7-bit ASCII +}; + +// Data structure for Launch action +struct FPDFEMB_LAUNCHACTION +{ + int new_window; // Whether a new window should be opened + char* file_name; // The file name, encoded in original charset (maybe MBCS) +}; + +// Function: FPDFEMB_Action_GetType +// Get type of an action +// Parameters: +// document - Handle to the document +// action - Handle to the action +// dest_type - Pointer to an integer receiving type of the destination. See the above +// FPDFEMB_DEST_xxx definitions +// data_size - Pointer to an integer receiving data block size for the destination. +// If this parameter is NULL, then data size won't be retrieved. +// Comments: +// Each different type of destination has different data structure. The "data_size" result +// indicates how many bytes is required to hold the destination data structure. The application +// can then allocate sufficient buffer and then call FPDFEMB_Bookmark_GetDest function to +// get the real data. +// +FPDFEMB_RESULT FPDFEMB_Action_GetType(FPDFEMB_DOCUMENT document, FPDFEMB_ACTION action, int* dest_type, int* data_size); + +// Function: FPDFEMB_Action_GetData +// Get detailed data of a particular action +// Parameters: +// document - Handle to the document +// action - Handle to the action +// buffer - Application allocated buffer receiving the destination data +// Return Value: +// Error code, or FPDFERR_SUCCESS for success. +// Comments: +// See data structure definition for different action type above. Please note +// the actual action data might use more space than the structure definition +// shows, to store things like file name or URL. So you should always call +// FPDFEMB_Action_GetType first to get data size then allocate enough buffer +// for this call. +// +FPDFEMB_RESULT FPDFEMB_Action_GetData(FPDFEMB_DOCUMENT document, FPDFEMB_ACTION action, void* buffer); + +// Function: FPDFEMB_Action_GetNext +// Get next action in an action chain +// Parameters: +// document - Handle to the document +// action - Handle to current action +// next - Receiving handle to next action. +// Return Value: +// Error code, or FPDFERR_SUCCESS for success. +// Comments: +// If there is no next action, the "next" parameter will be set to NULL after the function returns. +// +FPDFEMB_RESULT FPDFEMB_Action_GetNext(FPDFEMB_ACTION action, FPDFEMB_ACTION* next); + +/******************************************************************************************** +**** +**** Bookmark Information +**** +********************************************************************************************/ + +typedef void* FPDFEMB_BOOKMARK; + +// Function: FPDFEMB_Bookmark_GetFirstChild +// Get first child of a bookmark item, or first top level bookmark item +// Parameters: +// document - Handle to the document +// parent - Handle to the parent bookmark. +// Can be NULL if you want to get the first top level item. +// bookmark - Receiving handle to the first child or top level bookmark item. +// If result is NULL, then bookmark not found. +// Return Value: +// Error code, or FPDFERR_SUCCESS for success +// +FPDFEMB_RESULT FPDFEMB_Bookmark_GetFirstChild(FPDFEMB_DOCUMENT document, FPDFEMB_BOOKMARK parent, + FPDFEMB_BOOKMARK* bookmark); + +// Function: FPDFEMB_Bookmark_GetFirstChild +// Get next sibling of a bookmark item +// Parameters: +// document - Handle to the document +// bookmark - Handle to the bookmark +// sibling - Receiving the handle of next sibling. +// If result is NULL, then this is the last bookmark in this level. +// Return Value: +// Error code, or FPDFERR_SUCCESS for success +// +FPDFEMB_RESULT FPDFEMB_Bookmark_GetNextSibling(FPDFEMB_DOCUMENT document, FPDFEMB_BOOKMARK bookmark, + FPDFEMB_BOOKMARK* sibling); + +// Function: FPDFEMB_Bookmark_GetTitle +// Get title of a bookmark +// Parameters: +// bookmark - Handle to the bookmark +// buffer - A buffer allocated by the application, or NULL. +// bufsize - [IN/OUT] A pointer to a number indicating the buffer size, +// before this function call. After return, this place will store +// number of bytes used by the output (including terminator). +// Return Value: +// Error code, or FPDFERR_SUCCESS for success +// Comments: +// The title is output in Unicode, using UTF-16LE format. It's terminated by +// two consecutive zero bytes. +// +// If the "buffer" parameter is NULL, then the "bufsize" parameter will receive +// number of bytes required to store the bookmark title (including the two- +// byte terminator). +// +// If the buffer provided is smaller than the required size, then this function +// will not copy any data, return FPDFEMB_PARAM, and the required buffer size will +// also be put in "bufsize" parameter. +// +FPDFEMB_RESULT FPDFEMB_Bookmark_GetTitle(FPDFEMB_BOOKMARK bookmark, void* buffer, unsigned int* bufsize); + +// Function: FPDFEMB_Bookmark_GetPage +// Get page number of a bookmark pointing to +// Parameters: +// document - Handle to the document +// bookmark - Handle to the bookmark +// page - Receiving the page number. -1 if this bookmark doesn't actually +// point to a page inside the document. +// Return Value: +// Error code, or FPDFERR_SUCCESS for success +// Comments: +// Some bookmark might not point to a page, some bookmark might have more than one destination +// (action), for detailed information about a bookmark, you should call FPDFEMB_Bookmark_GetAction. +// +FPDFEMB_RESULT FPDFEMB_Bookmark_GetPage(FPDFEMB_DOCUMENT document, FPDFEMB_BOOKMARK bookmark, int* page); + +// Function: FPDFEMB_Bookmark_GetAction +// Get action(s) associated with a particular bookmark +// Parameters: +// document - Handle to the document +// bookmark - Handle to the bookmark +// action - Receiving handle of first action +// Return Value: +// Error code, or FPDFERR_SUCCESS for success. +// +FPDFEMB_RESULT FPDFEMB_Bookmark_GetAction(FPDFEMB_DOCUMENT document, FPDFEMB_BOOKMARK bookmark, FPDFEMB_ACTION* action); + +/******************************************************************************************** +**** +**** Hyperlink Information +**** +********************************************************************************************/ + +// Function: FPDFEMB_Link_GetCount +// Get number of hyperlinks inside a page +// Parameters: +// page - Page handle. +// link_count - Pointer to an integer receiving the number of links +// reserved - Must be zero now. +// Return Value: +// Error code, or FPDFERR_SUCCESS for success. +// Comments: +// This function must be called before any other link related function can +// be called for the page. +// +FPDFEMB_RESULT FPDFEMB_Link_GetCount(FPDFEMB_PAGE page, int* link_count, int reserved); + +// Function: FPDFEMB_Link_GetAction +// Get action(s) associated with a particular hyperlink +// Parameters: +// page - Page handle +// link_index - Zero-based index for the link +// action - Receiving handle of first action +// Return Value: +// Error code, or FPDFERR_SUCCESS for success. +// +FPDFEMB_RESULT FPDFEMB_Link_GetAction(FPDFEMB_PAGE page, int link_index, FPDFEMB_ACTION* action); + +// Function: FPDFEMB_Link_GetAreaCount +// Get number of area (quadrilaterals) for a link +// Parameters: +// page - Page handle +// link_index - Zero-based index for the link +// count - Pointer to an integer receiving number of quadrilaterals +// Return Value: +// Error code, or FPDFERR_SUCCESS for success. +// +FPDFEMB_RESULT FPDFEMB_Link_GetAreaCount(FPDFEMB_PAGE page, int link_index, int* count); + +// Function: FPDFEMB_Link_GetArea +// Get a particular quadrilateral for a link +// Parameters: +// page - Page handle +// link_index - Zero-based index for the link +// area_index - Zero-based index for the quadrilateral +// points - Pointer to an array consists 4 points, receiving coordinations +// Return Value: +// Error code, or FPDFERR_SUCCESS for success. +// Comments: +// The result in "points" array are the X/Y coordinations for the four vertices +// of the quadrilateral. Vertices are in the following order: lower left, lower +// right, upper right, upper left. +// +FPDFEMB_RESULT FPDFEMB_Link_GetArea(FPDFEMB_PAGE page, int link_index, int area_index, + FPDFEMB_POINT* points); + +/******************************************************************************************** +**** +**** Graphic Output (onto DIB) +**** +********************************************************************************************/ + +typedef void* FPDFEMB_FONT; + +// Function: FPDFEMB_OpenStandardFont +// Get ready to use a standard PDF font +// Parameters: +// font_name - Name of the font. See a list of supported fonts below. +// font_handle - Receiving the font handle. +// Return Value: +// Error code, or FPDFERR_SUCCESS for success. +// Comments: +// Currently supported standard fonts: +// Courier, Courier-Bold, Courier-BoldOblique, Courier-Oblique, +// Helvetica, Helvetica-Bold, Helvetica-BoldOblique, Helvetica-Oblique, +// Times-Roman, Times-Bold, Times-Italic, Times-BoldItalic, +// Symbol, ZapfDingbats. +// +FPDFEMB_RESULT FPDFEMB_OpenStandardFont(const char* font_name, FPDFEMB_FONT* font_handle); + +// Function: FPDFEMB_OpenSystemFont +// Get ready to use a system font +// Parameters: +// font_name - Font name +// charset - Charset ID (see above FPDFEMB_CHARSET_xxx constants) +// flags - Font flags (see above FPDFEMB_FONT_xxx constants) +// weight - Weight of the font. Range from 100 to 900. 400 is normal, +// 700 is bold. +// font_handle - Receiving the font handle. +// Return Value: +// Error code, or FPDFERR_SUCCESS for success. +// Comments: +// System font is supported only if either FPDFEMB_SetFontMapper or +// FPDFEMB_SetGlyphProvider is called. +// Font attributes (name, charset, flags and weight) can be all optional, if the +// font mapper or glyph provider doesn't make use of them. +// +FPDFEMB_RESULT FPDFEMB_OpenSystemFont(const char* font_name, int charset, unsigned int flags, int weight, + FPDFEMB_FONT* font_handle); + +// Function: FPDFEMB_CloseFont +// Close a font handle. +// Parameters: +// font_handle - Handle to the font. +// Return Value: +// Error code, or FPDFERR_SUCCESS for success. +// +FPDFEMB_RESULT FPDFEMB_CloseFont(FPDFEMB_FONT font_handle); + +struct FPDFEMB_TEXTMATRIX +{ + double a, b, c, d; +}; + +// Function: FPDFEMB_OutputText +// Output text string onto a DIB device. +// Parameters: +// dib - DIB handle, as the output device +// x, y - DIB coordinations for the origin point of the first character. +// font_handle - Handle to the font +// font_size - Font size in pixels +// matrix - Matrix for the text output. Can be NULL. +// text - Zero-terminated unicode text string +// argb - Color of the text, in 0xaarrggbb format. +// Return Value: +// Error code, or FPDFERR_SUCCESS for success. +// +FPDFEMB_RESULT FPDFEMB_OutputText(FPDFEMB_BITMAP dib, int x, int y, FPDFEMB_FONT font_handle, double font_size, + FPDFEMB_TEXTMATRIX* matrix, const FPDFEMB_WCHAR* text, unsigned long argb); + +#ifdef __cplusplus +}; +#endif + +#endif // #ifdef _FPDFEMB_H_ diff --git a/skia/images/fpdfemb_ext.h b/skia/images/fpdfemb_ext.h new file mode 100644 index 0000000..d82c4df --- /dev/null +++ b/skia/images/fpdfemb_ext.h @@ -0,0 +1,81 @@ +#ifdef __cplusplus +extern "C" { +#endif + +/** Extended interfaces for JPEG, JPEG2000 and JBIG2 decoders **/ +typedef struct +{ + /** Initialize the decoding context, with memory allocator provided by FPDFEMB. + Implementation should return a pointer to the decoding context. + */ + void* (*Init)(void* (*alloc_func)(unsigned int), void (*free_func)(void*)); + + /** Finish with the decoding. */ + void (*Finish)(void* pContext); + + /** Input JPEG encoded data from the source. + This function may be called multiple times during decoding progress. + */ + void (*Input)(void* pContext, const unsigned char* src_buf, unsigned long src_size); + + /** Read the header information. Return non-zero for success, 0 for failure */ + int (*ReadHeader)(void* pContext); + + /** Get info from the decoder, including image width, height and number of components */ + void (*GetInfo)(void* pContext, int* width, int* height, int* nComps); + + /** Read one scanline from decoded image */ + int (*ReadScanline)(void* pContext, unsigned char* dest_buf); + + /** Get number of available source bytes left in the input stream */ + unsigned long (*GetAvailInput)(void* pContext); +} FPDFEMB_JPEG_DECODER; + +void FPDFEMB_SetJpegDecoder(FPDFEMB_JPEG_DECODER* pDecoder); + +typedef struct +{ + /** Initialize the decoder with the full source data. + Implementation should return a pointer to the context. + */ + void* (*Init)(const unsigned char* src_buf, unsigned long src_size); + + /** Destroy the context */ + void (*Finish)(void* context); + + /** Get image info from the context, including width, height, number of components + in original codestream, and number of components in output image. For some + particular type of encoded image, like paletted image, these two numbers of + components may vary. + */ + void (*GetInfo)(void* context, unsigned long* width, unsigned long* height, + unsigned long* codestream_nComps, unsigned long* output_nComps); + + /** Do the real data decoding, output to a pre-allocated buffer. + bTranslateColor indicates whether the decoder should use JPEG2000 embedded + color space info to translate image into sRGB color space. + "offsets" array describes the byte order of all components. For example, + {2,1,0} means the first components is output to last byte. + */ + void (*Decode)(void* context, unsigned char* dest_buf, int pitch, + int bTranslateColor, unsigned char* offsets); +} FPDFEMB_JPEG2000_DECODER; + +void FPDFEMB_SetJpeg2000Decoder(FPDFEMB_JPEG2000_DECODER* pDecoder); + +typedef struct +{ + /** Do the whole decoding process. Supplied parameters include width, height, source image + data and size, global data and size (can be shared among different images), destination + buffer and scanline pitch in dest buffer. + */ + void (*Decode)(unsigned long width, unsigned long height, const unsigned char* src_buf, + unsigned long src_size, const unsigned char* global_buf, unsigned long global_size, + unsigned char* dest_buf, int dest_pitch); +} FPDFEMB_JBIG2_DECODER; + +void FPDFEMB_SetJbig2Decoder(FPDFEMB_JBIG2_DECODER* pDecoder); + +#ifdef __cplusplus +}; +#endif diff --git a/skia/include/DoxygenMain.dox b/skia/include/DoxygenMain.dox new file mode 100644 index 0000000..00fcc31 --- /dev/null +++ b/skia/include/DoxygenMain.dox @@ -0,0 +1,3 @@ +/** \mainpage notitle
+* \htmlinclude "SGL Spec. rev 9.htm"
+*/
\ No newline at end of file diff --git a/skia/include/Sk1DPathEffect.h b/skia/include/Sk1DPathEffect.h new file mode 100644 index 0000000..6679a26 --- /dev/null +++ b/skia/include/Sk1DPathEffect.h @@ -0,0 +1,92 @@ +/* Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef Sk1DPathEffect_DEFINED +#define Sk1DPathEffect_DEFINED + +#include "SkPathEffect.h" +#include "SkPath.h" + +class SkPathMeasure; + +// This class is not exported to java. +class Sk1DPathEffect : public SkPathEffect { +public: + // override from SkPathEffect + virtual bool filterPath(SkPath* dst, const SkPath& src, SkScalar* width); + +protected: + /** Called at the start of each contour, returns the initial offset + into that contour. + */ + virtual SkScalar begin(SkScalar contourLength) = 0; + /** Called with the current distance along the path, with the current matrix + for the point/tangent at the specified distance. + Return the distance to travel for the next call. If return <= 0, then that + contour is done. + */ + virtual SkScalar next(SkPath* dst, SkScalar distance, SkPathMeasure&) = 0; + +private: + typedef SkPathEffect INHERITED; +}; + +class SkPath1DPathEffect : public Sk1DPathEffect { +public: + enum Style { + kTranslate_Style, // translate the shape to each position + kRotate_Style, // rotate the shape about its center + kMorph_Style, // transform each point, and turn lines into curves + + kStyleCount + }; + + /** Dash by replicating the specified path. + @param path The path to replicate (dash) + @param advance The space between instances of path + @param phase distance (mod advance) along path for its initial position + @param style how to transform path at each point (based on the current + position and tangent) + */ + SkPath1DPathEffect(const SkPath& path, SkScalar advance, SkScalar phase, Style); + + // override from SkPathEffect + virtual bool filterPath(SkPath* dst, const SkPath& src, SkScalar* width); + +protected: + SkPath1DPathEffect(SkFlattenableReadBuffer& buffer); + + // overrides from Sk1DPathEffect + virtual SkScalar begin(SkScalar contourLength); + virtual SkScalar next(SkPath* dst, SkScalar distance, SkPathMeasure&); + // overrides from SkFlattenable + virtual void flatten(SkFlattenableWriteBuffer& ); + virtual Factory getFactory() { return CreateProc; } + +private: + SkPath fPath; // copied from constructor + SkScalar fAdvance; // copied from constructor + SkScalar fInitialOffset; // computed from phase + Style fStyle; // copied from constructor + + static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) { + return SkNEW_ARGS(SkPath1DPathEffect, (buffer)); + } + + typedef Sk1DPathEffect INHERITED; +}; + + +#endif diff --git a/skia/include/Sk2DPathEffect.h b/skia/include/Sk2DPathEffect.h new file mode 100644 index 0000000..832dbcc --- /dev/null +++ b/skia/include/Sk2DPathEffect.h @@ -0,0 +1,74 @@ +/* include/graphics/Sk2DPathEffect.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef Sk2DPathEffect_DEFINED +#define Sk2DPathEffect_DEFINED + +#include "SkPathEffect.h" +#include "SkMatrix.h" + +// This class is not exported to java. +class Sk2DPathEffect : public SkPathEffect { +public: + Sk2DPathEffect(const SkMatrix& mat); + + // overrides + // This method is not exported to java. + virtual bool filterPath(SkPath* dst, const SkPath& src, SkScalar* width); + + // overrides from SkFlattenable + // This method is not exported to java. + virtual void flatten(SkFlattenableWriteBuffer&); + + // This method is not exported to java. + virtual Factory getFactory(); + +protected: + /** New virtual, to be overridden by subclasses. + This is called once from filterPath, and provides the + uv parameter bounds for the path. Subsequent calls to + next() will receive u and v values within these bounds, + and then a call to end() will signal the end of processing. + */ + virtual void begin(const SkIRect& uvBounds, SkPath* dst); + virtual void next(const SkPoint& loc, int u, int v, SkPath* dst); + virtual void end(SkPath* dst); + + /** Low-level virtual called per span of locations in the u-direction. + The default implementation calls next() repeatedly with each + location. + */ + virtual void nextSpan(int u, int v, int ucount, SkPath* dst); + + const SkMatrix& getMatrix() const { return fMatrix; } + + // protected so that subclasses can call this during unflattening + Sk2DPathEffect(SkFlattenableReadBuffer&); + +private: + SkMatrix fMatrix, fInverse; + // illegal + Sk2DPathEffect(const Sk2DPathEffect&); + Sk2DPathEffect& operator=(const Sk2DPathEffect&); + + static SkFlattenable* CreateProc(SkFlattenableReadBuffer&); + + friend class Sk2DPathEffectBlitter; + typedef SkPathEffect INHERITED; +}; + +#endif diff --git a/skia/include/SkAnimator.h b/skia/include/SkAnimator.h new file mode 100644 index 0000000..5bed8c7 --- /dev/null +++ b/skia/include/SkAnimator.h @@ -0,0 +1,509 @@ +/* include/graphics/SkAnimator.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkAnimator_DEFINED +#define SkAnimator_DEFINED + +#include "SkScalar.h" +#include "SkKey.h" +#include "SkEventSink.h" + +class SkAnimateMaker; +class SkCanvas; +class SkDisplayable; +class SkEvent; +class SkExtras; +struct SkMemberInfo; +class SkPaint; +struct SkRect; +class SkStream; +class SkTypedArray; +class SkXMLParserError; +class SkDOM; +struct SkDOMNode; + +/** SkElementType is the type of element: a rectangle, a color, an animator, and so on. + This enum is incomplete and will be fleshed out in a future release */ +enum SkElementType { + kElementDummyType +}; +/** SkFieldType is the type of field: a scalar, a string, an integer, a boolean, and so on. + This enum is incomplete and will be fleshed out in a future release */ +enum SkFieldType { + kFieldDummyType +}; + +/** \class SkAnimator + + The SkAnimator class decodes an XML stream into a display list. The + display list can be drawn statically as a picture, or can drawn + different elements at different times to form a moving animation. + + SkAnimator does not read the system time on its own; it relies on the + caller to pass the current time. The caller can pause, speed up, or + reverse the animation by varying the time passed in. + + The XML describing the display list must conform to the schema + described by SkAnimateSchema.xsd. + + The XML must contain an <event> element to draw. Usually, it contains + an <event kind="onload" /> block to add some drawing elements to the + display list when the document is first decoded. + + Here's an "Hello World" XML sample: + + <screenplay> + <event kind="onload" > + <text text="Hello World" y="20" /> + </event> + </screenplay> + + To read and draw this sample: + + // choose one of these two + SkAnimator animator; // declare an animator instance on the stack + // SkAnimator* animator = new SkAnimator() // or one could instantiate the class + + // choose one of these three + animator.decodeMemory(buffer, size); // to read from RAM + animator.decodeStream(stream); // to read from a user-defined stream (e.g., a zip file) + animator.decodeURI(filename); // to read from a web location, or from a local text file + + // to draw to the current window: + SkCanvas canvas(getBitmap()); // create a canvas + animator.draw(canvas, &paint, 0); // draw the scene +*/ +class SkAnimator : public SkEventSink { +public: + SkAnimator(); + virtual ~SkAnimator(); + + /** Add a drawable extension to the graphics engine. Experimental. + @param extras A derived class that implements methods that identify and instantiate the class + */ + void addExtras(SkExtras* extras); + + /** Read in XML from a stream, and append it to the current + animator. Returns false if an error was encountered. + Error diagnostics are stored in fErrorCode and fLineNumber. + @param stream The stream to append. + @return true if the XML was parsed successfully. + */ + bool appendStream(SkStream* stream); + + /** Read in XML from memory. Returns true if the file can be + read without error. Returns false if an error was encountered. + Error diagnostics are stored in fErrorCode and fLineNumber. + @param buffer The XML text as UTF-8 characters. + @param size The XML text length in bytes. + @return true if the XML was parsed successfully. + */ + bool decodeMemory(const void* buffer, size_t size); + + /** Read in XML from a stream. Returns true if the file can be + read without error. Returns false if an error was encountered. + Error diagnostics are stored in fErrorCode and fLineNumber. + @param stream The stream containg the XML text as UTF-8 characters. + @return true if the XML was parsed successfully. + */ + virtual bool decodeStream(SkStream* stream); + + /** Parse the DOM tree starting at the specified node. Returns true if it can be + parsed without error. Returns false if an error was encountered. + Error diagnostics are stored in fErrorCode and fLineNumber. + @return true if the DOM was parsed successfully. + */ + virtual bool decodeDOM(const SkDOM&, const SkDOMNode*); + + /** Read in XML from a URI. Returns true if the file can be + read without error. Returns false if an error was encountered. + Error diagnostics are stored in fErrorCode and fLineNumber. + @param uri The complete url path to be read (either ftp, http or https). + @return true if the XML was parsed successfully. + */ + bool decodeURI(const char uri[]); + + /** Pass a char event, usually a keyboard symbol, to the animator. + This triggers events of the form <event kind="keyChar" key="... /> + @param ch The character to match against <event> element "key" + attributes. + @return true if the event was dispatched successfully. + */ + bool doCharEvent(SkUnichar ch); + + /** Experimental: + Pass a mouse click event along with the mouse coordinates to + the animator. This triggers events of the form <event kind="mouseDown" ... /> + and other mouse events. + @param state The mouse state, described by SkView::Click::State : values are + down == 0, moved == 1, up == 2 + @param x The x-position of the mouse + @param y The y-position of the mouse + @return true if the event was dispatched successfully. + */ + bool doClickEvent(int state, SkScalar x, SkScalar y); + + /** Pass a meta-key event, such as an arrow , to the animator. + This triggers events of the form <event kind="keyPress" code="... /> + @param code The key to match against <event> element "code" + attributes. + @return true if the event was dispatched successfully. + */ + bool doKeyEvent(SkKey code); + bool doKeyUpEvent(SkKey code); + + /** Send an event to the animator. The animator's clock is set + relative to the current time. + @return true if the event was dispatched successfully. + */ + bool doUserEvent(const SkEvent& evt); + + /** The possible results from the draw function. + */ + enum DifferenceType { + kNotDifferent, + kDifferent, + kPartiallyDifferent + }; + /** Draws one frame of the animation. The first call to draw always + draws the initial frame of the animation. Subsequent calls draw + the offset into the animation by + subtracting the initial time from the current time. + @param canvas The canvas to draw into. + @param paint The paint to draw with. + @param time The offset into the current animation. + @return kNotDifferent if there are no active animations; kDifferent if there are active animations; and + kPartiallyDifferent if the document contains an active <bounds> element that specifies a minimal + redraw area. + */ + DifferenceType draw(SkCanvas* canvas, SkPaint* paint, SkMSec time); + + /** Draws one frame of the animation, using a new Paint each time. + The first call to draw always + draws the initial frame of the animation. Subsequent calls draw + the offset into the animation by + subtracting the initial time from the current time. + @param canvas The canvas to draw into. + @param time The offset into the current animation. + @return kNotDifferent if there are no active animations; kDifferent if there are active animations; and + kPartiallyDifferent if the document contains an active <bounds> element that specifies a minimal + redraw area. + */ + DifferenceType draw(SkCanvas* canvas, SkMSec time); + + /** Experimental: + Helper to choose whether to return a SkView::Click handler. + @param x ignored + @param y ignored + @return true if a mouseDown event handler is enabled. + */ + bool findClickEvent(SkScalar x, SkScalar y); + + + /** Get the nested animator associated with this element, if any. + Use this to access a movie's event sink, to send events to movies. + @param element the value returned by getElement + @return the internal animator. + */ + const SkAnimator* getAnimator(const SkDisplayable* element) const; + + /** Returns the scalar value of the specified element's attribute[index] + @param element the value returned by getElement + @param field the value returned by getField + @param index the array entry + @return the integer value to retrieve, or SK_NaN32 if unsuccessful + */ + int32_t getArrayInt(const SkDisplayable* element, const SkMemberInfo* field, int index); + + /** Returns the scalar value of the specified element's attribute[index] + @param elementID is the value of the id attribute in the XML of this element + @param fieldName specifies the name of the attribute + @param index the array entry + @return the integer value to retrieve, or SK_NaN32 if unsuccessful + */ + int32_t getArrayInt(const char* elementID, const char* fieldName, int index); + + /** Returns the scalar value of the specified element's attribute[index] + @param element the value returned by getElement + @param field the value returned by getField + @param index the array entry + @return the scalar value to retrieve, or SK_ScalarNaN if unsuccessful + */ + SkScalar getArrayScalar(const SkDisplayable* element, const SkMemberInfo* field, int index); + + /** Returns the scalar value of the specified element's attribute[index] + @param elementID is the value of the id attribute in the XML of this element + @param fieldName specifies the name of the attribute + @param index the array entry + @return the scalar value to retrieve, or SK_ScalarNaN if unsuccessful + */ + SkScalar getArrayScalar(const char* elementID, const char* fieldName, int index); + + /** Returns the string value of the specified element's attribute[index] + @param element is a value returned by getElement + @param field is a value returned by getField + @param index the array entry + @return the string value to retrieve, or null if unsuccessful + */ + const char* getArrayString(const SkDisplayable* element, const SkMemberInfo* field, int index); + + /** Returns the string value of the specified element's attribute[index] + @param elementID is the value of the id attribute in the XML of this element + @param fieldName specifies the name of the attribute + @param index the array entry + @return the string value to retrieve, or null if unsuccessful + */ + const char* getArrayString(const char* elementID, const char* fieldName, int index); + + /** Returns the XML element corresponding to the given ID. + @param elementID is the value of the id attribute in the XML of this element + @return the element matching the ID, or null if the element can't be found + */ + const SkDisplayable* getElement(const char* elementID); + + /** Returns the element type corresponding to the XML element. + The element type matches the element name; for instance, <line> returns kElement_LineType + @param element is a value returned by getElement + @return element type, or 0 if the element can't be found + */ + SkElementType getElementType(const SkDisplayable* element); + + /** Returns the element type corresponding to the given ID. + @param elementID is the value of the id attribute in the XML of this element + @return element type, or 0 if the element can't be found + */ + SkElementType getElementType(const char* elementID); + + /** Returns the XML field of the named attribute in the XML element. + @param element is a value returned by getElement + @param fieldName is the attribute to return + @return the attribute matching the fieldName, or null if the element can't be found + */ + const SkMemberInfo* getField(const SkDisplayable* element, const char* fieldName); + + /** Returns the XML field of the named attribute in the XML element matching the elementID. + @param elementID is the value of the id attribute in the XML of this element + @param fieldName is the attribute to return + @return the attribute matching the fieldName, or null if the element can't be found + */ + const SkMemberInfo* getField(const char* elementID, const char* fieldName); + + /** Returns the value type coresponding to the element's attribute. + The value type matches the XML schema: and may be kField_BooleanType, kField_ScalarType, etc. + @param field is a value returned by getField + @return the attribute type, or 0 if the element can't be found + */ + SkFieldType getFieldType(const SkMemberInfo* field); + + /** Returns the value type coresponding to the element's attribute. + @param elementID is the value of the id attribute in the XML of this element + @param fieldName specifies the name of the attribute + @return the attribute type, or 0 if the element can't be found + */ + SkFieldType getFieldType(const char* elementID, const char* fieldName); + + /** Returns the recommended animation interval. Returns zero if no + interval is specified. + */ + SkMSec getInterval(); + + /** Returns the partial rectangle to invalidate after drawing. Call after draw() returns + kIsPartiallyDifferent to do a mimimal inval(). */ + void getInvalBounds(SkRect* inval); + + /** Returns the details of any error encountered while parsing the XML. + */ + const SkXMLParserError* getParserError(); + + /** Returns the details of any error encountered while parsing the XML as string. + */ + const char* getParserErrorString(); + + /** Returns the scalar value of the specified element's attribute + @param element is a value returned by getElement + @param field is a value returned by getField + @return the integer value to retrieve, or SK_NaN32 if not found + */ + int32_t getInt(const SkDisplayable* element, const SkMemberInfo* field); + + /** Returns the scalar value of the specified element's attribute + @param elementID is the value of the id attribute in the XML of this element + @param fieldName specifies the name of the attribute + @return the integer value to retrieve, or SK_NaN32 if not found + */ + int32_t getInt(const char* elementID, const char* fieldName); + + /** Returns the scalar value of the specified element's attribute + @param element is a value returned by getElement + @param field is a value returned by getField + @return the scalar value to retrieve, or SK_ScalarNaN if not found + */ + SkScalar getScalar(const SkDisplayable* element, const SkMemberInfo* field); + + /** Returns the scalar value of the specified element's attribute + @param elementID is the value of the id attribute in the XML of this element + @param fieldName specifies the name of the attribute + @return the scalar value to retrieve, or SK_ScalarNaN if not found + */ + SkScalar getScalar(const char* elementID, const char* fieldName); + + /** Returns the string value of the specified element's attribute + @param element is a value returned by getElement + @param field is a value returned by getField + @return the string value to retrieve, or null if not found + */ + const char* getString(const SkDisplayable* element, const SkMemberInfo* field); + + /** Returns the string value of the specified element's attribute + @param elementID is the value of the id attribute in the XML of this element + @param fieldName specifies the name of the attribute + @return the string value to retrieve, or null if not found + */ + const char* getString(const char* elementID, const char* fieldName); + + /** Gets the file default directory of the URL base path set explicitly or by reading the last URL. */ + const char* getURIBase(); + + /** Resets the animator to a newly created state with no animation data. */ + void initialize(); + + /** Experimental. Resets any active animations so that the next time passed is treated as + time zero. */ + void reset(); + + /** Sets the scalar value of the specified element's attribute + @param elementID is the value of the id attribute in the XML of this element + @param fieldName specifies the name of the attribute + @param array is the c-style array of integers + @param count is the length of the array + @return true if the value was set successfully + */ + bool setArrayInt(const char* elementID, const char* fieldName, const int* array, int count); + + /** Sets the scalar value of the specified element's attribute + @param elementID is the value of the id attribute in the XML of this element + @param fieldName specifies the name of the attribute + @param array is the c-style array of strings + @param count is the length of the array + @return true if the value was set successfully + */ + bool setArrayString(const char* elementID, const char* fieldName, const char** array, int count); + + /** Sets the scalar value of the specified element's attribute + @param elementID is the value of the id attribute in the XML of this element + @param fieldName specifies the name of the attribute + @param data the integer value to set + @return true if the value was set successfully + */ + bool setInt(const char* elementID, const char* fieldName, int32_t data); + + /** Sets the scalar value of the specified element's attribute + @param elementID is the value of the id attribute in the XML of this element + @param fieldName specifies the name of the attribute + @param data the scalar value to set + @return true if the value was set successfully + */ + bool setScalar(const char* elementID, const char* fieldName, SkScalar data); + + /** Sets the string value of the specified element's attribute + @param elementID is the value of the id attribute in the XML of this element + @param fieldName specifies the name of the attribute + @param data the string value to set + @return true if the value was set successfully + */ + bool setString(const char* elementID, const char* fieldName, const char* data); + + /** Sets the file default directory of the URL base path + @param path the directory path + */ + void setURIBase(const char* path); + + typedef void* Handler; + // This guy needs to be exported to java, so don't make it virtual + void setHostHandler(Handler handler) { + this->onSetHostHandler(handler); + } + + /** \class Timeline + Returns current time to animator. To return a custom timeline, create a child + class and override the getMSecs method. + */ + class Timeline { + public: + virtual ~Timeline() {} + + /** Returns the current time in milliseconds */ + virtual SkMSec getMSecs() const = 0; + }; + + /** Sets a user class to return the current time to the animator. + Optional; if not called, the system clock will be used by calling SkTime::GetMSecs instead. + @param callBack the time function + */ + void setTimeline(const Timeline& ); + + static void Init(bool runUnitTests); + static void Term(); + + /** The event sink events generated by the animation are posted to. + Screenplay also posts an inval event to this event sink after processing an + event to force a redraw. + @param target the event sink id + */ + void setHostEventSinkID(SkEventSinkID hostID); + SkEventSinkID getHostEventSinkID() const; + + // helper + void setHostEventSink(SkEventSink* sink) { + this->setHostEventSinkID(sink ? sink->getSinkID() : 0); + } + + virtual void setJavaOwner(Handler owner); + +#ifdef SK_DEBUG + virtual void eventDone(const SkEvent& evt); + virtual bool isTrackingEvents(); + static bool NoLeaks(); +#endif + +protected: + virtual void onSetHostHandler(Handler handler); + virtual void onEventPost(SkEvent*, SkEventSinkID); + virtual void onEventPostTime(SkEvent*, SkEventSinkID, SkMSec time); + +private: +// helper functions for setters + bool setArray(SkDisplayable* element, const SkMemberInfo* field, SkTypedArray array); + bool setArray(const char* elementID, const char* fieldName, SkTypedArray array); + bool setInt(SkDisplayable* element, const SkMemberInfo* field, int32_t data); + bool setScalar(SkDisplayable* element, const SkMemberInfo* field, SkScalar data); + bool setString(SkDisplayable* element, const SkMemberInfo* field, const char* data); + + virtual bool onEvent(const SkEvent&); + SkAnimateMaker* fMaker; + friend class SkAnimateMaker; + friend class SkAnimatorScript; + friend class SkAnimatorScript2; + friend class SkApply; + friend class SkDisplayMovie; + friend class SkDisplayType; + friend class SkPost; + friend class SkXMLAnimatorWriter; +}; + +#endif + diff --git a/skia/include/SkAnimatorView.h b/skia/include/SkAnimatorView.h new file mode 100644 index 0000000..44e951c --- /dev/null +++ b/skia/include/SkAnimatorView.h @@ -0,0 +1,48 @@ +/* include/graphics/SkAnimatorView.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkAnimatorView_DEFINED +#define SkAnimatorView_DEFINED + +#include "SkView.h" +#include "SkAnimator.h" + +class SkAnimatorView : public SkView { +public: + SkAnimatorView(); + virtual ~SkAnimatorView(); + + SkAnimator* getAnimator() const { return fAnimator; } + + bool decodeFile(const char path[]); + bool decodeMemory(const void* buffer, size_t size); + bool decodeStream(SkStream* stream); + +protected: + // overrides + virtual bool onEvent(const SkEvent&); + virtual void onDraw(SkCanvas*); + virtual void onInflate(const SkDOM&, const SkDOM::Node*); + +private: + SkAnimator* fAnimator; + + typedef SkView INHERITED; +}; + +#endif + diff --git a/skia/include/SkApplication.h b/skia/include/SkApplication.h new file mode 100644 index 0000000..91be3cf --- /dev/null +++ b/skia/include/SkApplication.h @@ -0,0 +1,27 @@ +/* include/graphics/SkApplication.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkApplication_DEFINED +#define SkApplication_DEFINED + +class SkOSWindow; + +extern SkOSWindow* create_sk_window(void* hwnd); +extern void application_init(); +extern void application_term(); + +#endif // SkApplication_DEFINED diff --git a/skia/include/SkAvoidXfermode.h b/skia/include/SkAvoidXfermode.h new file mode 100644 index 0000000..0696b92 --- /dev/null +++ b/skia/include/SkAvoidXfermode.h @@ -0,0 +1,74 @@ +/* include/graphics/SkAvoidXfermode.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkAvoidXfermode_DEFINED +#define SkAvoidXfermode_DEFINED + +#include "SkXfermode.h" + +/** \class SkAvoidXfermode + + This xfermode will draw the src everywhere except on top of the specified + color. +*/ +class SkAvoidXfermode : public SkXfermode { +public: + enum Mode { + kAvoidColor_Mode, //!< draw everywhere except on the opColor + kTargetColor_Mode //!< draw only on top of the opColor + }; + + /** This xfermode will draw the src everywhere except on top of the opColor + or, depending on the Mode, draw only on top of the opColor. + @param opColor the color to avoid (or to target depending on Mode). + note: the alpha in opColor is ignored + @param tolerance How closely we compare a pixel to the opColor. + 0 - only operate if exact match + 255 - maximum gradation (blending) based on how + similar the pixel is to our opColor (max tolerance) + @param mode If we should avoid or target the opColor + */ + SkAvoidXfermode(SkColor opColor, U8CPU tolerance, Mode mode); + + // overrides from SkXfermode + virtual void xfer32(SkPMColor dst[], const SkPMColor src[], int count, + const SkAlpha aa[]); + virtual void xfer16(uint16_t dst[], const SkPMColor src[], int count, + const SkAlpha aa[]); + virtual void xfer4444(uint16_t dst[], const SkPMColor src[], int count, + const SkAlpha aa[]); + virtual void xferA8(SkAlpha dst[], const SkPMColor src[], int count, + const SkAlpha aa[]); + + // overrides from SkFlattenable + virtual Factory getFactory(); + virtual void flatten(SkFlattenableWriteBuffer&); + +protected: + SkAvoidXfermode(SkFlattenableReadBuffer&); + +private: + SkColor fOpColor; + uint32_t fDistMul; // x.14 + Mode fMode; + + static SkFlattenable* Create(SkFlattenableReadBuffer&); + + typedef SkXfermode INHERITED; +}; + +#endif diff --git a/skia/include/SkBGViewArtist.h b/skia/include/SkBGViewArtist.h new file mode 100644 index 0000000..3ea264d --- /dev/null +++ b/skia/include/SkBGViewArtist.h @@ -0,0 +1,42 @@ +/* include/graphics/SkBGViewArtist.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkBGViewArtist_DEFINED +#define SkBGViewArtist_DEFINED + +#include "SkView.h" +#include "SkPaint.h" + +class SkBGViewArtist : public SkView::Artist { +public: + SkBGViewArtist(SkColor c = SK_ColorWHITE); + virtual ~SkBGViewArtist(); + + const SkPaint& paint() const { return fPaint; } + SkPaint& paint() { return fPaint; } + +protected: + // overrides + virtual void onDraw(SkView*, SkCanvas*); + virtual void onInflate(const SkDOM&, const SkDOM::Node*); + +private: + SkPaint fPaint; +}; + +#endif + diff --git a/skia/include/SkBML_WXMLParser.h b/skia/include/SkBML_WXMLParser.h new file mode 100644 index 0000000..b1da4ba --- /dev/null +++ b/skia/include/SkBML_WXMLParser.h @@ -0,0 +1,55 @@ +/* include/graphics/SkBML_WXMLParser.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkBML_WXMLParser_DEFINED +#define SkBML_WXMLParser_DEFINED + +#include "SkString.h" +#include "SkXMLParser.h" + +class SkStream; +class SkWStream; + +class BML_WXMLParser : public SkXMLParser { +public: + BML_WXMLParser(SkWStream& writer); + virtual ~BML_WXMLParser(); + static void Write(SkStream& s, const char filename[]); + + /** @cond UNIT_TEST */ + SkDEBUGCODE(static void UnitTest();) + /** @endcond */ +private: + virtual bool onAddAttribute(const char name[], const char value[]); + virtual bool onEndElement(const char name[]); + virtual bool onStartElement(const char name[]); + BML_WXMLParser& operator=(const BML_WXMLParser& src); +#ifdef SK_DEBUG + int fElemsCount, fElemsReused; + int fAttrsCount, fNamesReused, fValuesReused; +#endif + SkWStream& fWriter; + char* fElems[256]; + char* fAttrNames[256]; + char* fAttrValues[256]; + + // important that these are U8, so we get automatic wrap-around + U8 fNextElem, fNextAttrName, fNextAttrValue; +}; + +#endif // SkBML_WXMLParser_DEFINED + diff --git a/skia/include/SkBML_XMLParser.h b/skia/include/SkBML_XMLParser.h new file mode 100644 index 0000000..e2c3e29 --- /dev/null +++ b/skia/include/SkBML_XMLParser.h @@ -0,0 +1,40 @@ +/* include/graphics/SkBML_XMLParser.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkBML_XMLParser_DEFINED +#define SkBML_XMLParser_DEFINED + +class SkStream; +class SkWStream; +class SkXMLParser; +class SkXMLWriter; + +class BML_XMLParser { +public: + /** Read the byte XML stream and write the decompressed XML. + */ + static void Read(SkStream& s, SkXMLWriter& writer); + /** Read the byte XML stream and write the decompressed XML into a writable stream. + */ + static void Read(SkStream& s, SkWStream& output); + /** Read the byte XML stream and write the decompressed XML into an XML parser. + */ + static void Read(SkStream& s, SkXMLParser& output); +}; + +#endif // SkBML_XMLParser_DEFINED + diff --git a/skia/include/SkBitmap.h b/skia/include/SkBitmap.h new file mode 100644 index 0000000..72449e2 --- /dev/null +++ b/skia/include/SkBitmap.h @@ -0,0 +1,611 @@ +/* + * Copyright (C) 2006-2008 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SkBitmap_DEFINED +#define SkBitmap_DEFINED + +#include "Sk64.h" +#include "SkColor.h" +#include "SkPoint.h" +#include "SkRefCnt.h" + +#if defined(SK_BUILD_FOR_MAC) +#include <carbon/carbon.h> +#endif + +struct SkIRect; +class SkColorTable; +class SkPaint; +class SkPixelRef; +class SkRegion; +class SkFlattenableReadBuffer; +class SkFlattenableWriteBuffer; + +/** \class SkBitmap + + The SkBitmap class specifies a raster bitmap. A bitmap has an integer width + and height, and a format (config), and a pointer to the actual pixels. + Bitmaps can be drawn into a SkCanvas, but they are also used to specify the target + of a SkCanvas' drawing operations. +*/ +class SkBitmap { +public: + class Allocator; + + enum Config { + kNo_Config, //!< bitmap has not been configured + kA1_Config, //!< 1-bit per pixel, (0 is transparent, 1 is opaque) + kA8_Config, //!< 8-bits per pixel, with only alpha specified (0 is transparent, 0xFF is opaque) + kIndex8_Config, //!< 8-bits per pixel, using SkColorTable to specify the colors + kRGB_565_Config, //!< 16-bits per pixel, (see SkColorPriv.h for packing) + kARGB_4444_Config, //!< 16-bits per pixel, (see SkColorPriv.h for packing) + kARGB_8888_Config, //!< 32-bits per pixel, (see SkColorPriv.h for packing) + kRLE_Index8_Config, + + kConfigCount + }; + + /** Default construct creates a bitmap with zero width and height, and no pixels. + Its config is set to kNo_Config. + */ + SkBitmap(); + /** Constructor initializes the new bitmap by copying the src bitmap. All fields are copied, + but ownership of the pixels remains with the src bitmap. + */ + SkBitmap(const SkBitmap& src); + /** Decrements our (shared) pixel ownership if needed. + */ + ~SkBitmap(); + + /** Copies the src bitmap into this bitmap. Ownership of the src bitmap's pixels remains + with the src bitmap. + */ + SkBitmap& operator=(const SkBitmap& src); + /** Swap the fields of the two bitmaps. This routine is guaranteed to never fail or throw. + */ + // This method is not exported to java. + void swap(SkBitmap& other); + + /** Return true iff the bitmap has empty dimensions. + */ + bool empty() const { return 0 == fWidth || 0 == fHeight; } + + /** Return true iff the bitmap has no pixels nor a pixelref. Note: this can + return true even if the dimensions of the bitmap are > 0 (see empty()). + */ + bool isNull() const { return NULL == fPixels && NULL == fPixelRef; } + + /** Return the config for the bitmap. + */ + Config config() const { return (Config)fConfig; } + /** DEPRECATED, use config() + */ + Config getConfig() const { return this->config(); } + /** Return the bitmap's width, in pixels. + */ + int width() const { return fWidth; } + /** Return the bitmap's height, in pixels. + */ + int height() const { return fHeight; } + /** Return the number of bytes between subsequent rows of the bitmap. + */ + int rowBytes() const { return fRowBytes; } + + /** Return the shift amount per pixel (i.e. 0 for 1-byte per pixel, 1 for + 2-bytes per pixel configs, 2 for 4-bytes per pixel configs). Return 0 + for configs that are not at least 1-byte per pixel (e.g. kA1_Config + or kNo_Config) + */ + int shiftPerPixel() const { return fBytesPerPixel >> 1; } + + /** Return the number of bytes per pixel based on the config. If the config + does not have at least 1 byte per (e.g. kA1_Config) then 0 is returned. + */ + int bytesPerPixel() const { return fBytesPerPixel; } + + /** Return the rowbytes expressed as a number of pixels (like width and + height). Note, for 1-byte per pixel configs like kA8_Config, this will + return the same as rowBytes(). Is undefined for configs that are less + than 1-byte per pixel (e.g. kA1_Config) + */ + int rowBytesAsPixels() const { return fRowBytes >> (fBytesPerPixel >> 1); } + + /** Return the address of the pixels for this SkBitmap. + */ + void* getPixels() const { return fPixels; } + + /** Return the byte size of the pixels, based on the height and rowBytes. + Note this truncates the result to 32bits. Call getSize64() to detect + if the real size exceeds 32bits. + */ + size_t getSize() const { return fHeight * fRowBytes; } + + /** Return the byte size of the pixels, based on the height and rowBytes. + This routine is slightly slower than getSize(), but does not truncate + the answer to 32bits. + */ + Sk64 getSize64() const { + Sk64 size; + size.setMul(fHeight, fRowBytes); + return size; + } + + /** Returns true if the bitmap is opaque (has no translucent/transparent pixels). + */ + bool isOpaque() const; + /** Specify if this bitmap's pixels are all opaque or not. Is only meaningful for configs + that support per-pixel alpha (RGB32, A1, A8). + */ + void setIsOpaque(bool); + + /** Reset the bitmap to its initial state (see default constructor). If we are a (shared) + owner of the pixels, that ownership is decremented. + */ + void reset(); + + /** Given a config and a width, this computes the optimal rowBytes value. This is called automatically + if you pass 0 for rowBytes to setConfig(). + */ + static int ComputeRowBytes(Config c, int width); + + /** Return the bytes-per-pixel for the specified config. If the config is + not at least 1-byte per pixel, return 0, including for kNo_Config. + */ + static int ComputeBytesPerPixel(Config c); + + /** Return the shift-per-pixel for the specified config. If the config is + not at least 1-byte per pixel, return 0, including for kNo_Config. + */ + static int ComputeShiftPerPixel(Config c) { + return ComputeBytesPerPixel(c) >> 1; + } + + /** Set the bitmap's config and dimensions. If rowBytes is 0, then + ComputeRowBytes() is called to compute the optimal value. This resets + any pixel/colortable ownership, just like reset(). + */ + void setConfig(Config, int width, int height, int rowBytes = 0); + /** Use this to assign a new pixel address for an existing bitmap. This + will automatically release any pixelref previously installed. Only call + this if you are handling ownership/lifetime of the pixel memory. + @param pixels Address for the pixels, managed by the caller. + @param ctable ColorTable (or null) that matches the specified pixels + */ + void setPixels(void* p, SkColorTable* ctable = NULL); + + /** Use the standard HeapAllocator to create the pixelref that manages the + pixel memory. It will be sized based on the current width/height/config. + If this is called multiple times, a new pixelref object will be created + each time. + + @param ctable ColorTable (or null) to use with the pixels that will + be allocated. Only used if config == Index8_Config + @return true if the allocation succeeds. If not the pixelref field of + the bitmap will be unchanged. + */ + bool allocPixels(SkColorTable* ctable = NULL) { + return this->allocPixels(NULL, ctable); + } + + /** Use the specified Allocator to create the pixelref that manages the + pixel memory. It will be sized based on the current width/height/config. + If this is called multiple times, a new pixelref object will be created + each time. + + @param allocator The Allocator to use to create a pixelref that can + manage the pixel memory for the current + width/height/config. If allocator is NULL, the standard + HeapAllocator will be used. + @param ctable ColorTable (or null) to use with the pixels that will + be allocated. Only used if config == Index8_Config. + If it is non-null and the config is not Index8, it will + be ignored. + @return true if the allocation succeeds. If not the pixelref field of + the bitmap will be unchanged. + */ + bool allocPixels(Allocator* allocator, SkColorTable* ctable); + + /** Return the current pixelref object, of any + */ + SkPixelRef* pixelRef() const { return fPixelRef; } + /** Return the offset into the pixelref, if any. Will return 0 if there is + no pixelref installed. + */ + size_t pixelRefOffset() const { return fPixelRefOffset; } + /** Assign a pixelref and optional offset. Pixelrefs are reference counted, + so the existing one (if any) will be unref'd and the new one will be + ref'd. + */ + SkPixelRef* setPixelRef(SkPixelRef* pr, size_t offset = 0); + + /** Call this to ensure that the bitmap points to the current pixel address + in the pixelref. Balance it with a call to unlockPixels(). These calls + are harmless if there is no pixelref. + */ + void lockPixels() const; + /** When you are finished access the pixel memory, call this to balance a + previous call to lockPixels(). This allows pixelrefs that implement + cached/deferred image decoding to know when there are active clients of + a given image. + */ + void unlockPixels() const; + + /** Return the bitmap's colortable (if any). Does not affect the colortable's + reference count. + */ + SkColorTable* getColorTable() const { return fColorTable; } + + /** Returns a non-zero, unique value corresponding to the pixels in our + pixelref, or 0 if we do not have a pixelref. Each time the pixels are + changed (and notifyPixelsChanged is called), a different generation ID + will be returned. + */ + uint32_t getGenerationID() const; + + /** Call this if you have changed the contents of the pixels. This will in- + turn cause a different generation ID value to be returned from + getGenerationID(). + */ + void notifyPixelsChanged() const; + + /** Initialize the bitmap's pixels with the specified color+alpha, automatically converting into the correct format + for the bitmap's config. If the config is kRGB_565_Config, then the alpha value is ignored. + If the config is kA8_Config, then the r,g,b parameters are ignored. + */ + void eraseARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) const; + /** Initialize the bitmap's pixels with the specified color+alpha, automatically converting into the correct format + for the bitmap's config. If the config is kRGB_565_Config, then the alpha value is presumed + to be 0xFF. If the config is kA8_Config, then the r,g,b parameters are ignored and the + pixels are all set to 0xFF. + */ + void eraseRGB(U8CPU r, U8CPU g, U8CPU b) const { + this->eraseARGB(0xFF, r, g, b); + } + /** Initialize the bitmap's pixels with the specified color, automatically converting into the correct format + for the bitmap's config. If the config is kRGB_565_Config, then the color's alpha value is presumed + to be 0xFF. If the config is kA8_Config, then only the color's alpha value is used. + */ + void eraseColor(SkColor c) const { + this->eraseARGB(SkColorGetA(c), SkColorGetR(c), SkColorGetG(c), + SkColorGetB(c)); + } + + /** Scroll (a subset of) the contents of this bitmap by dx/dy. If there are + no pixels allocated (i.e. getPixels() returns null) the method will + still update the inval region (if present). + + @param subset The subset of the bitmap to scroll/move. To scroll the + entire contents, specify [0, 0, width, height] or just + pass null. + @param dx The amount to scroll in X + @param dy The amount to scroll in Y + @param inval Optional (may be null). Returns the area of the bitmap that + was scrolled away. E.g. if dx = dy = 0, then inval would + be set to empty. If dx >= width or dy >= height, then + inval would be set to the entire bounds of the bitmap. + @return true if the scroll was doable. Will return false if the bitmap + uses an unsupported config for scrolling (only kA8, + kIndex8, kRGB_565, kARGB_4444, kARGB_8888 are supported). + If no pixels are present (i.e. getPixels() returns false) + inval will still be updated, and true will be returned. + */ + bool scrollRect(const SkIRect* subset, int dx, int dy, + SkRegion* inval = NULL) const; + + /** Returns the address of the specified pixel. This performs a runtime + check to know the size of the pixels, and will return the same answer + as the corresponding size-specific method (e.g. getAddr16). Since the + check happens at runtime, it is much slower than using a size-specific + version. Unlike the size-specific methods, this routine also checks if + getPixels() returns null, and returns that. The size-specific routines + perform a debugging assert that getPixels() is not null, but they do + not do any runtime checks. + */ + void* getAddr(int x, int y) const; + + /** Returns the address of the pixel specified by x,y for 32bit pixels. + */ + inline uint32_t* getAddr32(int x, int y) const; + /** Returns the address of the pixel specified by x,y for 16bit pixels. + */ + inline uint16_t* getAddr16(int x, int y) const; + /** Returns the address of the pixel specified by x,y for 8bit pixels. + */ + inline uint8_t* getAddr8(int x, int y) const; + /** Returns the address of the byte containing the pixel specified by x,y + for 1bit pixels. + */ + inline uint8_t* getAddr1(int x, int y) const; + + /** Returns the color corresponding to the pixel specified by x,y for + colortable based bitmaps. + */ + inline SkPMColor getIndex8Color(int x, int y) const; + + // OS-specific helpers +#ifndef SK_USE_WXWIDGETS +#ifdef SK_BUILD_FOR_WIN + /** On Windows and PocketPC builds, this will draw the SkBitmap onto the + specified HDC + */ + void drawToHDC(HDC, int left, int top) const; +#elif defined(SK_BUILD_FOR_MAC) + /** On Mac OS X and Carbon builds, this will draw the SkBitmap onto the + specified WindowRef + */ + void drawToPort(WindowRef, CGContextRef) const; +#endif +#endif + + /** Set dst to be a setset of this bitmap. If possible, it will share the + pixel memory, and just point into a subset of it. However, if the config + does not support this, a local copy will be made and associated with + the dst bitmap. If the subset rectangle, intersected with the bitmap's + dimensions is empty, or if there is an unsupported config, false will be + returned and dst will be untouched. + @param dst The bitmap that will be set to a subset of this bitmap + @param subset The rectangle of pixels in this bitmap that dst will + reference. + @return true if the subset copy was successfully made. + */ + bool extractSubset(SkBitmap* dst, const SkIRect& subset) const; + + /** Tries to make a new bitmap based on the dimensions of this bitmap, + setting the new bitmap's config to the one specified, and then copying + this bitmap's pixels into the new bitmap. If the conversion is not + supported, or the allocator fails, then this method returns false and + dst is left unchanged. + @param dst The bitmap to be sized and allocated + @param c The desired config for dst + @param allocator Allocator used to allocate the pixelref for the dst + bitmap. If this is null, the standard HeapAllocator + will be used. + @return true if the copy could be made. + */ + bool copyTo(SkBitmap* dst, Config c, Allocator* allocator = NULL) const; + + bool hasMipMap() const; + void buildMipMap(bool forceRebuild = false); + void freeMipMap(); + + /** Given scale factors sx, sy, determine the miplevel available in the + bitmap, and return it (this is the amount to shift matrix iterators + by). If dst is not null, it is set to the correct level. + */ + int extractMipLevel(SkBitmap* dst, SkFixed sx, SkFixed sy); + + void extractAlpha(SkBitmap* dst) const { + this->extractAlpha(dst, NULL, NULL); + } + + void extractAlpha(SkBitmap* dst, const SkPaint* paint, + SkIPoint* offset) const; + + void flatten(SkFlattenableWriteBuffer&) const; + void unflatten(SkFlattenableReadBuffer&); + + SkDEBUGCODE(void validate() const;) + + class Allocator : public SkRefCnt { + public: + /** Allocate the pixel memory for the bitmap, given its dimensions and + config. Return true on success, where success means either setPixels + or setPixelRef was called. The pixels need not be locked when this + returns. If the config requires a colortable, it also must be + installed via setColorTable. If false is returned, the bitmap and + colortable should be left unchanged. + */ + virtual bool allocPixelRef(SkBitmap*, SkColorTable*) = 0; + }; + + /** Subclass of Allocator that returns a pixelref that allocates its pixel + memory from the heap. This is the default Allocator invoked by + allocPixels(). + */ + class HeapAllocator : public Allocator { + public: + virtual bool allocPixelRef(SkBitmap*, SkColorTable*); + }; + + class RLEPixels { + public: + RLEPixels(int width, int height); + virtual ~RLEPixels(); + + uint8_t* packedAtY(int y) const { + SkASSERT((unsigned)y < (unsigned)fHeight); + return fYPtrs[y]; + } + + // called by subclasses during creation + void setPackedAtY(int y, uint8_t* addr) { + SkASSERT((unsigned)y < (unsigned)fHeight); + fYPtrs[y] = addr; + } + + private: + uint8_t** fYPtrs; + int fHeight; + }; + +private: +#ifdef SK_SUPPORT_MIPMAP + struct MipMap; + mutable MipMap* fMipMap; +#endif + + mutable SkPixelRef* fPixelRef; + mutable size_t fPixelRefOffset; + mutable int fPixelLockCount; + // either user-specified (in which case it is not treated as mutable) + // or a cache of the returned value from fPixelRef->lockPixels() + mutable void* fPixels; + mutable SkColorTable* fColorTable; // only meaningful for kIndex8 + + enum Flags { + kImageIsOpaque_Flag = 0x01 + }; + + uint32_t fRowBytes; + uint16_t fWidth, fHeight; + uint8_t fConfig; + uint8_t fFlags; + uint8_t fBytesPerPixel; // based on config + + /* Unreference any pixelrefs or colortables + */ + void freePixels(); + void updatePixelsFromRef() const; + + static SkFixed ComputeMipLevel(SkFixed sx, SkFixed dy); +}; + +/** \class SkColorTable + + SkColorTable holds an array SkPMColors (premultiplied 32-bit colors) used by + 8-bit bitmaps, where the bitmap bytes are interpreted as indices into the colortable. +*/ +class SkColorTable : public SkRefCnt { +public: + /** Constructs an empty color table (zero colors). + */ + explicit SkColorTable(int count); + explicit SkColorTable(SkFlattenableReadBuffer&); + SkColorTable(const SkPMColor colors[], int count); + virtual ~SkColorTable(); + + enum Flags { + kColorsAreOpaque_Flag = 0x01 //!< if set, all of the colors in the table are opaque (alpha==0xFF) + }; + /** Returns the flag bits for the color table. These can be changed with setFlags(). + */ + unsigned getFlags() const { return fFlags; } + /** Set the flags for the color table. See the Flags enum for possible values. + */ + void setFlags(unsigned flags); + + /** Returns the number of colors in the table. + */ + int count() const { return fCount; } + + /** Returns the specified color from the table. In the debug build, this asserts that + the index is in range (0 <= index < count). + */ + SkPMColor operator[](int index) const { + SkASSERT(fColors != NULL && (unsigned)index < fCount); + return fColors[index]; + } + + /** Specify the number of colors in the color table. This does not initialize the colors + to any value, just allocates memory for them. To initialize the values, either call + setColors(array, count), or follow setCount(count) with a call to + lockColors()/{set the values}/unlockColors(true). + */ +// void setColors(int count) { this->setColors(NULL, count); } +// void setColors(const SkPMColor[], int count); + + /** Return the array of colors for reading and/or writing. This must be + balanced by a call to unlockColors(changed?), telling the colortable if + the colors were changed during the lock. + */ + SkPMColor* lockColors() { + SkDEBUGCODE(fColorLockCount += 1;) + return fColors; + } + /** Balancing call to lockColors(). If the colors have been changed, pass true. + */ + void unlockColors(bool changed); + + /** Similar to lockColors(), lock16BitCache() returns the array of + RGB16 colors that mirror the 32bit colors. However, this function + will return null if kColorsAreOpaque_Flag is not set. + Also, unlike lockColors(), the returned array here cannot be modified. + */ + const uint16_t* lock16BitCache(); + /** Balancing call to lock16BitCache(). + */ + void unlock16BitCache() { + SkASSERT(f16BitCacheLockCount > 0); + SkDEBUGCODE(f16BitCacheLockCount -= 1); + } + + void flatten(SkFlattenableWriteBuffer&) const; + +private: + SkPMColor* fColors; + uint16_t* f16BitCache; + uint16_t fCount; + uint8_t fFlags; + SkDEBUGCODE(int fColorLockCount;) + SkDEBUGCODE(int f16BitCacheLockCount;) + + void inval16BitCache(); +}; + +class SkAutoLockPixels { +public: + SkAutoLockPixels(const SkBitmap& bitmap) : fBitmap(bitmap) { + bitmap.lockPixels(); + } + ~SkAutoLockPixels() { + fBitmap.unlockPixels(); + } + +private: + const SkBitmap& fBitmap; +}; + +/////////////////////////////////////////////////////////////////////////////// + +inline uint32_t* SkBitmap::getAddr32(int x, int y) const { + SkASSERT(fPixels); + SkASSERT(fConfig == kARGB_8888_Config); + SkASSERT((unsigned)x < fWidth && (unsigned)y < fHeight); + return (uint32_t*)((char*)fPixels + y * fRowBytes + (x << 2)); +} + +inline uint16_t* SkBitmap::getAddr16(int x, int y) const { + SkASSERT(fPixels); + SkASSERT(fConfig == kRGB_565_Config || fConfig == kARGB_4444_Config); + SkASSERT((unsigned)x < fWidth && (unsigned)y < fHeight); + return (uint16_t*)((char*)fPixels + y * fRowBytes + (x << 1)); +} + +inline uint8_t* SkBitmap::getAddr8(int x, int y) const { + SkASSERT(fPixels); + SkASSERT(fConfig == kA8_Config || fConfig == kIndex8_Config); + SkASSERT((unsigned)x < fWidth && (unsigned)y < fHeight); + return (uint8_t*)fPixels + y * fRowBytes + x; +} + +inline SkPMColor SkBitmap::getIndex8Color(int x, int y) const { + SkASSERT(fPixels); + SkASSERT(fConfig == kIndex8_Config); + SkASSERT((unsigned)x < fWidth && (unsigned)y < fHeight); + SkASSERT(fColorTable); + return (*fColorTable)[*((const uint8_t*)fPixels + y * fRowBytes + x)]; +} + +// returns the address of the byte that contains the x coordinate +inline uint8_t* SkBitmap::getAddr1(int x, int y) const { + SkASSERT(fPixels); + SkASSERT(fConfig == kA1_Config); + SkASSERT((unsigned)x < fWidth && (unsigned)y < fHeight); + return (uint8_t*)fPixels + y * fRowBytes + (x >> 3); +} + +#endif + diff --git a/skia/include/SkBlurDrawLooper.h b/skia/include/SkBlurDrawLooper.h new file mode 100644 index 0000000..b1c3d20 --- /dev/null +++ b/skia/include/SkBlurDrawLooper.h @@ -0,0 +1,52 @@ +#ifndef SkBlurDrawLooper_DEFINED +#define SkBlurDrawLooper_DEFINED + +#include "SkDrawLooper.h" +#include "SkColor.h" + +class SkMaskFilter; + +/** \class SkBlurDrawLooper + This class draws a shadow of the object (possibly offset), and then draws + the original object in its original position. + <reed> should there be an option to just draw the shadow/blur layer? webkit? +*/ +class SkBlurDrawLooper : public SkDrawLooper { +public: + SkBlurDrawLooper(SkScalar radius, SkScalar dx, SkScalar dy, SkColor color); + virtual ~SkBlurDrawLooper(); + + // overrides from SkDrawLooper + virtual void init(SkCanvas*, SkPaint*); + virtual bool next(); + virtual void restore(); + +protected: + SkBlurDrawLooper(SkFlattenableReadBuffer&); + // overrides from SkFlattenable + virtual void flatten(SkFlattenableWriteBuffer& ); + virtual Factory getFactory() { return CreateProc; } + +private: + static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) { + return SkNEW_ARGS(SkBlurDrawLooper, (buffer)); } + + SkCanvas* fCanvas; + SkPaint* fPaint; + SkMaskFilter* fBlur; + SkScalar fDx, fDy; + SkColor fBlurColor; + SkColor fSavedColor; // remember the original + int fSaveCount; + + enum State { + kBeforeEdge, + kAfterEdge, + kDone + }; + State fState; + + typedef SkDrawLooper INHERITED; +}; + +#endif diff --git a/skia/include/SkBlurMaskFilter.h b/skia/include/SkBlurMaskFilter.h new file mode 100644 index 0000000..9126b6b --- /dev/null +++ b/skia/include/SkBlurMaskFilter.h @@ -0,0 +1,59 @@ +/* include/graphics/SkBlurMaskFilter.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkBlurMaskFilter_DEFINED +#define SkBlurMaskFilter_DEFINED + +// we include this since our callers will need to at least be able to ref/unref +#include "SkMaskFilter.h" +#include "SkScalar.h" + +class SkBlurMaskFilter { +public: + enum BlurStyle { + kNormal_BlurStyle, //!< fuzzy inside and outside + kSolid_BlurStyle, //!< solid inside, fuzzy outside + kOuter_BlurStyle, //!< nothing inside, fuzzy outside + kInner_BlurStyle, //!< fuzzy inside, nothing outside + + kBlurStyleCount + }; + + /** Create a blur maskfilter. + @param radius The radius to extend the blur from the original mask. Must be > 0. + @param style The BlurStyle to use + @return The new blur maskfilter + */ + static SkMaskFilter* Create(SkScalar radius, BlurStyle style); + + /** Create an emboss maskfilter + @param direction array of 3 scalars [x, y, z] specifying the direction of the light source + @param ambient 0...1 amount of ambient light + @param specular coefficient for specular highlights (e.g. 8) + @param blurRadius amount to blur before applying lighting (e.g. 3) + @return the emboss maskfilter + */ + static SkMaskFilter* CreateEmboss( const SkScalar direction[3], + SkScalar ambient, SkScalar specular, + SkScalar blurRadius); + +private: + SkBlurMaskFilter(); // can't be instantiated +}; + +#endif + diff --git a/skia/include/SkBorderView.h b/skia/include/SkBorderView.h new file mode 100644 index 0000000..400b1a7 --- /dev/null +++ b/skia/include/SkBorderView.h @@ -0,0 +1,49 @@ +/* include/graphics/SkBorderView.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkBorderView_DEFINED +#define SkBorderView_DEFINED + +#include "SkView.h" +#include "SkWidgetViews.h" +#include "SkAnimator.h" + +class SkBorderView : public SkWidgetView { +public: + SkBorderView(); + ~SkBorderView(); + void setSkin(const char skin[]); + SkScalar getLeft() const { return fLeft; } + SkScalar getRight() const { return fRight; } + SkScalar getTop() const { return fTop; } + SkScalar getBottom() const { return fBottom; } +protected: + //overrides + virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node); + virtual void onSizeChange(); + virtual void onDraw(SkCanvas* canvas); + virtual bool onEvent(const SkEvent& evt); +private: + SkAnimator fAnim; + SkScalar fLeft, fRight, fTop, fBottom; //margin on each side + SkRect fMargin; + + typedef SkWidgetView INHERITED; +}; + +#endif + diff --git a/skia/include/SkBounder.h b/skia/include/SkBounder.h new file mode 100644 index 0000000..4ab44cb --- /dev/null +++ b/skia/include/SkBounder.h @@ -0,0 +1,73 @@ +/* include/graphics/SkBounder.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkBounder_DEFINED +#define SkBounder_DEFINED + +#include "SkTypes.h" +#include "SkRefCnt.h" + +struct SkIRect; +struct SkPoint; +struct SkRect; +class SkPaint; +class SkPath; +class SkRegion; + +/** \class SkBounder + + Base class for intercepting the device bounds of shapes before they are drawn. + Install a subclass of this in your canvas. +*/ +class SkBounder : public SkRefCnt { +public: + /* Call to perform a clip test before calling onIRect. + Returns the result from onIRect. + */ + bool doIRect(const SkIRect&); + +protected: + /** Override in your subclass. This is called with the device bounds of an + object (text, geometry, image) just before it is drawn. If your method + returns false, the drawing for that shape is aborted. If your method + returns true, drawing continues. The bounds your method receives have already + been transformed in to device coordinates, and clipped to the current clip. + */ + virtual bool onIRect(const SkIRect&) = 0; + + /** Called after each shape has been drawn. The default implementation does + nothing, but your override could use this notification to signal itself + that the offscreen being rendered into needs to be updated to the screen. + */ + virtual void commit(); + +private: + bool doHairline(const SkPoint&, const SkPoint&, const SkPaint&); + bool doRect(const SkRect&, const SkPaint&); + bool doPath(const SkPath&, const SkPaint&, bool doFill); + void setClip(const SkRegion* clip) { fClip = clip; } + + const SkRegion* fClip; + friend class SkAutoBounderCommit; + friend class SkDraw; + friend class SkDrawIter; + friend struct Draw1Glyph; + friend class SkMaskFilter; +}; + +#endif + diff --git a/skia/include/SkCamera.h b/skia/include/SkCamera.h new file mode 100644 index 0000000..3536b06 --- /dev/null +++ b/skia/include/SkCamera.h @@ -0,0 +1,177 @@ +/* include/graphics/SkCamera.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + + + +// Inspired by Rob Johnson's most excellent QuickDraw GX sample code + +#ifndef SkCamera_DEFINED +#define SkCamera_DEFINED + +#include "Sk64.h" +#include "SkMatrix.h" + +class SkCanvas; + +#ifdef SK_SCALAR_IS_FIXED + typedef SkFract SkUnitScalar; + #define SK_UnitScalar1 SK_Fract1 + #define SkUnitScalarMul(a, b) SkFractMul(a, b) + #define SkUnitScalarDiv(a, b) SkFractDiv(a, b) +#else + typedef float SkUnitScalar; + #define SK_UnitScalar1 SK_Scalar1 + #define SkUnitScalarMul(a, b) SkScalarMul(a, b) + #define SkUnitScalarDiv(a, b) SkScalarDiv(a, b) +#endif + +struct SkUnit3D { + SkUnitScalar fX, fY, fZ; + + void set(SkUnitScalar x, SkUnitScalar y, SkUnitScalar z) + { + fX = x; fY = y; fZ = z; + } + static SkUnitScalar Dot(const SkUnit3D&, const SkUnit3D&); + static void Cross(const SkUnit3D&, const SkUnit3D&, SkUnit3D* cross); +}; + +struct SkPoint3D { + SkScalar fX, fY, fZ; + + void set(SkScalar x, SkScalar y, SkScalar z) + { + fX = x; fY = y; fZ = z; + } + SkScalar normalize(SkUnit3D*) const; +}; +typedef SkPoint3D SkVector3D; + +struct SkMatrix3D { + SkScalar fMat[3][4]; + + void reset(); + + void setRow(int row, SkScalar a, SkScalar b, SkScalar c, SkScalar d = 0) + { + SkASSERT((unsigned)row < 3); + fMat[row][0] = a; + fMat[row][1] = b; + fMat[row][2] = c; + fMat[row][3] = d; + } + + void setRotateX(SkScalar deg); + void setRotateY(SkScalar deg); + void setRotateZ(SkScalar deg); + void setTranslate(SkScalar x, SkScalar y, SkScalar z); + + void preRotateX(SkScalar deg); + void preRotateY(SkScalar deg); + void preRotateZ(SkScalar deg); + void preTranslate(SkScalar x, SkScalar y, SkScalar z); + + void setConcat(const SkMatrix3D& a, const SkMatrix3D& b); + void mapPoint(const SkPoint3D& src, SkPoint3D* dst) const; + void mapVector(const SkVector3D& src, SkVector3D* dst) const; + + void mapPoint(SkPoint3D* v) const + { + this->mapPoint(*v, v); + } + void mapVector(SkVector3D* v) const + { + this->mapVector(*v, v); + } +}; + +class SkPatch3D { +public: + SkPatch3D(); + + void reset(); + void transform(const SkMatrix3D&, SkPatch3D* dst = NULL) const; + + // dot a unit vector with the patch's normal + SkScalar dotWith(SkScalar dx, SkScalar dy, SkScalar dz) const; + SkScalar dotWith(const SkVector3D& v) const + { + return this->dotWith(v.fX, v.fY, v.fZ); + } + + // depreicated, but still here for animator (for now) + void rotate(SkScalar x, SkScalar y, SkScalar z) {} + void rotateDegrees(SkScalar x, SkScalar y, SkScalar z) {} + +private: +public: // make public for SkDraw3D for now + SkVector3D fU, fV; + SkPoint3D fOrigin; + + friend class SkCamera3D; +}; + +class SkCamera3D { +public: + SkCamera3D(); + + void reset(); + void update(); + void patchToMatrix(const SkPatch3D&, SkMatrix* matrix) const; + + SkPoint3D fLocation; + SkPoint3D fAxis; + SkPoint3D fZenith; + SkPoint3D fObserver; + +private: + mutable SkMatrix fOrientation; + mutable bool fNeedToUpdate; + + void doUpdate() const; +}; + +class Sk3DView : SkNoncopyable { +public: + Sk3DView(); + ~Sk3DView(); + + void save(); + void restore(); + + void translate(SkScalar x, SkScalar y, SkScalar z); + void rotateX(SkScalar deg); + void rotateY(SkScalar deg); + void rotateZ(SkScalar deg); + + void getMatrix(SkMatrix*) const; + void applyToCanvas(SkCanvas*) const; + + SkScalar dotWithNormal(SkScalar dx, SkScalar dy, SkScalar dz) const; + +private: + struct Rec { + Rec* fNext; + SkMatrix3D fMatrix; + }; + Rec* fRec; + Rec fInitialRec; + SkCamera3D fCamera; +}; + +#endif + diff --git a/skia/include/SkCanvas.h b/skia/include/SkCanvas.h new file mode 100644 index 0000000..d306073 --- /dev/null +++ b/skia/include/SkCanvas.h @@ -0,0 +1,764 @@ +/* + * Copyright (C) 2006-2008 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SkCanvas_DEFINED +#define SkCanvas_DEFINED + +#include "SkTypes.h" +#include "SkBitmap.h" +#include "SkDeque.h" +#include "SkPaint.h" +#include "SkRefCnt.h" +#include "SkPorterDuff.h" +#include "SkPath.h" +#include "SkRegion.h" + +class SkBounder; +class SkDevice; +class SkDraw; +class SkDrawFilter; +class SkPicture; + +/** \class SkCanvas + + A Canvas encapsulates all of the state about drawing into a device (bitmap). + This includes a reference to the device itself, and a stack of matrix/clip + values. For any given draw call (e.g. drawRect), the geometry of the object + being drawn is transformed by the concatenation of all the matrices in the + stack. The transformed geometry is clipped by the intersection of all of + the clips in the stack. + + While the Canvas holds the state of the drawing device, the state (style) + of the object being drawn is held by the Paint, which is provided as a + parameter to each of the draw() methods. The Paint holds attributes such as + color, typeface, textSize, strokeWidth, shader (e.g. gradients, patterns), + etc. +*/ +class SkCanvas : public SkRefCnt { +public: + /** Construct a canvas with the specified bitmap to draw into. + @param bitmap Specifies a bitmap for the canvas to draw into. Its + structure are copied to the canvas. + */ + explicit SkCanvas(const SkBitmap& bitmap); + /** Construct a canvas with the specified device to draw into. + @param device Specifies a device for the canvas to draw into. The + device may be null. + */ + explicit SkCanvas(SkDevice* device = NULL); + virtual ~SkCanvas(); + + /////////////////////////////////////////////////////////////////////////// + + virtual bool getViewport(SkIPoint*) const; + virtual bool setViewport(int x, int y); + + /** Return the canvas' device object, which may be null. The device holds + the bitmap of the pixels that the canvas draws into. The reference count + of the returned device is not changed by this call. + */ + SkDevice* getDevice() const; + + /** Specify a device for this canvas to draw into. If it is not null, its + reference count is incremented. If the canvas was already holding a + device, its reference count is decremented. The new device is returned. + */ + SkDevice* setDevice(SkDevice* device); + + /** Specify a bitmap for the canvas to draw into. This is a help method for + setDevice(), and it creates a device for the bitmap by calling + createDevice(). The structure of the bitmap is copied into the device. + */ + virtual SkDevice* setBitmapDevice(const SkBitmap& bitmap); + + /////////////////////////////////////////////////////////////////////////// + + enum SaveFlags { + /** save the matrix state, restoring it on restore() */ + kMatrix_SaveFlag = 0x01, + /** save the clip state, restoring it on restore() */ + kClip_SaveFlag = 0x02, + /** the layer needs to support per-pixel alpha */ + kHasAlphaLayer_SaveFlag = 0x04, + /** the layer needs to support 8-bits per color component */ + kFullColorLayer_SaveFlag = 0x08, + /** the layer should clip against the bounds argument */ + kClipToLayer_SaveFlag = 0x10, + + // helper masks for common choices + kMatrixClip_SaveFlag = 0x03, + kARGB_NoClipLayer_SaveFlag = 0x0F, + kARGB_ClipLayer_SaveFlag = 0x1F + }; + + /** This call saves the current matrix and clip information, and pushes a + copy onto a private stack. Subsequent calls to translate, scale, + rotate, skew, concat or clipRect, clipPath all operate on this copy. + When the balancing call to restore() is made, this copy is deleted and + the previous matrix/clip state is restored. + @return The value to pass to restoreToCount() to balance this save() + */ + virtual int save(SaveFlags flags = kMatrixClip_SaveFlag); + + /** This behaves the same as save(), but in addition it allocates an + offscreen bitmap. All drawing calls are directed there, and only when + the balancing call to restore() is made is that offscreen transfered to + the canvas (or the previous layer). Subsequent calls to translate, + scale, rotate, skew, concat or clipRect, clipPath all operate on this + copy. When the balancing call to restore() is made, this copy is deleted + and the previous matrix/clip state is restored. + @param bounds (may be null) the maximum size the offscreen bitmap needs + to be (in local coordinates) + @param paint (may be null) This is copied, and is applied to the + offscreen when restore() is called + @param flags LayerFlags + @return The value to pass to restoreToCount() to balance this save() + */ + virtual int saveLayer(const SkRect* bounds, const SkPaint* paint, + SaveFlags flags = kARGB_ClipLayer_SaveFlag); + + /** This behaves the same as save(), but in addition it allocates an + offscreen bitmap. All drawing calls are directed there, and only when + the balancing call to restore() is made is that offscreen transfered to + the canvas (or the previous layer). Subsequent calls to translate, + scale, rotate, skew, concat or clipRect, clipPath all operate on this + copy. When the balancing call to restore() is made, this copy is deleted + and the previous matrix/clip state is restored. + @param bounds (may be null) the maximum size the offscreen bitmap needs + to be (in local coordinates) + @param alpha This is applied to the offscreen when restore() is called. + @param flags LayerFlags + @return The value to pass to restoreToCount() to balance this save() + */ + int saveLayerAlpha(const SkRect* bounds, U8CPU alpha, + SaveFlags flags = kARGB_ClipLayer_SaveFlag); + + /** This call balances a previous call to save(), and is used to remove all + modifications to the matrix/clip state since the last save call. It is + an error to call restore() more times than save() was called. + */ + virtual void restore(); + + /** Returns the number of matrix/clip states on the SkCanvas' private stack. + This will equal # save() calls - # restore() calls. + */ + int getSaveCount() const; + + /** Efficient way to pop any calls to save() that happened after the save + count reached saveCount. It is an error for saveCount to be less than + getSaveCount() + @param saveCount The number of save() levels to restore from + */ + void restoreToCount(int saveCount); + + /** Preconcat the current matrix with the specified translation + @param dx The distance to translate in X + @param dy The distance to translate in Y + returns true if the operation succeeded (e.g. did not overflow) + */ + virtual bool translate(SkScalar dx, SkScalar dy); + + /** Preconcat the current matrix with the specified scale. + @param sx The amount to scale in X + @param sy The amount to scale in Y + returns true if the operation succeeded (e.g. did not overflow) + */ + virtual bool scale(SkScalar sx, SkScalar sy); + + /** Preconcat the current matrix with the specified rotation. + @param degrees The amount to rotate, in degrees + returns true if the operation succeeded (e.g. did not overflow) + */ + virtual bool rotate(SkScalar degrees); + + /** Preconcat the current matrix with the specified skew. + @param sx The amount to skew in X + @param sy The amount to skew in Y + returns true if the operation succeeded (e.g. did not overflow) + */ + virtual bool skew(SkScalar sx, SkScalar sy); + + /** Preconcat the current matrix with the specified matrix. + @param matrix The matrix to preconcatenate with the current matrix + @return true if the operation succeeded (e.g. did not overflow) + */ + virtual bool concat(const SkMatrix& matrix); + + /** Replace the current matrix with a copy of the specified matrix. + @param matrix The matrix that will be copied into the current matrix. + */ + virtual void setMatrix(const SkMatrix& matrix); + + /** Helper for setMatrix(identity). Sets the current matrix to identity. + */ + void resetMatrix(); + + /** Modify the current clip with the specified rectangle. + @param rect The rect to intersect with the current clip + @param op The region op to apply to the current clip + @return true if the canvas' clip is non-empty + */ + virtual bool clipRect(const SkRect& rect, + SkRegion::Op op = SkRegion::kIntersect_Op); + + /** Modify the current clip with the specified path. + @param path The path to apply to the current clip + @param op The region op to apply to the current clip + @return true if the canvas' new clip is non-empty + */ + virtual bool clipPath(const SkPath& path, + SkRegion::Op op = SkRegion::kIntersect_Op); + + /** Modify the current clip with the specified region. Note that unlike + clipRect() and clipPath() which transform their arguments by the current + matrix, clipRegion() assumes its argument is already in device + coordinates, and so no transformation is performed. + @param deviceRgn The region to apply to the current clip + @param op The region op to apply to the current clip + @return true if the canvas' new clip is non-empty + */ + virtual bool clipRegion(const SkRegion& deviceRgn, + SkRegion::Op op = SkRegion::kIntersect_Op); + + /** Helper for clipRegion(rgn, kReplace_Op). Sets the current clip to the + specified region. This does not intersect or in any other way account + for the existing clip region. + @param deviceRgn The region to copy into the current clip. + @return true if the new clip region is non-empty + */ + bool setClipRegion(const SkRegion& deviceRgn) { + return this->clipRegion(deviceRgn, SkRegion::kReplace_Op); + } + + /** Enum describing how to treat edges when performing quick-reject tests + of a geometry against the current clip. Treating them as antialiased + (kAA_EdgeType) will take into account the extra pixels that may be drawn + if the edge does not lie exactly on a device pixel boundary (after being + transformed by the current matrix). + */ + enum EdgeType { + /** Treat the edges as B&W (not antialiased) for the purposes of testing + against the current clip + */ + kBW_EdgeType, + /** Treat the edges as antialiased for the purposes of testing + against the current clip + */ + kAA_EdgeType + }; + + /** Return true if the specified rectangle, after being transformed by the + current matrix, would lie completely outside of the current clip. Call + this to check if an area you intend to draw into is clipped out (and + therefore you can skip making the draw calls). + @param rect the rect to compare with the current clip + @param et specifies how to treat the edges (see EdgeType) + @return true if the rect (transformed by the canvas' matrix) does not + intersect with the canvas' clip + */ + bool quickReject(const SkRect& rect, EdgeType et) const; + + /** Return true if the specified path, after being transformed by the + current matrix, would lie completely outside of the current clip. Call + this to check if an area you intend to draw into is clipped out (and + therefore you can skip making the draw calls). Note, for speed it may + return false even if the path itself might not intersect the clip + (i.e. the bounds of the path intersects, but the path does not). + @param path The path to compare with the current clip + @param et specifies how to treat the edges (see EdgeType) + @return true if the path (transformed by the canvas' matrix) does not + intersect with the canvas' clip + */ + bool quickReject(const SkPath& path, EdgeType et) const; + + /** Return true if the horizontal band specified by top and bottom is + completely clipped out. This is a conservative calculation, meaning + that it is possible that if the method returns false, the band may still + in fact be clipped out, but the converse is not true. If this method + returns true, then the band is guaranteed to be clipped out. + @param top The top of the horizontal band to compare with the clip + @param bottom The bottom of the horizontal and to compare with the clip + @return true if the horizontal band is completely clipped out (i.e. does + not intersect the current clip) + */ + bool quickRejectY(SkScalar top, SkScalar bottom, EdgeType et) const; + + /** Return the bounds of the current clip (in local coordinates) in the + bounds parameter, and return true if it is non-empty. This can be useful + in a way similar to quickReject, in that it tells you that drawing + outside of these bounds will be clipped out. + */ + bool getClipBounds(SkRect* bounds) const; + + /** Fill the entire canvas' bitmap (restricted to the current clip) with the + specified ARGB color, using the specified PorterDuff mode. + @param a the alpha component (0..255) of the color to fill the canvas + @param r the red component (0..255) of the color to fill the canvas + @param g the green component (0..255) of the color to fill the canvas + @param b the blue component (0..255) of the color to fill the canvas + @param mode the mode to apply the color in (defaults to SrcOver) + */ + void drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b, + SkPorterDuff::Mode mode = SkPorterDuff::kSrcOver_Mode); + + /** Fill the entire canvas' bitmap (restricted to the current clip) with the + specified color and porter-duff xfermode. + @param color the color to draw with + @param mode the mode to apply the color in (defaults to SrcOver) + */ + void drawColor(SkColor color, + SkPorterDuff::Mode mode = SkPorterDuff::kSrcOver_Mode); + + /** Fill the entire canvas' bitmap (restricted to the current clip) with the + specified paint. + @param paint The paint used to fill the canvas + */ + virtual void drawPaint(const SkPaint& paint); + + enum PointMode { + /** drawPoints draws each point separately */ + kPoints_PointMode, + /** drawPoints draws each pair of points as a line segment */ + kLines_PointMode, + /** drawPoints draws the array of points as a polygon */ + kPolygon_PointMode + }; + + /** Draw a series of points, interpreted based on the PointMode mode. For + all modes, the count parameter is interpreted as the total number of + points. For kLine mode, count/2 line segments are drawn. + For kPoint mode, each point is drawn centered at its coordinate, and its + size is specified by the paint's stroke-width. It draws as a square, + unless the paint's cap-type is round, in which the points are drawn as + circles. + For kLine mode, each pair of points is drawn as a line segment, + respecting the paint's settings for cap/join/width. + For kPolygon mode, the entire array is drawn as a series of connected + line segments. + Note that, while similar, kLine and kPolygon modes draw slightly + differently than the equivalent path built with a series of moveto, + lineto calls, in that the path will draw all of its contours at once, + with no interactions if contours intersect each other (think XOR + xfermode). drawPoints always draws each element one at a time. + @param mode PointMode specifying how to draw the array of points. + @param count The number of points in the array + @param pts Array of points to draw + @param paint The paint used to draw the points + */ + virtual void drawPoints(PointMode mode, size_t count, const SkPoint pts[], + const SkPaint& paint); + + /** Helper method for drawing a single point. See drawPoints() for a more + details. + */ + void drawPoint(SkScalar x, SkScalar y, const SkPaint& paint); + + /** Draws a single pixel in the specified color. + @param x The X coordinate of which pixel to draw + @param y The Y coordiante of which pixel to draw + @param color The color to draw + */ + void drawPoint(SkScalar x, SkScalar y, SkColor color); + + /** Draw a line segment with the specified start and stop x,y coordinates, + using the specified paint. NOTE: since a line is always "framed", the + paint's Style is ignored. + @param x0 The x-coordinate of the start point of the line + @param y0 The y-coordinate of the start point of the line + @param x1 The x-coordinate of the end point of the line + @param y1 The y-coordinate of the end point of the line + @param paint The paint used to draw the line + */ + void drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1, + const SkPaint& paint); + + /** Draw the specified rectangle using the specified paint. The rectangle + will be filled or stroked based on the Style in the paint. + @param rect The rect to be drawn + @param paint The paint used to draw the rect + */ + virtual void drawRect(const SkRect& rect, const SkPaint& paint); + + /** Draw the specified rectangle using the specified paint. The rectangle + will be filled or framed based on the Style in the paint. + @param rect The rect to be drawn + @param paint The paint used to draw the rect + */ + void drawIRect(const SkIRect& rect, const SkPaint& paint) + { + SkRect r; + r.set(rect); // promotes the ints to scalars + this->drawRect(r, paint); + } + + /** Draw the specified rectangle using the specified paint. The rectangle + will be filled or framed based on the Style in the paint. + @param left The left side of the rectangle to be drawn + @param top The top side of the rectangle to be drawn + @param right The right side of the rectangle to be drawn + @param bottom The bottom side of the rectangle to be drawn + @param paint The paint used to draw the rect + */ + void drawRectCoords(SkScalar left, SkScalar top, SkScalar right, + SkScalar bottom, const SkPaint& paint); + + /** Draw the specified oval using the specified paint. The oval will be + filled or framed based on the Style in the paint. + @param oval The rectangle bounds of the oval to be drawn + @param paint The paint used to draw the oval + */ + void drawOval(const SkRect& oval, const SkPaint&); + + /** Draw the specified circle using the specified paint. If radius is <= 0, + then nothing will be drawn. The circle will be filled + or framed based on the Style in the paint. + @param cx The x-coordinate of the center of the cirle to be drawn + @param cy The y-coordinate of the center of the cirle to be drawn + @param radius The radius of the cirle to be drawn + @param paint The paint used to draw the circle + */ + void drawCircle(SkScalar cx, SkScalar cy, SkScalar radius, + const SkPaint& paint); + + /** Draw the specified arc, which will be scaled to fit inside the + specified oval. + @param oval The bounds of oval used to define the shape of the arc + @param startAngle Starting angle (in degrees) where the arc begins + @param sweepAngle Sweep angle (in degrees) measured clockwise + @param useCenter true means include the center of the oval. For filling + this will draw a wedge. False means just use the arc. + @param paint The paint used to draw the arc + */ + void drawArc(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle, + bool useCenter, const SkPaint& paint); + + /** Draw the specified round-rect using the specified paint. The round-rect + will be filled or framed based on the Style in the paint. + @param rect The rectangular bounds of the roundRect to be drawn + @param rx The x-radius of the oval used to round the corners + @param ry The y-radius of the oval used to round the corners + @param paint The paint used to draw the roundRect + */ + void drawRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry, + const SkPaint& paint); + + /** Draw the specified path using the specified paint. The path will be + filled or framed based on the Style in the paint. + @param path The path to be drawn + @param paint The paint used to draw the path + */ + virtual void drawPath(const SkPath& path, const SkPaint& paint); + + /** Draw the specified bitmap, with its top/left corner at (x,y), using the + specified paint, transformed by the current matrix. Note: if the paint + contains a maskfilter that generates a mask which extends beyond the + bitmap's original width/height, then the bitmap will be drawn as if it + were in a Shader with CLAMP mode. Thus the color outside of the original + width/height will be the edge color replicated. + @param bitmap The bitmap to be drawn + @param left The position of the left side of the bitmap being drawn + @param top The position of the top side of the bitmap being drawn + @param paint The paint used to draw the bitmap, or NULL + */ + virtual void drawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top, + const SkPaint* paint = NULL); + + /** Draw the specified bitmap, with the specified matrix applied (before the + canvas' matrix is applied). + @param bitmap The bitmap to be drawn + @param src Optional: specify the subset of the bitmap to be drawn + @param dst The destination rectangle where the scaled/translated + image will be drawn + @param paint The paint used to draw the bitmap, or NULL + */ + virtual void drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src, + const SkRect& dst, const SkPaint* paint = NULL); + + virtual void drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& m, + const SkPaint* paint = NULL); + + /** Draw the specified bitmap, with its top/left corner at (x,y), + NOT transformed by the current matrix. Note: if the paint + contains a maskfilter that generates a mask which extends beyond the + bitmap's original width/height, then the bitmap will be drawn as if it + were in a Shader with CLAMP mode. Thus the color outside of the original + width/height will be the edge color replicated. + @param bitmap The bitmap to be drawn + @param left The position of the left side of the bitmap being drawn + @param top The position of the top side of the bitmap being drawn + @param paint The paint used to draw the bitmap, or NULL + */ + virtual void drawSprite(const SkBitmap& bitmap, int left, int top, + const SkPaint* paint = NULL); + + /** Draw the text, with origin at (x,y), using the specified paint. + The origin is interpreted based on the Align setting in the paint. + @param text The text to be drawn + @param byteLength The number of bytes to read from the text parameter + @param x The x-coordinate of the origin of the text being drawn + @param y The y-coordinate of the origin of the text being drawn + @param paint The paint used for the text (e.g. color, size, style) + */ + virtual void drawText(const void* text, size_t byteLength, SkScalar x, + SkScalar y, const SkPaint& paint); + + /** Draw the text, with each character/glyph origin specified by the pos[] + array. The origin is interpreted by the Align setting in the paint. + @param text The text to be drawn + @param byteLength The number of bytes to read from the text parameter + @param pos Array of positions, used to position each character + @param paint The paint used for the text (e.g. color, size, style) + */ + virtual void drawPosText(const void* text, size_t byteLength, + const SkPoint pos[], const SkPaint& paint); + + /** Draw the text, with each character/glyph origin specified by the x + coordinate taken from the xpos[] array, and the y from the constY param. + The origin is interpreted by the Align setting in the paint. + @param text The text to be drawn + @param byteLength The number of bytes to read from the text parameter + @param xpos Array of x-positions, used to position each character + @param constY The shared Y coordinate for all of the positions + @param paint The paint used for the text (e.g. color, size, style) + */ + virtual void drawPosTextH(const void* text, size_t byteLength, + const SkScalar xpos[], SkScalar constY, + const SkPaint& paint); + + /** Draw the text, with origin at (x,y), using the specified paint, along + the specified path. The paint's Align setting determins where along the + path to start the text. + @param text The text to be drawn + @param byteLength The number of bytes to read from the text parameter + @param path The path the text should follow for its baseline + @param hOffset The distance along the path to add to the text's + starting position + @param vOffset The distance above(-) or below(+) the path to + position the text + @param paint The paint used for the text + */ + void drawTextOnPathHV(const void* text, size_t byteLength, + const SkPath& path, SkScalar hOffset, + SkScalar vOffset, const SkPaint& paint); + + /** Draw the text, with origin at (x,y), using the specified paint, along + the specified path. The paint's Align setting determins where along the + path to start the text. + @param text The text to be drawn + @param byteLength The number of bytes to read from the text parameter + @param path The path the text should follow for its baseline + @param matrix (may be null) Applied to the text before it is + mapped onto the path + @param paint The paint used for the text + */ + virtual void drawTextOnPath(const void* text, size_t byteLength, + const SkPath& path, const SkMatrix* matrix, + const SkPaint& paint); + + /** Draw the picture into this canvas. This method effective brackets the + playback of the picture's draw calls with save/restore, so the state + of this canvas will be unchanged after this call. This contrasts with + the more immediate method SkPicture::draw(), which does not bracket + the canvas with save/restore, thus the canvas may be left in a changed + state after the call. + @param picture The recorded drawing commands to playback into this + canvas. + */ + virtual void drawPicture(SkPicture& picture); + + enum VertexMode { + kTriangles_VertexMode, + kTriangleStrip_VertexMode, + kTriangleFan_VertexMode + }; + + /** Draw the array of vertices, interpreted as triangles (based on mode). + @param vmode How to interpret the array of vertices + @param vertexCount The number of points in the vertices array (and + corresponding texs and colors arrays if non-null) + @param vertices Array of vertices for the mesh + @param texs May be null. If not null, specifies the coordinate + in texture space for each vertex. + @param colors May be null. If not null, specifies a color for each + vertex, to be interpolated across the triangle. + @param xmode Used if both texs and colors are present. In this + case the colors are combined with the texture using mode, + before being drawn using the paint. If mode is null, then + the porter-duff MULTIPLY mode is used. + @param indices If not null, array of indices to reference into the + vertex (texs, colors) array. + @param indexCount number of entries in the indices array (if not null) + @param paint Specifies the shader/texture if present. + */ + virtual void drawVertices(VertexMode vmode, int vertexCount, + const SkPoint vertices[], const SkPoint texs[], + const SkColor colors[], SkXfermode* xmode, + const uint16_t indices[], int indexCount, + const SkPaint& paint); + + ////////////////////////////////////////////////////////////////////////// + + /** Get the current bounder object. + The bounder's reference count is unchaged. + @return the canva's bounder (or NULL). + */ + SkBounder* getBounder() const { return fBounder; } + + /** Set a new bounder (or NULL). + Pass NULL to clear any previous bounder. + As a convenience, the parameter passed is also returned. + If a previous bounder exists, its reference count is decremented. + If bounder is not NULL, its reference count is incremented. + @param bounder the new bounder (or NULL) to be installed in the canvas + @return the set bounder object + */ + virtual SkBounder* setBounder(SkBounder* bounder); + + /** Get the current filter object. The filter's reference count is not + affected. The filter is part of the state this is affected by + save/restore. + @return the canvas' filter (or NULL). + */ + SkDrawFilter* getDrawFilter() const; + + /** Set the new filter (or NULL). Pass NULL to clear any existing filter. + As a convenience, the parameter is returned. If an existing filter + exists, its refcnt is decrement. If the new filter is not null, its + refcnt is incremented. The filter is part of the state this is affected + by save/restore. + @param filter the new filter (or NULL) + @return the new filter + */ + virtual SkDrawFilter* setDrawFilter(SkDrawFilter* filter); + + ////////////////////////////////////////////////////////////////////////// + + /** Return the current matrix on the canvas. + This does not account for the translate in any of the devices. + @return The current matrix on the canvas. + */ + const SkMatrix& getTotalMatrix() const; + + /** Return the current device clip (concatenation of all clip calls). + This does not account for the translate in any of the devices. + @return the current device clip (concatenation of all clip calls). + */ + const SkRegion& getTotalClip() const; + + /** May be overridden by subclasses. This returns a compatible device + for this canvas, with the specified config/width/height. If isOpaque + is true, then the underlying bitmap is optimized to assume that every + pixel will be drawn to, and thus it does not need to clear the alpha + channel ahead of time (assuming the specified config supports per-pixel + alpha.) If isOpaque is false, then the bitmap should clear its alpha + channel. + */ + virtual SkDevice* createDevice(SkBitmap::Config, int width, int height, + bool isOpaque, bool isForLayer); + + /////////////////////////////////////////////////////////////////////////// + + /** After calling saveLayer(), there can be any number of devices that make + up the top-most drawing area. LayerIter can be used to iterate through + those devices. Note that the iterator is only valid until the next API + call made on the canvas. Ownership of all pointers in the iterator stays + with the canvas, so none of them should be modified or deleted. + */ + class LayerIter /*: SkNoncopyable*/ { + public: + /** Initialize iterator with canvas, and set values for 1st device */ + LayerIter(SkCanvas*, bool skipEmptyClips); + ~LayerIter(); + + /** Return true if the iterator is done */ + bool done() const { return fDone; } + /** Cycle to the next device */ + void next(); + + // These reflect the current device in the iterator + + SkDevice* device() const; + const SkMatrix& matrix() const; + const SkRegion& clip() const; + int x() const; + int y() const; + + private: + // used to embed the SkDrawIter object directly in our instance, w/o + // having to expose that class def to the public. There is an assert + // in our constructor to ensure that fStorage is large enough + // (though needs to be a compile-time-assert!) + uint32_t fStorage[11]; + class SkDrawIter* fImpl; // this points at fStorage + bool fDone; + }; + +protected: + // all of the drawBitmap variants call this guy + virtual void commonDrawBitmap(const SkBitmap&, const SkMatrix& m, + const SkPaint& paint); + +private: + class MCRec; + + SkDeque fMCStack; + // points to top of stack + MCRec* fMCRec; + // the first N recs that can fit here mean we won't call malloc + uint32_t fMCRecStorage[32]; + + SkBounder* fBounder; + + void prepareForDeviceDraw(SkDevice*); + + bool fDeviceCMDirty; // cleared by updateDeviceCMCache() + void updateDeviceCMCache(); + + friend class SkDrawIter; // needs setupDrawForLayerDevice() + + SkDevice* init(SkDevice*); + void internalDrawBitmap(const SkBitmap&, const SkMatrix& m, + const SkPaint* paint); + void drawDevice(SkDevice*, int x, int y, const SkPaint*); + // shared by save() and saveLayer() + int internalSave(SaveFlags flags); + void internalRestore(); +}; + +/** Stack helper class to automatically call restoreToCount() on the canvas + when this object goes out of scope. Use this to guarantee that the canvas + is restored to a known state. +*/ +class SkAutoCanvasRestore : SkNoncopyable { +public: + SkAutoCanvasRestore(SkCanvas* canvas, bool doSave) : fCanvas(canvas) { + SkASSERT(canvas); + fSaveCount = canvas->getSaveCount(); + if (doSave) { + canvas->save(); + } + } + ~SkAutoCanvasRestore() { + fCanvas->restoreToCount(fSaveCount); + } + +private: + SkCanvas* fCanvas; + int fSaveCount; +}; + +#endif + diff --git a/skia/include/SkColor.h b/skia/include/SkColor.h new file mode 100644 index 0000000..f1cf1b1 --- /dev/null +++ b/skia/include/SkColor.h @@ -0,0 +1,156 @@ +/* include/graphics/SkColor.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkColor_DEFINED +#define SkColor_DEFINED + +#include "SkScalar.h" + +/** \file SkColor.h + + Types and macros for colors +*/ + +/** 8-bit type for an alpha value. 0xFF is 100% opaque, 0x00 is 100% transparent. +*/ +typedef uint8_t SkAlpha; +/** 32 bit ARGB color value, not premultiplied. The color components are always in + a known order. This is different from SkPMColor, which has its bytes in a configuration + dependent order, to match the format of kARGB32 bitmaps. SkColor is the type used to + specify colors in SkPaint and in gradients. +*/ +typedef uint32_t SkColor; + +/** Return a SkColor value from 8 bit component values +*/ +static inline SkColor SkColorSetARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) +{ + SkASSERT(a <= 255 && r <= 255 && g <= 255 && b <= 255); + + return (a << 24) | (r << 16) | (g << 8) | (b << 0); +} + +/** Return a SkColor value from 8 bit component values, with an implied value + of 0xFF for alpha (fully opaque) +*/ +#define SkColorSetRGB(r, g, b) SkColorSetARGB(0xFF, r, g, b) + +/** return the alpha byte from a SkColor value */ +#define SkColorGetA(color) (((color) >> 24) & 0xFF) +/** return the red byte from a SkColor value */ +#define SkColorGetR(color) (((color) >> 16) & 0xFF) +/** return the green byte from a SkColor value */ +#define SkColorGetG(color) (((color) >> 8) & 0xFF) +/** return the blue byte from a SkColor value */ +#define SkColorGetB(color) (((color) >> 0) & 0xFF) + +static inline SkColor SkColorSetA(SkColor c, U8CPU a) { + return (c & 0x00FFFFFF) | (a << 24); +} + +// common colors + +#define SK_ColorBLACK 0xFF000000 //!< black SkColor value +#define SK_ColorDKGRAY 0xFF444444 //!< dark gray SkColor value +#define SK_ColorGRAY 0xFF888888 //!< gray SkColor value +#define SK_ColorLTGRAY 0xFFCCCCCC //!< light gray SkColor value +#define SK_ColorWHITE 0xFFFFFFFF //!< white SkColor value + +#define SK_ColorRED 0xFFFF0000 //!< red SkColor value +#define SK_ColorGREEN 0xFF00FF00 //!< green SkColor value +#define SK_ColorBLUE 0xFF0000FF //!< blue SkColor value +#define SK_ColorYELLOW 0xFFFFFF00 //!< yellow SkColor value +#define SK_ColorCYAN 0xFF00FFFF //!< cyan SkColor value +#define SK_ColorMAGENTA 0xFFFF00FF //!< magenta SkColor value + +//////////////////////////////////////////////////////////////////////// + +/** Convert RGB components to HSV. + hsv[0] is Hue [0 .. 360) + hsv[1] is Saturation [0...1] + hsv[2] is Value [0...1] + @param red red component value [0..255] + @param green green component value [0..255] + @param blue blue component value [0..255] + @param hsv 3 element array which holds the resulting HSV components. +*/ +void SkRGBToHSV(U8CPU red, U8CPU green, U8CPU blue, SkScalar hsv[3]); + +/** Convert the argb color to its HSV components. + hsv[0] is Hue [0 .. 360) + hsv[1] is Saturation [0...1] + hsv[2] is Value [0...1] + @param color the argb color to convert. Note: the alpha component is ignored. + @param hsv 3 element array which holds the resulting HSV components. +*/ +static inline void SkColorToHSV(SkColor color, SkScalar hsv[3]) +{ + SkRGBToHSV(SkColorGetR(color), SkColorGetG(color), SkColorGetB(color), hsv); +} + +/** Convert HSV components to an ARGB color. The alpha component is passed through unchanged. + hsv[0] is Hue [0 .. 360) + hsv[1] is Saturation [0...1] + hsv[2] is Value [0...1] + If hsv values are out of range, they are pinned. + @param alpha the alpha component of the returned argb color. + @param hsv 3 element array which holds the input HSV components. + @return the resulting argb color +*/ +SkColor SkHSVToColor(U8CPU alpha, const SkScalar hsv[3]); + +/** Convert HSV components to an ARGB color. The alpha component set to 0xFF. + hsv[0] is Hue [0 .. 360) + hsv[1] is Saturation [0...1] + hsv[2] is Value [0...1] + If hsv values are out of range, they are pinned. + @param hsv 3 element array which holds the input HSV components. + @return the resulting argb color +*/ +static inline SkColor SkHSVToColor(const SkScalar hsv[3]) +{ + return SkHSVToColor(0xFF, hsv); +} + +//////////////////////////////////////////////////////////////////////// + +/** 32 bit ARGB color value, premultiplied. The byte order for this value is + configuration dependent, matching the format of kARGB32 bitmaps. This is different + from SkColor, which is nonpremultiplied, and is always in the same byte order. +*/ +typedef uint32_t SkPMColor; + +/** Return a SkPMColor value from unpremultiplied 8 bit component values +*/ +SkPMColor SkPreMultiplyARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b); +/** Return a SkPMColor value from a SkColor value. This is done by multiplying the color + components by the color's alpha, and by arranging the bytes in a configuration + dependent order, to match the format of kARGB32 bitmaps. +*/ +SkPMColor SkPreMultiplyColor(SkColor c); + +/** Define a function pointer type for combining two premultiplied colors +*/ +typedef SkPMColor (*SkXfermodeProc)(SkPMColor src, SkPMColor dst); + +/** Define a function pointer type for combining a premultiplied src color + and a 16bit device color. +*/ +typedef uint16_t (*SkXfermodeProc16)(SkPMColor src, uint16_t dst); + +#endif + diff --git a/skia/include/SkColorFilter.h b/skia/include/SkColorFilter.h new file mode 100644 index 0000000..bc89974 --- /dev/null +++ b/skia/include/SkColorFilter.h @@ -0,0 +1,126 @@ +/* include/graphics/SkColorFilter.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkColorFilter_DEFINED +#define SkColorFilter_DEFINED + +#include "SkColor.h" +#include "SkFlattenable.h" +#include "SkPorterDuff.h" + +class SkColorFilter : public SkFlattenable { +public: + /** Called with a scanline of colors, as if there was a shader installed. + The implementation writes out its filtered version into result[]. + Note: shader and result may be the same buffer. + @param src array of colors, possibly generated by a shader + @param count the number of entries in the src[] and result[] arrays + @param result written by the filter + */ + virtual void filterSpan(const SkPMColor src[], int count, + SkPMColor result[]) = 0; + /** Called with a scanline of colors, as if there was a shader installed. + The implementation writes out its filtered version into result[]. + Note: shader and result may be the same buffer. + @param src array of colors, possibly generated by a shader + @param count the number of entries in the src[] and result[] arrays + @param result written by the filter + */ + virtual void filterSpan16(const uint16_t shader[], int count, + uint16_t result[]); + + enum Flags { + /** If set the filter methods will not change the alpha channel of the + colors. + */ + kAlphaUnchanged_Flag = 0x01, + /** If set, this subclass implements filterSpan16(). If this flag is + set, then kAlphaUnchanged_Flag must also be set. + */ + kHasFilter16_Flag = 0x02 + }; + + /** Returns the flags for this filter. Override in subclasses to return + custom flags. + */ + virtual uint32_t getFlags() { return 0; } + + /** Create a colorfilter that uses the specified color and porter-duff mode. + If porterDuffMode is DST, this function will return NULL (since that + mode will have no effect on the result). + @param srcColor The source color used with the specified mode + @param mode The porter-duff mode that is applied to each color in + the colorfilter's filterSpan[16,32] methods + @return colorfilter object that applies the src color and porter-duff + mode, or NULL if the mode will have no effect. + */ + static SkColorFilter* CreatePorterDuffFilter(SkColor srcColor, + SkPorterDuff::Mode mode); + + /** Create a colorfilter that calls through to the specified procs to + filter the colors. The SkXfermodeProc parameter must be non-null, but + the SkXfermodeProc16 is optional, and may be null. + */ + static SkColorFilter* CreatXfermodeProcFilter(SkColor srcColor, + SkXfermodeProc proc, + SkXfermodeProc16 proc16 = NULL); + + /** Create a colorfilter that multiplies the RGB channels by one color, and + then adds a second color, pinning the result for each component to + [0..255]. The alpha components of the mul and add arguments + are ignored. + */ + static SkColorFilter* CreateLightingFilter(SkColor mul, SkColor add); + +protected: + SkColorFilter() {} + SkColorFilter(SkFlattenableReadBuffer& rb) : INHERITED(rb) {} + +private: + typedef SkFlattenable INHERITED; +}; + +#include "SkShader.h" + +class SkFilterShader : public SkShader { +public: + SkFilterShader(SkShader* shader, SkColorFilter* filter); + virtual ~SkFilterShader(); + + // override + virtual uint32_t getFlags(); + virtual bool setContext(const SkBitmap& device, const SkPaint& paint, + const SkMatrix& matrix); + virtual void shadeSpan(int x, int y, SkPMColor result[], int count); + virtual void shadeSpan16(int x, int y, uint16_t result[], int count); + virtual void beginSession(); + virtual void endSession(); + +protected: + SkFilterShader(SkFlattenableReadBuffer& ); + virtual void flatten(SkFlattenableWriteBuffer& ); + virtual Factory getFactory() { return CreateProc; } +private: + static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) { + return SkNEW_ARGS(SkFilterShader, (buffer)); } + SkShader* fShader; + SkColorFilter* fFilter; + + typedef SkShader INHERITED; +}; + +#endif diff --git a/skia/include/SkColorMatrix.h b/skia/include/SkColorMatrix.h new file mode 100644 index 0000000..dd4b29f --- /dev/null +++ b/skia/include/SkColorMatrix.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2007 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SkColorMatrix_DEFINED +#define SkColorMatrix_DEFINED + +#include "SkScalar.h" + +class SkColorMatrix { +public: + SkScalar fMat[20]; + + void setIdentity(); + void setScale(SkScalar rScale, SkScalar gScale, SkScalar bScale, + SkScalar aScale = SK_Scalar1); + void preScale(SkScalar rScale, SkScalar gScale, SkScalar bScale, + SkScalar aScale = SK_Scalar1); + void postScale(SkScalar rScale, SkScalar gScale, SkScalar bScale, + SkScalar aScale = SK_Scalar1); + + enum Axis { + kR_Axis = 0, + kG_Axis = 1, + kB_Axis = 2 + }; + void setRotate(Axis, SkScalar degrees); + void setSinCos(Axis, SkScalar sine, SkScalar cosine); + void preRotate(Axis, SkScalar degrees); + void postRotate(Axis, SkScalar degrees); + + void setConcat(const SkColorMatrix& a, const SkColorMatrix& b); + void preConcat(const SkColorMatrix& mat) { this->setConcat(*this, mat); } + void postConcat(const SkColorMatrix& mat) { this->setConcat(mat, *this); } + + void setSaturation(SkScalar sat); + void setRGB2YUV(); + void setYUV2RGB(); +}; + +#endif diff --git a/skia/include/SkColorMatrixFilter.h b/skia/include/SkColorMatrixFilter.h new file mode 100644 index 0000000..4798d37 --- /dev/null +++ b/skia/include/SkColorMatrixFilter.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2007 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SkColorMatrixFilter_DEFINED +#define SkColorMatrixFilter_DEFINED + +#include "SkColorFilter.h" +#include "SkColorMatrix.h" + +class SkColorMatrixFilter : public SkColorFilter { +public: + SkColorMatrixFilter(); + explicit SkColorMatrixFilter(const SkColorMatrix&); + SkColorMatrixFilter(const SkScalar array[20]); + + void setMatrix(const SkColorMatrix&); + void setArray(const SkScalar array[20]); + + // overrides from SkColorFilter + virtual void filterSpan(const SkPMColor src[], int count, SkPMColor[]); + virtual void filterSpan16(const uint16_t src[], int count, uint16_t[]); + virtual uint32_t getFlags(); + + // overrides for SkFlattenable + virtual void flatten(SkFlattenableWriteBuffer& buffer); + + struct State { + int32_t fArray[20]; + int fShift; + int32_t fResult[4]; + }; + +protected: + // overrides for SkFlattenable + virtual Factory getFactory(); + + SkColorMatrixFilter(SkFlattenableReadBuffer& buffer); + +private: + static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer); + + typedef void (*Proc)(State*, unsigned r, unsigned g, unsigned b, + unsigned a); + + Proc fProc; + State fState; + uint32_t fFlags; + + void setup(const SkScalar array[20]); + + typedef SkColorFilter INHERITED; +}; + +#endif diff --git a/skia/include/SkColorPriv.h b/skia/include/SkColorPriv.h new file mode 100644 index 0000000..f35aa24 --- /dev/null +++ b/skia/include/SkColorPriv.h @@ -0,0 +1,657 @@ +/* + * Copyright (C) 2006-2008 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SkColorPriv_DEFINED +#define SkColorPriv_DEFINED + +// turn this own for extra debug checking when blending onto 565 +#ifdef SK_DEBUG + #define CHECK_FOR_565_OVERFLOW +#endif + +#include "SkColor.h" +#include "SkMath.h" + +/** Turn 0..255 into 0..256 by adding 1 at the half-way point. Used to turn a + byte into a scale value, so that we can say scale * value >> 8 instead of + alpha * value / 255. + + In debugging, asserts that alpha is 0..255 +*/ +static inline unsigned SkAlpha255To256(U8CPU alpha) { + SkASSERT(SkToU8(alpha) == alpha); + return alpha + (alpha >> 7); +} + +/** Multiplify value by 0..256, and shift the result down 8 + (i.e. return (value * alpha256) >> 8) + */ +#define SkAlphaMul(value, alpha256) (SkMulS16(value, alpha256) >> 8) + +// The caller may want negative values, so keep all params signed (int) +// so we don't accidentally slip into unsigned math and lose the sign +// extension when we shift (in SkAlphaMul) +inline int SkAlphaBlend(int src, int dst, int scale256) { + SkASSERT((unsigned)scale256 <= 256); + return dst + SkAlphaMul(src - dst, scale256); +} + +#define SK_R16_BITS 5 +#define SK_G16_BITS 6 +#define SK_B16_BITS 5 + +#define SK_R16_SHIFT (SK_B16_BITS + SK_G16_BITS) +#define SK_G16_SHIFT (SK_B16_BITS) +#define SK_B16_SHIFT 0 + +#define SK_R16_MASK ((1 << SK_R16_BITS) - 1) +#define SK_G16_MASK ((1 << SK_G16_BITS) - 1) +#define SK_B16_MASK ((1 << SK_B16_BITS) - 1) + +#define SkGetPackedR16(color) (((unsigned)(color) >> SK_R16_SHIFT) & SK_R16_MASK) +#define SkGetPackedG16(color) (((unsigned)(color) >> SK_G16_SHIFT) & SK_G16_MASK) +#define SkGetPackedB16(color) (((unsigned)(color) >> SK_B16_SHIFT) & SK_B16_MASK) + +#define SkR16Assert(r) SkASSERT((unsigned)(r) <= SK_R16_MASK) +#define SkG16Assert(g) SkASSERT((unsigned)(g) <= SK_G16_MASK) +#define SkB16Assert(b) SkASSERT((unsigned)(b) <= SK_B16_MASK) + +static inline uint16_t SkPackRGB16(unsigned r, unsigned g, unsigned b) { + SkASSERT(r <= SK_R16_MASK); + SkASSERT(g <= SK_G16_MASK); + SkASSERT(b <= SK_B16_MASK); + + return SkToU16((r << SK_R16_SHIFT) | (g << SK_G16_SHIFT) | (b << SK_B16_SHIFT)); +} + +#define SK_R16_MASK_IN_PLACE (SK_R16_MASK << SK_R16_SHIFT) +#define SK_G16_MASK_IN_PLACE (SK_G16_MASK << SK_G16_SHIFT) +#define SK_B16_MASK_IN_PLACE (SK_B16_MASK << SK_B16_SHIFT) + +/** Expand the 16bit color into a 32bit value that can be scaled all at once + by a value up to 32. Used in conjunction with SkCompact_rgb_16. +*/ +static inline uint32_t SkExpand_rgb_16(U16CPU c) { + SkASSERT(c == (uint16_t)c); + + return ((c & SK_G16_MASK_IN_PLACE) << 16) | (c & ~SK_G16_MASK_IN_PLACE); +} + +/** Compress an expanded value (from SkExpand_rgb_16) back down to a 16bit + color value. The computation yields only 16bits of valid data, but we claim + to return 32bits, so that the compiler won't generate extra instructions to + "clean" the top 16bits. However, the top 16 can contain garbage, so it is + up to the caller to safely ignore them. +*/ +static inline U16CPU SkCompact_rgb_16(uint32_t c) { + return ((c >> 16) & SK_G16_MASK_IN_PLACE) | (c & ~SK_G16_MASK_IN_PLACE); +} + +/** Scale the 16bit color value by the 0..256 scale parameter. + The computation yields only 16bits of valid data, but we claim + to return 32bits, so that the compiler won't generate extra instructions to + "clean" the top 16bits. +*/ +static inline U16CPU SkAlphaMulRGB16(U16CPU c, unsigned scale) { + return SkCompact_rgb_16(SkExpand_rgb_16(c) * (scale >> 3) >> 5); +} + +// this helper explicitly returns a clean 16bit value (but slower) +#define SkAlphaMulRGB16_ToU16(c, s) (uint16_t)SkAlphaMulRGB16(c, s) + +/** Blend src and dst 16bit colors by the 0..256 scale parameter. + The computation yields only 16bits of valid data, but we claim + to return 32bits, so that the compiler won't generate extra instructions to + "clean" the top 16bits. +*/ +static inline U16CPU SkBlendRGB16(U16CPU src, U16CPU dst, int srcScale) { + SkASSERT((unsigned)srcScale <= 256); + + srcScale >>= 3; + + uint32_t src32 = SkExpand_rgb_16(src); + uint32_t dst32 = SkExpand_rgb_16(dst); + return SkCompact_rgb_16(dst32 + ((src32 - dst32) * srcScale >> 5)); +} + +static inline void SkBlendRGB16(const uint16_t src[], uint16_t dst[], + int srcScale, int count) { + SkASSERT(count > 0); + SkASSERT((unsigned)srcScale <= 256); + + srcScale >>= 3; + + do { + uint32_t src32 = SkExpand_rgb_16(*src++); + uint32_t dst32 = SkExpand_rgb_16(*dst); + *dst++ = SkCompact_rgb_16(dst32 + ((src32 - dst32) * srcScale >> 5)); + } while (--count > 0); +} + +#ifdef SK_DEBUG + static U16CPU SkRGB16Add(U16CPU a, U16CPU b) { + SkASSERT(SkGetPackedR16(a) + SkGetPackedR16(b) <= SK_R16_MASK); + SkASSERT(SkGetPackedG16(a) + SkGetPackedG16(b) <= SK_G16_MASK); + SkASSERT(SkGetPackedB16(a) + SkGetPackedB16(b) <= SK_B16_MASK); + + return a + b; + } +#else + #define SkRGB16Add(a, b) ((a) + (b)) +#endif + +///////////////////////////////////////////////////////////////////////////////////////////// + +#define SK_A32_BITS 8 +#define SK_R32_BITS 8 +#define SK_G32_BITS 8 +#define SK_B32_BITS 8 + +/* we check to see if the SHIFT value has already been defined (SkUserConfig.h) + if not, we define it ourself to some default values. We default to OpenGL + order (in memory: r,g,b,a) +*/ +#ifndef SK_A32_SHIFT + #ifdef SK_CPU_BENDIAN + #define SK_R32_SHIFT 24 + #define SK_G32_SHIFT 16 + #define SK_B32_SHIFT 8 + #define SK_A32_SHIFT 0 + #else + #define SK_R32_SHIFT 0 + #define SK_G32_SHIFT 8 + #define SK_B32_SHIFT 16 + #define SK_A32_SHIFT 24 + #endif +#endif + +#define SK_A32_MASK ((1 << SK_A32_BITS) - 1) +#define SK_R32_MASK ((1 << SK_R32_BITS) - 1) +#define SK_G32_MASK ((1 << SK_G32_BITS) - 1) +#define SK_B32_MASK ((1 << SK_B32_BITS) - 1) + +#define SkGetPackedA32(packed) ((uint32_t)((packed) << (24 - SK_A32_SHIFT)) >> 24) +#define SkGetPackedR32(packed) ((uint32_t)((packed) << (24 - SK_R32_SHIFT)) >> 24) +#define SkGetPackedG32(packed) ((uint32_t)((packed) << (24 - SK_G32_SHIFT)) >> 24) +#define SkGetPackedB32(packed) ((uint32_t)((packed) << (24 - SK_B32_SHIFT)) >> 24) + +#define SkA32Assert(a) SkASSERT((unsigned)(a) <= SK_A32_MASK) +#define SkR32Assert(r) SkASSERT((unsigned)(r) <= SK_R32_MASK) +#define SkG32Assert(g) SkASSERT((unsigned)(g) <= SK_G32_MASK) +#define SkB32Assert(b) SkASSERT((unsigned)(b) <= SK_B32_MASK) + +#ifdef SK_DEBUG + inline void SkPMColorAssert(SkPMColor c) { + unsigned a = SkGetPackedA32(c); + unsigned r = SkGetPackedR32(c); + unsigned g = SkGetPackedG32(c); + unsigned b = SkGetPackedB32(c); + + SkA32Assert(a); + SkASSERT(r <= a); + SkASSERT(g <= a); + SkASSERT(b <= a); + } +#else + #define SkPMColorAssert(c) +#endif + +inline SkPMColor SkPackARGB32(U8CPU a, U8CPU r, U8CPU g, U8CPU b) { + SkA32Assert(a); + SkASSERT(r <= a); + SkASSERT(g <= a); + SkASSERT(b <= a); + + return (a << SK_A32_SHIFT) | (r << SK_R32_SHIFT) | + (g << SK_G32_SHIFT) | (b << SK_B32_SHIFT); +} + +extern const uint32_t gMask_00FF00FF; + +inline uint32_t SkAlphaMulQ(uint32_t c, unsigned scale) { + uint32_t mask = gMask_00FF00FF; +// uint32_t mask = 0xFF00FF; + + uint32_t rb = ((c & mask) * scale) >> 8; + uint32_t ag = ((c >> 8) & mask) * scale; + return (rb & mask) | (ag & ~mask); +} + +inline SkPMColor SkPMSrcOver(SkPMColor src, SkPMColor dst) { + return src + SkAlphaMulQ(dst, SkAlpha255To256(255 - SkGetPackedA32(src))); +} + +inline SkPMColor SkBlendARGB32(SkPMColor src, SkPMColor dst, U8CPU aa) { + SkASSERT((unsigned)aa <= 255); + + unsigned src_scale = SkAlpha255To256(aa); + unsigned dst_scale = SkAlpha255To256(255 - SkAlphaMul(SkGetPackedA32(src), src_scale)); + + return SkAlphaMulQ(src, src_scale) + SkAlphaMulQ(dst, dst_scale); +} + +//////////////////////////////////////////////////////////////////////////////////////////// +// Convert a 32bit pixel to a 16bit pixel (no dither) + +#define SkR32ToR16_MACRO(r) ((unsigned)(r) >> (SK_R32_BITS - SK_R16_BITS)) +#define SkG32ToG16_MACRO(g) ((unsigned)(g) >> (SK_G32_BITS - SK_G16_BITS)) +#define SkB32ToB16_MACRO(b) ((unsigned)(b) >> (SK_B32_BITS - SK_B16_BITS)) + +#ifdef SK_DEBUG + inline unsigned SkR32ToR16(unsigned r) + { + SkR32Assert(r); + return SkR32ToR16_MACRO(r); + } + inline unsigned SkG32ToG16(unsigned g) + { + SkG32Assert(g); + return SkG32ToG16_MACRO(g); + } + inline unsigned SkB32ToB16(unsigned b) + { + SkB32Assert(b); + return SkB32ToB16_MACRO(b); + } +#else + #define SkR32ToR16(r) SkR32ToR16_MACRO(r) + #define SkG32ToG16(g) SkG32ToG16_MACRO(g) + #define SkB32ToB16(b) SkB32ToB16_MACRO(b) +#endif + +#define SkPacked32ToR16(c) (((unsigned)(c) >> (SK_R32_SHIFT + SK_R32_BITS - SK_R16_BITS)) & SK_R16_MASK) +#define SkPacked32ToG16(c) (((unsigned)(c) >> (SK_G32_SHIFT + SK_G32_BITS - SK_G16_BITS)) & SK_G16_MASK) +#define SkPacked32ToB16(c) (((unsigned)(c) >> (SK_B32_SHIFT + SK_B32_BITS - SK_B16_BITS)) & SK_B16_MASK) + +inline U16CPU SkPixel32ToPixel16(SkPMColor c) +{ + unsigned r = ((c >> (SK_R32_SHIFT + (8 - SK_R16_BITS))) & SK_R16_MASK) << SK_R16_SHIFT; + unsigned g = ((c >> (SK_G32_SHIFT + (8 - SK_G16_BITS))) & SK_G16_MASK) << SK_G16_SHIFT; + unsigned b = ((c >> (SK_B32_SHIFT + (8 - SK_B16_BITS))) & SK_B16_MASK) << SK_B16_SHIFT; + return r | g | b; +} + +inline U16CPU SkPack888ToRGB16(U8CPU r, U8CPU g, U8CPU b) +{ + return (SkR32ToR16(r) << SK_R16_SHIFT) | + (SkG32ToG16(g) << SK_G16_SHIFT) | + (SkB32ToB16(b) << SK_B16_SHIFT); +} + +#define SkPixel32ToPixel16_ToU16(src) SkToU16(SkPixel32ToPixel16(src)) + +///////////////////////////////////////////////////////////////////////////////////////// +// Fast dither from 32->16 + +#define SkShouldDitherXY(x, y) (((x) ^ (y)) & 1) + +inline uint16_t SkDitherPack888ToRGB16(U8CPU r, U8CPU g, U8CPU b) +{ + r = ((r << 1) - ((r >> (8 - SK_R16_BITS) << (8 - SK_R16_BITS)) | (r >> SK_R16_BITS))) >> (8 - SK_R16_BITS); + g = ((g << 1) - ((g >> (8 - SK_G16_BITS) << (8 - SK_G16_BITS)) | (g >> SK_G16_BITS))) >> (8 - SK_G16_BITS); + b = ((b << 1) - ((b >> (8 - SK_B16_BITS) << (8 - SK_B16_BITS)) | (b >> SK_B16_BITS))) >> (8 - SK_B16_BITS); + + return SkPackRGB16(r, g, b); +} + +inline uint16_t SkDitherPixel32ToPixel16(SkPMColor c) +{ + return SkDitherPack888ToRGB16(SkGetPackedR32(c), SkGetPackedG32(c), SkGetPackedB32(c)); +} + +/* Return c in expanded_rgb_16 format, but also scaled up by 32 (5 bits) + It is now suitable for combining with a scaled expanded_rgb_16 color + as in SkSrcOver32To16(). + We must do this 565 high-bit replication, in order for the subsequent add + to saturate properly (and not overflow). If we take the 8 bits as is, it is + possible to overflow. +*/ +static inline uint32_t SkPMColorToExpanded16x5(SkPMColor c) +{ + unsigned sr = SkPacked32ToR16(c); + unsigned sg = SkPacked32ToG16(c); + unsigned sb = SkPacked32ToB16(c); + + sr = (sr << 5) | sr; + sg = (sg << 5) | (sg >> 1); + sb = (sb << 5) | sb; + return (sr << 11) | (sg << 21) | (sb << 0); +} + +/* SrcOver the 32bit src color with the 16bit dst, returning a 16bit value + (with dirt in the high 16bits, so caller beware). +*/ +static inline U16CPU SkSrcOver32To16(SkPMColor src, uint16_t dst) { + unsigned sr = SkGetPackedR32(src); + unsigned sg = SkGetPackedG32(src); + unsigned sb = SkGetPackedB32(src); + + unsigned dr = SkGetPackedR16(dst); + unsigned dg = SkGetPackedG16(dst); + unsigned db = SkGetPackedB16(dst); + + unsigned isa = 255 - SkGetPackedA32(src); + + dr = (sr + SkMul16ShiftRound(dr, isa, SK_R16_BITS)) >> (8 - SK_R16_BITS); + dg = (sg + SkMul16ShiftRound(dg, isa, SK_G16_BITS)) >> (8 - SK_G16_BITS); + db = (sb + SkMul16ShiftRound(db, isa, SK_B16_BITS)) >> (8 - SK_B16_BITS); + + return SkPackRGB16(dr, dg, db); +} + +//////////////////////////////////////////////////////////////////////////////////////////// +// Convert a 16bit pixel to a 32bit pixel + +inline unsigned SkR16ToR32(unsigned r) +{ + return (r << (8 - SK_R16_BITS)) | (r >> (2 * SK_R16_BITS - 8)); +} +inline unsigned SkG16ToG32(unsigned g) +{ + return (g << (8 - SK_G16_BITS)) | (g >> (2 * SK_G16_BITS - 8)); +} +inline unsigned SkB16ToB32(unsigned b) +{ + return (b << (8 - SK_B16_BITS)) | (b >> (2 * SK_B16_BITS - 8)); +} + +#define SkPacked16ToR32(c) SkR16ToR32(SkGetPackedR16(c)) +#define SkPacked16ToG32(c) SkG16ToG32(SkGetPackedG16(c)) +#define SkPacked16ToB32(c) SkB16ToB32(SkGetPackedB16(c)) + +inline SkPMColor SkPixel16ToPixel32(U16CPU src) +{ + SkASSERT(src == SkToU16(src)); + + unsigned r = SkPacked16ToR32(src); + unsigned g = SkPacked16ToG32(src); + unsigned b = SkPacked16ToB32(src); + + SkASSERT((r >> (8 - SK_R16_BITS)) == SkGetPackedR16(src)); + SkASSERT((g >> (8 - SK_G16_BITS)) == SkGetPackedG16(src)); + SkASSERT((b >> (8 - SK_B16_BITS)) == SkGetPackedB16(src)); + + return SkPackARGB32(0xFF, r, g, b); +} + +/////////////////////////////////////////////////////////////////////////////// + +typedef uint16_t SkPMColor16; + +// Put in OpenGL order (r g b a) +#define SK_A4444_SHIFT 0 +#define SK_R4444_SHIFT 12 +#define SK_G4444_SHIFT 8 +#define SK_B4444_SHIFT 4 + +#define SkA32To4444(a) ((unsigned)(a) >> 4) +#define SkR32To4444(r) ((unsigned)(r) >> 4) +#define SkG32To4444(g) ((unsigned)(g) >> 4) +#define SkB32To4444(b) ((unsigned)(b) >> 4) + +static U8CPU SkReplicateNibble(unsigned nib) +{ + SkASSERT(nib <= 0xF); + return (nib << 4) | nib; +} + +#define SkA4444ToA32(a) SkReplicateNibble(a) +#define SkR4444ToR32(r) SkReplicateNibble(r) +#define SkG4444ToG32(g) SkReplicateNibble(g) +#define SkB4444ToB32(b) SkReplicateNibble(b) + +#define SkGetPackedA4444(c) (((unsigned)(c) >> SK_A4444_SHIFT) & 0xF) +#define SkGetPackedR4444(c) (((unsigned)(c) >> SK_R4444_SHIFT) & 0xF) +#define SkGetPackedG4444(c) (((unsigned)(c) >> SK_G4444_SHIFT) & 0xF) +#define SkGetPackedB4444(c) (((unsigned)(c) >> SK_B4444_SHIFT) & 0xF) + +#define SkPacked4444ToA32(c) SkReplicateNibble(SkGetPackedA4444(c)) +#define SkPacked4444ToR32(c) SkReplicateNibble(SkGetPackedR4444(c)) +#define SkPacked4444ToG32(c) SkReplicateNibble(SkGetPackedG4444(c)) +#define SkPacked4444ToB32(c) SkReplicateNibble(SkGetPackedB4444(c)) + +#ifdef SK_DEBUG +static inline void SkPMColor16Assert(U16CPU c) +{ + unsigned a = SkGetPackedA4444(c); + unsigned r = SkGetPackedR4444(c); + unsigned g = SkGetPackedG4444(c); + unsigned b = SkGetPackedB4444(c); + + SkASSERT(a <= 0xF); + SkASSERT(r <= a); + SkASSERT(g <= a); + SkASSERT(b <= a); +} +#else +#define SkPMColor16Assert(c) +#endif + +static inline unsigned SkAlpha15To16(unsigned a) +{ + SkASSERT(a <= 0xF); + return a + (a >> 3); +} + +#ifdef SK_DEBUG + static inline int SkAlphaMul4(int value, int scale) + { + SkASSERT((unsigned)scale <= 0x10); + return value * scale >> 4; + } +#else + #define SkAlphaMul4(value, scale) ((value) * (scale) >> 4) +#endif + +static inline unsigned SkR4444ToR565(unsigned r) +{ + SkASSERT(r <= 0xF); + return (r << (SK_R16_BITS - 4)) | (r >> (8 - SK_R16_BITS)); +} + +static inline unsigned SkG4444ToG565(unsigned g) +{ + SkASSERT(g <= 0xF); + return (g << (SK_G16_BITS - 4)) | (g >> (8 - SK_G16_BITS)); +} + +static inline unsigned SkB4444ToB565(unsigned b) +{ + SkASSERT(b <= 0xF); + return (b << (SK_B16_BITS - 4)) | (b >> (8 - SK_B16_BITS)); +} + +static inline SkPMColor16 SkPackARGB4444(unsigned a, unsigned r, + unsigned g, unsigned b) +{ + SkASSERT(a <= 0xF); + SkASSERT(r <= a); + SkASSERT(g <= a); + SkASSERT(b <= a); + + return (SkPMColor16)((a << SK_A4444_SHIFT) | (r << SK_R4444_SHIFT) | + (g << SK_G4444_SHIFT) | (b << SK_B4444_SHIFT)); +} + +extern const uint16_t gMask_0F0F; + +inline U16CPU SkAlphaMulQ4(U16CPU c, unsigned scale) +{ + SkASSERT(scale <= 16); + + const unsigned mask = 0xF0F; //gMask_0F0F; + +#if 0 + unsigned rb = ((c & mask) * scale) >> 4; + unsigned ag = ((c >> 4) & mask) * scale; + return (rb & mask) | (ag & ~mask); +#else + c = (c & mask) | ((c & (mask << 4)) << 12); + c = c * scale >> 4; + return (c & mask) | ((c >> 12) & (mask << 4)); +#endif +} + +/** Expand the SkPMColor16 color into a 32bit value that can be scaled all at + once by a value up to 16. Used in conjunction with SkCompact_4444. +*/ +inline uint32_t SkExpand_4444(U16CPU c) +{ + SkASSERT(c == (uint16_t)c); + + const unsigned mask = 0xF0F; //gMask_0F0F; + return (c & mask) | ((c & ~mask) << 12); +} + +/** Compress an expanded value (from SkExpand_4444) back down to a SkPMColor16. + NOTE: this explicitly does not clean the top 16 bits (which may be garbage). + It does this for speed, since if it is being written directly to 16bits of + memory, the top 16bits will be ignored. Casting the result to uint16_t here + would add 2 more instructions, slow us down. It is up to the caller to + perform the cast if needed. +*/ +static inline U16CPU SkCompact_4444(uint32_t c) +{ + const unsigned mask = 0xF0F; //gMask_0F0F; + return (c & mask) | ((c >> 12) & ~mask); +} + +static inline uint16_t SkSrcOver4444To16(SkPMColor16 s, uint16_t d) +{ + unsigned sa = SkGetPackedA4444(s); + unsigned sr = SkR4444ToR565(SkGetPackedR4444(s)); + unsigned sg = SkG4444ToG565(SkGetPackedG4444(s)); + unsigned sb = SkB4444ToB565(SkGetPackedB4444(s)); + + // To avoid overflow, we have to clear the low bit of the synthetic sg + // if the src alpha is <= 7. + // to see why, try blending 0x4444 on top of 565-white and watch green + // overflow (sum == 64) + sg &= ~(~(sa >> 3) & 1); + + unsigned scale = SkAlpha15To16(15 - sa); + unsigned dr = SkAlphaMul4(SkGetPackedR16(d), scale); + unsigned dg = SkAlphaMul4(SkGetPackedG16(d), scale); + unsigned db = SkAlphaMul4(SkGetPackedB16(d), scale); + +#if 0 + if (sg + dg > 63) { + SkDebugf("---- SkSrcOver4444To16 src=%x dst=%x scale=%d, sg=%d dg=%d\n", s, d, scale, sg, dg); + } +#endif + return SkPackRGB16(sr + dr, sg + dg, sb + db); +} + +static inline uint16_t SkBlend4444To16(SkPMColor16 src, uint16_t dst, int scale16) +{ + SkASSERT((unsigned)scale16 <= 16); + + return SkSrcOver4444To16(SkAlphaMulQ4(src, scale16), dst); +} + +static inline uint16_t SkBlend4444(SkPMColor16 src, SkPMColor16 dst, int scale16) +{ + SkASSERT((unsigned)scale16 <= 16); + + uint32_t src32 = SkExpand_4444(src) * scale16; + // the scaled srcAlpha is the bottom byte +#ifdef SK_DEBUG + { + unsigned srcA = SkGetPackedA4444(src) * scale16; + SkASSERT(srcA == (src32 & 0xFF)); + } +#endif + unsigned dstScale = SkAlpha255To256(255 - (src32 & 0xFF)) >> 4; + uint32_t dst32 = SkExpand_4444(dst) * dstScale; + return SkCompact_4444((src32 + dst32) >> 4); +} + +static inline SkPMColor SkPixel4444ToPixel32(U16CPU c) +{ + uint32_t d = (SkGetPackedA4444(c) << SK_A32_SHIFT) | + (SkGetPackedR4444(c) << SK_R32_SHIFT) | + (SkGetPackedG4444(c) << SK_G32_SHIFT) | + (SkGetPackedB4444(c) << SK_B32_SHIFT); + return d | (d << 4); +} + +static inline SkPMColor16 SkPixel32ToPixel4444(SkPMColor c) +{ + return (((c >> (SK_A32_SHIFT + 4)) & 0xF) << SK_A4444_SHIFT) | + (((c >> (SK_R32_SHIFT + 4)) & 0xF) << SK_R4444_SHIFT) | + (((c >> (SK_G32_SHIFT + 4)) & 0xF) << SK_G4444_SHIFT) | + (((c >> (SK_B32_SHIFT + 4)) & 0xF) << SK_B4444_SHIFT); +} + +// cheap 2x2 dither +static inline SkPMColor16 SkDitherARGB32To4444(U8CPU a, U8CPU r, + U8CPU g, U8CPU b) +{ + a = ((a << 1) - ((a >> 4 << 4) | (a >> 4))) >> 4; + r = ((r << 1) - ((r >> 4 << 4) | (r >> 4))) >> 4; + g = ((g << 1) - ((g >> 4 << 4) | (g >> 4))) >> 4; + b = ((b << 1) - ((b >> 4 << 4) | (b >> 4))) >> 4; + + return SkPackARGB4444(a, r, g, b); +} + +static inline SkPMColor16 SkDitherPixel32To4444(SkPMColor c) +{ + return SkDitherARGB32To4444(SkGetPackedA32(c), SkGetPackedR32(c), + SkGetPackedG32(c), SkGetPackedB32(c)); +} + +/* Assumes 16bit is in standard RGBA order. + Transforms a normal ARGB_8888 into the same byte order as + expanded ARGB_4444, but keeps each component 8bits +*/ +static uint32_t SkExpand_8888(SkPMColor c) +{ + return (((c >> SK_R32_SHIFT) & 0xFF) << 24) | + (((c >> SK_G32_SHIFT) & 0xFF) << 8) | + (((c >> SK_B32_SHIFT) & 0xFF) << 16) | + (((c >> SK_A32_SHIFT) & 0xFF) << 0); +} + +/* Undo the operation of SkExpand_8888, turning the argument back into + a SkPMColor. +*/ +static SkPMColor SkCompact_8888(uint32_t c) +{ + return (((c >> 24) & 0xFF) << SK_R32_SHIFT) | + (((c >> 8) & 0xFF) << SK_G32_SHIFT) | + (((c >> 16) & 0xFF) << SK_B32_SHIFT) | + (((c >> 0) & 0xFF) << SK_A32_SHIFT); +} + +/* Like SkExpand_8888, this transforms a pmcolor into the expanded 4444 format, + but this routine just keeps the high 4bits of each component in the low + 4bits of the result (just like a newly expanded PMColor16). +*/ +static uint32_t SkExpand32_4444(SkPMColor c) +{ + return (((c >> (SK_R32_SHIFT + 4)) & 0xF) << 24) | + (((c >> (SK_G32_SHIFT + 4)) & 0xF) << 8) | + (((c >> (SK_B32_SHIFT + 4)) & 0xF) << 16) | + (((c >> (SK_A32_SHIFT + 4)) & 0xF) << 0); +} + +// takes two values and alternamtes them as part of a memset16 +// used for cheap 2x2 dithering when the colors are opaque +void sk_dither_memset16(uint16_t dst[], uint16_t value, uint16_t other, int n); + +#endif + diff --git a/skia/include/SkColorShader.h b/skia/include/SkColorShader.h new file mode 100644 index 0000000..5a50fa9 --- /dev/null +++ b/skia/include/SkColorShader.h @@ -0,0 +1,62 @@ +/* Copyright 2007, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkColorShader_DEFINED +#define SkColorShader_DEFINED + +#include "SkShader.h" + +/** \class SkColorShader + A Shader that represents a single color. In general, this effect can be + accomplished by just using the color field on the paint, but if an + actual shader object is needed, this provides that feature. +*/ +class SkColorShader : public SkShader { +public: + /** Create a ColorShader that will inherit its color from the Paint + at draw time. + */ + SkColorShader() : fInheritColor(true) {} + /** Create a ColorShader that ignores the color in the paint, and uses the + specified color. Note: like all shaders, at draw time the paint's alpha + will be respected, and is applied to the specified color. + */ + SkColorShader(SkColor c) : fColor(c), fInheritColor(false) {} + + virtual uint32_t getFlags(); + virtual uint8_t getSpan16Alpha() const; + virtual bool setContext(const SkBitmap& device, const SkPaint& paint, + const SkMatrix& matrix); + virtual void shadeSpan(int x, int y, SkPMColor span[], int count); + virtual void shadeSpan16(int x, int y, uint16_t span[], int count); + virtual void shadeSpanAlpha(int x, int y, uint8_t alpha[], int count); + +protected: + SkColorShader(SkFlattenableReadBuffer& ); + virtual void flatten(SkFlattenableWriteBuffer& ); + virtual Factory getFactory() { return CreateProc; } +private: + static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) { + return SkNEW_ARGS(SkColorShader, (buffer)); + } + SkColor fColor; // ignored if fInheritColor is true + SkPMColor fPMColor; // cached after setContext() + uint16_t fColor16; // cached after setContext() + SkBool8 fInheritColor; + + typedef SkShader INHERITED; +}; + +#endif diff --git a/skia/include/SkCornerPathEffect.h b/skia/include/SkCornerPathEffect.h new file mode 100644 index 0000000..c62718b --- /dev/null +++ b/skia/include/SkCornerPathEffect.h @@ -0,0 +1,62 @@ +/* include/graphics/SkCornerPathEffect.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkCornerPathEffect_DEFINED +#define SkCornerPathEffect_DEFINED + +#include "SkPathEffect.h" + +/** \class SkCornerPathEffect + + SkCornerPathEffect is a subclass of SkPathEffect that can turn sharp corners + into various treatments (e.g. rounded corners) +*/ +class SkCornerPathEffect : public SkPathEffect { +public: + /** radius must be > 0 to have an effect. It specifies the distance from each corner + that should be "rounded". + */ + SkCornerPathEffect(SkScalar radius); + virtual ~SkCornerPathEffect(); + + // overrides for SkPathEffect + // This method is not exported to java. + virtual bool filterPath(SkPath* dst, const SkPath& src, SkScalar* width); + + // overrides for SkFlattenable + // This method is not exported to java. + virtual Factory getFactory(); + // This method is not exported to java. + virtual void flatten(SkFlattenableWriteBuffer&); + +protected: + SkCornerPathEffect(SkFlattenableReadBuffer&); + +private: + SkScalar fRadius; + + static SkFlattenable* CreateProc(SkFlattenableReadBuffer&); + + // illegal + SkCornerPathEffect(const SkCornerPathEffect&); + SkCornerPathEffect& operator=(const SkCornerPathEffect&); + + typedef SkPathEffect INHERITED; +}; + +#endif + diff --git a/skia/include/SkCullPoints.h b/skia/include/SkCullPoints.h new file mode 100644 index 0000000..1a4ef52 --- /dev/null +++ b/skia/include/SkCullPoints.h @@ -0,0 +1,76 @@ +/* include/graphics/SkCullPoints.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkCullPoints_DEFINED +#define SkCullPoints_DEFINED + +#include "SkRect.h" + +class SkCullPoints { +public: + SkCullPoints(); + SkCullPoints(const SkIRect& r); + + void reset(const SkIRect& r); + + /** Start a contour at (x,y). Follow this with call(s) to lineTo(...) + */ + void moveTo(int x, int y); + + enum LineToResult { + kNo_Result, //!< line segment was completely clipped out + kLineTo_Result, //!< path.lineTo(pts[1]); + kMoveToLineTo_Result //!< path.moveTo(pts[0]); path.lineTo(pts[1]); + }; + /** Connect a line to the previous call to lineTo (or moveTo). + */ + LineToResult lineTo(int x, int y, SkIPoint pts[2]); + +private: + SkIRect fR; // the caller's rectangle + SkIPoint fAsQuad[4]; // cache of fR as 4 points + SkIPoint fPrevPt; // private state + LineToResult fPrevResult; // private state + + bool sect_test(int x0, int y0, int x1, int y1) const; +}; + +///////////////////////////////////////////////////////////////////////////////// + +class SkPath; + +/** \class SkCullPointsPath + + Similar to SkCullPoints, but this class handles the return values + from lineTo, and automatically builds a SkPath with the result(s). +*/ +class SkCullPointsPath { +public: + SkCullPointsPath(); + SkCullPointsPath(const SkIRect& r, SkPath* dst); + + void reset(const SkIRect& r, SkPath* dst); + + void moveTo(int x, int y); + void lineTo(int x, int y); + +private: + SkCullPoints fCP; + SkPath* fPath; +}; + +#endif diff --git a/skia/include/SkDOM.h b/skia/include/SkDOM.h new file mode 100644 index 0000000..3cd184d --- /dev/null +++ b/skia/include/SkDOM.h @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2006-2008 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SkDOM_DEFINED +#define SkDOM_DEFINED + +#include "SkChunkAlloc.h" +#include "SkMath.h" +#include "SkScalar.h" +#include "SkTemplates.h" + +struct SkDOMNode; +struct SkDOMAttr; + +class SkDOM { +public: + SkDOM(); + ~SkDOM(); + + typedef SkDOMNode Node; + typedef SkDOMAttr Attr; + + /** Returns null on failure + */ + const Node* build(const char doc[], size_t len); + const Node* copy(const SkDOM& dom, const Node* node); + + const Node* getRootNode() const; + + enum Type { + kElement_Type, + kText_Type + }; + Type getType(const Node*) const; + + const char* getName(const Node*) const; + const Node* getFirstChild(const Node*, const char elem[] = NULL) const; + const Node* getNextSibling(const Node*, const char elem[] = NULL) const; + + const char* findAttr(const Node*, const char attrName[]) const; + const Attr* getFirstAttr(const Node*) const; + const Attr* getNextAttr(const Node*, const Attr*) const; + const char* getAttrName(const Node*, const Attr*) const; + const char* getAttrValue(const Node*, const Attr*) const; + + // helpers for walking children + int countChildren(const Node* node, const char elem[] = NULL) const; + + // helpers for calling SkParse + bool findS32(const Node*, const char name[], int32_t* value) const; + bool findScalars(const Node*, const char name[], SkScalar value[], int count) const; + bool findHex(const Node*, const char name[], uint32_t* value) const; + bool findBool(const Node*, const char name[], bool*) const; + int findList(const Node*, const char name[], const char list[]) const; + + bool findScalar(const Node* node, const char name[], SkScalar value[]) const + { + return this->findScalars(node, name, value, 1); + } + + bool hasAttr(const Node*, const char name[], const char value[]) const; + bool hasS32(const Node*, const char name[], int32_t value) const; + bool hasScalar(const Node*, const char name[], SkScalar value) const; + bool hasHex(const Node*, const char name[], uint32_t value) const; + bool hasBool(const Node*, const char name[], bool value) const; + + class AttrIter { + public: + AttrIter(const class SkDOM&, const Node*); + const char* next(const char** value); + private: + const Attr* fAttr; + const Attr* fStop; + }; + + SkDEBUGCODE(void dump(const Node* node = NULL, int tabLevel = 0) const;) + SkDEBUGCODE(static void UnitTest();) + +private: + SkChunkAlloc fAlloc; + Node* fRoot; + friend class AttrIter; + friend class SkDOMParser; +}; + +#endif + diff --git a/skia/include/SkDashPathEffect.h b/skia/include/SkDashPathEffect.h new file mode 100644 index 0000000..cb7161e --- /dev/null +++ b/skia/include/SkDashPathEffect.h @@ -0,0 +1,66 @@ +/* include/graphics/SkDashPathEffect.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkDashPathEffect_DEFINED +#define SkDashPathEffect_DEFINED + +#include "SkPathEffect.h" + +/** \class SkDashPathEffect + + SkDashPathEffect is a subclass of SkPathEffect that implements dashing +*/ +class SkDashPathEffect : public SkPathEffect { +public: + /** The intervals array must contain an even number of entries (>=2), with the even + indices specifying the "on" intervals, and the odd indices specifying the "off" + intervals. phase is an offset into the intervals array (mod the sum of all of the + intervals). + Note: only affects framed paths + */ + SkDashPathEffect(const SkScalar intervals[], int count, SkScalar phase, bool scaleToFit = false); + virtual ~SkDashPathEffect(); + + // overrides for SkPathEffect + // This method is not exported to java. + virtual bool filterPath(SkPath* dst, const SkPath& src, SkScalar* width); + + // overrides for SkFlattenable + // This method is not exported to java. + virtual Factory getFactory(); + // This method is not exported to java. + virtual void flatten(SkFlattenableWriteBuffer&); + +protected: + SkDashPathEffect(SkFlattenableReadBuffer&); + +private: + SkScalar* fIntervals; + int32_t fCount; + // computed from phase + SkScalar fInitialDashLength; + int32_t fInitialDashIndex; + SkScalar fIntervalLength; + bool fScaleToFit; + + static SkFlattenable* CreateProc(SkFlattenableReadBuffer&); + + typedef SkPathEffect INHERITED; +}; + +#endif + diff --git a/skia/include/SkDeque.h b/skia/include/SkDeque.h new file mode 100644 index 0000000..b66a541 --- /dev/null +++ b/skia/include/SkDeque.h @@ -0,0 +1,75 @@ +/* include/graphics/SkDeque.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkDeque_DEFINED +#define SkDeque_DEFINED + +#include "SkTypes.h" + +class SkDeque : SkNoncopyable { +public: + explicit SkDeque(size_t elemSize); + SkDeque(size_t elemSize, void* storage, size_t storageSize); + ~SkDeque(); + + bool empty() const { return 0 == fCount; } + int count() const { return fCount; } + size_t elemSize() const { return fElemSize; } + + const void* front() const; + const void* back() const; + + void* front() { + return (void*)((const SkDeque*)this)->front(); + } + + void* back() { + return (void*)((const SkDeque*)this)->back(); + } + + void* push_front(); + void* push_back(); + + void pop_front(); + void pop_back(); + +private: + struct Head; + +public: + class Iter { + public: + Iter(const SkDeque& d); + void* next(); + + private: + SkDeque::Head* fHead; + char* fPos; + size_t fElemSize; + }; + +private: + Head* fFront; + Head* fBack; + size_t fElemSize; + void* fInitialStorage; + int fCount; + + friend class Iter; +}; + +#endif diff --git a/skia/include/SkDescriptor.h b/skia/include/SkDescriptor.h new file mode 100644 index 0000000..870390b --- /dev/null +++ b/skia/include/SkDescriptor.h @@ -0,0 +1,188 @@ +/* include/graphics/SkDescriptor.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkDescriptor_DEFINED +#define SkDescriptor_DEFINED + +#include "SkTypes.h" + +class SkDescriptor : SkNoncopyable { +public: + static size_t ComputeOverhead(int entryCount) + { + SkASSERT(entryCount >= 0); + return sizeof(SkDescriptor) + entryCount * sizeof(Entry); + } + + static SkDescriptor* Alloc(size_t length) + { + SkASSERT(SkAlign4(length) == length); + SkDescriptor* desc = (SkDescriptor*)sk_malloc_throw(length); + return desc; + } + + static void Free(SkDescriptor* desc) + { + sk_free(desc); + } + + void init() + { + fLength = sizeof(SkDescriptor); + fCount = 0; + } + + uint32_t getLength() const { return fLength; } + + void* addEntry(uint32_t tag, uint32_t length, const void* data = NULL) + { + SkASSERT(tag); + SkASSERT(SkAlign4(length) == length); + SkASSERT(this->findEntry(tag, NULL) == NULL); + + Entry* entry = (Entry*)((char*)this + fLength); + entry->fTag = tag; + entry->fLen = length; + if (data) + memcpy(entry + 1, data, length); + + fCount += 1; + fLength += sizeof(Entry) + length; + return (entry + 1); // return its data + } + + void computeChecksum() + { + fChecksum = SkDescriptor::ComputeChecksum(this); + } + +#ifdef SK_DEBUG + void assertChecksum() const + { + SkASSERT(fChecksum == SkDescriptor::ComputeChecksum(this)); + } +#endif + + const void* findEntry(uint32_t tag, uint32_t* length) const + { + const Entry* entry = (const Entry*)(this + 1); + int count = fCount; + + while (--count >= 0) + { + if (entry->fTag == tag) + { + if (length) + *length = entry->fLen; + return entry + 1; + } + entry = (const Entry*)((const char*)(entry + 1) + entry->fLen); + } + return NULL; + } + + SkDescriptor* copy() const + { + SkDescriptor* desc = SkDescriptor::Alloc(fLength); + memcpy(desc, this, fLength); + return desc; + } + + bool equals(const SkDescriptor& other) const + { + // probe to see if we have a good checksum algo +// SkASSERT(a.fChecksum != b.fChecksum || memcmp(&a, &b, a.fLength) == 0); + + // the first value we should look at is the checksum, so this loop + // should terminate early if they descriptors are different. + // NOTE: if we wrote a sentinel value at the end of each, we chould + // remove the aa < stop test in the loop... + const uint32_t* aa = (const uint32_t*)this; + const uint32_t* bb = (const uint32_t*)&other; + const uint32_t* stop = (const uint32_t*)((const char*)aa + fLength); + do { + if (*aa++ != *bb++) + return false; + } while (aa < stop); + return true; + } + + struct Entry { + uint32_t fTag; + uint32_t fLen; + }; + +#ifdef SK_DEBUG + uint32_t getChecksum() const { return fChecksum; } + uint32_t getCount() const { return fCount; } +#endif + +private: + uint32_t fChecksum; // must be first + uint32_t fLength; // must be second + uint32_t fCount; + + static uint32_t ComputeChecksum(const SkDescriptor* desc) + { + const uint32_t* ptr = (const uint32_t*)desc + 1; // skip the checksum field + const uint32_t* stop = (const uint32_t*)((const char*)desc + desc->fLength); + uint32_t sum = 0; + + SkASSERT(ptr < stop); + do { + sum = (sum << 1) | (sum >> 31); + sum ^= *ptr++; + } while (ptr < stop); + + return sum; + } + + // private so no one can create one except our factories + SkDescriptor() {} +}; + +#include "SkScalerContext.h" + +class SkAutoDescriptor : SkNoncopyable { +public: + SkAutoDescriptor(size_t size) + { + if (size <= sizeof(fStorage)) + fDesc = (SkDescriptor*)(void*)fStorage; + else + fDesc = SkDescriptor::Alloc(size); + } + ~SkAutoDescriptor() + { + if (fDesc != (SkDescriptor*)(void*)fStorage) + SkDescriptor::Free(fDesc); + } + SkDescriptor* getDesc() const { return fDesc; } +private: + enum { + kStorageSize = sizeof(SkDescriptor) + + sizeof(SkDescriptor::Entry) + sizeof(SkScalerContext::Rec) // for rec + + sizeof(SkDescriptor::Entry) + sizeof(void*) // for typeface + + 32 // slop for occational small extras + }; + SkDescriptor* fDesc; + uint32_t fStorage[(kStorageSize + 3) >> 2]; +}; + + +#endif + diff --git a/skia/include/SkDevice.h b/skia/include/SkDevice.h new file mode 100644 index 0000000..cc3fe5e --- /dev/null +++ b/skia/include/SkDevice.h @@ -0,0 +1,126 @@ +#ifndef SkDevice_DEFINED +#define SkDevice_DEFINED + +#include "SkRefCnt.h" +#include "SkBitmap.h" +#include "SkCanvas.h" +#include "SkColor.h" + +class SkDraw; +struct SkIRect; +class SkMatrix; +class SkRegion; + +class SkDevice : public SkRefCnt { +public: + SkDevice(); + /** Construct a new device, extracting the width/height/config/isOpaque values from + the bitmap. If transferPixelOwnership is true, and the bitmap claims to own its + own pixels (getOwnsPixels() == true), then transfer this responsibility to the + device, and call setOwnsPixels(false) on the bitmap. + + Subclasses may override the destructor, which is virtual, even though this class + doesn't have one. SkRefCnt does. + + @param bitmap A copy of this bitmap is made and stored in the device + */ + SkDevice(const SkBitmap& bitmap); + + /** Return the width of the device (in pixels). + */ + int width() const { return fBitmap.width(); } + /** Return the height of the device (in pixels). + */ + int height() const { return fBitmap.height(); } + /** Return the bitmap config of the device's pixels + */ + SkBitmap::Config config() const { return fBitmap.getConfig(); } + /** Returns true if the device's bitmap's config treats every pixels as + implicitly opaque. + */ + bool isOpaque() const { return fBitmap.isOpaque(); } + + /** Return the bounds of the device + */ + void getBounds(SkIRect* bounds) const; + + /** Return true if the specified rectangle intersects the bounds of the + device. If sect is not NULL and there is an intersection, sect returns + the intersection. + */ + bool intersects(const SkIRect& r, SkIRect* sect = NULL) const; + + /** Return the bitmap associated with this device. Call this each time you need + to access the bitmap, as it notifies the subclass to perform any flushing + etc. before you examine the pixels. + @param changePixels set to true if the caller plans to change the pixels + @return the device's bitmap + */ + const SkBitmap& accessBitmap(bool changePixels); + + /** Helper to erase the entire device to the specified color (including + alpha). + */ + void eraseColor(SkColor eraseColor); + + /** Called when this device is installed into a Canvas. Balanaced by a call + to unlockPixels() when the device is removed from a Canvas. + */ + virtual void lockPixels(); + virtual void unlockPixels(); + + /** Called with the correct matrix and clip before this device is drawn + to using those settings. If your subclass overrides this, be sure to + call through to the base class as well. + */ + virtual void setMatrixClip(const SkMatrix&, const SkRegion&); + + /** Called when this device gains focus (i.e becomes the current device + for drawing). + */ + virtual void gainFocus(SkCanvas*) {} + + /** These are called inside the per-device-layer loop for each draw call. + When these are called, we have already applied any saveLayer operations, + and are handling any looping from the paint, and any effects from the + DrawFilter. + */ + virtual void drawPaint(const SkDraw&, const SkPaint& paint); + virtual void drawPoints(const SkDraw&, SkCanvas::PointMode mode, size_t count, + const SkPoint[], const SkPaint& paint); + virtual void drawRect(const SkDraw&, const SkRect& r, + const SkPaint& paint); + virtual void drawPath(const SkDraw&, const SkPath& path, + const SkPaint& paint); + virtual void drawBitmap(const SkDraw&, const SkBitmap& bitmap, + const SkMatrix& matrix, const SkPaint& paint); + virtual void drawSprite(const SkDraw&, const SkBitmap& bitmap, + int x, int y, const SkPaint& paint); + virtual void drawText(const SkDraw&, const void* text, size_t len, + SkScalar x, SkScalar y, const SkPaint& paint); + virtual void drawPosText(const SkDraw&, const void* text, size_t len, + const SkScalar pos[], SkScalar constY, + int scalarsPerPos, const SkPaint& paint); + virtual void drawTextOnPath(const SkDraw&, const void* text, size_t len, + const SkPath& path, const SkMatrix* matrix, + const SkPaint& paint); + virtual void drawVertices(const SkDraw&, SkCanvas::VertexMode, int vertexCount, + const SkPoint verts[], const SkPoint texs[], + const SkColor colors[], SkXfermode* xmode, + const uint16_t indices[], int indexCount, + const SkPaint& paint); + virtual void drawDevice(const SkDraw&, SkDevice*, int x, int y, + const SkPaint&); + +protected: + /** Update as needed the pixel value in the bitmap, so that the caller can access + the pixels directly. Note: only the pixels field should be altered. The config/width/height/rowbytes + must remain unchanged. + */ + virtual void onAccessBitmap(SkBitmap*); + +private: + SkBitmap fBitmap; +}; + +#endif diff --git a/skia/include/SkDiscretePathEffect.h b/skia/include/SkDiscretePathEffect.h new file mode 100644 index 0000000..c5fc9d4 --- /dev/null +++ b/skia/include/SkDiscretePathEffect.h @@ -0,0 +1,57 @@ +/* include/graphics/SkDiscretePathEffect.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkDiscretePathEffect_DEFINED +#define SkDiscretePathEffect_DEFINED + +#include "SkPathEffect.h" + +/** \class SkDiscretePathEffect + + This path effect chops a path into discrete segments, and randomly displaces them. +*/ +class SkDiscretePathEffect : public SkPathEffect { +public: + /** Break the path into segments of segLength length, and randomly move the endpoints + away from the original path by a maximum of deviation. + Note: works on filled or framed paths + */ + SkDiscretePathEffect(SkScalar segLength, SkScalar deviation); + + // overrides for SkPathEffect + // This method is not exported to java. + virtual bool filterPath(SkPath* dst, const SkPath& src, SkScalar* width); + + // overrides for SkFlattenable + // This method is not exported to java. + virtual Factory getFactory(); + // This method is not exported to java. + virtual void flatten(SkFlattenableWriteBuffer&); + +protected: + SkDiscretePathEffect(SkFlattenableReadBuffer&); + +private: + SkScalar fSegLength, fPerterb; + + static SkFlattenable* CreateProc(SkFlattenableReadBuffer&); + + typedef SkPathEffect INHERITED; +}; + +#endif + diff --git a/skia/include/SkDither.h b/skia/include/SkDither.h new file mode 100644 index 0000000..d486437 --- /dev/null +++ b/skia/include/SkDither.h @@ -0,0 +1,189 @@ +#ifndef SkDither_DEFINED +#define SkDither_DEFINED + +#include "SkColorPriv.h" + +#define SK_DitherValueMax4444 15 +#define SK_DitherValueMax565 7 + +/* need to use macros for bit-counts for each component, and then + move these into SkColorPriv.h +*/ + +#define SkDITHER_R32_FOR_565_MACRO(r, d) (r + d - (r >> 5)) +#define SkDITHER_G32_FOR_565_MACRO(g, d) (g + (d >> 1) - (g >> 6)) +#define SkDITHER_B32_FOR_565_MACRO(b, d) (b + d - (b >> 5)) + +#define SkDITHER_A32_FOR_4444_MACRO(a, d) (a + 15 - (a >> 4)) +#define SkDITHER_R32_FOR_4444_MACRO(r, d) (r + d - (r >> 4)) +#define SkDITHER_G32_FOR_4444_MACRO(g, d) (g + d - (g >> 4)) +#define SkDITHER_B32_FOR_4444_MACRO(b, d) (b + d - (b >> 4)) + +#ifdef SK_DEBUG + inline unsigned SkDITHER_R32_FOR_565(unsigned r, unsigned d) + { + SkASSERT(d <= SK_DitherValueMax565); + SkA32Assert(r); + r = SkDITHER_R32_FOR_565_MACRO(r, d); + SkA32Assert(r); + return r; + } + inline unsigned SkDITHER_G32_FOR_565(unsigned g, unsigned d) + { + SkASSERT(d <= SK_DitherValueMax565); + SkG32Assert(g); + g = SkDITHER_G32_FOR_565_MACRO(g, d); + SkG32Assert(g); + return g; + } + inline unsigned SkDITHER_B32_FOR_565(unsigned b, unsigned d) + { + SkASSERT(d <= SK_DitherValueMax565); + SkB32Assert(b); + b = SkDITHER_B32_FOR_565_MACRO(b, d); + SkB32Assert(b); + return b; + } +#else + #define SkDITHER_R32_FOR_565(r, d) SkDITHER_R32_FOR_565_MACRO(r, d) + #define SkDITHER_G32_FOR_565(g, d) SkDITHER_G32_FOR_565_MACRO(g, d) + #define SkDITHER_B32_FOR_565(b, d) SkDITHER_B32_FOR_565_MACRO(b, d) +#endif + +#define SkDITHER_R32To565(r, d) SkR32ToR16(SkDITHER_R32_FOR_565(r, d)) +#define SkDITHER_G32To565(g, d) SkG32ToG16(SkDITHER_G32_FOR_565(g, d)) +#define SkDITHER_B32To565(b, d) SkB32ToB16(SkDITHER_B32_FOR_565(b, d)) + +#define SkDITHER_A32To4444(a, d) SkA32To4444(SkDITHER_A32_FOR_4444_MACRO(a, d)) +#define SkDITHER_R32To4444(r, d) SkR32To4444(SkDITHER_R32_FOR_4444_MACRO(r, d)) +#define SkDITHER_G32To4444(g, d) SkG32To4444(SkDITHER_G32_FOR_4444_MACRO(g, d)) +#define SkDITHER_B32To4444(b, d) SkB32To4444(SkDITHER_B32_FOR_4444_MACRO(b, d)) + +static inline SkPMColor SkDitherARGB32For565(SkPMColor c, unsigned dither) +{ + SkASSERT(dither <= SK_DitherValueMax565); + + unsigned sa = SkGetPackedA32(c); + dither = SkAlphaMul(dither, SkAlpha255To256(sa)); + + unsigned sr = SkGetPackedR32(c); + unsigned sg = SkGetPackedG32(c); + unsigned sb = SkGetPackedB32(c); + sr = SkDITHER_R32_FOR_565(sr, dither); + sg = SkDITHER_G32_FOR_565(sg, dither); + sb = SkDITHER_B32_FOR_565(sb, dither); + + return SkPackARGB32(sa, sr, sg, sb); +} + +static inline SkPMColor SkDitherRGB32For565(SkPMColor c, unsigned dither) +{ + SkASSERT(dither <= SK_DitherValueMax565); + + unsigned sr = SkGetPackedR32(c); + unsigned sg = SkGetPackedG32(c); + unsigned sb = SkGetPackedB32(c); + sr = SkDITHER_R32_FOR_565(sr, dither); + sg = SkDITHER_G32_FOR_565(sg, dither); + sb = SkDITHER_B32_FOR_565(sb, dither); + + return SkPackARGB32(0xFF, sr, sg, sb); +} + +static inline uint16_t SkDitherRGBTo565(U8CPU r, U8CPU g, U8CPU b, + unsigned dither) +{ + SkASSERT(dither <= SK_DitherValueMax565); + r = SkDITHER_R32To565(r, dither); + g = SkDITHER_G32To565(g, dither); + b = SkDITHER_B32To565(b, dither); + return SkPackRGB16(r, g, b); +} + +static inline uint16_t SkDitherRGB32To565(SkPMColor c, unsigned dither) +{ + SkASSERT(dither <= SK_DitherValueMax565); + + unsigned sr = SkGetPackedR32(c); + unsigned sg = SkGetPackedG32(c); + unsigned sb = SkGetPackedB32(c); + sr = SkDITHER_R32To565(sr, dither); + sg = SkDITHER_G32To565(sg, dither); + sb = SkDITHER_B32To565(sb, dither); + + return SkPackRGB16(sr, sg, sb); +} + +static inline uint16_t SkDitherARGB32To565(U8CPU sa, SkPMColor c, unsigned dither) +{ + SkASSERT(dither <= SK_DitherValueMax565); + dither = SkAlphaMul(dither, SkAlpha255To256(sa)); + + unsigned sr = SkGetPackedR32(c); + unsigned sg = SkGetPackedG32(c); + unsigned sb = SkGetPackedB32(c); + sr = SkDITHER_R32To565(sr, dither); + sg = SkDITHER_G32To565(sg, dither); + sb = SkDITHER_B32To565(sb, dither); + + return SkPackRGB16(sr, sg, sb); +} + +///////////////////////// 4444 + +static inline SkPMColor16 SkDitherARGB32To4444(U8CPU a, U8CPU r, U8CPU g, + U8CPU b, unsigned dither) +{ + dither = SkAlphaMul(dither, SkAlpha255To256(a)); + + a = SkDITHER_A32To4444(a, dither); + r = SkDITHER_R32To4444(r, dither); + g = SkDITHER_G32To4444(g, dither); + b = SkDITHER_B32To4444(b, dither); + + return SkPackARGB4444(a, r, g, b); +} + +static inline SkPMColor16 SkDitherARGB32To4444(SkPMColor c, unsigned dither) +{ + unsigned a = SkGetPackedA32(c); + unsigned r = SkGetPackedR32(c); + unsigned g = SkGetPackedG32(c); + unsigned b = SkGetPackedB32(c); + + dither = SkAlphaMul(dither, SkAlpha255To256(a)); + + a = SkDITHER_A32To4444(a, dither); + r = SkDITHER_R32To4444(r, dither); + g = SkDITHER_G32To4444(g, dither); + b = SkDITHER_B32To4444(b, dither); + + return SkPackARGB4444(a, r, g, b); +} + +// TODO: need dither routines for 565 -> 4444 + +// this toggles between a 4x4 and a 1x4 array +//#define ENABLE_DITHER_MATRIX_4X4 + +#ifdef ENABLE_DITHER_MATRIX_4X4 + extern const uint8_t gDitherMatrix_4Bit_4X4[4][4]; + extern const uint8_t gDitherMatrix_3Bit_4X4[4][4]; + + #define DITHER_4444_SCAN(y) const uint8_t* dither_scan = gDitherMatrix_4Bit_4X4[(y) & 3] + #define DITHER_565_SCAN(y) const uint8_t* dither_scan = gDitherMatrix_3Bit_4X4[(y) & 3] + + #define DITHER_VALUE(x) dither_scan[(x) & 3] +#else + extern const uint16_t gDitherMatrix_4Bit_16[4]; + extern const uint16_t gDitherMatrix_3Bit_16[4]; + + #define DITHER_4444_SCAN(y) const uint16_t dither_scan = gDitherMatrix_4Bit_16[(y) & 3] + #define DITHER_565_SCAN(y) const uint16_t dither_scan = gDitherMatrix_3Bit_16[(y) & 3] + + #define DITHER_VALUE(x) ((dither_scan >> (((x) & 3) << 2)) & 0xF) +#endif + +#define DITHER_INC_X(x) ++(x) + +#endif diff --git a/skia/include/SkDraw.h b/skia/include/SkDraw.h new file mode 100644 index 0000000..39fe9206 --- /dev/null +++ b/skia/include/SkDraw.h @@ -0,0 +1,125 @@ +/* libs/graphics/sgl/SkDraw.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkDraw_DEFINED +#define SkDraw_DEFINED + +#include "SkBitmap.h" +#include "SkCanvas.h" +#include "SkMask.h" +#include "SkMatrix.h" +#include "SkPaint.h" +#include "SkRect.h" +#include "SkAutoKern.h" + +class SkBounder; +class SkDevice; +class SkPath; +class SkRegion; +struct SkDrawProcs; + +class SkDraw { +public: + SkDraw() : fDevice(NULL), fBounder(NULL), fProcs(NULL) {} + SkDraw(const SkDraw& src); + + void drawPaint(const SkPaint&) const; + void drawPoints(SkCanvas::PointMode, size_t count, const SkPoint[], + const SkPaint&) const; + void drawRect(const SkRect&, const SkPaint&) const; + /* To save on mallocs, we allow a flag that tells us that srcPath is + mutable, so that we don't have to make copies of it as we transform it. + */ + void drawPath(const SkPath& srcPath, const SkPaint&, + const SkMatrix* prePathMatrix, bool pathIsMutable) const; + void drawBitmap(const SkBitmap&, const SkMatrix&, const SkPaint&) const; + void drawSprite(const SkBitmap&, int x, int y, const SkPaint&) const; + void drawText(const char text[], size_t byteLength, SkScalar x, + SkScalar y, const SkPaint& paint) const; + void drawPosText(const char text[], size_t byteLength, + const SkScalar pos[], SkScalar constY, + int scalarsPerPosition, const SkPaint& paint) const; + void drawTextOnPath(const char text[], size_t byteLength, + const SkPath&, const SkMatrix*, const SkPaint&) const; + void drawVertices(SkCanvas::VertexMode vmode, int count, + const SkPoint vertices[], const SkPoint textures[], + const SkColor colors[], SkXfermode* mode, + const uint16_t indices[], int ptCount, + const SkPaint& paint) const; + + void drawPath(const SkPath& src, const SkPaint& paint) const { + this->drawPath(src, paint, NULL, false); + } + + /** Helper function that creates a mask from a path and an optional maskfilter. + Note however, that the resulting mask will not have been actually filtered, + that must be done afterwards (by calling filterMask). The maskfilter is provided + solely to assist in computing the mask's bounds (if the mode requests that). + */ + static bool DrawToMask(const SkPath& devPath, const SkIRect* clipBounds, + SkMaskFilter* filter, const SkMatrix* filterMatrix, + SkMask* mask, SkMask::CreateMode mode); + +private: + void drawText_asPaths(const char text[], size_t byteLength, + SkScalar x, SkScalar y, const SkPaint&) const; + void drawDevMask(const SkMask& mask, const SkPaint&) const; + void drawBitmapAsMask(const SkBitmap&, const SkPaint&) const; + +public: + const SkBitmap* fBitmap; // required + const SkMatrix* fMatrix; // required + const SkRegion* fClip; // required + SkDevice* fDevice; // optional + SkBounder* fBounder; // optional + SkDrawProcs* fProcs; // optional + +#ifdef SK_DEBUG + void validate() const; +#endif +}; + +#include "SkGlyphCache.h" + +class SkTextToPathIter { +public: + SkTextToPathIter(const char text[], size_t length, const SkPaint&, + bool applyStrokeAndPathEffects, bool forceLinearTextOn); + ~SkTextToPathIter(); + + const SkPaint& getPaint() const { return fPaint; } + SkScalar getPathScale() const { return fScale; } + + const SkPath* next(SkScalar* xpos); //!< returns nil when there are no more paths + +private: + SkGlyphCache* fCache; + SkPaint fPaint; + SkScalar fScale; + SkFixed fPrevAdvance; + const char* fText; + const char* fStop; + SkMeasureCacheProc fGlyphCacheProc; + + const SkPath* fPath; // returned in next + SkScalar fXPos; // accumulated xpos, returned in next + SkAutoKern fAutoKern; +}; + +#endif + + diff --git a/skia/include/SkDrawExtraPathEffect.h b/skia/include/SkDrawExtraPathEffect.h new file mode 100644 index 0000000..50a43c6 --- /dev/null +++ b/skia/include/SkDrawExtraPathEffect.h @@ -0,0 +1,6 @@ +#ifndef SK_DRAW_EXTRA_PATH_EFFECT_H +#define SK_DRAW_EXTRA_PATH_EFFECT_H +class SkAnimator; +void InitializeSkExtraPathEffects(SkAnimator* animator); +#endif + diff --git a/skia/include/SkDrawFilter.h b/skia/include/SkDrawFilter.h new file mode 100644 index 0000000..ddf0f74 --- /dev/null +++ b/skia/include/SkDrawFilter.h @@ -0,0 +1,40 @@ +#ifndef SkDrawFilter_DEFINED +#define SkDrawFilter_DEFINED + +#include "SkRefCnt.h" + +////////////////// EXPERIMENTAL <reed> ////////////////////////// + +class SkCanvas; +class SkPaint; + +/** Right before something is being draw, filter() is called with the + current canvas and paint. If it returns true, then drawing proceeds + with the (possibly modified) canvas/paint, and then restore() is called + to restore the canvas/paint to their state before filter() was called. + If filter returns false, canvas/paint should not have been changed, and + restore() will not be called. +*/ +class SkDrawFilter : public SkRefCnt { +public: + enum Type { + kPaint_Type, + kPoint_Type, + kLine_Type, + kBitmap_Type, + kRect_Type, + kPath_Type, + kText_Type + }; + + /** Return true to allow the draw to continue (with possibly modified + canvas/paint). If true is returned, then restore() will be called. + */ + virtual bool filter(SkCanvas*, SkPaint*, Type) = 0; + /** If filter() returned true, then restore() will be called to restore the + canvas/paint to their previous states + */ + virtual void restore(SkCanvas*, SkPaint*, Type) = 0; +}; + +#endif diff --git a/skia/include/SkDrawLooper.h b/skia/include/SkDrawLooper.h new file mode 100644 index 0000000..80c11c3 --- /dev/null +++ b/skia/include/SkDrawLooper.h @@ -0,0 +1,41 @@ +#ifndef SkDrawLooper_DEFINED +#define SkDrawLooper_DEFINED + +#include "SkFlattenable.h" + +////////////////// EXPERIMENTAL <reed> ////////////////////////// + +class SkCanvas; +class SkPaint; + +/** \class SkDrawLooper + Subclasses of SkDrawLooper can be attached to a SkPaint. Where they are, + and something is drawn to a canvas with that paint, the looper subclass will + be called, allowing it to modify the canvas and/or paint for that draw call. + More than that, via the next() method, the looper can modify the draw to be + invoked multiple times (hence the name loop-er), allow it to perform effects + like shadows or frame/fills, that require more than one pass. +*/ +class SkDrawLooper : public SkFlattenable { +public: + /** Called right before something is being drawn to the specified canvas + with the specified paint. Subclass that want to modify either parameter + can do so now. + */ + virtual void init(SkCanvas*, SkPaint*) {} + /** Called in a loop (after init()). Each time true is returned, the object + is drawn (possibly with a modified canvas and/or paint). When false is + finally returned, drawing for the object stops. + */ + virtual bool next() { return false; } + /** Called after the looper has finally returned false from next(), allowing + the looper to restore the canvas/paint to their original states. + <reed> is this required, since the subclass knows when it is done??? + <reed> should we pass the canvas/paint here, and/or to the next call + so that subclasses don't need to retain pointers to them during the + loop? + */ + virtual void restore() {} +}; + +#endif diff --git a/skia/include/SkEmbossMaskFilter.h b/skia/include/SkEmbossMaskFilter.h new file mode 100644 index 0000000..dfb8cc6 --- /dev/null +++ b/skia/include/SkEmbossMaskFilter.h @@ -0,0 +1,64 @@ +/* include/graphics/SkEmbossMaskFilter.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkEmbossMaskFilter_DEFINED +#define SkEmbossMaskFilter_DEFINED + +#include "SkMaskFilter.h" + +/** \class SkEmbossMaskFilter + + This mask filter creates a 3D emboss look, by specifying a light and blur amount. +*/ +class SkEmbossMaskFilter : public SkMaskFilter { +public: + struct Light { + SkScalar fDirection[3]; // x,y,z + uint16_t fPad; + uint8_t fAmbient; + uint8_t fSpecular; // exponent, 4.4 right now + }; + + SkEmbossMaskFilter(const Light& light, SkScalar blurRadius); + + // overrides from SkMaskFilter + // This method is not exported to java. + virtual SkMask::Format getFormat(); + // This method is not exported to java. + virtual bool filterMask(SkMask* dst, const SkMask& src, const SkMatrix& matrix, SkIPoint* margin); + + // overrides from SkFlattenable + + // This method is not exported to java. + virtual Factory getFactory(); + // This method is not exported to java. + virtual void flatten(SkFlattenableWriteBuffer&); + +protected: + SkEmbossMaskFilter(SkFlattenableReadBuffer&); + +private: + Light fLight; + SkScalar fBlurRadius; + + static SkFlattenable* CreateProc(SkFlattenableReadBuffer&); + + typedef SkMaskFilter INHERITED; +}; + +#endif + diff --git a/skia/include/SkEvent.h b/skia/include/SkEvent.h new file mode 100644 index 0000000..1830be3 --- /dev/null +++ b/skia/include/SkEvent.h @@ -0,0 +1,245 @@ +/* include/graphics/SkEvent.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkEvent_DEFINED +#define SkEvent_DEFINED + +#include "SkDOM.h" +#include "SkMetaData.h" +#include "SkString.h" + +//class SkOSWindow; + +/** Unique 32bit id used to identify an instance of SkEventSink. When events are + posted, they are posted to a specific sinkID. When it is time to dispatch the + event, the sinkID is used to find the specific SkEventSink object. If it is found, + its doEvent() method is called with the event. +*/ +typedef uint32_t SkEventSinkID; + +/** \class SkEvent + + SkEvents are used to communicate type-safe information to SkEventSinks. + SkEventSinks (including SkViews) each have a unique ID, which is stored + in an event. This ID is used to target the event once it has been "posted". +*/ +class SkEvent { +public: + /** Default construct, creating an empty event. + */ + SkEvent(); + /** Construct a new event with the specified type. + */ + explicit SkEvent(const SkString& type); + /** Construct a new event with the specified type. + */ + explicit SkEvent(const char type[]); + /** Construct a new event by copying the fields from the src event. + */ + SkEvent(const SkEvent& src); + ~SkEvent(); + +// /** Return the event's type (will never be null) */ +// const char* getType() const; + /** Copy the event's type into the specified SkString parameter */ + void getType(SkString* str) const; + /** Returns true if the event's type matches exactly the specified type (case sensitive) */ + bool isType(const SkString& str) const; + /** Returns true if the event's type matches exactly the specified type (case sensitive) */ + bool isType(const char type[], size_t len = 0) const; + /** Set the event's type to the specified string. + In XML, use the "type" attribute. + */ + void setType(const SkString&); + /** Set the event's type to the specified string. + In XML, use the "type" attribute. + */ + void setType(const char type[], size_t len = 0); + + /** Return the event's unnamed 32bit field. Default value is 0 */ + uint32_t getFast32() const { return f32; } + /** Set the event's unnamed 32bit field. In XML, use + the subelement <data fast32=... /> + */ + void setFast32(uint32_t x) { f32 = x; } + + /** Return true if the event contains the named 32bit field, and return the field + in value (if value is non-null). If there is no matching named field, return false + and ignore the value parameter. + */ + bool findS32(const char name[], int32_t* value = NULL) const { return fMeta.findS32(name, value); } + /** Return true if the event contains the named SkScalar field, and return the field + in value (if value is non-null). If there is no matching named field, return false + and ignore the value parameter. + */ + bool findScalar(const char name[], SkScalar* value = NULL) const { return fMeta.findScalar(name, value); } + /** Return true if the event contains the named SkScalar field, and return the fields + in value[] (if value is non-null), and return the number of SkScalars in count (if count is non-null). + If there is no matching named field, return false and ignore the value and count parameters. + */ + const SkScalar* findScalars(const char name[], int* count, SkScalar values[] = NULL) const { return fMeta.findScalars(name, count, values); } + /** Return the value of the named string field, or if no matching named field exists, return null. + */ + const char* findString(const char name[]) const { return fMeta.findString(name); } + /** Return true if the event contains the named pointer field, and return the field + in value (if value is non-null). If there is no matching named field, return false + and ignore the value parameter. + */ + bool findPtr(const char name[], void** value) const { return fMeta.findPtr(name, value); } + bool findBool(const char name[], bool* value) const { return fMeta.findBool(name, value); } + + /** Returns true if ethe event contains the named 32bit field, and if it equals the specified value */ + bool hasS32(const char name[], int32_t value) const { return fMeta.hasS32(name, value); } + /** Returns true if ethe event contains the named SkScalar field, and if it equals the specified value */ + bool hasScalar(const char name[], SkScalar value) const { return fMeta.hasScalar(name, value); } + /** Returns true if ethe event contains the named string field, and if it equals (using strcmp) the specified value */ + bool hasString(const char name[], const char value[]) const { return fMeta.hasString(name, value); } + /** Returns true if ethe event contains the named pointer field, and if it equals the specified value */ + bool hasPtr(const char name[], void* value) const { return fMeta.hasPtr(name, value); } + bool hasBool(const char name[], bool value) const { return fMeta.hasBool(name, value); } + + /** Add/replace the named 32bit field to the event. In XML use the subelement <data name=... s32=... /> */ + void setS32(const char name[], int32_t value) { fMeta.setS32(name, value); } + /** Add/replace the named SkScalar field to the event. In XML use the subelement <data name=... scalar=... /> */ + void setScalar(const char name[], SkScalar value) { fMeta.setScalar(name, value); } + /** Add/replace the named SkScalar[] field to the event. */ + SkScalar* setScalars(const char name[], int count, const SkScalar values[] = NULL) { return fMeta.setScalars(name, count, values); } + /** Add/replace the named string field to the event. In XML use the subelement <data name=... string=... */ + void setString(const char name[], const SkString& value) { fMeta.setString(name, value.c_str()); } + /** Add/replace the named string field to the event. In XML use the subelement <data name=... string=... */ + void setString(const char name[], const char value[]) { fMeta.setString(name, value); } + /** Add/replace the named pointer field to the event. There is no XML equivalent for this call */ + void setPtr(const char name[], void* value) { fMeta.setPtr(name, value); } + void setBool(const char name[], bool value) { fMeta.setBool(name, value); } + + /** Return the underlying metadata object */ + SkMetaData& getMetaData() { return fMeta; } + /** Return the underlying metadata object */ + const SkMetaData& getMetaData() const { return fMeta; } + + void tron() { SkDEBUGCODE(fDebugTrace = true;) } + void troff() { SkDEBUGCODE(fDebugTrace = false;) } + bool isDebugTrace() const + { +#ifdef SK_DEBUG + return fDebugTrace; +#else + return false; +#endif + } + + /** Call this to initialize the event from the specified XML node */ + void inflate(const SkDOM&, const SkDOM::Node*); + + SkDEBUGCODE(void dump(const char title[] = NULL);) + + /** Post the specified event to the event queue, targeting the specified eventsink, with an optional + delay. The event must be dynamically allocated for this. It cannot be a global or on the stack. + After this call, ownership is transfered to the system, so the caller must not retain + the event's ptr. Returns false if the event could not be posted (which means it will have been deleted). + */ + static bool Post(SkEvent* evt, SkEventSinkID targetID, SkMSec delay = 0); + /** Post the specified event to the event queue, targeting the specified eventsink, to be delivered on/after the + specified millisecond time. The event must be dynamically allocated for this. It cannot be a global or on the stack. + After this call, ownership is transfered to the system, so the caller must not retain + the event's ptr. Returns false if the event could not be posted (which means it will have been deleted). + */ + static bool PostTime(SkEvent* evt, SkEventSinkID targetID, SkMSec time); + + /** Helper method for calling SkEvent::PostTime(this, ...), where the caller specifies a delay. + The real "time" will be computed automatically by sampling the clock and adding its value + to delay. + */ + bool post(SkEventSinkID sinkID, SkMSec delay = 0) + { + return SkEvent::Post(this, sinkID, delay); + } + + void postTime(SkEventSinkID sinkID, SkMSec time) + { + SkEvent::PostTime(this, sinkID, time); + } + + /////////////////////////////////////////////// + /** Porting layer must call these functions **/ + /////////////////////////////////////////////// + + /** Global initialization function for the SkEvent system. Should be called exactly + once before any other event method is called, and should be called after the + call to SkGraphics::Init(). + */ + static void Init(); + /** Global cleanup function for the SkEvent system. Should be called exactly once after + all event methods have been called, and should be called before calling SkGraphics::Term(). + */ + static void Term(); + + /** Call this to process one event from the queue. If it returns true, there are more events + to process. + */ + static bool ProcessEvent(); + /** Call this whenever the requested timer has expired (requested by a call to SetQueueTimer). + It will post any delayed events whose time as "expired" onto the event queue. + It may also call SignalQueueTimer() and SignalNonEmptyQueue(). + */ + static void ServiceQueueTimer(); + + //////////////////////////////////////////////////// + /** Porting layer must implement these functions **/ + //////////////////////////////////////////////////// + + /** Called whenever an SkEvent is posted to an empty queue, so that the OS + can be told to later call Dequeue(). + */ + static void SignalNonEmptyQueue(); + /** Called whenever the delay until the next delayed event changes. If zero is + passed, then there are no more queued delay events. + */ + static void SignalQueueTimer(SkMSec delay); + +#ifndef SK_USE_WXWIDGETS +#ifdef SK_BUILD_FOR_WIN + static bool WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); +#elif defined(SK_BUILD_FOR_UNIXx) + static uint32_t HandleTimer(uint32_t, void*); + static bool WndProc(Display*, Window, XEvent&); +#endif +#else + // Don't know yet what this will be + //static bool CustomEvent(); +#endif + +private: + SkMetaData fMeta; + mutable char* fType; // may be characters with low bit set to know that it is not a pointer + uint32_t f32; + SkDEBUGCODE(bool fDebugTrace;) + + // these are for our implementation of the event queue + SkEventSinkID fTargetID; + SkMSec fTime; + SkEvent* fNextEvent; // either in the delay or normal event queue + void initialize(const char* type, size_t typeLen); + + static bool Enqueue(SkEvent* evt); + static SkMSec EnqueueTime(SkEvent* evt, SkMSec time); + static SkEvent* Dequeue(SkEventSinkID* targetID); + static bool QHasEvents(); +}; + +#endif + diff --git a/skia/include/SkEventSink.h b/skia/include/SkEventSink.h new file mode 100644 index 0000000..071b651 --- /dev/null +++ b/skia/include/SkEventSink.h @@ -0,0 +1,104 @@ +/* include/graphics/SkEventSink.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkEventSink_DEFINED +#define SkEventSink_DEFINED + +#include "SkRefCnt.h" +#include "SkEvent.h" + +struct SkTagList; + +/** \class SkEventSink + + SkEventSink is the base class for all objects that receive SkEvents. +*/ +class SkEventSink : public SkRefCnt { +public: + SkEventSink(); + virtual ~SkEventSink(); + + /** Returns this eventsink's unique ID. Use this to post SkEvents to + this eventsink. + */ + SkEventSinkID getSinkID() const { return fID; } + + /** Call this to pass an event to this object for processing. Returns true if the + event was handled. + */ + bool doEvent(const SkEvent&); + /** Returns true if the sink (or one of its subclasses) understands the event as a query. + If so, the sink may modify the event to communicate its "answer". + */ + bool doQuery(SkEvent* query); + + /** Add sinkID to the list of listeners, to receive events from calls to sendToListeners() + and postToListeners(). If sinkID already exists in the listener list, no change is made. + */ + void addListenerID(SkEventSinkID sinkID); + /** Copy listeners from one event sink to another, typically from parent to child. + @param from the event sink to copy the listeners from + */ + void copyListeners(const SkEventSink& from); + /** Remove sinkID from the list of listeners. If sinkID does not appear in the list, + no change is made. + */ + void removeListenerID(SkEventSinkID); + /** Returns true if there are 1 or more listeners attached to this eventsink + */ + bool hasListeners() const; + /** Posts a copy of evt to each of the eventsinks in the lisener list. + */ + void postToListeners(const SkEvent& evt, SkMSec delay = 0); + + enum EventResult { + kHandled_EventResult, //!< the eventsink returned true from its doEvent method + kNotHandled_EventResult, //!< the eventsink returned false from its doEvent method + kSinkNotFound_EventResult //!< no matching eventsink was found for the event's getSink(). + }; + /** DoEvent handles searching for an eventsink object that matches the targetID. + If one is found, it calls the sink's doEvent method, returning + either kHandled_EventResult or kNotHandled_EventResult. If no matching + eventsink is found, kSinkNotFound_EventResult is returned. + */ + static EventResult DoEvent(const SkEvent&, SkEventSinkID targetID); + + /** Returns the matching eventsink, or null if not found + */ + static SkEventSink* FindSink(SkEventSinkID); + +protected: + /** Override this to handle events in your subclass. Be sure to call the inherited version + for events that you don't handle. + */ + virtual bool onEvent(const SkEvent&); + virtual bool onQuery(SkEvent*); + + SkTagList* findTagList(U8CPU tag) const; + void addTagList(SkTagList*); + void removeTagList(U8CPU tag); + +private: + SkEventSinkID fID; + SkTagList* fTagHead; + + // for our private link-list + SkEventSink* fNextSink; +}; + +#endif + diff --git a/skia/include/SkFlattenable.h b/skia/include/SkFlattenable.h new file mode 100644 index 0000000..8b2db8a --- /dev/null +++ b/skia/include/SkFlattenable.h @@ -0,0 +1,202 @@ +/* include/graphics/SkFlattenable.h + ** + ** Copyright 2006, Google Inc. + ** + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** + ** http://www.apache.org/licenses/LICENSE-2.0 + ** + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + */ + +#ifndef SkFlattenable_DEFINED +#define SkFlattenable_DEFINED + +#include "SkRefCnt.h" +#include "SkBitmap.h" +#include "SkReader32.h" +#include "SkTDArray.h" +#include "SkWriter32.h" + +class SkFlattenableReadBuffer; +class SkFlattenableWriteBuffer; + +/** \class SkFlattenable + + SkFlattenable is the base class for objects that need to be flattened + into a data stream for either transport or as part of the key to the + font cache. + */ +class SkFlattenable : public SkRefCnt { +public: + typedef SkFlattenable* (*Factory)(SkFlattenableReadBuffer&); + + SkFlattenable() {} + + /** Implement this to return a factory function pointer that can be called + to recreate your class given a buffer (previously written to by your + override of flatten(). + */ + virtual Factory getFactory() = 0; + /** Override this to write data specific to your subclass into the buffer, + being sure to call your super-class' version first. This data will later + be passed to your Factory function, returned by getFactory(). + */ + virtual void flatten(SkFlattenableWriteBuffer&); + + static Factory NameToFactory(const char name[]); + static const char* FactoryToName(Factory); + static void Register(const char name[], Factory); + + class Registrar { + public: + Registrar(const char name[], Factory factory) { + SkFlattenable::Register(name, factory); + } + }; + +protected: + SkFlattenable(SkFlattenableReadBuffer&) {} +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +class SkTypeface; + +class SkFlattenableReadBuffer : public SkReader32 { +public: + SkFlattenableReadBuffer(); + explicit SkFlattenableReadBuffer(const void* data); + SkFlattenableReadBuffer(const void* data, size_t size); + + void setRefCntArray(SkRefCnt* array[], int count) { + fRCArray = array; + fRCCount = count; + } + + void setTypefaceArray(SkTypeface* array[], int count) { + fTFArray = array; + fTFCount = count; + } + + void setFactoryPlayback(SkFlattenable::Factory array[], int count) { + fFactoryArray = array; + fFactoryCount = count; + } + + SkTypeface* readTypeface(); + SkRefCnt* readRefCnt(); + void* readFunctionPtr(); + SkFlattenable* readFlattenable(); + +private: + SkRefCnt** fRCArray; + int fRCCount; + + SkTypeface** fTFArray; + int fTFCount; + + SkFlattenable::Factory* fFactoryArray; + int fFactoryCount; + + typedef SkReader32 INHERITED; +}; + +/////////////////////////////////////////////////////////////////////////////// + +#include "SkPtrRecorder.h" + +class SkRefCntRecorder : public SkPtrRecorder { +public: + virtual ~SkRefCntRecorder(); + + /** Add a refcnt object to the set and ref it if not already present, + or if it is already present, do nothing. Either way, returns 0 if obj + is null, or a base-1 index if obj is not null. + */ + uint32_t record(SkRefCnt* ref) { + return this->recordPtr(ref); + } + + // This does not change the owner counts on the objects + void get(SkRefCnt* array[]) const { + this->getPtrs((void**)array); + } + +protected: + // overrides + virtual void incPtr(void*); + virtual void decPtr(void*); + +private: + typedef SkPtrRecorder INHERITED; +}; + +class SkFactoryRecorder : public SkPtrRecorder { +public: + /** Add a factory to the set. If it is null return 0, otherwise return a + base-1 index for the factory. + */ + uint32_t record(SkFlattenable::Factory fact) { + return this->recordPtr((void*)fact); + } + + void get(SkFlattenable::Factory array[]) const { + this->getPtrs((void**)array); + } + +private: + typedef SkPtrRecorder INHERITED; +}; + +class SkFlattenableWriteBuffer : public SkWriter32 { +public: + SkFlattenableWriteBuffer(size_t minSize); + virtual ~SkFlattenableWriteBuffer(); + + void writeTypeface(SkTypeface*); + void writeRefCnt(SkRefCnt*); + void writeFunctionPtr(void*); + void writeFlattenable(SkFlattenable* flattenable); + + SkRefCntRecorder* getTypefaceRecorder() const { return fTFRecorder; } + SkRefCntRecorder* setTypefaceRecorder(SkRefCntRecorder*); + + SkRefCntRecorder* getRefCntRecorder() const { return fRCRecorder; } + SkRefCntRecorder* setRefCntRecorder(SkRefCntRecorder*); + + SkFactoryRecorder* getFactoryRecorder() const { return fFactoryRecorder; } + SkFactoryRecorder* setFactoryRecorder(SkFactoryRecorder*); + + enum Flags { + kCrossProcess_Flag = 0x01 + }; + Flags getFlags() const { return fFlags; } + void setFlags(Flags flags) { fFlags = flags; } + + bool isCrossProcess() const { return (fFlags & kCrossProcess_Flag) != 0; } + + bool persistBitmapPixels() const { + return (fFlags & kCrossProcess_Flag) != 0; + } + + bool persistTypeface() const { return (fFlags & kCrossProcess_Flag) != 0; } + +private: + Flags fFlags; + SkRefCntRecorder* fTFRecorder; + SkRefCntRecorder* fRCRecorder; + SkFactoryRecorder* fFactoryRecorder; + + typedef SkWriter32 INHERITED; +}; + +#endif + diff --git a/skia/include/SkFontCodec.h b/skia/include/SkFontCodec.h new file mode 100644 index 0000000..0a4ca05 --- /dev/null +++ b/skia/include/SkFontCodec.h @@ -0,0 +1,37 @@ +/* include/graphics/SkFontCodec.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkFontCodec_DEFINED +#define SkFontCodec_DEFINED + +#include "SkSFNT.h" + +class SkFontCodec { +public: + static void Compress(SkSFNT& font, const char fileName[]); + + /* Format is [count] + [instruction, bitcount] * count + Allocated with sk_malloc() + */ + static U8* BuildInstrHuffmanTable(SkSFNT&); + static U8* BuildOutlineHuffmanTable(SkSFNT& font); + + SkDEBUGCODE(static void UnitTest();) +}; + +#endif + diff --git a/skia/include/SkFontHost.h b/skia/include/SkFontHost.h new file mode 100644 index 0000000..7440295 --- /dev/null +++ b/skia/include/SkFontHost.h @@ -0,0 +1,100 @@ +/* include/graphics/SkFontHost.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkFontHost_DEFINED +#define SkFontHost_DEFINED + +#include "SkScalerContext.h" +#include "SkTypeface.h" + +class SkDescriptor; +class SkStream; +class SkWStream; + +/** \class SkFontHost + + This class is ported to each environment. It is responsible for bridging the gap + between SkTypeface and the resulting platform-specific instance of SkScalerContext. +*/ +class SkFontHost { +public: + /** Return the closest matching typeface given either an existing family + (specified by a typeface in that family) or by a familyName, and a + requested style. + 1) If familyFace is null, use famillyName. + 2) If famillyName is null, use familyFace. + 3) If both are null, return the default font that best matches style + */ + static SkTypeface* FindTypeface(const SkTypeface* familyFace, + const char famillyName[], + SkTypeface::Style style); + + /** Return the typeface associated with the uniqueID, or null if that ID + does not match any faces. + */ + static SkTypeface* ResolveTypeface(uint32_t uniqueID); + + /** Return a new stream to read the font data, or null if the uniqueID does + not match an existing typeface + */ + static SkStream* OpenStream(uint32_t uniqueID); + static void CloseStream(uint32_t uniqueID, SkStream*); + + /** Return a new typeface given the data buffer (owned by the caller). + If the data does not represent a valid font, return null. + */ + static SkTypeface* CreateTypeface(SkStream*); + + /////////////////////////////////////////////////////////////////////////// + + /** Write a unique identifier to the stream, so that the same typeface can + be retrieved with Deserialize(). + */ + static void Serialize(const SkTypeface*, SkWStream*); + static SkTypeface* Deserialize(SkStream*); + + /////////////////////////////////////////////////////////////////////////// + + /** Return a subclass of SkScalarContext + */ + static SkScalerContext* CreateScalerContext(const SkDescriptor* desc); + /** Return a scalercontext using the "fallback" font. If there is no designated + fallback, return null. + */ + static SkScalerContext* CreateFallbackScalerContext(const SkScalerContext::Rec&); + + /** Return the number of bytes (approx) that should be purged from the font + cache. The input parameter is the cache's estimate of how much as been + allocated by the cache so far. + To purge (basically) everything, return the input parameter. + To purge nothing, return 0 + */ + static size_t ShouldPurgeFontCache(size_t sizeAllocatedSoFar); + + /** Return SkScalerContext gamma flag, or 0, based on the paint that will be + used to draw something with antialiasing. + */ + static int ComputeGammaFlag(const SkPaint& paint); + + /** Return NULL or a pointer to 256 bytes for the black (table[0]) and + white (table[1]) gamma tables. + */ + static void GetGammaTables(const uint8_t* tables[2]); +}; + +#endif + diff --git a/skia/include/SkGLCanvas.h b/skia/include/SkGLCanvas.h new file mode 100644 index 0000000..bb731b4 --- /dev/null +++ b/skia/include/SkGLCanvas.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2006-2008 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SkGLCanvas_DEFINED +#define SkGLCanvas_DEFINED + +#include "SkCanvas.h" + +#ifdef SK_BUILD_FOR_MAC + #include <OpenGL/gl.h> +#elif defined(ANDROID) + #include <GLES/gl.h> +#endif + +class SkGLDevice; +class SkGLClipIter; + +class SkGLCanvas : public SkCanvas { +public: + // notice, we do NOT allow the SkCanvas(bitmap) constructor, since that + // does not create a SkGLDevice, which we require + SkGLCanvas(); + virtual ~SkGLCanvas(); + + // overrides from SkCanvas + + virtual bool getViewport(SkIPoint*) const; + virtual bool setViewport(int width, int height); + + virtual SkDevice* createDevice(SkBitmap::Config, int width, int height, + bool isOpaque, bool isForLayer); + + // settings for the global texture cache + + static size_t GetTextureCacheMaxCount(); + static size_t GetTextureCacheMaxSize(); + static void SetTextureCacheMaxCount(size_t count); + static void SetTextureCacheMaxSize(size_t size); + +private: + SkIPoint fViewportSize; + + // need to disallow this guy + virtual SkDevice* setBitmapDevice(const SkBitmap& bitmap) { + sk_throw(); + return NULL; + } + + typedef SkCanvas INHERITED; +}; + +#endif + diff --git a/skia/include/SkGlobals.h b/skia/include/SkGlobals.h new file mode 100644 index 0000000..586f689 --- /dev/null +++ b/skia/include/SkGlobals.h @@ -0,0 +1,68 @@ +/* include/graphics/SkGlobals.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkGlobals_DEFINED +#define SkGlobals_DEFINED + +#include "SkThread.h" + +class SkGlobals { +public: + class Rec { + public: + virtual ~Rec(); + private: + Rec* fNext; + uint32_t fTag; + + friend class SkGlobals; + }; + + /** Look for a matching Rec for the specified tag. If one is found, return it. + If one is not found, if create_proc is null, return null, else + call the proc, and if it returns a Rec, add it to the global list + and return it. + + create_proc can NOT call back into SkGlobals::Find (it would deadlock) + */ + static Rec* Find(uint32_t tag, Rec* (*create_proc)()); + /** Helper for Find, when you want to assert that the Rec is already in the list + */ + static Rec* Get(uint32_t tag) + { + Rec* rec = SkGlobals::Find(tag, NULL); + SkASSERT(rec); + return rec; + } + + // used by porting layer + struct BootStrap { + SkMutex fMutex; + Rec* fHead; + }; + +private: + static void Init(); + static void Term(); + friend class SkGraphics; + + // This last function is implemented in the porting layer + static BootStrap& GetBootStrap(); +}; + +#endif + diff --git a/skia/include/SkGradientShader.h b/skia/include/SkGradientShader.h new file mode 100644 index 0000000..ee1c976 --- /dev/null +++ b/skia/include/SkGradientShader.h @@ -0,0 +1,98 @@ +/* include/graphics/SkGradientShader.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkGradientShader_DEFINED +#define SkGradientShader_DEFINED + +#include "SkShader.h" + +class SkUnitMapper; + +/** \class SkGradientShader + + SkGradientShader hosts factories for creating subclasses of SkShader that + render linear and radial gradients. +*/ +class SkGradientShader { +public: + /** Returns a shader that generates a linear gradient between the two + specified points. + <p /> + CreateLinear returns a shader with a reference count of 1. + The caller should decrement the shader's reference count when done with the shader. + It is an error for count to be < 2. + @param pts The start and end points for the gradient. + @param colors The array[count] of colors, to be distributed between the two points + @param pos May be NULL. array[count] of SkScalars, or NULL, of the relative position of + each corresponding color in the colors array. If this is NULL, + the the colors are distributed evenly between the start and end point. + If this is not null, the values must begin with 0, end with 1.0, and + intermediate values must be strictly increasing. + @param count Must be >=2. The number of colors (and pos if not NULL) entries. + @param mode The tiling mode + @param mapper May be NULL. Callback to modify the spread of the colors. + */ + static SkShader* CreateLinear( const SkPoint pts[2], + const SkColor colors[], const SkScalar pos[], int count, + SkShader::TileMode mode, + SkUnitMapper* mapper = NULL); + + /** Returns a shader that generates a radial gradient given the center and radius. + <p /> + CreateRadial returns a shader with a reference count of 1. + The caller should decrement the shader's reference count when done with the shader. + It is an error for colorCount to be < 2, or for radius to be <= 0. + @param center The center of the circle for this gradient + @param radius Must be positive. The radius of the circle for this gradient + @param colors The array[count] of colors, to be distributed between the center and edge of the circle + @param pos May be NULL. The array[count] of SkScalars, or NULL, of the relative position of + each corresponding color in the colors array. If this is NULL, + the the colors are distributed evenly between the center and edge of the circle. + If this is not null, the values must begin with 0, end with 1.0, and + intermediate values must be strictly increasing. + @param count Must be >= 2. The number of colors (and pos if not NULL) entries + @param mode The tiling mode + @param mapper May be NULL. Callback to modify the spread of the colors. + */ + static SkShader* CreateRadial( const SkPoint& center, SkScalar radius, + const SkColor colors[], const SkScalar pos[], int count, + SkShader::TileMode mode, + SkUnitMapper* mapper = NULL); + + /** Returns a shader that generates a sweep gradient given a center. + <p /> + CreateRadial returns a shader with a reference count of 1. + The caller should decrement the shader's reference count when done with the shader. + It is an error for colorCount to be < 2. + @param cx The X coordinate of the center of the sweep + @param cx The Y coordinate of the center of the sweep + @param colors The array[count] of colors, to be distributed around the center. + @param pos May be NULL. The array[count] of SkScalars, or NULL, of the relative position of + each corresponding color in the colors array. If this is NULL, + the the colors are distributed evenly between the center and edge of the circle. + If this is not null, the values must begin with 0, end with 1.0, and + intermediate values must be strictly increasing. + @param count Must be >= 2. The number of colors (and pos if not NULL) entries + @param mapper May be NULL. Callback to modify the spread of the colors. + */ + static SkShader* CreateSweep(SkScalar cx, SkScalar cy, + const SkColor colors[], const SkScalar pos[], + int count, SkUnitMapper* mapper = NULL); +}; + +#endif + diff --git a/skia/include/SkGraphics.h b/skia/include/SkGraphics.h new file mode 100644 index 0000000..841e1a3 --- /dev/null +++ b/skia/include/SkGraphics.h @@ -0,0 +1,47 @@ +/* include/graphics/SkGraphics.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkGraphics_DEFINED +#define SkGraphics_DEFINED + +#include "SkTypes.h" + +class SkGraphics { +public: + static void Init(bool runUnitTests); + static void Term(); + + /** Return the (approximate) number of bytes used by the font cache. + */ + static size_t GetFontCacheUsed(); + + /** Attempt to purge the font cache until <= the specified amount remains + in the cache. Specifying 0 will attempt to purge the entire cache. + Returns true if some amount was purged from the font cache. + */ + static bool SetFontCacheUsed(size_t usageInBytes); + +private: + /** This is automatically called by SkGraphics::Init(), and must be + implemented by the host OS. This allows the host OS to register a callback + with the C++ runtime to call SkGraphics::FreeCaches() + */ + static void InstallNewHandler(); +}; + +#endif + diff --git a/skia/include/SkImageDecoder.h b/skia/include/SkImageDecoder.h new file mode 100644 index 0000000..8a93ef9 --- /dev/null +++ b/skia/include/SkImageDecoder.h @@ -0,0 +1,261 @@ +/* include/graphics/SkImageDecoder.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkImageDecoder_DEFINED +#define SkImageDecoder_DEFINED + +#include "SkBitmap.h" +#include "SkRefCnt.h" + +class SkStream; + +/** \class SkImageDecoder + + Base class for decoding compressed images into a SkBitmap +*/ +class SkImageDecoder { +public: + virtual ~SkImageDecoder(); + + enum Format { + kUnknown_Format, + kBMP_Format, + kGIF_Format, + kICO_Format, + kJPEG_Format, + kPNG_Format, + kWBMP_Format, + + kLastKnownFormat = kWBMP_Format + }; + + /** Return the compressed data's format (see Format enum) + */ + virtual Format getFormat() const; + + /** Returns true if the decoder should try to dither the resulting image. + The default setting is true. + */ + bool getDitherImage() const { return fDitherImage; } + + /** Set to true if the the decoder should try to dither the resulting image. + The default setting is true. + */ + void setDitherImage(bool dither) { fDitherImage = dither; } + + /** \class Peeker + + Base class for optional callbacks to retrieve meta/chunk data out of + an image as it is being decoded. + */ + class Peeker : public SkRefCnt { + public: + /** Return true to continue decoding, or false to indicate an error, which + will cause the decoder to not return the image. + */ + virtual bool peek(const char tag[], const void* data, size_t length) = 0; + }; + + Peeker* getPeeker() const { return fPeeker; } + Peeker* setPeeker(Peeker*); + + /** \class Peeker + + Base class for optional callbacks to retrieve meta/chunk data out of + an image as it is being decoded. + */ + class Chooser : public SkRefCnt { + public: + virtual void begin(int count) {} + virtual void inspect(int index, SkBitmap::Config config, int width, int height) {} + /** Return the index of the subimage you want, or -1 to choose none of them. + */ + virtual int choose() = 0; + }; + + Chooser* getChooser() const { return fChooser; } + Chooser* setChooser(Chooser*); + + SkBitmap::Allocator* getAllocator() const { return fAllocator; } + SkBitmap::Allocator* setAllocator(SkBitmap::Allocator*); + + // sample-size, if set to > 1, tells the decoder to return a smaller than + // original bitmap, sampling 1 pixel for every size pixels. e.g. if sample + // size is set to 3, then the returned bitmap will be 1/3 as wide and high, + // and will contain 1/9 as many pixels as the original. + // Note: this is a hint, and the codec may choose to ignore this, or only + // approximate the sample size. + int getSampleSize() const { return fSampleSize; } + void setSampleSize(int size); + void resetSampleSize() { this->setSampleSize(1); } + + /** Passed to the decode method. If kDecodeBounds_Mode is passed, then + only the bitmap's width/height/config need be set. If kDecodePixels_Mode + is passed, then the bitmap must have pixels or a pixelRef. + */ + enum Mode { + kDecodeBounds_Mode, //!< only return width/height/config in bitmap + kDecodePixels_Mode //!< return entire bitmap (including pixels) + }; + + /** Given a stream, decode it into the specified bitmap. + If the decoder can decompress the image, it calls bitmap.setConfig(), + and then if the Mode is kDecodePixels_Mode, call allocPixelRef(), + which will allocated a pixelRef. To access the pixel memory, the codec + needs to call lockPixels/unlockPixels on the + bitmap. It can then set the pixels with the decompressed image. + If the image cannot be decompressed, return false. + + note: document use of Allocator, Peeker and Chooser <reed> + */ + virtual bool onDecode(SkStream*, SkBitmap* bitmap, SkBitmap::Config pref, + Mode) = 0; + + /** Given a stream, this will try to find an appropriate decoder object. + If none is found, the method returns NULL. + */ + static SkImageDecoder* Factory(SkStream*); + + /** Decode the image stored in the specified file, and store the result + in bitmap. Return true for success or false on failure. + + If pref is kNo_Config, then the decoder is free to choose the most natural + config given the image data. If pref something other than kNo_Config, + the decoder will attempt to decode the image into that format, unless + there is a conflict (e.g. the image has per-pixel alpha and the bitmap's + config does not support that), in which case the decoder will choose a + closest match configuration. + */ + static bool DecodeFile(const char file[], SkBitmap* bitmap, + SkBitmap::Config prefConfig, Mode); + static bool DecodeFile(const char file[], SkBitmap* bitmap) + { + return DecodeFile(file, bitmap, SkBitmap::kNo_Config, kDecodePixels_Mode); + } + /** Decode the image stored in the specified memory buffer, and store the + result in bitmap. Return true for success or false on failure. + + If pref is kNo_Config, then the decoder is free to choose the most natural + config given the image data. If pref something other than kNo_Config, + the decoder will attempt to decode the image into that format, unless + there is a conflict (e.g. the image has per-pixel alpha and the bitmap's + config does not support that), in which case the decoder will choose a + closest match configuration. + */ + static bool DecodeMemory(const void* buffer, size_t size, SkBitmap* bitmap, + SkBitmap::Config prefConfig, Mode); + static bool DecodeMemory(const void* buffer, size_t size, SkBitmap* bitmap) + { + return DecodeMemory(buffer, size, bitmap, SkBitmap::kNo_Config, + kDecodePixels_Mode); + } + /** Decode the image stored in the specified SkStream, and store the result + in bitmap. Return true for success or false on failure. + + If pref is kNo_Config, then the decoder is free to choose the most + natural config given the image data. If pref something other than + kNo_Config, the decoder will attempt to decode the image into that + format, unless there is a conflict (e.g. the image has per-pixel alpha + and the bitmap's config does not support that), in which case the + decoder will choose a closest match configuration. + */ + static bool DecodeStream(SkStream* stream, SkBitmap* bitmap, + SkBitmap::Config prefConfig, Mode); + static bool DecodeStream(SkStream* stream, SkBitmap* bitmap) + { + return DecodeStream(stream, bitmap, SkBitmap::kNo_Config, + kDecodePixels_Mode); + } + + /* Given a format, return true if there is a currently installed decoder + for that format. Since a given build may not include all codecs (to save + code-size), this may return false. + */ + static bool SupportsFormat(Format); + + /** Return the default config for the running device. + Currently this used as a suggestion to image decoders that need to guess + what config they should decode into. + Default is kNo_Config, but this can be changed with SetDeviceConfig() + */ + static SkBitmap::Config GetDeviceConfig(); + /** Set the default config for the running device. + Currently this used as a suggestion to image decoders that need to guess + what config they should decode into. + Default is kNo_Config. + This can be queried with GetDeviceConfig() + */ + static void SetDeviceConfig(SkBitmap::Config); + + /** @cond UNIT_TEST */ + SkDEBUGCODE(static void UnitTest();) + /** @endcond */ + +protected: + SkImageDecoder(); + + // helper function for decoders to handle the (common) case where there is only + // once choice available in the image file. + bool chooseFromOneChoice(SkBitmap::Config config, int width, int height) const; + + /* Helper for subclasses. Call this to allocate the pixel memory given the bitmap's + width/height/rowbytes/config. Returns true on success. This method handles checking + for an optional Allocator. + */ + bool allocPixelRef(SkBitmap*, SkColorTable*) const; + +private: + Peeker* fPeeker; + Chooser* fChooser; + SkBitmap::Allocator* fAllocator; + int fSampleSize; + bool fDitherImage; + + // illegal + SkImageDecoder(const SkImageDecoder&); + SkImageDecoder& operator=(const SkImageDecoder&); +}; + +#ifdef SK_SUPPORT_IMAGE_ENCODE + +class SkWStream; + +class SkImageEncoder { +public: + enum Type { + kJPEG_Type, + kPNG_Type + }; + static SkImageEncoder* Create(Type); + + virtual ~SkImageEncoder(); + + /* Quality ranges from 0..100 */ + + bool encodeFile(const char file[], const SkBitmap&, int quality = 80); + bool encodeStream(SkWStream*, const SkBitmap&, int quality = 80); + +protected: + virtual bool onEncode(SkWStream*, const SkBitmap&, int quality) = 0; +}; + +#endif /* SK_SUPPORT_IMAGE_ENCODE */ + +/////////////////////////////////////////////////////////////////////// + +#endif + diff --git a/skia/include/SkImageRef.h b/skia/include/SkImageRef.h new file mode 100644 index 0000000..6ffb340 --- /dev/null +++ b/skia/include/SkImageRef.h @@ -0,0 +1,83 @@ +#ifndef SkImageRef_DEFINED +#define SkImageRef_DEFINED + +#include "SkPixelRef.h" +#include "SkBitmap.h" +#include "SkImageDecoder.h" +#include "SkString.h" + +class SkImageRefPool; +class SkStream; + +// define this to enable dumping whenever we add/remove/purge an imageref +//#define DUMP_IMAGEREF_LIFECYCLE + +class SkImageRef : public SkPixelRef { +public: + /** Create a new imageref from a stream. NOTE: the stream is not copied, but + since it may be accessed from another thread, the caller must ensure + that this imageref is the only owner of the stream. i.e. - sole + ownership of the stream object is transferred to this imageref object. + + @param stream The stream containing the encoded image data. Ownership + of this stream is transferred to the imageref, and + therefore the stream's ownercount must be 1. + @param config The preferred config of the decoded bitmap. + @param sampleSize Requested sampleSize for decoding. Defaults to 1. + */ + SkImageRef(SkStream*, SkBitmap::Config config, int sampleSize = 1); + virtual ~SkImageRef(); + + const char* getName() const { return fName.c_str(); } + void setName(const char name[]) { fName.set(name); } + + /** Return true if the image can be decoded. If so, and bitmap is non-null, + call its setConfig() with the corresponding values, but explicitly will + not set its pixels or colortable. Use SkPixelRef::lockPixels() for that. + */ + bool getInfo(SkBitmap* bm); + + // overrides + virtual void flatten(SkFlattenableWriteBuffer&) const; + +protected: + /** Override if you want to install a custom allocator. + When this is called we will have already acquired the mutex! + */ + virtual bool onDecode(SkImageDecoder* codec, SkStream*, SkBitmap*, + SkBitmap::Config, SkImageDecoder::Mode); + + /* Overrides from SkPixelRef + When these are called, we will have already acquired the mutex! + */ + + virtual void* onLockPixels(SkColorTable**); + // override this in your subclass to clean up when we're unlocking pixels + virtual void onUnlockPixels(); + + SkImageRef(SkFlattenableReadBuffer&); + + SkBitmap fBitmap; + +private: + SkStream* setStream(SkStream*); + // called with mutex already held. returns true if the bitmap is in the + // requested state (or further, i.e. has pixels) + bool prepareBitmap(SkImageDecoder::Mode); + + SkStream* fStream; + SkBitmap::Config fConfig; + int fSampleSize; + bool fErrorInDecoding; + + SkString fName; // for debugging + + friend class SkImageRefPool; + + SkImageRef* fPrev, *fNext; + size_t ramUsed() const; + + typedef SkPixelRef INHERITED; +}; + +#endif diff --git a/skia/include/SkImageRef_GlobalPool.h b/skia/include/SkImageRef_GlobalPool.h new file mode 100644 index 0000000..5469697 --- /dev/null +++ b/skia/include/SkImageRef_GlobalPool.h @@ -0,0 +1,56 @@ +#ifndef SkImageRef_GlobalPool_DEFINED +#define SkImageRef_GlobalPool_DEFINED + +#include "SkImageRef.h" + +class SkImageRef_GlobalPool : public SkImageRef { +public: + // if pool is null, use the global pool + SkImageRef_GlobalPool(SkStream*, SkBitmap::Config, int sampleSize = 1); + virtual ~SkImageRef_GlobalPool(); + + // overrides + virtual Factory getFactory() const { + return Create; + } + static SkPixelRef* Create(SkFlattenableReadBuffer&); + + // API to control the global pool + + /** Return the amount specified as the budget for the cache (in bytes). + */ + static size_t GetRAMBudget(); + + /** Set a new budget value for the cache. + */ + static void SetRAMBudget(size_t); + + /** Return how much ram is currently in use by the global cache. + */ + static size_t GetRAMUsed(); + + /** Free up (approximately) enough such that the amount used by the cache + is <= the specified amount. Since some images may be "in use", the + amount actually freed may not always result in a ram usage value <= + to the requested amount. In addition, because of the + chunky nature of the cache, the resulting usage may be < the requested + amount. + */ + static void SetRAMUsed(size_t usageInBytes); + + static void DumpPool(); + +protected: + virtual bool onDecode(SkImageDecoder* codec, SkStream* stream, + SkBitmap* bitmap, SkBitmap::Config config, + SkImageDecoder::Mode mode); + + virtual void onUnlockPixels(); + + SkImageRef_GlobalPool(SkFlattenableReadBuffer&); + +private: + typedef SkImageRef INHERITED; +}; + +#endif diff --git a/skia/include/SkImageView.h b/skia/include/SkImageView.h new file mode 100644 index 0000000..974a611 --- /dev/null +++ b/skia/include/SkImageView.h @@ -0,0 +1,76 @@ +/* include/graphics/SkImageView.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkImageView_DEFINED +#define SkImageView_DEFINED + +#include "SkView.h" +#include "SkString.h" + +class SkAnimator; +class SkBitmap; +struct SkMatrix; + +class SkImageView : public SkView { +public: + SkImageView(); + virtual ~SkImageView(); + + void getUri(SkString*) const; + void setUri(const char []); + void setUri(const SkString&); + + + enum ScaleType { + kMatrix_ScaleType, + kFitXY_ScaleType, + kFitStart_ScaleType, + kFitCenter_ScaleType, + kFitEnd_ScaleType + }; + ScaleType getScaleType() const { return (ScaleType)fScaleType; } + void setScaleType(ScaleType); + + bool getImageMatrix(SkMatrix*) const; + void setImageMatrix(const SkMatrix*); + +protected: + // overrides + virtual bool onEvent(const SkEvent&); + virtual void onDraw(SkCanvas*); + virtual void onInflate(const SkDOM&, const SkDOMNode*); + +private: + SkString fUri; + SkMatrix* fMatrix; // null or copy of caller's matrix ,,,,, + union { + SkAnimator* fAnim; + SkBitmap* fBitmap; + } fData; + uint8_t fScaleType; + SkBool8 fDataIsAnim; // as opposed to bitmap + SkBool8 fUriIsValid; + + void onUriChange(); + bool getDataBounds(SkRect* bounds); + bool freeData(); + bool ensureUriIsLoaded(); + + typedef SkView INHERITED; +}; + +#endif diff --git a/skia/include/SkJS.h b/skia/include/SkJS.h new file mode 100644 index 0000000..f584d6f --- /dev/null +++ b/skia/include/SkJS.h @@ -0,0 +1,48 @@ +/* include/graphics/SkJS.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkTypes.h" +#include "SkWindow.h" + +extern "C" { + typedef long JSWord; + typedef JSWord jsword; + typedef jsword jsval; + typedef struct JSRuntime JSRuntime; + typedef struct JSContext JSContext; + typedef struct JSObject JSObject; +} + +class SkString; + +class SkJS : public SkOSWindow { +public: + SkJS(void* hwnd); + ~SkJS(); + SkBool EvaluateScript(const char* script, jsval* rVal); + SkBool ValueToString(jsval value, SkString* string); +#ifdef SK_DEBUG + static void Test(void* hwnd); +#endif +protected: + void InitializeDisplayables(const SkBitmap& , JSContext *, JSObject *, JSObject *); + void DisposeDisplayables(); + JSRuntime *fRuntime; + JSContext *fContext; + JSObject *fGlobal; +}; + diff --git a/skia/include/SkKernel33MaskFilter.h b/skia/include/SkKernel33MaskFilter.h new file mode 100644 index 0000000..3fbc9cd --- /dev/null +++ b/skia/include/SkKernel33MaskFilter.h @@ -0,0 +1,57 @@ +#ifndef SkKernel33MaskFilter_DEFINED +#define SkKernel33MaskFilter_DEFINED + +#include "SkMaskFilter.h" + +class SkKernel33ProcMaskFilter : public SkMaskFilter { +public: + SkKernel33ProcMaskFilter(unsigned percent256 = 256) + : fPercent256(percent256) {} + + virtual uint8_t computeValue(uint8_t* const* srcRows) = 0; + + // overrides from SkMaskFilter + virtual SkMask::Format getFormat(); + virtual bool filterMask(SkMask*, const SkMask&, const SkMatrix&, SkIPoint*); + + // overrides from SkFlattenable + virtual void flatten(SkFlattenableWriteBuffer& wb); + +protected: + SkKernel33ProcMaskFilter(SkFlattenableReadBuffer& rb); + +private: + int fPercent256; + + typedef SkMaskFilter INHERITED; +}; + +/////////////////////////////////////////////////////////////////////////////// + +class SkKernel33MaskFilter : public SkKernel33ProcMaskFilter { +public: + SkKernel33MaskFilter(const int coeff[3][3], int shift, int percent256 = 256) + : SkKernel33ProcMaskFilter(percent256) + { + memcpy(fKernel, coeff, 9 * sizeof(int)); + fShift = shift; + } + + // override from SkKernel33ProcMaskFilter + virtual uint8_t computeValue(uint8_t* const* srcRows); + + // overrides from SkFlattenable + virtual void flatten(SkFlattenableWriteBuffer& wb); + virtual Factory getFactory(); + +private: + int fKernel[3][3]; + int fShift; + + SkKernel33MaskFilter(SkFlattenableReadBuffer& rb); + static SkFlattenable* Create(SkFlattenableReadBuffer& rb); + + typedef SkKernel33ProcMaskFilter INHERITED; +}; + +#endif diff --git a/skia/include/SkKey.h b/skia/include/SkKey.h new file mode 100644 index 0000000..2d0a8aa --- /dev/null +++ b/skia/include/SkKey.h @@ -0,0 +1,64 @@ +/* include/graphics/SkKey.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkKey_DEFINED +#define SkKey_DEFINED + +#include "SkTypes.h" + +enum SkKey { + //reordering these to match android.app.KeyEvent + kNONE_SkKey, //corresponds to android's UNKNOWN + + kLeftSoftKey_SkKey, + kRightSoftKey_SkKey, + + kHome_SkKey, //!< the home key - added to match android + kBack_SkKey, //!< (CLR) + kSend_SkKey, //!< the green (talk) key + kEnd_SkKey, //!< the red key + + k0_SkKey, + k1_SkKey, + k2_SkKey, + k3_SkKey, + k4_SkKey, + k5_SkKey, + k6_SkKey, + k7_SkKey, + k8_SkKey, + k9_SkKey, + kStar_SkKey, //!< the * key + kHash_SkKey, //!< the # key + + kUp_SkKey, + kDown_SkKey, + kLeft_SkKey, + kRight_SkKey, + + kOK_SkKey, //!< the center key + + kVolUp_SkKey, //!< volume up - match android + kVolDown_SkKey, //!< volume down - same + kPower_SkKey, //!< power button - same + kCamera_SkKey, //!< camera - same + + kSkKeyCount +}; + +#endif + diff --git a/skia/include/SkLayerRasterizer.h b/skia/include/SkLayerRasterizer.h new file mode 100644 index 0000000..6a59e94 --- /dev/null +++ b/skia/include/SkLayerRasterizer.h @@ -0,0 +1,64 @@ +/* include/graphics/SkLayerRasterizer.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkLayerRasterizer_DEFINED +#define SkLayerRasterizer_DEFINED + +#include "SkRasterizer.h" +#include "SkDeque.h" +#include "SkScalar.h" + +class SkPaint; + +class SkLayerRasterizer : public SkRasterizer { +public: + SkLayerRasterizer(); + virtual ~SkLayerRasterizer(); + + void addLayer(const SkPaint& paint) + { + this->addLayer(paint, 0, 0); + } + + /** Add a new layer (above any previous layers) to the rasterizer. + The layer will extract those fields that affect the mask from + the specified paint, but will not retain a reference to the paint + object itself, so it may be reused without danger of side-effects. + */ + void addLayer(const SkPaint& paint, SkScalar dx, SkScalar dy); + + // overrides from SkFlattenable + virtual Factory getFactory(); + virtual void flatten(SkFlattenableWriteBuffer&); + +protected: + SkLayerRasterizer(SkFlattenableReadBuffer&); + + // override from SkRasterizer + virtual bool onRasterize(const SkPath& path, const SkMatrix& matrix, + const SkIRect* clipBounds, + SkMask* mask, SkMask::CreateMode mode); + +private: + SkDeque fLayers; + + static SkFlattenable* CreateProc(SkFlattenableReadBuffer&); + + typedef SkRasterizer INHERITED; +}; + +#endif diff --git a/skia/include/SkMMapStream.h b/skia/include/SkMMapStream.h new file mode 100644 index 0000000..196231b --- /dev/null +++ b/skia/include/SkMMapStream.h @@ -0,0 +1,22 @@ +#ifndef SkMMapStream_DEFINED +#define SkMMapStream_DEFINED + +#include "SkStream.h" + +class SkMMAPStream : public SkMemoryStream { +public: + SkMMAPStream(const char filename[]); + virtual ~SkMMAPStream(); + + virtual void setMemory(const void* data, size_t length); +private: + int fFildes; + void* fAddr; + size_t fSize; + + void closeMMap(); + + typedef SkMemoryStream INHERITED; +}; + +#endif diff --git a/skia/include/SkMask.h b/skia/include/SkMask.h new file mode 100644 index 0000000..d5c14ea --- /dev/null +++ b/skia/include/SkMask.h @@ -0,0 +1,87 @@ +/* include/graphics/SkMask.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkMask_DEFINED +#define SkMask_DEFINED + +#include "SkRect.h" + +/** \class SkMask + SkMask is used to describe alpha bitmaps, either 1bit, 8bit, or + the 3-channel 3D format. These are passed to SkMaskFilter objects. +*/ +struct SkMask { + enum Format { + kBW_Format, //!< 1bit per pixel mask (e.g. monochrome) + kA8_Format, //!< 8bits per pixel mask (e.g. antialiasing) + k3D_Format, //!< 3 8bit per pixl planes: alpha, mul, add + kLCD_Format //!< 3 bytes/pixel: r/g/b + }; + + enum { + kCountMaskFormats = kLCD_Format + 1 + }; + + uint8_t* fImage; + SkIRect fBounds; + uint16_t fRowBytes; + uint8_t fFormat; // Format + + /** Return the byte size of the mask, assuming only 1 plane. + Does not account for k3D_Format. For that, use computeFormatImageSize() + */ + size_t computeImageSize() const; + /** Return the byte size of the mask, taking into account + any extra planes (e.g. k3D_Format). + */ + size_t computeTotalImageSize() const; + + /** Returns the address of the byte that holds the specified bit. + Asserts that the mask is kBW_Format, and that x,y are in range. + x,y are in the same coordiate space as fBounds. + */ + uint8_t* getAddr1(int x, int y) const + { + SkASSERT(fFormat == kBW_Format); + SkASSERT(fBounds.contains(x, y)); + SkASSERT(fImage != NULL); + return fImage + ((x - fBounds.fLeft) >> 3) + (y - fBounds.fTop) * fRowBytes; + } + /** Returns the address of the specified byte. + Asserts that the mask is kA8_Format, and that x,y are in range. + x,y are in the same coordiate space as fBounds. + */ + uint8_t* getAddr(int x, int y) const + { + SkASSERT(fFormat != kBW_Format); + SkASSERT(fBounds.contains(x, y)); + SkASSERT(fImage != NULL); + return fImage + x - fBounds.fLeft + (y - fBounds.fTop) * fRowBytes; + } + + static uint8_t* AllocImage(size_t bytes); + static void FreeImage(void* image); + + enum CreateMode { + kJustComputeBounds_CreateMode, //!< compute bounds and return + kJustRenderImage_CreateMode, //!< render into preallocate mask + kComputeBoundsAndRenderImage_CreateMode //!< compute bounds, alloc image and render into it + }; +}; + +#endif + diff --git a/skia/include/SkMaskFilter.h b/skia/include/SkMaskFilter.h new file mode 100644 index 0000000..243972f --- /dev/null +++ b/skia/include/SkMaskFilter.h @@ -0,0 +1,101 @@ +/* include/graphics/SkMaskFilter.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkMaskFilter_DEFINED +#define SkMaskFilter_DEFINED + +#include "SkFlattenable.h" +#include "SkMask.h" + +class SkBlitter; +class SkBounder; +class SkMatrix; +class SkPath; +class SkRegion; + +/** \class SkMaskFilter + + SkMaskFilter is the base class for object that perform transformations on + an alpha-channel mask before drawing it. A subclass of SkMaskFilter may be + installed into a SkPaint. Once there, each time a primitive is drawn, it + is first scan converted into a SkMask::kA8_Format mask, and handed to the + filter, calling its filterMask() method. If this returns true, then the + new mask is used to render into the device. + + Blur and emboss are implemented as subclasses of SkMaskFilter. +*/ +class SkMaskFilter : public SkFlattenable { +public: + SkMaskFilter() {} + + /** Returns the format of the resulting mask that this subclass will return + when its filterMask() method is called. + */ + virtual SkMask::Format getFormat() = 0; + + /** Create a new mask by filter the src mask. + If src.fImage == null, then do not allocate or create the dst image + but do fill out the other fields in dstMask. + If you do allocate a dst image, use SkMask::AllocImage() + If this returns false, dst mask is ignored. + @param dst the result of the filter. If src.fImage == null, dst should not allocate its image + @param src the original image to be filtered. + @param matrix the CTM + @param margin if not null, return the buffer dx/dy need when calculating the effect. Used when + drawing a clipped object to know how much larger to allocate the src before + applying the filter. If returning false, ignore this parameter. + @return true if the dst mask was correctly created. + */ + virtual bool filterMask(SkMask* dst, const SkMask& src, const SkMatrix&, SkIPoint* margin); + + /** Helper method that, given a path in device space, will rasterize it into a kA8_Format mask + and then call filterMask(). If this returns true, the specified blitter will be called + to render that mask. Returns false if filterMask() returned false. + This method is not exported to java. + */ + bool filterPath(const SkPath& devPath, const SkMatrix& devMatrix, + const SkRegion& devClip, SkBounder*, SkBlitter* blitter); + + virtual void flatten(SkFlattenableWriteBuffer& ) {} +protected: + // empty for now, but lets get our subclass to remember to init us for the future + SkMaskFilter(SkFlattenableReadBuffer&) {} +}; + +/** \class SkAutoMaskImage + + Stack class used to manage the fImage buffer in a SkMask. + When this object loses scope, the buffer is freed with SkMask::FreeImage(). +*/ +class SkAutoMaskImage { +public: + SkAutoMaskImage(SkMask* mask, bool alloc) + { + if (alloc) + mask->fImage = SkMask::AllocImage(mask->computeImageSize()); + fImage = mask->fImage; + } + ~SkAutoMaskImage() + { + SkMask::FreeImage(fImage); + } +private: + uint8_t* fImage; +}; + +#endif + diff --git a/skia/include/SkMetaData.h b/skia/include/SkMetaData.h new file mode 100644 index 0000000..e87278b --- /dev/null +++ b/skia/include/SkMetaData.h @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2006-2008 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SkMetaData_DEFINED +#define SkMetaData_DEFINED + +#include "SkScalar.h" + +class SkMetaData { +public: + SkMetaData(); + SkMetaData(const SkMetaData& src); + ~SkMetaData(); + + SkMetaData& operator=(const SkMetaData& src); + + void reset(); + + bool findS32(const char name[], int32_t* value = NULL) const; + bool findScalar(const char name[], SkScalar* value = NULL) const; + const SkScalar* findScalars(const char name[], int* count, SkScalar values[] = NULL) const; + const char* findString(const char name[]) const; + bool findPtr(const char name[], void** value = NULL) const; + bool findBool(const char name[], bool* value = NULL) const; + + bool hasS32(const char name[], int32_t value) const + { + int32_t v; + return this->findS32(name, &v) && v == value; + } + bool hasScalar(const char name[], SkScalar value) const + { + SkScalar v; + return this->findScalar(name, &v) && v == value; + } + bool hasString(const char name[], const char value[]) const + { + const char* v = this->findString(name); + return v == NULL && value == NULL || + v != NULL && value != NULL && !strcmp(v, value); + } + bool hasPtr(const char name[], void* value) const + { + void* v; + return this->findPtr(name, &v) && v == value; + } + bool hasBool(const char name[], bool value) const + { + bool v; + return this->findBool(name, &v) && v == value; + } + + void setS32(const char name[], int32_t value); + void setScalar(const char name[], SkScalar value); + SkScalar* setScalars(const char name[], int count, const SkScalar values[] = NULL); + void setString(const char name[], const char value[]); + void setPtr(const char name[], void* value); + void setBool(const char name[], bool value); + + bool removeS32(const char name[]); + bool removeScalar(const char name[]); + bool removeString(const char name[]); + bool removePtr(const char name[]); + bool removeBool(const char name[]); + + SkDEBUGCODE(static void UnitTest();) + + enum Type { + kS32_Type, + kScalar_Type, + kString_Type, + kPtr_Type, + kBool_Type, + + kTypeCount + }; + + struct Rec; + class Iter; + friend class Iter; + + class Iter { + public: + Iter() : fRec(NULL) {} + Iter(const SkMetaData&); + + /** Reset the iterator, so that calling next() will return the first + data element. This is done implicitly in the constructor. + */ + void reset(const SkMetaData&); + + /** Each time next is called, it returns the name of the next data element, + or null when there are no more elements. If non-null is returned, then the + element's type is returned (if not null), and the number of data values + is returned in count (if not null). + */ + const char* next(Type*, int* count); + + private: + Rec* fRec; + }; + +public: + struct Rec { + Rec* fNext; + uint16_t fDataCount; // number of elements + uint8_t fDataLen; // sizeof a single element +#ifdef SK_DEBUG + Type fType; +#else + uint8_t fType; +#endif + +#ifdef SK_DEBUG + const char* fName; + union { + int32_t fS32; + SkScalar fScalar; + const char* fString; + void* fPtr; + bool fBool; + } fData; +#endif + + const void* data() const { return (this + 1); } + void* data() { return (this + 1); } + const char* name() const { return (const char*)this->data() + fDataLen * fDataCount; } + char* name() { return (char*)this->data() + fDataLen * fDataCount; } + + static Rec* Alloc(size_t); + static void Free(Rec*); + }; + Rec* fRec; + + const Rec* find(const char name[], Type) const; + void* set(const char name[], const void* data, size_t len, Type, int count); + bool remove(const char name[], Type); +}; + +#endif + diff --git a/skia/include/SkMovie.h b/skia/include/SkMovie.h new file mode 100644 index 0000000..d1ac280 --- /dev/null +++ b/skia/include/SkMovie.h @@ -0,0 +1,67 @@ +#ifndef SkMovie_DEFINED +#define SkMovie_DEFINED + +#include "SkRefCnt.h" +#include "SkCanvas.h" + +class SkStream; + +class SkMovie : public SkRefCnt { +public: + /** Try to create a movie from the stream. If the stream format is not + supported, return NULL. + */ + static SkMovie* DecodeStream(SkStream*); + /** Try to create a movie from the specified file path. If the file is not + found, or the format is not supported, return NULL. If a movie is + returned, the stream may be retained by the movie (via ref()) until + the movie is finished with it (by calling unref()). + */ + static SkMovie* DecodeFile(const char path[]); + /** Try to create a movie from the specified memory. + If the format is not supported, return NULL. If a movie is returned, + the data will have been read or copied, and so the caller may free + it. + */ + static SkMovie* DecodeMemory(const void* data, size_t length); + + SkMSec duration(); + int width(); + int height(); + int isOpaque(); + + /** Specify the time code (between 0...duration) to sample a bitmap + from the movie. Returns true if this time code generated a different + bitmap/frame from the previous state (i.e. true means you need to + redraw). + */ + bool setTime(SkMSec); + + // return the right bitmap for the current time code + const SkBitmap& bitmap(); + +protected: + struct Info { + SkMSec fDuration; + int fWidth; + int fHeight; + bool fIsOpaque; + }; + + virtual bool onGetInfo(Info*) = 0; + virtual bool onSetTime(SkMSec) = 0; + virtual bool onGetBitmap(SkBitmap*) = 0; + + // visible for subclasses + SkMovie(); + +private: + Info fInfo; + SkMSec fCurrTime; + SkBitmap fBitmap; + bool fNeedBitmap; + + void ensureInfo(); +}; + +#endif diff --git a/skia/include/SkNinePatch.h b/skia/include/SkNinePatch.h new file mode 100644 index 0000000..a10d8be --- /dev/null +++ b/skia/include/SkNinePatch.h @@ -0,0 +1,41 @@ +/* +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkNinePatch_DEFINED +#define SkNinePatch_DEFINED + +#include "SkRect.h" +#include "SkRegion.h" + +class SkBitmap; +class SkCanvas; +class SkPaint; + +class SkNinePatch { +public: + static void DrawNine(SkCanvas* canvas, const SkRect& dst, + const SkBitmap& bitmap, const SkIRect& margins, + const SkPaint* paint = NULL); + + static void DrawMesh(SkCanvas* canvas, const SkRect& dst, + const SkBitmap& bitmap, + const int32_t xDivs[], int numXDivs, + const int32_t yDivs[], int numYDivs, + const SkPaint* paint = NULL); +}; + +#endif diff --git a/skia/include/SkOSFile.h b/skia/include/SkOSFile.h new file mode 100644 index 0000000..a62d65a --- /dev/null +++ b/skia/include/SkOSFile.h @@ -0,0 +1,87 @@ +/* include/graphics/SkOSFile.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +// Copyright Skia Inc. 2004 - 2005 +// +#ifndef SkOSFile_DEFINED +#define SkOSFile_DEFINED + +#include "SkString.h" + +#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_UNIX) + #include <dirent.h> +#endif + +struct SkFILE; + +enum SkFILE_Flags { + kRead_SkFILE_Flag = 0x01, + kWrite_SkFILE_Flag = 0x02 +}; + +SkFILE* sk_fopen(const char path[], SkFILE_Flags); +void sk_fclose(SkFILE*); + +size_t sk_fgetsize(SkFILE*); +/** Return true if the file could seek back to the beginning +*/ +bool sk_frewind(SkFILE*); + +size_t sk_fread(void* buffer, size_t byteCount, SkFILE*); +size_t sk_fwrite(const void* buffer, size_t byteCount, SkFILE*); +void sk_fflush(SkFILE*); + +int sk_fseek( SkFILE*, size_t, int ); +size_t sk_ftell( SkFILE* ); + +class SkOSFile { +public: + class Iter { + public: + Iter(); + Iter(const char path[], const char suffix[] = NULL); + ~Iter(); + + void reset(const char path[], const char suffix[] = NULL); + bool next(SkString* name, bool getDir = false); + + private: +#ifdef SK_BUILD_FOR_WIN + HANDLE fHandle; + uint16_t* fPath16; +#elif defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_UNIX) + DIR* fDIR; + SkString fPath, fSuffix; +#endif + }; +}; + +class SkUTF16_Str { +public: + SkUTF16_Str(const char src[]); + ~SkUTF16_Str() + { + sk_free(fStr); + } + const uint16_t* get() const { return fStr; } + +private: + uint16_t* fStr; +}; + +#endif + diff --git a/skia/include/SkOSMenu.h b/skia/include/SkOSMenu.h new file mode 100644 index 0000000..3b429b4 --- /dev/null +++ b/skia/include/SkOSMenu.h @@ -0,0 +1,56 @@ +/* include/graphics/SkOSMenu.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkOSMenu_DEFINED +#define SkOSMenu_DEFINED + +#include "SkEvent.h" +#include "SkTDArray.h" + +class SkOSMenu { +public: + explicit SkOSMenu(const char title[]); + ~SkOSMenu(); + + const char* getTitle() const { return fTitle; } + + void appendItem(const char title[], const char eventType[], int32_t eventData); + + // called by SkOSWindow when it receives an OS menu event + int countItems() const; + const char* getItem(int index, uint32_t* cmdID) const; + + SkEvent* createEvent(uint32_t os_cmd); + +private: + const char* fTitle; + + struct Item { + const char* fTitle; + const char* fEventType; + uint32_t fEventData; + uint32_t fOSCmd; // internal + }; + SkTDArray<Item> fItems; + + // illegal + SkOSMenu(const SkOSMenu&); + SkOSMenu& operator=(const SkOSMenu&); +}; + +#endif + diff --git a/skia/include/SkOSSound.h b/skia/include/SkOSSound.h new file mode 100644 index 0000000..034e1bd --- /dev/null +++ b/skia/include/SkOSSound.h @@ -0,0 +1,37 @@ +/* include/graphics/SkOSSound.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkOSSound_DEFINED +#define SkOSSound_DEFINED + +#include "SkTypes.h" + +class SkOSSound { +public: + static void Play(const char path[]); + static void Pause(); + static void Resume(); + static bool TogglePause(); // returns true if we are now playing, or false if we're now paused + static void Stop(); + + // volume runs from 0 (silent) to 0xFF (max-volume) + static uint8_t GetVolume(); + static void SetVolume(U8CPU volume); +}; + +#endif + diff --git a/skia/include/SkOSWindow_Mac.h b/skia/include/SkOSWindow_Mac.h new file mode 100644 index 0000000..8c4628f --- /dev/null +++ b/skia/include/SkOSWindow_Mac.h @@ -0,0 +1,50 @@ +/* include/graphics/SkOSWindow_Mac.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkOSWindow_Mac_DEFINED +#define SkOSWindow_Mac_DEFINED + +#include "SkWindow.h" + +class SkOSWindow : public SkWindow { +public: + SkOSWindow(void* hwnd); + + void* getHWND() const { return fHWND; } + void updateSize(); + + static bool PostEvent(SkEvent* evt, SkEventSinkID, SkMSec delay); + + static pascal OSStatus SkOSWindow::EventHandler( EventHandlerCallRef inHandler, EventRef inEvent, void* userData ); + +protected: + // overrides from SkWindow + virtual void onHandleInval(const SkIRect&); + // overrides from SkView + virtual void onAddMenu(const SkOSMenu*); + virtual void onSetTitle(const char[]); + +private: + void* fHWND; + + void doPaint(void* ctx); + + typedef SkWindow INHERITED; +}; + +#endif + diff --git a/skia/include/SkOSWindow_Unix.h b/skia/include/SkOSWindow_Unix.h new file mode 100644 index 0000000..b616ca9 --- /dev/null +++ b/skia/include/SkOSWindow_Unix.h @@ -0,0 +1,61 @@ +/* include/graphics/SkOSWindow_Unix.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkOSWindow_Unix_DEFINED +#define SkOSWindow_Unix_DEFINED + +#include "SkWindow.h" +#include <X11/Xlib.h> + +struct SkUnixWindow { + Display* fDisplay; + Window fWin; + size_t fOSWin; +}; + +class SkOSWindow : public SkWindow { +public: + SkOSWindow(Display* display, Window win); + + void* getHWND() const { return (void*)fUnixWindow.fWin; } + void* getDisplay() const { return (void*)fUnixWindow.fDisplay; } + void* getUnixWindow() const { return (void*)&fUnixWindow; } + void setSize(int width, int height); + void updateSize(); + + static bool PostEvent(SkEvent* evt, SkEventSinkID, SkMSec delay); + + static bool WndProc(SkUnixWindow* w, XEvent &e); + +protected: + // overrides from SkWindow + virtual void onHandleInval(const SkIRect&); + // overrides from SkView + virtual void onAddMenu(const SkOSMenu*); + +private: + SkUnixWindow fUnixWindow; + + void doPaint(); + + void* fMBar; + + typedef SkWindow INHERITED; +}; + +#endif + diff --git a/skia/include/SkOSWindow_Win.h b/skia/include/SkOSWindow_Win.h new file mode 100644 index 0000000..79b99e7 --- /dev/null +++ b/skia/include/SkOSWindow_Win.h @@ -0,0 +1,60 @@ +/* include/graphics/SkOSWindow_Win.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkOSWindow_Win_DEFINED +#define SkOSWindow_Win_DEFINED + +#include "SkWindow.h" + +class SkOSWindow : public SkWindow { +public: + SkOSWindow(void* hwnd); + + void* getHWND() const { return fHWND; } + void setSize(int width, int height); + void updateSize(); + + static bool PostEvent(SkEvent* evt, SkEventSinkID, SkMSec delay); + + static bool WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); + static bool SkOSWindow::QuitOnDeactivate(HWND hWnd); + + enum { + SK_WM_SkEvent = WM_APP + 1000, + SK_WM_SkTimerID = 0xFFFF // just need a non-zero value + }; + +protected: + virtual bool quitOnDeactivate() { return true; } + + // overrides from SkWindow + virtual void onHandleInval(const SkIRect&); + // overrides from SkView + virtual void onAddMenu(const SkOSMenu*); + +private: + void* fHWND; + + void doPaint(void* ctx); + + HMENU fMBar; + + typedef SkWindow INHERITED; +}; + +#endif + diff --git a/skia/include/SkOSWindow_wxwidgets.h b/skia/include/SkOSWindow_wxwidgets.h new file mode 100644 index 0000000..7d7bb80e --- /dev/null +++ b/skia/include/SkOSWindow_wxwidgets.h @@ -0,0 +1,55 @@ +/* include/graphics/SkOSWindow_wxwidgets.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +/* + * SkOSWindow_wxwidgets.h + * wxwidgets + * + * Created by phanna on 12/14/05. + * Copyright 2005 __MyCompanyName__. All rights reserved. + * + */ + +#ifndef SkOSWindow_wxwidgets_DEFINED +#define SkOSWindow_wxwidgets_DEFINED + +#include "SkWindow.h" +#include "wx/frame.h" + +class SkOSWindow: public SkWindow +{ +public: + SkOSWindow(); + SkOSWindow(const wxString& title, int x, int y, int width, int height); + ~SkOSWindow(); + + wxFrame* getWXFrame() const { return fFrame; } + + void updateSize(); + +protected: + virtual void onHandleInval(const SkIRect&); + virtual void onAddMenu(const SkOSMenu*); + +private: + wxFrame* fFrame; + typedef SkWindow INHERITED; + +}; + +#endifpedef SkWindow INHERITED; + diff --git a/skia/include/SkPackBits.h b/skia/include/SkPackBits.h new file mode 100644 index 0000000..4bbfb55 --- /dev/null +++ b/skia/include/SkPackBits.h @@ -0,0 +1,70 @@ +#ifndef SkPackBits_DEFINED +#define SkPackBits_DEFINED + +#include "SkTypes.h" + +class SkPackBits { +public: + /** Given the number of 16bit values that will be passed to Pack16, + returns the worst-case size needed for the dst[] buffer. + */ + static size_t ComputeMaxSize16(int count); + + /** Given the number of 8bit values that will be passed to Pack8, + returns the worst-case size needed for the dst[] buffer. + */ + static size_t ComputeMaxSize8(int count); + + /** Write the src array into a packed format. The packing process may end + up writing more bytes than it read, so dst[] must be large enough. + @param src Input array of 16bit values + @param count Number of entries in src[] + @param dst Buffer (allocated by caller) to write the packed data + into + @return the number of bytes written to dst[] + */ + static size_t Pack16(const uint16_t src[], int count, uint8_t dst[]); + + /** Write the src array into a packed format. The packing process may end + up writing more bytes than it read, so dst[] must be large enough. + @param src Input array of 8bit values + @param count Number of entries in src[] + @param dst Buffer (allocated by caller) to write the packed data + into + @return the number of bytes written to dst[] + */ + static size_t Pack8(const uint8_t src[], int count, uint8_t dst[]); + + /** Unpack the data in src[], and expand it into dst[]. The src[] data was + written by a previous call to Pack16. + @param src Input data to unpack, previously created by Pack16. + @param srcSize Number of bytes of src to unpack + @param dst Buffer (allocated by caller) to expand the src[] into. + @return the number of dst elements (not bytes) written into dst. + */ + static int Unpack16(const uint8_t src[], size_t srcSize, uint16_t dst[]); + + /** Unpack the data in src[], and expand it into dst[]. The src[] data was + written by a previous call to Pack8. + @param src Input data to unpack, previously created by Pack8. + @param srcSize Number of bytes of src to unpack + @param dst Buffer (allocated by caller) to expand the src[] into. + @return the number of bytes written into dst. + */ + static int Unpack8(const uint8_t src[], size_t srcSize, uint8_t dst[]); + + /** Unpack the data from src[], skip the first dstSkip bytes, then write + dstWrite bytes into dst[]. The src[] data was written by a previous + call to Pack8. Return the number of bytes actually writtten into dst[] + @param src Input data to unpack, previously created by Pack8. + @param dst Buffer (allocated by caller) to expand the src[] into. + @param dstSkip Number of bytes of unpacked src to skip before writing + into dst + @param dstWrite Number of bytes of unpacked src to write into dst (after + skipping dstSkip bytes) + */ + static void Unpack8(uint8_t dst[], size_t dstSkip, size_t dstWrite, + const uint8_t src[]); +}; + +#endif diff --git a/skia/include/SkPaint.h b/skia/include/SkPaint.h new file mode 100644 index 0000000..b556de8 --- /dev/null +++ b/skia/include/SkPaint.h @@ -0,0 +1,786 @@ +/* include/graphics/SkPaint.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkPaint_DEFINED +#define SkPaint_DEFINED + +#include "SkColor.h" +#include "SkMath.h" +#include "SkPorterDuff.h" + +class SkAutoGlyphCache; +class SkColorFilter; +class SkDescriptor; +class SkFlattenableReadBuffer; +class SkFlattenableWriteBuffer; +struct SkGlyph; +struct SkRect; +class SkGlyphCache; +class SkMaskFilter; +class SkMatrix; +class SkPath; +class SkPathEffect; +class SkRasterizer; +class SkShader; +class SkDrawLooper; +class SkTypeface; +class SkXfermode; + +typedef const SkGlyph& (*SkDrawCacheProc)(SkGlyphCache*, const char**, + SkFixed x, SkFixed y); + +typedef const SkGlyph& (*SkMeasureCacheProc)(SkGlyphCache*, const char**); + +/** \class SkPaint + + The SkPaint class holds the style and color information about how to draw + geometries, text and bitmaps. +*/ +class SkPaint { +public: + SkPaint(); + SkPaint(const SkPaint& paint); + ~SkPaint(); + + SkPaint& operator=(const SkPaint&); + + friend int operator==(const SkPaint& a, const SkPaint& b); + friend int operator!=(const SkPaint& a, const SkPaint& b) + { + return !(a == b); + } + + void flatten(SkFlattenableWriteBuffer&) const; + void unflatten(SkFlattenableReadBuffer&); + + /** Restores the paint to its initial settings. + */ + void reset(); + + /** Specifies the bit values that are stored in the paint's flags. + */ + enum Flags { + kAntiAlias_Flag = 0x01, //!< mask to enable antialiasing + kFilterBitmap_Flag = 0x02, //!< mask to enable bitmap filtering + kDither_Flag = 0x04, //!< mask to enable dithering + kUnderlineText_Flag = 0x08, //!< mask to enable underline text + kStrikeThruText_Flag = 0x10, //!< mask to enable strike-thru text + kFakeBoldText_Flag = 0x20, //!< mask to enable fake-bold text + kLinearText_Flag = 0x40, //!< mask to enable linear-text + kSubpixelText_Flag = 0x80, //!< mask to enable subpixel-text + kDevKernText_Flag = 0x100, //!< mask to enable device kerning text + + kAllFlags = 0x1FF + }; + + /** Return the paint's flags. Use the Flag enum to test flag values. + @return the paint's flags (see enums ending in _Flag for bit masks) + */ + uint32_t getFlags() const { return fFlags; } + + /** Set the paint's flags. Use the Flag enum to specific flag values. + @param flags The new flag bits for the paint (see Flags enum) + */ + void setFlags(uint32_t flags); + + /** Helper for getFlags(), returning true if kAntiAlias_Flag bit is set + @return true if the antialias bit is set in the paint's flags. + */ + bool isAntiAlias() const + { + return SkToBool(this->getFlags() & kAntiAlias_Flag); + } + + /** Helper for setFlags(), setting or clearing the kAntiAlias_Flag bit + @param aa true to enable antialiasing, false to disable it + */ + void setAntiAlias(bool aa); + + /** Helper for getFlags(), returning true if kDither_Flag bit is set + @return true if the dithering bit is set in the paint's flags. + */ + bool isDither() const + { + return SkToBool(this->getFlags() & kDither_Flag); + } + + /** Helper for setFlags(), setting or clearing the kDither_Flag bit + @param dither true to enable dithering, false to disable it + */ + void setDither(bool dither); + + /** Helper for getFlags(), returning true if kLinearText_Flag bit is set + @return true if the lineartext bit is set in the paint's flags + */ + bool isLinearText() const + { + return SkToBool(this->getFlags() & kLinearText_Flag); + } + + /** Helper for setFlags(), setting or clearing the kLinearText_Flag bit + @param linearText true to set the linearText bit in the paint's flags, + false to clear it. + */ + void setLinearText(bool linearText); + + /** Helper for getFlags(), returning true if kSubpixelText_Flag bit is set + @return true if the lineartext bit is set in the paint's flags + */ + bool isSubpixelText() const + { + return SkToBool(this->getFlags() & kSubpixelText_Flag); + } + + /** Helper for setFlags(), setting or clearing the kSubpixelText_Flag bit + @param subpixelText true to set the subpixelText bit in the paint's + flags, false to clear it. + */ + void setSubpixelText(bool subpixelText); + + /** Helper for getFlags(), returning true if kUnderlineText_Flag bit is set + @return true if the underlineText bit is set in the paint's flags. + */ + bool isUnderlineText() const + { + return SkToBool(this->getFlags() & kUnderlineText_Flag); + } + + /** Helper for setFlags(), setting or clearing the kUnderlineText_Flag bit + @param underlineText true to set the underlineText bit in the paint's + flags, false to clear it. + */ + void setUnderlineText(bool underlineText); + + /** Helper for getFlags(), returns true if kStrikeThruText_Flag bit is set + @return true if the strikeThruText bit is set in the paint's flags. + */ + bool isStrikeThruText() const + { + return SkToBool(this->getFlags() & kStrikeThruText_Flag); + } + + /** Helper for setFlags(), setting or clearing the kStrikeThruText_Flag bit + @param strikeThruText true to set the strikeThruText bit in the + paint's flags, false to clear it. + */ + void setStrikeThruText(bool strikeThruText); + + /** Helper for getFlags(), returns true if kFakeBoldText_Flag bit is set + @return true if the kFakeBoldText_Flag bit is set in the paint's flags. + */ + bool isFakeBoldText() const + { + return SkToBool(this->getFlags() & kFakeBoldText_Flag); + } + + /** Helper for setFlags(), setting or clearing the kFakeBoldText_Flag bit + @param fakeBoldText true to set the kFakeBoldText_Flag bit in the paint's + flags, false to clear it. + */ + void setFakeBoldText(bool fakeBoldText); + + /** Helper for getFlags(), returns true if kDevKernText_Flag bit is set + @return true if the kernText bit is set in the paint's flags. + */ + bool isDevKernText() const + { + return SkToBool(this->getFlags() & kDevKernText_Flag); + } + + /** Helper for setFlags(), setting or clearing the kKernText_Flag bit + @param kernText true to set the kKernText_Flag bit in the paint's + flags, false to clear it. + */ + void setDevKernText(bool devKernText); + + bool isFilterBitmap() const + { + return SkToBool(this->getFlags() & kFilterBitmap_Flag); + } + + void setFilterBitmap(bool filterBitmap); + + /** Styles apply to rect, oval, path, and text. + Bitmaps are always drawn in "fill", and lines are always drawn in + "stroke". + */ + enum Style { + kFill_Style, //!< fill with the paint's color + kStroke_Style, //!< stroke with the paint's color + kStrokeAndFill_Style, //!< fill and stroke with the paint's color + + kStyleCount, + }; + + /** Return the paint's style, used for controlling how primitives' + geometries are interpreted (except for drawBitmap, which always assumes + kFill_Style). + @return the paint's Style + */ + Style getStyle() const { return (Style)fStyle; } + + /** Set the paint's style, used for controlling how primitives' + geometries are interpreted (except for drawBitmap, which always assumes + Fill). + @param style The new style to set in the paint + */ + void setStyle(Style style); + + /** Return the paint's color. Note that the color is a 32bit value + containing alpha as well as r,g,b. This 32bit value is not + premultiplied, meaning that its alpha can be any value, regardless of + the values of r,g,b. + @return the paint's color (and alpha). + */ + SkColor getColor() const { return fColor; } + + /** Set the paint's color. Note that the color is a 32bit value containing + alpha as well as r,g,b. This 32bit value is not premultiplied, meaning + that its alpha can be any value, regardless of the values of r,g,b. + @param color The new color (including alpha) to set in the paint. + */ + void setColor(SkColor color); + + /** Helper to getColor() that just returns the color's alpha value. + @return the alpha component of the paint's color. + */ + uint8_t getAlpha() const { return SkToU8(SkColorGetA(fColor)); } + + /** Helper to setColor(), that only assigns the color's alpha value, + leaving its r,g,b values unchanged. + @param a set the alpha component (0..255) of the paint's color. + */ + void setAlpha(U8CPU a); + + /** Helper to setColor(), that takes a,r,g,b and constructs the color value + using SkColorSetARGB() + @param a The new alpha component (0..255) of the paint's color. + @param r The new red component (0..255) of the paint's color. + @param g The new green component (0..255) of the paint's color. + @param b The new blue component (0..255) of the paint's color. + */ + void setARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b); + + /** Return the width for stroking. + <p /> + A value of 0 strokes in hairline mode. + Hairlines always draw 1-pixel wide, regardless of the matrix. + @return the paint's stroke width, used whenever the paint's style is + Stroke or StrokeAndFill. + */ + SkScalar getStrokeWidth() const { return fWidth; } + + /** Set the width for stroking. + Pass 0 to stroke in hairline mode. + Hairlines always draw 1-pixel wide, regardless of the matrix. + @param width set the paint's stroke width, used whenever the paint's + style is Stroke or StrokeAndFill. + */ + void setStrokeWidth(SkScalar width); + + /** Return the paint's stroke miter value. This is used to control the + behavior of miter joins when the joins angle is sharp. + @return the paint's miter limit, used whenever the paint's style is + Stroke or StrokeAndFill. + */ + SkScalar getStrokeMiter() const { return fMiterLimit; } + + /** Set the paint's stroke miter value. This is used to control the + behavior of miter joins when the joins angle is sharp. This value must + be >= 0. + @param miter set the miter limit on the paint, used whenever the + paint's style is Stroke or StrokeAndFill. + */ + void setStrokeMiter(SkScalar miter); + + /** Cap enum specifies the settings for the paint's strokecap. This is the + treatment that is applied to the beginning and end of each non-closed + contour (e.g. lines). + */ + enum Cap { + kButt_Cap, //!< begin/end contours with no extension + kRound_Cap, //!< begin/end contours with a semi-circle extension + kSquare_Cap, //!< begin/end contours with a half square extension + + kCapCount, + kDefault_Cap = kButt_Cap + }; + + /** Join enum specifies the settings for the paint's strokejoin. This is + the treatment that is applied to corners in paths and rectangles. + */ + enum Join { + kMiter_Join, //!< connect path segments with a sharp join + kRound_Join, //!< connect path segments with a round join + kBevel_Join, //!< connect path segments with a flat bevel join + + kJoinCount, + kDefault_Join = kMiter_Join + }; + + /** Return the paint's stroke cap type, controlling how the start and end + of stroked lines and paths are treated. + @return the line cap style for the paint, used whenever the paint's + style is Stroke or StrokeAndFill. + */ + Cap getStrokeCap() const { return (Cap)fCapType; } + + /** Set the paint's stroke cap type. + @param cap set the paint's line cap style, used whenever the paint's + style is Stroke or StrokeAndFill. + */ + void setStrokeCap(Cap cap); + + /** Return the paint's stroke join type. + @return the paint's line join style, used whenever the paint's style is + Stroke or StrokeAndFill. + */ + Join getStrokeJoin() const { return (Join)fJoinType; } + + /** Set the paint's stroke join type. + @param join set the paint's line join style, used whenever the paint's + style is Stroke or StrokeAndFill. + */ + void setStrokeJoin(Join join); + + /** Applies any/all effects (patheffect, stroking) to src, returning the + result in dst. The result is that drawing src with this paint will be + the same as drawing dst with a default paint (at least from the + geometric perspective). + @param src input path + @param dst output path (may be the same as src) + @return true if the path should be filled, or false if it should be + drawn with a hairline (width == 0) + */ + bool getFillPath(const SkPath& src, SkPath* dst) const; + + /** Get the paint's shader object. + <p /> + The shader's reference count is not affected. + @return the paint's shader (or NULL) + */ + SkShader* getShader() const { return fShader; } + + /** Set or clear the shader object. + <p /> + Pass NULL to clear any previous shader. + As a convenience, the parameter passed is also returned. + If a previous shader exists, its reference count is decremented. + If shader is not NULL, its reference count is incremented. + @param shader May be NULL. The shader to be installed in the paint + @return shader + */ + SkShader* setShader(SkShader* shader); + + /** Get the paint's colorfilter. If there is a colorfilter, its reference + count is not changed. + @return the paint's colorfilter (or NULL) + */ + SkColorFilter* getColorFilter() const { return fColorFilter; } + + /** Set or clear the paint's colorfilter, returning the parameter. + <p /> + If the paint already has a filter, its reference count is decremented. + If filter is not NULL, its reference count is incremented. + @param filter May be NULL. The filter to be installed in the paint + @return filter + */ + SkColorFilter* setColorFilter(SkColorFilter* filter); + + /** Get the paint's xfermode object. + <p /> + The xfermode's reference count is not affected. + @return the paint's xfermode (or NULL) + */ + SkXfermode* getXfermode() const { return fXfermode; } + + /** Set or clear the xfermode object. + <p /> + Pass NULL to clear any previous xfermode. + As a convenience, the parameter passed is also returned. + If a previous xfermode exists, its reference count is decremented. + If xfermode is not NULL, its reference count is incremented. + @param xfermode May be NULL. The new xfermode to be installed in the + paint + @return xfermode + */ + SkXfermode* setXfermode(SkXfermode* xfermode); + + /** Helper for setXfermode, passing the corresponding xfermode object + returned from the PorterDuff factory. + @param mode The porter-duff mode used to create an xfermode for the + paint. + @return the resulting xfermode object (or NULL if the mode is + SrcOver) + */ + SkXfermode* setPorterDuffXfermode(SkPorterDuff::Mode mode); + + /** Get the paint's patheffect object. + <p /> + The patheffect reference count is not affected. + @return the paint's patheffect (or NULL) + */ + SkPathEffect* getPathEffect() const { return fPathEffect; } + + /** Set or clear the patheffect object. + <p /> + Pass NULL to clear any previous patheffect. + As a convenience, the parameter passed is also returned. + If a previous patheffect exists, its reference count is decremented. + If patheffect is not NULL, its reference count is incremented. + @param effect May be NULL. The new patheffect to be installed in the + paint + @return effect + */ + SkPathEffect* setPathEffect(SkPathEffect* effect); + + /** Get the paint's maskfilter object. + <p /> + The maskfilter reference count is not affected. + @return the paint's maskfilter (or NULL) + */ + SkMaskFilter* getMaskFilter() const { return fMaskFilter; } + + /** Set or clear the maskfilter object. + <p /> + Pass NULL to clear any previous maskfilter. + As a convenience, the parameter passed is also returned. + If a previous maskfilter exists, its reference count is decremented. + If maskfilter is not NULL, its reference count is incremented. + @param maskfilter May be NULL. The new maskfilter to be installed in + the paint + @return maskfilter + */ + SkMaskFilter* setMaskFilter(SkMaskFilter* maskfilter); + + // These attributes are for text/fonts + + /** Get the paint's typeface object. + <p /> + The typeface object identifies which font to use when drawing or + measuring text. The typeface reference count is not affected. + @return the paint's typeface (or NULL) + */ + SkTypeface* getTypeface() const { return fTypeface; } + + /** Set or clear the typeface object. + <p /> + Pass NULL to clear any previous typeface. + As a convenience, the parameter passed is also returned. + If a previous typeface exists, its reference count is decremented. + If typeface is not NULL, its reference count is incremented. + @param typeface May be NULL. The new typeface to be installed in the + paint + @return typeface + */ + SkTypeface* setTypeface(SkTypeface* typeface); + + /** Get the paint's rasterizer (or NULL). + <p /> + The raster controls how paths/text are turned into alpha masks. + @return the paint's rasterizer (or NULL) + */ + SkRasterizer* getRasterizer() const { return fRasterizer; } + + /** Set or clear the rasterizer object. + <p /> + Pass NULL to clear any previous rasterizer. + As a convenience, the parameter passed is also returned. + If a previous rasterizer exists in the paint, its reference count is + decremented. If rasterizer is not NULL, its reference count is + incremented. + @param rasterizer May be NULL. The new rasterizer to be installed in + the paint. + @return rasterizer + */ + SkRasterizer* setRasterizer(SkRasterizer* rasterizer); + + SkDrawLooper* getLooper() const { return fLooper; } + SkDrawLooper* setLooper(SkDrawLooper*); + + enum Align { + kLeft_Align, + kCenter_Align, + kRight_Align, + + kAlignCount + }; + /** Return the paint's Align value for drawing text. + @return the paint's Align value for drawing text. + */ + Align getTextAlign() const { return (Align)fTextAlign; } + /** Set the paint's text alignment. + @param align set the paint's Align value for drawing text. + */ + void setTextAlign(Align align); + + /** Return the paint's text size. + @return the paint's text size. + */ + SkScalar getTextSize() const { return fTextSize; } + + /** Set the paint's text size. This value must be > 0 + @param textSize set the paint's text size. + */ + void setTextSize(SkScalar textSize); + + /** Return the paint's horizontal scale factor for text. The default value + is 1.0. + @return the paint's scale factor in X for drawing/measuring text + */ + SkScalar getTextScaleX() const { return fTextScaleX; } + + /** Set the paint's horizontal scale factor for text. The default value + is 1.0. Values > 1.0 will stretch the text wider. Values < 1.0 will + stretch the text narrower. + @param scaleX set the paint's scale factor in X for drawing/measuring + text. + */ + void setTextScaleX(SkScalar scaleX); + + /** Return the paint's horizontal skew factor for text. The default value + is 0. + @return the paint's skew factor in X for drawing text. + */ + SkScalar getTextSkewX() const { return fTextSkewX; } + + /** Set the paint's horizontal skew factor for text. The default value + is 0. For approximating oblique text, use values around -0.25. + @param skewX set the paint's skew factor in X for drawing text. + */ + void setTextSkewX(SkScalar skewX); + + /** Describes how to interpret the text parameters that are passed to paint + methods like measureText() and getTextWidths(). + */ + enum TextEncoding { + kUTF8_TextEncoding, //!< the text parameters are UTF8 + kUTF16_TextEncoding, //!< the text parameters are UTF16 + kGlyphID_TextEncoding //!< the text parameters are glyph indices + }; + + TextEncoding getTextEncoding() const + { + return (TextEncoding)fTextEncoding; + } + + void setTextEncoding(TextEncoding encoding); + + struct FontMetrics { + SkScalar fTop; //!< The greatest distance above the baseline for any glyph (will be <= 0) + SkScalar fAscent; //!< The recommended distance above the baseline (will be <= 0) + SkScalar fDescent; //!< The recommended distance below the baseline (will be >= 0) + SkScalar fBottom; //!< The greatest distance below the baseline for any glyph (will be >= 0) + SkScalar fLeading; //!< The recommended distance to add between lines of text (will be >= 0) + }; + + /** Return the recommend spacing between lines (which will be + fDescent - fAscent + fLeading). + If metrics is not null, return in it the font metrics for the + typeface/pointsize/etc. currently set in the paint. + @param metrics If not null, returns the font metrics for the + current typeface/pointsize/etc setting in this + paint. + @param scale If not 0, return width as if the canvas were scaled + by this value + @param return the recommended spacing between lines + */ + SkScalar getFontMetrics(FontMetrics* metrics, SkScalar scale = 0) const; + + /** Return the recommend line spacing. This will be + fDescent - fAscent + fLeading + */ + SkScalar getFontSpacing() const { return this->getFontMetrics(NULL, 0); } + + /** Convert the specified text into glyph IDs, returning the number of + glyphs ID written. If glyphs is NULL, it is ignore and only the count + is returned. + */ + int textToGlyphs(const void* text, size_t byteLength, + uint16_t glyphs[]) const; + + /** Return the number of drawable units in the specified text buffer. + This looks at the current TextEncoding field of the paint. If you also + want to have the text converted into glyph IDs, call textToGlyphs + instead. + */ + int countText(const void* text, size_t byteLength) const + { + return this->textToGlyphs(text, byteLength, NULL); + } + + /** Return the width of the text. + @param text The text to be measured + @param length Number of bytes of text to measure + @param bounds If not NULL, returns the bounds of the text, + relative to (0, 0). + @param scale If not 0, return width as if the canvas were scaled + by this value + @return The advance width of the text + */ + SkScalar measureText(const void* text, size_t length, + SkRect* bounds, SkScalar scale = 0) const; + + /** Return the width of the text. + @param text Address of the text + @param length Number of bytes of text to measure + @return The width of the text + */ + SkScalar measureText(const void* text, size_t length) const + { + return this->measureText(text, length, NULL, 0); + } + + /** Specify the direction the text buffer should be processed in breakText() + */ + enum TextBufferDirection { + /** When measuring text for breakText(), begin at the start of the text + buffer and proceed forward through the data. This is the default. + */ + kForward_TextBufferDirection, + /** When measuring text for breakText(), begin at the end of the text + buffer and proceed backwards through the data. + */ + kBackward_TextBufferDirection + }; + + /** Return the width of the text. + @param text The text to be measured + @param length Number of bytes of text to measure + @param maxWidth Maximum width. Only the subset of text whose accumulated + widths are <= maxWidth are measured. + @param measuredWidth Optional. If non-null, this returns the actual + width of the measured text. + @param tbd Optional. The direction the text buffer should be + traversed during measuring. + @return The number of bytes of text that were measured. Will be + <= length. + */ + size_t breakText(const void* text, size_t length, SkScalar maxWidth, + SkScalar* measuredWidth = NULL, + TextBufferDirection tbd = kForward_TextBufferDirection) + const; + + /** Return the advance widths for the characters in the string. + @param text the text + @param byteLength number of bytes to of text + @param widths If not null, returns the array of advance widths of + the glyphs. If not NULL, must be at least a large + as the number of unichars in the specified text. + @param bounds If not null, returns the bounds for each of + character, relative to (0, 0) + @return the number of unichars in the specified text. + */ + int getTextWidths(const void* text, size_t byteLength, SkScalar widths[], + SkRect bounds[] = NULL) const; + + /** Return the path (outline) for the specified text. + Note: just like SkCanvas::drawText, this will respect the Align setting + in the paint. + */ + void getTextPath(const void* text, size_t length, SkScalar x, SkScalar y, + SkPath* path) const; + +private: + SkTypeface* fTypeface; + SkScalar fTextSize; + SkScalar fTextScaleX; + SkScalar fTextSkewX; + + SkPathEffect* fPathEffect; + SkShader* fShader; + SkXfermode* fXfermode; + SkMaskFilter* fMaskFilter; + SkColorFilter* fColorFilter; + SkRasterizer* fRasterizer; + SkDrawLooper* fLooper; + + SkColor fColor; + SkScalar fWidth; + SkScalar fMiterLimit; + unsigned fFlags : 9; + unsigned fTextAlign : 2; + unsigned fCapType : 2; + unsigned fJoinType : 2; + unsigned fStyle : 2; + unsigned fTextEncoding : 2; // 3 values + + SkDrawCacheProc getDrawCacheProc() const; + SkMeasureCacheProc getMeasureCacheProc(TextBufferDirection dir, + bool needFullMetrics) const; + + SkScalar measure_text(SkGlyphCache*, const char* text, size_t length, + int* count, SkRect* bounds) const; + + SkGlyphCache* detachCache(const SkMatrix*) const; + + void descriptorProc(const SkMatrix* deviceMatrix, + void (*proc)(const SkDescriptor*, void*), + void* context) const; + + enum { + kCanonicalTextSizeForPaths = 64 + }; + friend class SkCanvas; + friend class SkDraw; + friend class SkAutoGlyphCache; + friend class SkTextToPathIter; +}; + +////////////////////////////////////////////////////////////////////////// + +#include "SkPathEffect.h" + +/** \class SkStrokePathEffect + + SkStrokePathEffect simulates stroking inside a patheffect, allowing the + caller to have explicit control of when to stroke a path. Typically this is + used if the caller wants to stroke before another patheffect is applied + (using SkComposePathEffect or SkSumPathEffect). +*/ +class SkStrokePathEffect : public SkPathEffect { +public: + SkStrokePathEffect(const SkPaint&); + SkStrokePathEffect(SkScalar width, SkPaint::Style, SkPaint::Join, + SkPaint::Cap, SkScalar miterLimit = -1); + + // overrides + // This method is not exported to java. + virtual bool filterPath(SkPath* dst, const SkPath& src, SkScalar* width); + + // overrides for SkFlattenable + // This method is not exported to java. + virtual void flatten(SkFlattenableWriteBuffer&); + // This method is not exported to java. + virtual Factory getFactory(); + +private: + SkScalar fWidth, fMiter; + uint8_t fStyle, fJoin, fCap; + + static SkFlattenable* CreateProc(SkFlattenableReadBuffer&); + SkStrokePathEffect(SkFlattenableReadBuffer&); + + typedef SkPathEffect INHERITED; + + // illegal + SkStrokePathEffect(const SkStrokePathEffect&); + SkStrokePathEffect& operator=(const SkStrokePathEffect&); +}; + +#endif + diff --git a/skia/include/SkPaintFlagsDrawFilter.h b/skia/include/SkPaintFlagsDrawFilter.h new file mode 100644 index 0000000..7f489f9 --- /dev/null +++ b/skia/include/SkPaintFlagsDrawFilter.h @@ -0,0 +1,21 @@ +#ifndef SkPaintFlagsDrawFilter_DEFINED +#define SkPaintFlagsDrawFilter_DEFINED + +#include "SkDrawFilter.h" + +class SkPaintFlagsDrawFilter : public SkDrawFilter { +public: + SkPaintFlagsDrawFilter(uint32_t clearFlags, uint32_t setFlags); + + // overrides + virtual bool filter(SkCanvas*, SkPaint*, Type); + virtual void restore(SkCanvas*, SkPaint*, Type); + +private: + uint32_t fPrevFlags; // local cache for filter/restore + uint16_t fClearFlags; // user specified + uint16_t fSetFlags; // user specified +}; + +#endif + diff --git a/skia/include/SkParse.h b/skia/include/SkParse.h new file mode 100644 index 0000000..b066f6a --- /dev/null +++ b/skia/include/SkParse.h @@ -0,0 +1,46 @@ +/* include/graphics/SkParse.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkParse_DEFINED +#define SkParse_DEFINED + +#include "SkColor.h" +#include "SkMath.h" + +class SkParse { +public: + static int Count(const char str[]); // number of scalars or int values + static int Count(const char str[], char separator); + static const char* FindColor(const char str[], SkColor* value); + static const char* FindHex(const char str[], uint32_t* value); + static const char* FindMSec(const char str[], SkMSec* value); + static const char* FindNamedColor(const char str[], size_t len, SkColor* color); + static const char* FindS32(const char str[], int32_t* value); + static const char* FindScalar(const char str[], SkScalar* value); + static const char* FindScalars(const char str[], SkScalar value[], int count); + + static bool FindBool(const char str[], bool* value); + // return the index of str in list[], or -1 if not found + static int FindList(const char str[], const char list[]); +#ifdef SK_SUPPORT_UNITTEST + static void TestColor(); + static void UnitTest(); +#endif +}; + +#endif + diff --git a/skia/include/SkParsePaint.h b/skia/include/SkParsePaint.h new file mode 100644 index 0000000..32b84f9 --- /dev/null +++ b/skia/include/SkParsePaint.h @@ -0,0 +1,35 @@ +/* include/graphics/SkParsePaint.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkParsePaint_DEFINED +#define SkParsePaint_DEFINED + +#include "SkPaint.h" +#include "SkDOM.h" + +/** "color" color + "opacity" scalar [0..1] + "stroke-width" scalar (0...inf) + "text-size" scalar (0..inf) + "is-stroke" bool + "is-antialias" bool + "is-lineartext" bool +*/ +void SkPaint_Inflate(SkPaint*, const SkDOM&, const SkDOM::Node*); + +#endif + diff --git a/skia/include/SkPath.h b/skia/include/SkPath.h new file mode 100644 index 0000000..f5859a9 --- /dev/null +++ b/skia/include/SkPath.h @@ -0,0 +1,585 @@ +/* + * Copyright (C) 2006-2008 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SkPath_DEFINED +#define SkPath_DEFINED + +#include "SkMatrix.h" +#include "SkTDArray.h" + +class SkFlattenableReadBuffer; +class SkFlattenableWriteBuffer; +class SkString; + +/** \class SkPath + + The SkPath class encapsulates compound (multiple contour) geometric paths + consisting of straight line segments, quadratic curves, and cubic curves. +*/ +class SkPath { +public: + SkPath(); + SkPath(const SkPath&); + ~SkPath(); + + SkPath& operator=(const SkPath&); + + enum FillType { + /** Specifies that "inside" is computed by a non-zero sum of signed + edge crossings + */ + kWinding_FillType, + /** Specifies that "inside" is computed by an odd number of edge + crossings + */ + kEvenOdd_FillType, + /** Same as Winding, but draws outside of the path, rather than inside + */ + kInverseWinding_FillType, + /** Same as EvenOdd, but draws outside of the path, rather than inside + */ + kInverseEvenOdd_FillType + }; + + /** Return the path's fill type. This is used to define how "inside" is + computed. The default value is kWinding_FillType. + + @return the path's fill type + */ + FillType getFillType() const { return (FillType)fFillType; } + + /** Set the path's fill type. This is used to define how "inside" is + computed. The default value is kWinding_FillType. + + @param ft The new fill type for this path + */ + void setFillType(FillType ft) { fFillType = SkToU8(ft); } + + /** Returns true if the filltype is one of the Inverse variants */ + bool isInverseFillType() const { return (fFillType & 2) != 0; } + + /** Toggle between inverse and normal filltypes. This reverse the return + value of isInverseFillType() + */ + void toggleInverseFillType() { fFillType ^= 2; } + + /** Clear any lines and curves from the path, making it empty. This frees up + internal storage associated with those segments. + This does NOT change the fill-type setting. + */ + void reset(); + + /** Similar to reset(), in that all lines and curves are removed from the + path. However, any internal storage for those lines/curves is retained, + making reuse of the path potentially faster. + This does NOT change the fill-type setting. + */ + void rewind(); + + /** Returns true if the path is empty (contains no lines or curves) + + @return true if the path is empty (contains no lines or curves) + */ + bool isEmpty() const; + + /** Returns true if the path specifies a rectangle. If so, and if rect is + not null, set rect to the bounds of the path. If the path does not + specify a rectangle, return false and ignore rect. + + @param rect If not null, returns the bounds of the path if it specifies + a rectangle + @return true if the path specifies a rectangle + */ + bool isRect(SkRect* rect) const; + + /** Returns the number of points in the path. Up to max points are copied. + + @param points If not null, receives up to max points + @param max The maximum number of points to copy into points + @return the actual number of points in the path + */ + int getPoints(SkPoint points[], int max) const; + + //! Swap contents of this and other. Guaranteed not to throw + void swap(SkPath& other); + + enum BoundsType { + /** compute the bounds of the path's control points, may be larger than + with kExact_BoundsType, but may be faster to compute + */ + kFast_BoundsType, + /** compute the exact bounds of the path, may be smaller than with + kFast_BoundsType, but may be slower to compute + */ + kExact_BoundsType + }; + + /** Compute the bounds of the path, and write the answer into bounds. If the + path contains 0 or 1 points, the bounds is set to (0,0,0,0) + + @param bounds Returns the computed bounds of the path + @param btype Specifies if the computed bounds should be exact + (slower) or approximate (faster) + */ + void computeBounds(SkRect* bounds, BoundsType btype) const; + + /** Calling this will, if the internal cache of the bounds is out of date, + update it so that subsequent calls to computeBounds will be instanteous. + This also means that any copies or simple transformations of the path + will inherit the cached bounds. + */ + void updateBoundsCache() const; + + // Construction methods + + /** Hint to the path to prepare for adding more points. This can allow the + path to more efficiently grow its storage. + + @param extraPtCount The number of extra points the path should + preallocate for. + */ + void incReserve(unsigned extraPtCount); + + /** Set the beginning of the next contour to the point (x,y). + + @param x The x-coordinate of the start of a new contour + @param y The y-coordinate of the start of a new contour + */ + void moveTo(SkScalar x, SkScalar y); + + /** Set the beginning of the next contour to the point + + @param p The start of a new contour + */ + void moveTo(const SkPoint& p) { + this->moveTo(p.fX, p.fY); + } + + /** Set the beginning of the next contour relative to the last point on the + previous contour. If there is no previous contour, this is treated the + same as moveTo(). + + @param dx The amount to add to the x-coordinate of the end of the + previous contour, to specify the start of a new contour + @param dy The amount to add to the y-coordinate of the end of the + previous contour, to specify the start of a new contour + */ + void rMoveTo(SkScalar dx, SkScalar dy); + + /** Add a line from the last point to the specified point (x,y). If no + moveTo() call has been made for this contour, the first point is + automatically set to (0,0). + + @param x The x-coordinate of the end of a line + @param y The y-coordinate of the end of a line + */ + void lineTo(SkScalar x, SkScalar y); + + /** Add a line from the last point to the specified point. If no moveTo() + call has been made for this contour, the first point is automatically + set to (0,0). + + @param p The end of a line + */ + void lineTo(const SkPoint& p) { + this->lineTo(p.fX, p.fY); + } + + /** Same as lineTo, but the coordinates are considered relative to the last + point on this contour. If there is no previous point, then a moveTo(0,0) + is inserted automatically. + + @param dx The amount to add to the x-coordinate of the previous point + on this contour, to specify a line + @param dy The amount to add to the y-coordinate of the previous point + on this contour, to specify a line + */ + void rLineTo(SkScalar dx, SkScalar dy); + + /** Add a quadratic bezier from the last point, approaching control point + (x1,y1), and ending at (x2,y2). If no moveTo() call has been made for + this contour, the first point is automatically set to (0,0). + + @param x1 The x-coordinate of the control point on a quadratic curve + @param y1 The y-coordinate of the control point on a quadratic curve + @param x2 The x-coordinate of the end point on a quadratic curve + @param y2 The y-coordinate of the end point on a quadratic curve + */ + void quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2); + + /** Add a quadratic bezier from the last point, approaching control point + p1, and ending at p2. If no moveTo() call has been made for this + contour, the first point is automatically set to (0,0). + + @param p1 The control point on a quadratic curve + @param p2 The end point on a quadratic curve + */ + void quadTo(const SkPoint& p1, const SkPoint& p2) { + this->quadTo(p1.fX, p1.fY, p2.fX, p2.fY); + } + + /** Same as quadTo, but the coordinates are considered relative to the last + point on this contour. If there is no previous point, then a moveTo(0,0) + is inserted automatically. + + @param dx1 The amount to add to the x-coordinate of the last point on + this contour, to specify the control point of a quadratic curve + @param dy1 The amount to add to the y-coordinate of the last point on + this contour, to specify the control point of a quadratic curve + @param dx2 The amount to add to the x-coordinate of the last point on + this contour, to specify the end point of a quadratic curve + @param dy2 The amount to add to the y-coordinate of the last point on + this contour, to specify the end point of a quadratic curve + */ + void rQuadTo(SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2); + + /** Add a cubic bezier from the last point, approaching control points + (x1,y1) and (x2,y2), and ending at (x3,y3). If no moveTo() call has been + made for this contour, the first point is automatically set to (0,0). + + @param x1 The x-coordinate of the 1st control point on a cubic curve + @param y1 The y-coordinate of the 1st control point on a cubic curve + @param x2 The x-coordinate of the 2nd control point on a cubic curve + @param y2 The y-coordinate of the 2nd control point on a cubic curve + @param x3 The x-coordinate of the end point on a cubic curve + @param y3 The y-coordinate of the end point on a cubic curve + */ + void cubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, + SkScalar x3, SkScalar y3); + + /** Add a cubic bezier from the last point, approaching control points p1 + and p2, and ending at p3. If no moveTo() call has been made for this + contour, the first point is automatically set to (0,0). + + @param p1 The 1st control point on a cubic curve + @param p2 The 2nd control point on a cubic curve + @param p3 The end point on a cubic curve + */ + void cubicTo(const SkPoint& p1, const SkPoint& p2, const SkPoint& p3) { + this->cubicTo(p1.fX, p1.fY, p2.fX, p2.fY, p3.fX, p3.fY); + } + + /** Same as cubicTo, but the coordinates are considered relative to the + current point on this contour. If there is no previous point, then a + moveTo(0,0) is inserted automatically. + + @param dx1 The amount to add to the x-coordinate of the last point on + this contour, to specify the 1st control point of a cubic curve + @param dy1 The amount to add to the y-coordinate of the last point on + this contour, to specify the 1st control point of a cubic curve + @param dx2 The amount to add to the x-coordinate of the last point on + this contour, to specify the 2nd control point of a cubic curve + @param dy2 The amount to add to the y-coordinate of the last point on + this contour, to specify the 2nd control point of a cubic curve + @param dx3 The amount to add to the x-coordinate of the last point on + this contour, to specify the end point of a cubic curve + @param dy3 The amount to add to the y-coordinate of the last point on + this contour, to specify the end point of a cubic curve + */ + void rCubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, + SkScalar x3, SkScalar y3); + + /** Append the specified arc to the path as a new contour. If the start of + the path is different from the path's current last point, then an + automatic lineTo() is added to connect the current contour to the start + of the arc. However, if the path is empty, then we call moveTo() with + the first point of the arc. + + @param oval The bounding oval defining the shape and size of the arc + @param startAngle Starting angle (in degrees) where the arc begins + @param sweepAngle Sweep angle (in degrees) measured clockwise + @param forceMoveTo If true, always begin a new contour with the arc + */ + void arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle, + bool forceMoveTo); + + /** Append a line and arc to the current path. This is the same as the + PostScript call "arct". + */ + void arcTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, + SkScalar radius); + + /** Append a line and arc to the current path. This is the same as the + PostScript call "arct". + */ + void arcTo(const SkPoint p1, const SkPoint p2, SkScalar radius) { + this->arcTo(p1.fX, p1.fY, p2.fX, p2.fY, radius); + } + + /** Close the current contour. If the current point is not equal to the + first point of the contour, a line segment is automatically added. + */ + void close(); + + enum Direction { + /** clockwise direction for adding closed contours */ + kCW_Direction, + /** counter-clockwise direction for adding closed contours */ + kCCW_Direction + }; + + /** Add a closed rectangle contour to the path + @param rect The rectangle to add as a closed contour to the path + @param dir The direction to wind the rectangle's contour + */ + void addRect(const SkRect& rect, Direction dir = kCW_Direction); + + /** Add a closed rectangle contour to the path + + @param left The left side of a rectangle to add as a closed contour + to the path + @param top The top of a rectangle to add as a closed contour to the + path + @param right The right side of a rectangle to add as a closed contour + to the path + @param bottom The bottom of a rectangle to add as a closed contour to + the path + @param dir The direction to wind the rectangle's contour + */ + void addRect(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom, + Direction dir = kCW_Direction); + + /** Add a closed oval contour to the path + + @param oval The bounding oval to add as a closed contour to the path + @param dir The direction to wind the oval's contour + */ + void addOval(const SkRect& oval, Direction dir = kCW_Direction); + + /** Add a closed circle contour to the path + + @param x The x-coordinate of the center of a circle to add as a + closed contour to the path + @param y The y-coordinate of the center of a circle to add as a + closed contour to the path + @param radius The radius of a circle to add as a closed contour to the + path + @param dir The direction to wind the circle's contour + */ + void addCircle(SkScalar x, SkScalar y, SkScalar radius, + Direction dir = kCW_Direction); + + /** Add the specified arc to the path as a new contour. + + @param oval The bounds of oval used to define the size of the arc + @param startAngle Starting angle (in degrees) where the arc begins + @param sweepAngle Sweep angle (in degrees) measured clockwise + */ + void addArc(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle); + + /** Add a closed round-rectangle contour to the path + @param rect The bounds of a round-rectangle to add as a closed contour + @param rx The x-radius of the rounded corners on the round-rectangle + @param ry The y-radius of the rounded corners on the round-rectangle + @param dir The direction to wind the round-rectangle's contour + */ + void addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry, + Direction dir = kCW_Direction); + + /** Add a closed round-rectangle contour to the path. Each corner receives + two radius values [X, Y]. The corners are ordered top-left, top-right, + bottom-right, bottom-left. + @param rect The bounds of a round-rectangle to add as a closed contour + @param radii Array of 8 scalars, 4 [X,Y] pairs for each corner + @param dir The direction to wind the round-rectangle's contour + */ + void addRoundRect(const SkRect& rect, const SkScalar radii[], + Direction dir = kCW_Direction); + + /** Add a copy of src to the path, offset by (dx,dy) + @param src The path to add as a new contour + @param dx The amount to translate the path in X as it is added + @param dx The amount to translate the path in Y as it is added + */ + void addPath(const SkPath& src, SkScalar dx, SkScalar dy); + + /** Add a copy of src to the path + */ + void addPath(const SkPath& src) { + SkMatrix m; + m.reset(); + this->addPath(src, m); + } + + /** Add a copy of src to the path, transformed by matrix + @param src The path to add as a new contour + */ + void addPath(const SkPath& src, const SkMatrix& matrix); + + /** Offset the path by (dx,dy), returning true on success + + @param dx The amount in the X direction to offset the entire path + @param dy The amount in the Y direction to offset the entire path + @param dst The translated path is written here + */ + void offset(SkScalar dx, SkScalar dy, SkPath* dst) const; + + /** Offset the path by (dx,dy), returning true on success + + @param dx The amount in the X direction to offset the entire path + @param dy The amount in the Y direction to offset the entire path + */ + void offset(SkScalar dx, SkScalar dy) { + this->offset(dx, dy, this); + } + + /** Transform the points in this path by matrix, and write the answer into + dst. + + @param matrix The matrix to apply to the path + @param dst The transformed path is written here + */ + void transform(const SkMatrix& matrix, SkPath* dst) const; + + /** Transform the points in this path by matrix + + @param matrix The matrix to apply to the path + */ + void transform(const SkMatrix& matrix) { + this->transform(matrix, this); + } + + /** Return the last point on the path. If no points have been added, (0,0) + is returned. + + @param lastPt The last point on the path is returned here + */ + void getLastPt(SkPoint* lastPt) const; + + /** Set the last point on the path. If no points have been added, + moveTo(x,y) is automatically called. + + @param x The new x-coordinate for the last point + @param y The new y-coordinate for the last point + */ + void setLastPt(SkScalar x, SkScalar y); + + /** Set the last point on the path. If no points have been added, moveTo(p) + is automatically called. + + @param p The new location for the last point + */ + void setLastPt(const SkPoint& p) { + this->setLastPt(p.fX, p.fY); + } + + enum Verb { + kMove_Verb, //!< iter.next returns 1 point + kLine_Verb, //!< iter.next returns 2 points + kQuad_Verb, //!< iter.next returns 3 points + kCubic_Verb, //!< iter.next returns 4 points + kClose_Verb, //!< iter.next returns 1 point (the last point) + kDone_Verb //!< iter.next returns 0 points + }; + + /** Iterate through all of the segments (lines, quadratics, cubics) of + each contours in a path. + */ + class Iter { + public: + Iter(); + Iter(const SkPath&, bool forceClose); + + void setPath(const SkPath&, bool forceClose); + + /** Return the next verb in this iteration of the path. When all + segments have been visited, return kDone_Verb. + + @param pts The points representing the current verb and/or segment + @return The verb for the current segment + */ + Verb next(SkPoint pts[4]); + + /** If next() returns kLine_Verb, then this query returns true if the + line was the result of a close() command (i.e. the end point is the + initial moveto for this contour). If next() returned a different + verb, this returns an undefined value. + + @return If the last call to next() returned kLine_Verb, return true + if it was the result of an explicit close command. + */ + bool isCloseLine() const { return SkToBool(fCloseLine); } + + /** Returns true if the current contour is closed (has a kClose_Verb) + @return true if the current contour is closed (has a kClose_Verb) + */ + bool isClosedContour() const; + + private: + const SkPoint* fPts; + const uint8_t* fVerbs; + const uint8_t* fVerbStop; + SkPoint fMoveTo; + SkPoint fLastPt; + SkBool8 fForceClose; + SkBool8 fNeedClose; + SkBool8 fNeedMoveTo; + SkBool8 fCloseLine; + + bool cons_moveTo(SkPoint pts[1]); + Verb autoClose(SkPoint pts[2]); + }; + +#ifdef SK_DEBUG + /** @cond UNIT_TEST */ + void dump(bool forceClose, const char title[] = NULL) const; + static void UnitTest(); + /** @endcond */ +#endif + + void flatten(SkFlattenableWriteBuffer&) const; + void unflatten(SkFlattenableReadBuffer&); + + /** Subdivide the path so that no segment is longer that dist. + If bendLines is true, then turn all line segments into curves. + If dst == null, then the original path itself is modified (not const!) + */ + void subdivide(SkScalar dist, bool bendLines, SkPath* dst = NULL) const; + + /** Return an SVG-compatible string of the path. + */ + void toString(SkString*) const; + + SkDEBUGCODE(void validate() const;) + +private: + SkTDArray<SkPoint> fPts; + SkTDArray<uint8_t> fVerbs; + mutable SkRect fFastBounds; + mutable uint8_t fFastBoundsIsDirty; + uint8_t fFillType; + + friend class Iter; + void cons_moveto(); + + friend class SkPathStroker; + /* Append the first contour of path, ignoring path's initial point. If no + moveTo() call has been made for this contour, the first point is + automatically set to (0,0). + */ + void pathTo(const SkPath& path); + + /* Append, in reverse order, the first contour of path, ignoring path's + last point. If no moveTo() call has been made for this contour, the + first point is automatically set to (0,0). + */ + void reversePathTo(const SkPath&); + + friend const SkPoint* sk_get_path_points(const SkPath&, int index); +}; + +#endif + diff --git a/skia/include/SkPathEffect.h b/skia/include/SkPathEffect.h new file mode 100644 index 0000000..03ea6a4 --- /dev/null +++ b/skia/include/SkPathEffect.h @@ -0,0 +1,145 @@ +/* include/graphics/SkPathEffect.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkPathEffect_DEFINED +#define SkPathEffect_DEFINED + +#include "SkFlattenable.h" + +class SkPath; + +/** \class SkPathEffect + + SkPathEffect is the base class for objects in the SkPaint that affect + the geometry of a drawing primitive before it is transformed by the + canvas' matrix and drawn. + + Dashing is implemented as a subclass of SkPathEffect. +*/ +class SkPathEffect : public SkFlattenable { +public: + // This method is not exported to java. + SkPathEffect() {} + + /** Given a src path and a width value, return true if the patheffect + has produced a new path (dst) and a new width value. If false is returned, + ignore dst and width. + On input, width >= 0 means the src should be stroked + On output, width >= 0 means the dst should be stroked + */ + virtual bool filterPath(SkPath* dst, const SkPath& src, SkScalar* width) = 0; + +private: + // illegal + SkPathEffect(const SkPathEffect&); + SkPathEffect& operator=(const SkPathEffect&); +}; + +/** \class SkPairPathEffect + + Common baseclass for Compose and Sum. This subclass manages two pathEffects, + including flattening them. It does nothing in filterPath, and is only useful + for managing the lifetimes of its two arguments. +*/ +class SkPairPathEffect : public SkPathEffect { +public: + SkPairPathEffect(SkPathEffect* pe0, SkPathEffect* pe1); + virtual ~SkPairPathEffect(); + +protected: + SkPairPathEffect(SkFlattenableReadBuffer&); + virtual void flatten(SkFlattenableWriteBuffer&); + // these are visible to our subclasses + SkPathEffect* fPE0, *fPE1; + +private: + typedef SkPathEffect INHERITED; +}; + +/** \class SkComposePathEffect + + This subclass of SkPathEffect composes its two arguments, to create + a compound pathEffect. +*/ +class SkComposePathEffect : public SkPairPathEffect { +public: + /** Construct a pathEffect whose effect is to apply first the inner pathEffect + and the the outer pathEffect (e.g. outer(inner(path))) + The reference counts for outer and inner are both incremented in the constructor, + and decremented in the destructor. + */ + SkComposePathEffect(SkPathEffect* outer, SkPathEffect* inner) + : INHERITED(outer, inner) {} + + // overrides + + // This method is not exported to java. + virtual bool filterPath(SkPath* dst, const SkPath& src, SkScalar* width); + +protected: + virtual Factory getFactory() { return CreateProc; } + +private: + static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) { + return SkNEW_ARGS(SkComposePathEffect, (buffer)); + } + SkComposePathEffect(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {} + + // illegal + SkComposePathEffect(const SkComposePathEffect&); + SkComposePathEffect& operator=(const SkComposePathEffect&); + + typedef SkPairPathEffect INHERITED; +}; + +/** \class SkSumPathEffect + + This subclass of SkPathEffect applies two pathEffects, one after the other. + Its filterPath() returns true if either of the effects succeeded. +*/ +class SkSumPathEffect : public SkPairPathEffect { +public: + /** Construct a pathEffect whose effect is to apply two effects, in sequence. + (e.g. first(path) + second(path)) + The reference counts for first and second are both incremented in the constructor, + and decremented in the destructor. + */ + SkSumPathEffect(SkPathEffect* first, SkPathEffect* second) + : INHERITED(first, second) {} + + // overrides + // This method is not exported to java. + virtual bool filterPath(SkPath* dst, const SkPath& src, SkScalar* width); + +protected: + virtual Factory getFactory() { return CreateProc; } + +private: + static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) { + return SkNEW_ARGS(SkSumPathEffect, (buffer)); + } + SkSumPathEffect(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {} + + // illegal + SkSumPathEffect(const SkSumPathEffect&); + SkSumPathEffect& operator=(const SkSumPathEffect&); + + typedef SkPairPathEffect INHERITED; +}; + +#endif + diff --git a/skia/include/SkPathMeasure.h b/skia/include/SkPathMeasure.h new file mode 100644 index 0000000..f1ce8ab --- /dev/null +++ b/skia/include/SkPathMeasure.h @@ -0,0 +1,116 @@ +/* include/graphics/SkPathMeasure.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkPathMeasure_DEFINED +#define SkPathMeasure_DEFINED + +#include "SkPath.h" +#include "SkTDArray.h" + +class SkPathMeasure : SkNoncopyable { +public: + SkPathMeasure(); + /** Initialize the pathmeasure with the specified path. The path must remain valid + for the lifetime of the measure object, or until setPath() is called with + a different path (or null), since the measure object keeps a pointer to the + path object (does not copy its data). + */ + SkPathMeasure(const SkPath& path, bool forceClosed); + ~SkPathMeasure(); + + /** Reset the pathmeasure with the specified path. The path must remain valid + for the lifetime of the measure object, or until setPath() is called with + a different path (or null), since the measure object keeps a pointer to the + path object (does not copy its data). + */ + void setPath(const SkPath*, bool forceClosed); + + /** Return the total length of the current contour, or 0 if no path + is associated (e.g. resetPath(null)) + */ + SkScalar getLength(); + + /** Pins distance to 0 <= distance <= getLength(), and then computes + the corresponding position and tangent. + Returns false if there is no path, or a zero-length path was specified, in which case + position and tangent are unchanged. + */ + bool getPosTan(SkScalar distance, SkPoint* position, SkVector* tangent); + + enum MatrixFlags { + kGetPosition_MatrixFlag = 0x01, + kGetTangent_MatrixFlag = 0x02, + kGetPosAndTan_MatrixFlag = kGetPosition_MatrixFlag | kGetTangent_MatrixFlag + }; + /** Pins distance to 0 <= distance <= getLength(), and then computes + the corresponding matrix (by calling getPosTan). + Returns false if there is no path, or a zero-length path was specified, in which case + matrix is unchanged. + */ + bool getMatrix(SkScalar distance, SkMatrix* matrix, MatrixFlags flags = kGetPosAndTan_MatrixFlag); + /** Given a start and stop distance, return in dst the intervening segment(s). + If the segment is zero-length, return false, else return true. + startD and stopD are pinned to legal values (0..getLength()). If startD <= stopD + then return false (and leave dst untouched). + Begin the segment with a moveTo if startWithMoveTo is true + */ + bool getSegment(SkScalar startD, SkScalar stopD, SkPath* dst, bool startWithMoveTo); + + /** Return true if the current contour is closed() + */ + bool isClosed(); + + /** Move to the next contour in the path. Return true if one exists, or false if + we're done with the path. + */ + bool nextContour(); + +#ifdef SK_DEBUG + void dump(); + static void UnitTest(); +#endif + +private: + SkPath::Iter fIter; + const SkPath* fPath; + SkScalar fLength; // relative to the current contour + int fFirstPtIndex; // relative to the current contour + bool fIsClosed; // relative to the current contour + bool fForceClosed; + + struct Segment { + SkScalar fDistance; // total distance up to this point + unsigned fPtIndex : 15; + unsigned fTValue : 15; + unsigned fType : 2; + + SkScalar getScalarT() const; + }; + SkTDArray<Segment> fSegments; + + static const Segment* NextSegment(const Segment*); + + void buildSegments(); + SkScalar compute_quad_segs(const SkPoint pts[3], SkScalar distance, + int mint, int maxt, int ptIndex); + SkScalar compute_cubic_segs(const SkPoint pts[3], SkScalar distance, + int mint, int maxt, int ptIndex); + const Segment* distanceToSegment(SkScalar distance, SkScalar* t); +}; + +#endif + diff --git a/skia/include/SkPicture.h b/skia/include/SkPicture.h new file mode 100644 index 0000000..15836de --- /dev/null +++ b/skia/include/SkPicture.h @@ -0,0 +1,119 @@ +/* +** +** Copyright 2007, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkPicture_DEFINED +#define SkPicture_DEFINED + +#include "SkRefCnt.h" + +class SkCanvas; +class SkPicturePlayback; +class SkPictureRecord; +class SkStream; +class SkWStream; + +/** \class SkPicture + + The SkPicture class records the drawing commands made to a canvas, to + be played back at a later time. +*/ +class SkPicture : public SkRefCnt { +public: + /** The constructor prepares the picture to record. + @param width the width of the virtual device the picture records. + @param height the height of the virtual device the picture records. + */ + SkPicture(); + /** Make a copy of the contents of src. If src records more drawing after + this call, those elements will not appear in this picture. + */ + SkPicture(const SkPicture& src); + explicit SkPicture(SkStream*); + virtual ~SkPicture(); + + /** + * Swap the contents of the two pictures. Guaranteed to succeed. + */ + void swap(SkPicture& other); + + /** Returns the canvas that records the drawing commands. + @return the picture canvas. + */ + SkCanvas* beginRecording(int width, int height); + /** Returns the recording canvas if one is active, or NULL if recording is + not active. This does not alter the refcnt on the canvas (if present). + */ + SkCanvas* getRecordingCanvas() const; + /** Signal that the caller is done recording. This invalidates the canvas + returned by beginRecording/getRecordingCanvas, and prepares the picture + for drawing. Note: this happens implicitly the first time the picture + is drawn. + */ + void endRecording(); + + /** Replays the drawing commands on the specified canvas. This internally + calls endRecording() if that has not already been called. + @param surface the canvas receiving the drawing commands. + */ + void draw(SkCanvas* surface); + + /** Return the width of the picture's recording canvas. This + value reflects what was passed to setSize(), and does not necessarily + reflect the bounds of what has been recorded into the picture. + @return the width of the picture's recording canvas + */ + int width() const { return fWidth; } + + /** Return the height of the picture's recording canvas. This + value reflects what was passed to setSize(), and does not necessarily + reflect the bounds of what has been recorded into the picture. + @return the height of the picture's recording canvas + */ + int height() const { return fHeight; } + + void serialize(SkWStream*) const; + +private: + int fWidth, fHeight; + SkPictureRecord* fRecord; + SkPicturePlayback* fPlayback; + + friend class SkFlatPicture; + friend class SkPicturePlayback; +}; + +class SkAutoPictureRecord : SkNoncopyable { +public: + SkAutoPictureRecord(SkPicture* pict, int width, int height) { + fPicture = pict; + fCanvas = pict->beginRecording(width, height); + } + ~SkAutoPictureRecord() { + fPicture->endRecording(); + } + + /** Return the canvas to draw into for recording into the picture. + */ + SkCanvas* getRecordingCanvas() const { return fCanvas; } + +private: + SkPicture* fPicture; + SkCanvas* fCanvas; +}; + + +#endif diff --git a/skia/include/SkPixelRef.h b/skia/include/SkPixelRef.h new file mode 100644 index 0000000..4bab04e --- /dev/null +++ b/skia/include/SkPixelRef.h @@ -0,0 +1,136 @@ +#ifndef SkPixelRef_DEFINED +#define SkPixelRef_DEFINED + +#include "SkRefCnt.h" +#include "SkString.h" + +class SkColorTable; +class SkMutex; +class SkFlattenableReadBuffer; +class SkFlattenableWriteBuffer; + +/** \class SkPixelRef + + This class is the smart container for pixel memory, and is used with + SkBitmap. A pixelref is installed into a bitmap, and then the bitmap can + access the actual pixel memory by calling lockPixels/unlockPixels. + + This class can be shared/accessed between multiple threads. +*/ +class SkPixelRef : public SkRefCnt { +public: + explicit SkPixelRef(SkMutex* mutex = NULL); + + /** Return the pixel memory returned from lockPixels, or null if the + lockCount is 0. + */ + void* pixels() const { return fPixels; } + + /** Return the current colorTable (if any) if pixels are locked, or null. + */ + SkColorTable* colorTable() const { return fColorTable; } + + /** Return the current lockcount (defaults to 0) + */ + int getLockCount() const { return fLockCount; } + + /** Call to access the pixel memory, which is returned. Balance with a call + to unlockPixels(). + */ + void lockPixels(); + /** Call to balanace a previous call to lockPixels(). Returns the pixels + (or null) after the unlock. NOTE: lock calls can be nested, but the + matching number of unlock calls must be made in order to free the + memory (if the subclass implements caching/deferred-decoding.) + */ + void unlockPixels(); + + /** Returns a non-zero, unique value corresponding to the pixels in this + pixelref. Each time the pixels are changed (and notifyPixelsChanged is + called), a different generation ID will be returned. + */ + uint32_t getGenerationID() const; + + /** Call this if you have changed the contents of the pixels. This will in- + turn cause a different generation ID value to be returned from + getGenerationID(). + */ + void notifyPixelsChanged(); + + /** Returns true if this pixelref is marked as immutable, meaning that the + contents of its pixels will not change for the lifetime of the pixelref. + */ + bool isImmutable() const { return fIsImmutable; } + + /** Marks this pixelref is immutable, meaning that the contents of its + pixels will not change for the lifetime of the pixelref. This state can + be set on a pixelref, but it cannot be cleared once it is set. + */ + void setImmutable(); + + /** Return the optional URI string associated with this pixelref. May be + null. + */ + const char* getURI() const { return fURI.size() ? fURI.c_str() : NULL; } + + /** Assign a URI string to this pixelref. If not null, the string is copied. + */ + void setURI(const char uri[], size_t len = (size_t)-1) { + fURI.set(uri, len); + } + + /** Assign a URI string to this pixelref. + */ + void setURI(const SkString& uri) { fURI = uri; } + + // serialization + + typedef SkPixelRef* (*Factory)(SkFlattenableReadBuffer&); + + virtual Factory getFactory() const { return NULL; } + virtual void flatten(SkFlattenableWriteBuffer&) const; + + static Factory NameToFactory(const char name[]); + static const char* FactoryToName(Factory); + static void Register(const char name[], Factory); + + class Registrar { + public: + Registrar(const char name[], Factory factory) { + SkPixelRef::Register(name, factory); + } + }; + +protected: + /** Called when the lockCount goes from 0 to 1. The caller will have already + acquire a mutex for thread safety, so this method need not do that. + */ + virtual void* onLockPixels(SkColorTable**) = 0; + /** Called when the lock count goes from 1 to 0. The caller will have + already acquire a mutex for thread safety, so this method need not do + that. + */ + virtual void onUnlockPixels() = 0; + + /** Return the mutex associated with this pixelref. This value is assigned + in the constructor, and cannot change during the lifetime of the object. + */ + SkMutex* mutex() const { return fMutex; } + + SkPixelRef(SkFlattenableReadBuffer&, SkMutex*); + +private: + SkMutex* fMutex; // must remain in scope for the life of this object + void* fPixels; + SkColorTable* fColorTable; // we do not track ownership, subclass does + int fLockCount; + + mutable uint32_t fGenerationID; + + SkString fURI; + + // can go from false to true, but never from true to false + bool fIsImmutable; +}; + +#endif diff --git a/skia/include/SkPixelXorXfermode.h b/skia/include/SkPixelXorXfermode.h new file mode 100644 index 0000000..d755d11 --- /dev/null +++ b/skia/include/SkPixelXorXfermode.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2007 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SkPixelXorXfermode_DEFINED +#define SkPixelXorXfermode_DEFINED + +#include "SkXfermode.h" + +/** SkPixelXorXfermode implements a simple pixel xor (op ^ src ^ dst). + This transformation does not follow premultiplied conventions, therefore + this proc *always* returns an opaque color (alpha == 255). Thus it is + not really usefull for operating on blended colors. +*/ +class SkPixelXorXfermode : public SkXfermode { +public: + SkPixelXorXfermode(SkColor opColor) : fOpColor(opColor) {} + + // override from SkFlattenable + virtual Factory getFactory(); + virtual void flatten(SkFlattenableWriteBuffer&); + +protected: + // override from SkXfermode + virtual SkPMColor xferColor(SkPMColor src, SkPMColor dst); + +private: + SkColor fOpColor; + + SkPixelXorXfermode(SkFlattenableReadBuffer& rb); + // our private factory + static SkFlattenable* Create(SkFlattenableReadBuffer&); + + typedef SkXfermode INHERITED; +}; + +#endif + diff --git a/skia/include/SkPorterDuff.h b/skia/include/SkPorterDuff.h new file mode 100644 index 0000000..8ea2363 --- /dev/null +++ b/skia/include/SkPorterDuff.h @@ -0,0 +1,80 @@ +/* include/graphics/SkPorterDuff.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkPorterDuff_DEFINED +#define SkPorterDuff_DEFINED + +#include "SkColor.h" + +class SkXfermode; + +class SkPorterDuff { +public: + /** List of predefined xfermodes. In general, the algebra for the modes + uses the following symbols: + Sa, Sc - source alpha and color + Da, Dc - destination alpha and color (before compositing) + [a, c] - Resulting (alpha, color) values + For these equations, the colors are in premultiplied state. + If no xfermode is specified, kSrcOver is assumed. + */ + enum Mode { + kClear_Mode, //!< [0, 0] + kSrc_Mode, //!< [Sa, Sc] + kDst_Mode, //!< [Da, Dc] + kSrcOver_Mode, //!< [Sa + Da - Sa*Da, Rc = Sc + (1 - Sa)*Dc] + kDstOver_Mode, //!< [Sa + Da - Sa*Da, Rc = Dc + (1 - Da)*Sc] + kSrcIn_Mode, //!< [Sa * Da, Sc * Da] + kDstIn_Mode, //!< [Sa * Da, Sa * Dc] + kSrcOut_Mode, //!< [Sa * (1 - Da), Sc * (1 - Da)] + kDstOut_Mode, //!< [Da * (1 - Sa), Dc * (1 - Sa)] + kSrcATop_Mode, //!< [Da, Sc * Da + (1 - Sa) * Dc] + kDstATop_Mode, //!< [Sa, Sa * Dc + Sc * (1 - Da)] + kXor_Mode, //!< [Sa + Da - 2 * Sa * Da, Sc * (1 - Da) + (1 - Sa) * Dc] + kDarken_Mode, //!< [Sa + Da - Sa*Da, Sc\u00B7(1 - Da) + Dc\u00B7(1 - Sa) + min(Sc, Dc)] + kLighten_Mode, //!< [Sa + Da - Sa*Da, Sc\u00B7(1 - Da) + Dc\u00B7(1 - Sa) + max(Sc, Dc)] + kMultiply_Mode, //!< [Sa * Da, Sc * Dc] + kScreen_Mode, //!< [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] + + kModeCount + }; + /** Return an SkXfermode object for the specified mode. + */ + static SkXfermode* CreateXfermode(Mode mode); + + /** Return a function pointer to a routine that applies the specified + porter-duff transfer mode. + */ + static SkXfermodeProc GetXfermodeProc(Mode mode); + + /** Return a function pointer to a routine that applies the specified + porter-duff transfer mode and srcColor to a 16bit device color. Note, + if the mode+srcColor might return a non-opaque color, then there is not + 16bit proc, and this will return NULL. + */ + static SkXfermodeProc16 GetXfermodeProc16(Mode mode, SkColor srcColor); + + /** If the specified xfermode advertises itself as one of the porterduff + modes (via SkXfermode::Coeff), return true and if not null, set mode + to the corresponding porterduff mode. If it is not recognized as a one, + return false and ignore the mode parameter. + */ + static bool IsMode(SkXfermode*, Mode* mode); +}; + +#endif + diff --git a/skia/include/SkProgressBarView.h b/skia/include/SkProgressBarView.h new file mode 100644 index 0000000..5783f83 --- /dev/null +++ b/skia/include/SkProgressBarView.h @@ -0,0 +1,58 @@ +/* include/graphics/SkProgressBarView.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkProgressBarView_DEFINED +#define SkProgressBarView_DEFINED + +#include "SkView.h" +#include "SkWidgetViews.h" +#include "SkAnimator.h" + +class SkProgressBarView : public SkWidgetView { + public: + SkProgressBarView(); + //SkProgressBarView(int max); + + //inflate: "sk-progress" + + void reset(); //reset progress to zero + void setProgress(int progress); + void changeProgress(int diff); + void setMax(int max); + + int getProgress() const { return fProgress; } + int getMax() const { return fMax; } + + protected: + //overrides + virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node); + virtual void onSizeChange(); + virtual void onDraw(SkCanvas* canvas); + virtual bool onEvent(const SkEvent& evt); + + private: + SkAnimator fAnim; + int fProgress; + int fMax; + + typedef SkWidgetView INHERITED; +}; + + + + +#endif diff --git a/skia/include/SkPtrRecorder.h b/skia/include/SkPtrRecorder.h new file mode 100644 index 0000000..969b816 --- /dev/null +++ b/skia/include/SkPtrRecorder.h @@ -0,0 +1,32 @@ +#ifndef SkPtrRecorder_DEFINED +#define SkPtrRecorder_DEFINED + +#include "SkRefCnt.h" +#include "SkTDArray.h" + +class SkPtrRecorder : public SkRefCnt { +public: + uint32_t recordPtr(void*); + + size_t count() const { return fList.count(); } + void getPtrs(void* array[]) const; + + void reset(); + +protected: + virtual void incPtr(void* ptr) {} + virtual void decPtr(void* ptr) {} + +private: + struct Pair { + void* fPtr; + uint32_t fIndex; + }; + SkTDArray<Pair> fList; + + static int Cmp(const Pair& a, const Pair& b); + + typedef SkRefCnt INHERITED; +}; + +#endif diff --git a/skia/include/SkRasterizer.h b/skia/include/SkRasterizer.h new file mode 100644 index 0000000..b717c14 --- /dev/null +++ b/skia/include/SkRasterizer.h @@ -0,0 +1,51 @@ +/* include/graphics/SkRasterizer.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkRasterizer_DEFINED +#define SkRasterizer_DEFINED + +#include "SkFlattenable.h" +#include "SkMask.h" + +class SkMaskFilter; +class SkMatrix; +class SkPath; +struct SkIRect; + +class SkRasterizer : public SkFlattenable { +public: + SkRasterizer() {} + + /** Turn the path into a mask, respecting the specified local->device matrix. + */ + bool rasterize(const SkPath& path, const SkMatrix& matrix, + const SkIRect* clipBounds, SkMaskFilter* filter, + SkMask* mask, SkMask::CreateMode mode); + + virtual void flatten(SkFlattenableWriteBuffer& ) {} +protected: + SkRasterizer(SkFlattenableReadBuffer&); + + virtual bool onRasterize(const SkPath& path, const SkMatrix& matrix, + const SkIRect* clipBounds, + SkMask* mask, SkMask::CreateMode mode); + +private: + typedef SkFlattenable INHERITED; +}; + +#endif diff --git a/skia/include/SkReader32.h b/skia/include/SkReader32.h new file mode 100644 index 0000000..174c1ec --- /dev/null +++ b/skia/include/SkReader32.h @@ -0,0 +1,99 @@ +#ifndef SkReader32_DEFINED +#define SkReader32_DEFINED + +#include "SkTypes.h" + +#include "SkScalar.h" +#include "SkPoint.h" +#include "SkRect.h" + +class SkReader32 : SkNoncopyable { +public: + SkReader32() : fCurr(NULL), fStop(NULL), fBase(NULL) {} + SkReader32(const void* data, size_t size) { + this->setMemory(data, size); + } + + void setMemory(const void* data, size_t size) { + SkASSERT(ptr_align_4(data)); + SkASSERT(SkAlign4(size) == size); + + fBase = fCurr = (const char*)data; + fStop = (const char*)data + size; + } + + uint32_t size() const { return static_cast<uint32_t>(fStop - fBase); } + uint32_t offset() const { return static_cast<uint32_t>(fCurr - fBase); } + bool eof() const { return fCurr >= fStop; } + const void* base() const { return fBase; } + const void* peek() const { return fCurr; } + void rewind() { fCurr = fBase; } + + void setOffset(size_t offset) { + SkASSERT(SkAlign4(offset) == offset); + SkASSERT(offset <= this->size()); + fCurr = fBase + offset; + } + + bool readBool() { return this->readInt() != 0; } + + int32_t readInt() { + SkASSERT(ptr_align_4(fCurr)); + int32_t value = *(const int32_t*)fCurr; + fCurr += sizeof(value); + SkASSERT(fCurr <= fStop); + return value; + } + + SkScalar readScalar() { + SkASSERT(ptr_align_4(fCurr)); + SkScalar value = *(const SkScalar*)fCurr; + fCurr += sizeof(value); + SkASSERT(fCurr <= fStop); + return value; + } + + const SkPoint* skipPoint() { + return (const SkPoint*)this->skip(sizeof(SkPoint)); + } + + const SkRect* skipRect() { + return (const SkRect*)this->skip(sizeof(SkRect)); + } + + const void* skip(size_t size) { + SkASSERT(ptr_align_4(fCurr)); + const void* addr = fCurr; + fCurr += SkAlign4(size); + SkASSERT(fCurr <= fStop); + return addr; + } + + void read(void* dst, size_t size) { + SkASSERT(dst != NULL); + SkASSERT(ptr_align_4(fCurr)); + memcpy(dst, fCurr, size); + fCurr += SkAlign4(size); + SkASSERT(fCurr <= fStop); + } + + uint8_t readU8() { return (uint8_t)this->readInt(); } + uint16_t readU16() { return (uint16_t)this->readInt(); } + int32_t readS32() { return this->readInt(); } + uint32_t readU32() { return this->readInt(); } + +private: + // these are always 4-byte aligned + const char* fCurr; // current position within buffer + const char* fStop; // end of buffer + const char* fBase; // beginning of buffer + +#ifdef SK_DEBUG + static bool ptr_align_4(const void* ptr) + { + return (((const char*)ptr - (const char*)NULL) & 3) == 0; + } +#endif +}; + +#endif diff --git a/skia/include/SkRefCnt.h b/skia/include/SkRefCnt.h new file mode 100644 index 0000000..8a5abbf --- /dev/null +++ b/skia/include/SkRefCnt.h @@ -0,0 +1,135 @@ +/* include/graphics/SkRefCnt.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkRefCnt_DEFINED +#define SkRefCnt_DEFINED + +#include "SkThread.h" + +/** \class SkRefCnt + + SkRefCnt is the base class for objects that may be shared by multiple + objects. When a new owner wants a reference, it calls ref(). When an owner + wants to release its reference, it calls unref(). When the shared object's + reference count goes to zero as the result of an unref() call, its (virtual) + destructor is called. It is an error for the destructor to be called + explicitly (or via the object going out of scope on the stack or calling + delete) if getRefCnt() > 1. +*/ +class SkRefCnt : SkNoncopyable { +public: + /** Default construct, initializing the reference count to 1. + */ + SkRefCnt() : fRefCnt(1) {} + + /** Destruct, asserting that the reference count is 1. + */ + virtual ~SkRefCnt() { SkASSERT(fRefCnt == 1); } + + /** Return the reference count. + */ + int32_t getRefCnt() const { return fRefCnt; } + + /** Increment the reference count. Must be balanced by a call to unref(). + */ + void ref() const { + SkASSERT(fRefCnt > 0); + sk_atomic_inc(&fRefCnt); + } + + /** Decrement the reference count. If the reference count is 1 before the + decrement, then call delete on the object. Note that if this is the + case, then the object needs to have been allocated via new, and not on + the stack. + */ + void unref() const { + SkASSERT(fRefCnt > 0); + if (sk_atomic_dec(&fRefCnt) == 1) { + fRefCnt = 1; // so our destructor won't complain + SkDELETE(this); + } + } + + /** Helper version of ref(), that first checks to see if this is not null. + If this is null, then do nothing. + */ + void safeRef() const { + if (this) { + this->ref(); + } + } + + /** Helper version of unref(), that first checks to see if this is not null. + If this is null, then do nothing. + */ + void safeUnref() const { + if (this) { + this->unref(); + } + } + +private: + mutable int32_t fRefCnt; +}; + +/** \class SkAutoUnref + + SkAutoUnref is a stack-helper class that will automatically call unref() on + the object it points to when the SkAutoUnref object goes out of scope. + If obj is null, do nothing. +*/ +class SkAutoUnref : SkNoncopyable { +public: + SkAutoUnref(SkRefCnt* obj) : fObj(obj) {} + ~SkAutoUnref(); + + SkRefCnt* get() const { return fObj; } + + /** If the hosted object is null, do nothing and return false, else call + ref() on it and return true + */ + bool ref(); + + /** If the hosted object is null, do nothing and return false, else call + unref() on it, set its reference to null, and return true + */ + bool unref(); + + /** If the hosted object is null, do nothing and return NULL, else call + unref() on it, set its reference to null, and return the object + */ + SkRefCnt* detach(); + +private: + SkRefCnt* fObj; +}; + +/////////////////////////////////////////////////////////////////////////////// + +/** Helper macro to safely assign one SkRefCnt[TS]* to another, checking for + null in on each side of the assignment, and ensuring that ref() is called + before unref(), in case the two pointers point to the same object. +*/ +#define SkRefCnt_SafeAssign(dst, src) \ + do { \ + if (src) src->ref(); \ + if (dst) dst->unref(); \ + dst = src; \ + } while (0) + +#endif + diff --git a/skia/include/SkSVGAttribute.h b/skia/include/SkSVGAttribute.h new file mode 100644 index 0000000..4b14d04 --- /dev/null +++ b/skia/include/SkSVGAttribute.h @@ -0,0 +1,50 @@ +/* include/graphics/SkSVGAttribute.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkSVGAttribute_DEFINED +#define SkSVGAttribute_DEFINED + +#include "SkTypes.h" + +struct SkSVGAttribute { + const char* fName; +#ifdef SK_DEBUG + size_t fOffset; +#endif +}; + +#ifndef SK_OFFSETOF +#define SK_OFFSETOF(a, b) (((size_t) (&(((a*) 1)->b)))-1) +#endif + +#ifdef SK_DEBUG +#define SVG_ATTRIBUTE(attr) { #attr, SK_OFFSETOF(BASE_CLASS, f_##attr) } +#define SVG_LITERAL_ATTRIBUTE(svgAttr, cAttr) { #svgAttr, SK_OFFSETOF(BASE_CLASS, cAttr) } +#else +#define SVG_ATTRIBUTE(attr) { #attr } +#define SVG_LITERAL_ATTRIBUTE(svgAttr, cAttr) { #svgAttr } +#endif + +#define SVG_ADD_ATTRIBUTE(attr) \ + if (f_##attr.size() > 0) \ + parser._addAttributeLen(#attr, f_##attr.c_str(), f_##attr.size()) + +#define SVG_ADD_ATTRIBUTE_ALIAS(attr, alias) \ + if (f_##alias.size() > 0) \ + parser._addAttributeLen(#attr, f_##alias.c_str(), f_##alias.size()) + +#endif // SkSVGAttribute_DEFINED diff --git a/skia/include/SkSVGBase.h b/skia/include/SkSVGBase.h new file mode 100644 index 0000000..fff98cc --- /dev/null +++ b/skia/include/SkSVGBase.h @@ -0,0 +1,34 @@ +/* include/graphics/SkSVGBase.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkSVGBase_DEFINED +#define SkSVGBase_DEFINED + +#include "SkSVGAttribute.h" + +class SkSVGParser; + +class SkSVGBase { +public: + virtual ~SkSVGBase(); + virtual void addAttribute(SkSVGParser& parser, int attrIndex, + const char* attrValue, size_t attrLength); + virtual int getAttributes(const SkSVGAttribute** attrPtr) = 0; +}; + +#endif // SkSVGBase_DEFINEDes(const SkSVGAttribute** attrPtr) = 0; + diff --git a/skia/include/SkSVGPaintState.h b/skia/include/SkSVGPaintState.h new file mode 100644 index 0000000..2f30e42 --- /dev/null +++ b/skia/include/SkSVGPaintState.h @@ -0,0 +1,97 @@ +/* include/graphics/SkSVGPaintState.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkSVGPaintState_DEFINED +#define SkSVGPaintState_DEFINED + +#include "SkSVGBase.h" +#include "SkString.h" + +class SkSVGPaint : public SkSVGBase { +public: + enum Field { + kInitial = -1, + kClipPath, + kClipRule, + kEnableBackground, + kFill, + kFillRule, + kFilter, + kFontFamily, + kFontSize, + kLetterSpacing, + kMask, + kOpacity, + kStopColor, + kStopOpacity, + kStroke, + kStroke_Dasharray, + kStroke_Linecap, + kStroke_Linejoin, + kStroke_Miterlimit, + kStroke_Width, + kStyle, + kTransform, + kTerminal + }; + + SkSVGPaint(); + virtual void addAttribute(SkSVGParser& parser, int attrIndex, + const char* attrValue, size_t attrLength); + bool flush(SkSVGParser& , bool isFlushable, bool isDef); + virtual int getAttributes(const SkSVGAttribute** attrPtr); + static void Push(SkSVGPaint** head, SkSVGPaint* add); + static void Pop(SkSVGPaint** head); + SkString* operator[](int index); + SkString fInitial; + SkString f_clipPath; + SkString f_clipRule; + SkString f_enableBackground; + SkString f_fill; + SkString f_fillRule; + SkString f_filter; + SkString f_fontFamily; + SkString f_fontSize; + SkString f_letterSpacing; + SkString f_mask; + SkString f_opacity; + SkString f_stopColor; + SkString f_stopOpacity; + SkString f_stroke; + SkString f_strokeDasharray; + SkString f_strokeLinecap; + SkString f_strokeLinejoin; + SkString f_strokeMiterlimit; + SkString f_strokeWidth; + SkString f_style; // unused, but allows array access to the rest + SkString f_transform; +#ifdef SK_DEBUG + SkString fTerminal; +#endif + SkString fTransformID; + static SkSVGAttribute gAttributes[]; + static const int kAttributesSize; +private: + void setSave(SkSVGParser& ); + bool writeChangedAttributes(SkSVGParser& , SkSVGPaint& , bool* changed); + bool writeChangedElements(SkSVGParser& , SkSVGPaint& , bool* changed); + SkSVGPaint* fNext; + friend class SkSVGParser; + typedef SkSVGPaint BASE_CLASS; +}; + +#endif // SkSVGPaintState_DEFINED diff --git a/skia/include/SkSVGParser.h b/skia/include/SkSVGParser.h new file mode 100644 index 0000000..60cbcbd --- /dev/null +++ b/skia/include/SkSVGParser.h @@ -0,0 +1,82 @@ +/* include/graphics/SkSVGParser.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkSVGParser_DEFINED +#define SkSVGParser_DEFINED + +#include "SkMatrix.h" +#include "SkTDict.h" +#include "SkTDStack.h" +#include "SkSVGPaintState.h" +#include "SkSVGTypes.h" +#include "SkStream.h" +#include "SkString.h" +#include "SkXMLParser.h" +#include "SkXMLWriter.h" + +class SkSVGBase; +class SkSVGElement; + +class SkSVGParser : public SkXMLParser { +public: + SkSVGParser(); + virtual ~SkSVGParser(); + void _addAttribute(const char* attrName, const char* attrValue) { + fXMLWriter.addAttribute(attrName, attrValue); } + void _addAttribute(const char* attrName, SkString& attrValue) { + fXMLWriter.addAttribute(attrName, attrValue.c_str()); } + void _addAttributeLen(const char* attrName, const char* attrValue, size_t len) { + fXMLWriter.addAttributeLen(attrName, attrValue, len); } + void _endElement() { fXMLWriter.endElement(); } + int findAttribute(SkSVGBase* , const char* attrValue, size_t len, bool isPaint); + const char* getFinal(); + SkTDict<SkSVGElement*>& getIDs() { return fIDs; } + SkString& getPaintLast(SkSVGPaint::Field field); + void _startElement(const char name[]) { fXMLWriter.startElement(name); } + void translate(SkSVGElement*, bool isDef); + void translateMatrix(SkString& , SkString* id); + static void ConvertToArray(SkString& vals); +protected: + virtual bool onAddAttribute(const char name[], const char value[]); + bool onAddAttributeLen(const char name[], const char value[], size_t len); + virtual bool onEndElement(const char elem[]); + virtual bool onStartElement(const char elem[]); + bool onStartElementLen(const char elem[], size_t len); + virtual bool onText(const char text[], int len); +private: + bool isStrokeAndFill(SkSVGPaint** stroke, SkSVGPaint** fill); + static SkSVGElement* CreateElement(SkSVGTypes type, SkSVGElement* parent); + static void Delete(SkTDArray<SkSVGElement*>& fChildren); + static SkSVGTypes GetType(const char name[], size_t len); + SkSVGPaint* fHead; + SkSVGPaint fEmptyPaint; + SkSVGPaint fLastFlush; + SkString fLastColor; + SkMatrix fLastTransform; + SkTDArray<SkSVGElement*> fChildren; + SkTDict<SkSVGElement*> fIDs; + SkTDArray<SkSVGElement*> fParents; + SkDynamicMemoryWStream fStream; + SkXMLStreamWriter fXMLWriter; + SkSVGElement* fCurrElement; + SkBool8 fInSVG; + SkBool8 fSuppressPaint; + friend class SkSVGPaint; + friend class SkSVGGradient; +}; + +#endif // SkSVGParser_DEFINED diff --git a/skia/include/SkSVGTypes.h b/skia/include/SkSVGTypes.h new file mode 100644 index 0000000..24404ad --- /dev/null +++ b/skia/include/SkSVGTypes.h @@ -0,0 +1,47 @@ +/* include/graphics/SkSVGTypes.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkSVGTypes_DEFINED +#define SkSVGTypes_DEFINED + +enum SkSVGTypes { + SkSVGType_Circle, + SkSVGType_ClipPath, + SkSVGType_Defs, + SkSVGType_Ellipse, + SkSVGType_FeColorMatrix, + SkSVGType_Filter, + SkSVGType_G, + SkSVGType_Image, + SkSVGType_Line, + SkSVGType_LinearGradient, + SkSVGType_Mask, + SkSVGType_Metadata, + SkSVGType_Path, + SkSVGType_Polygon, + SkSVGType_Polyline, + SkSVGType_RadialGradient, + SkSVGType_Rect, + SkSVGType_SVG, + SkSVGType_Stop, + SkSVGType_Symbol, + SkSVGType_Text, + SkSVGType_Tspan, + SkSVGType_Use +}; + +#endif // SkSVGTypes_DEFINED diff --git a/skia/include/SkScalerContext.h b/skia/include/SkScalerContext.h new file mode 100644 index 0000000..0b8981f --- /dev/null +++ b/skia/include/SkScalerContext.h @@ -0,0 +1,215 @@ +/* include/graphics/SkScalerContext.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkScalerContext_DEFINED +#define SkScalerContext_DEFINED + +#include "SkMask.h" +#include "SkMatrix.h" +#include "SkPaint.h" +#include "SkPath.h" +#include "SkPoint.h" + +class SkDescriptor; +class SkMaskFilter; +class SkPathEffect; +class SkRasterizer; + +// needs to be != to any valid SkMask::Format +#define MASK_FORMAT_JUST_ADVANCE (0xFF) + +struct SkGlyph { + void* fImage; + SkPath* fPath; + SkFixed fAdvanceX, fAdvanceY; + + uint32_t fID; + uint16_t fWidth, fHeight; + int16_t fTop, fLeft; + + uint8_t fMaskFormat; + int8_t fRsbDelta, fLsbDelta; // used by auto-kerning + + unsigned rowBytes() const { + unsigned rb = fWidth; + if (SkMask::kBW_Format == fMaskFormat) { + rb = (rb + 7) >> 3; + } else { + rb = SkAlign4(rb); + } + return rb; + } + + bool isJustAdvance() const { + return MASK_FORMAT_JUST_ADVANCE == fMaskFormat; + } + + bool isFullMetrics() const { + return MASK_FORMAT_JUST_ADVANCE != fMaskFormat; + } + + uint16_t getGlyphID() const { + return ID2Code(fID); + } + + unsigned getGlyphID(unsigned baseGlyphCount) const { + unsigned code = ID2Code(fID); + SkASSERT(code >= baseGlyphCount); + return code - baseGlyphCount; + } + + unsigned getSubX() const { + return ID2SubX(fID); + } + + SkFixed getSubXFixed() const { + return SubToFixed(ID2SubX(fID)); + } + + SkFixed getSubYFixed() const { + return SubToFixed(ID2SubY(fID)); + } + + size_t computeImageSize() const; + + enum { + kSubBits = 2, + kSubMask = ((1 << kSubBits) - 1), + kSubShift = 24, // must be large enough for glyphs and unichars + kCodeMask = ((1 << kSubShift) - 1) + }; + + static unsigned ID2Code(uint32_t id) { + return id & kCodeMask; + } + + static unsigned ID2SubX(uint32_t id) { + return id >> (kSubShift + kSubBits); + } + + static unsigned ID2SubY(uint32_t id) { + return (id >> kSubShift) & kSubMask; + } + + static unsigned FixedToSub(SkFixed n) { + return (n >> (16 - kSubBits)) & kSubMask; + } + + static SkFixed SubToFixed(unsigned sub) { + SkASSERT(sub <= kSubMask); + return sub << (16 - kSubBits); + } + + static uint32_t MakeID(unsigned code) { + return code; + } + + static uint32_t MakeID(unsigned code, SkFixed x, SkFixed y) { + SkASSERT(code <= kCodeMask); + x = FixedToSub(x); + y = FixedToSub(y); + return (x << (kSubShift + kSubBits)) | (y << kSubShift) | code; + } + + void toMask(SkMask* mask) const; +}; + +class SkScalerContext { +public: + enum Hints { + kNo_Hints, + kSubpixel_Hints, + kNormal_Hints + }; + enum Flags { + kFrameAndFill_Flag = 0x01, + kDevKernText_Flag = 0x02, + kGammaForBlack_Flag = 0x04, // illegal to set both Gamma flags + kGammaForWhite_Flag = 0x08 // illegal to set both Gamma flags + }; + struct Rec { + uint32_t fFontID; + SkScalar fTextSize, fPreScaleX, fPreSkewX; + SkScalar fPost2x2[2][2]; + SkScalar fFrameWidth, fMiterLimit; + uint8_t fHints; + uint8_t fMaskFormat; + uint8_t fStrokeJoin; + uint8_t fFlags; + + void getMatrixFrom2x2(SkMatrix*) const; + void getLocalMatrix(SkMatrix*) const; + void getSingleMatrix(SkMatrix*) const; + }; + + SkScalerContext(const SkDescriptor* desc); + virtual ~SkScalerContext(); + + void setBaseGlyphCount(unsigned baseGlyphCount) { + fBaseGlyphCount = baseGlyphCount; + } + + uint16_t charToGlyphID(SkUnichar uni); + + unsigned getGlyphCount() const { return this->generateGlyphCount(); } + void getAdvance(SkGlyph*); + void getMetrics(SkGlyph*); + void getImage(const SkGlyph&); + void getPath(const SkGlyph&, SkPath*); + void getFontMetrics(SkPaint::FontMetrics* mX, + SkPaint::FontMetrics* mY); + + static inline void MakeRec(const SkPaint&, const SkMatrix*, Rec* rec); + static SkScalerContext* Create(const SkDescriptor*); + +protected: + Rec fRec; + unsigned fBaseGlyphCount; + + virtual unsigned generateGlyphCount() const = 0; + virtual uint16_t generateCharToGlyph(SkUnichar) = 0; + virtual void generateAdvance(SkGlyph*) = 0; + virtual void generateMetrics(SkGlyph*) = 0; + virtual void generateImage(const SkGlyph&) = 0; + virtual void generatePath(const SkGlyph&, SkPath*) = 0; + virtual void generateFontMetrics(SkPaint::FontMetrics* mX, + SkPaint::FontMetrics* mY) = 0; + +private: + SkPathEffect* fPathEffect; + SkMaskFilter* fMaskFilter; + SkRasterizer* fRasterizer; + SkScalar fDevFrameWidth; + + void internalGetPath(const SkGlyph& glyph, SkPath* fillPath, + SkPath* devPath, SkMatrix* fillToDevMatrix); + + mutable SkScalerContext* fAuxScalerContext; + + SkScalerContext* getGlyphContext(const SkGlyph& glyph) const; + + // return loaded fAuxScalerContext or NULL + SkScalerContext* loadAuxContext() const; +}; + +#define kRec_SkDescriptorTag SkSetFourByteTag('s', 'r', 'e', 'c') +#define kPathEffect_SkDescriptorTag SkSetFourByteTag('p', 't', 'h', 'e') +#define kMaskFilter_SkDescriptorTag SkSetFourByteTag('m', 's', 'k', 'f') +#define kRasterizer_SkDescriptorTag SkSetFourByteTag('r', 'a', 's', 't') + +#endif + diff --git a/skia/include/SkScrollBarView.h b/skia/include/SkScrollBarView.h new file mode 100644 index 0000000..514ad74 --- /dev/null +++ b/skia/include/SkScrollBarView.h @@ -0,0 +1,53 @@ +/* include/graphics/SkScrollBarView.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkScrollBarView_DEFINED +#define SkScrollBarView_DEFINED + +#include "SkView.h" +#include "SkWidgetViews.h" +#include "SkAnimator.h" + +class SkScrollBarView : public SkWidgetView { +public: + SkScrollBarView(); + + unsigned getStart() const { return fStartPoint; } + unsigned getShown() const { return fShownLength; } + unsigned getTotal() const { return fTotalLength; } + + void setStart(unsigned start); + void setShown(unsigned shown); + void setTotal(unsigned total); + +protected: + //overrides + virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node); + virtual void onSizeChange(); + virtual void onDraw(SkCanvas* canvas); + virtual bool onEvent(const SkEvent& evt); + +private: + SkAnimator fAnim; + unsigned fTotalLength, fStartPoint, fShownLength; + + void adjust(); + + typedef SkWidgetView INHERITED; +}; +#endif + diff --git a/skia/include/SkShader.h b/skia/include/SkShader.h new file mode 100644 index 0000000..69ae15a --- /dev/null +++ b/skia/include/SkShader.h @@ -0,0 +1,185 @@ +/* include/graphics/SkShader.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkShader_DEFINED +#define SkShader_DEFINED + +#include "SkBitmap.h" +#include "SkFlattenable.h" +#include "SkMask.h" +#include "SkMatrix.h" +#include "SkPaint.h" + +class SkPath; + +/** \class SkShader + + SkShader is the based class for objects that return horizontal spans of colors during drawing. + A subclass of SkShader is installed in a SkPaint calling paint.setShader(shader). After that + any object (other than a bitmap) that is drawn with that paint will get its color(s) from the + shader. +*/ +class SkShader : public SkFlattenable { +public: + SkShader(); + virtual ~SkShader(); + + /** Return true if the shader has a non-identity local matrix. + @param localM Optional: If not null, return the shader's local matrix + @return true if the shader has a non-identity local matrix. + */ + bool getLocalMatrix(SkMatrix* localM) const; + /** Set the shader's local matrix. + @param localM The shader's new local matrix. + */ + void setLocalMatrix(const SkMatrix& localM); + /** Reset the shader's local matrix to identity. + */ + void resetLocalMatrix(); + + enum TileMode { + kClamp_TileMode, //!< replicate the edge color if the shader draws outside of its original bounds + kRepeat_TileMode, //!< repeat the shader's image horizontally and vertically + kMirror_TileMode, //!< repeat the shader's image horizontally and vertically, alternating mirror images so that adjacent images always seam + + kTileModeCount + }; + + // override these in your subclass + + enum Flags { + //!< set if all of the colors will be opaque + kOpaqueAlpha_Flag = 0x01, + //! set if this shader's shadeSpan16() method can be called + kHasSpan16_Flag = 0x02, + /** Set this bit if the shader's native data type is instrinsically 16 + bit, meaning that calling the 32bit shadeSpan() entry point will + mean the the impl has to up-sample 16bit data into 32bit. Used as a + a means of clearing a dither request if the it will have no effect + */ + kIntrinsicly16_Flag = 0x04 + }; + + /** Called sometimes before drawing with this shader. + Return the type of alpha your shader will return. + The default implementation returns 0. Your subclass should override if it can + (even sometimes) report a non-zero value, since that will enable various blitters + to perform faster. + */ + virtual uint32_t getFlags() { return 0; } + + /** Return the alpha associated with the data returned by shadeSpan16(). If + kHasSpan16_Flag is not set, this value is meaningless. + */ + virtual uint8_t getSpan16Alpha() const { return fPaintAlpha; } + + /** Called once before drawing, with the current paint and + device matrix. Return true if your shader supports these + parameters, or false if not. If false is returned, nothing + will be drawn. + */ + virtual bool setContext( const SkBitmap& device, + const SkPaint& paint, + const SkMatrix& matrix); + + /** Called for each span of the object being drawn. Your subclass + should set the appropriate colors (with premultiplied alpha) that + correspond to the specified device coordinates. + */ + virtual void shadeSpan(int x, int y, SkPMColor[], int count) = 0; + /** Called only for 16bit devices when getFlags() returns kOpaqueAlphaFlag | kHasSpan16_Flag + */ + virtual void shadeSpan16(int x, int y, uint16_t[], int count); + /** Similar to shadeSpan, but only returns the alpha-channel for a span. + The default implementation calls shadeSpan() and then extracts the alpha + values from the returned colors. + */ + virtual void shadeSpanAlpha(int x, int y, uint8_t alpha[], int count); + + /** Helper function that returns true if this shader's shadeSpan16() method can + be called. + */ + bool canCallShadeSpan16() + { + return SkShader::CanCallShadeSpan16(this->getFlags()); + } + + /** Helper to check the flags to know if it is legal to call shadeSpan16() + */ + static bool CanCallShadeSpan16(uint32_t flags) { + return (flags & kHasSpan16_Flag) != 0; + } + + /** Called before a session using the shader begins. Some shaders override + this to defer some of their work (like calling bitmap.lockPixels()). + Must be balanced by a call to endSession. + */ + virtual void beginSession(); + virtual void endSession(); + + /** Optional methods for shaders that can pretend to be a bitmap/texture + to play along with opengl. Default just returns false and ignores + the out parameters. + */ + virtual bool asABitmap(SkBitmap* outTexture, SkMatrix* outMatrix, + TileMode xy[2]); + + ////////////////////////////////////////////////////////////////////////// + // Factory methods for stock shaders + + /** Call this to create a new shader that will draw with the specified bitmap. + @param src The bitmap to use inside the shader + @param tmx The tiling mode to use when sampling the bitmap in the x-direction. + @param tmy The tiling mode to use when sampling the bitmap in the y-direction. + @return Returns a new shader object. Note: this function never returns null. + */ + static SkShader* CreateBitmapShader(const SkBitmap& src, + TileMode tmx, TileMode tmy); + + virtual void flatten(SkFlattenableWriteBuffer& ); +protected: + enum MatrixClass { + kLinear_MatrixClass, // no perspective + kFixedStepInX_MatrixClass, // fast perspective, need to call fixedStepInX() each scanline + kPerspective_MatrixClass // slow perspective, need to mappoints each pixel + }; + static MatrixClass ComputeMatrixClass(const SkMatrix&); + + // These can be called by your subclass after setContext() has been called + uint8_t getPaintAlpha() const { return fPaintAlpha; } + SkBitmap::Config getDeviceConfig() const { return (SkBitmap::Config)fDeviceConfig; } + const SkMatrix& getTotalInverse() const { return fTotalInverse; } + MatrixClass getInverseClass() const { return (MatrixClass)fTotalInverseClass; } + + SkShader(SkFlattenableReadBuffer& ); +private: + SkMatrix* fLocalMatrix; + SkMatrix fTotalInverse; + uint8_t fPaintAlpha; + uint8_t fDeviceConfig; + uint8_t fTotalInverseClass; + SkDEBUGCODE(SkBool8 fInSession;) + + static SkShader* CreateBitmapShader(const SkBitmap& src, + TileMode, TileMode, + void* storage, size_t storageSize); + friend class SkAutoBitmapShaderInstall; + typedef SkFlattenable INHERITED; +}; + +#endif + diff --git a/skia/include/SkShaderExtras.h b/skia/include/SkShaderExtras.h new file mode 100644 index 0000000..8c43f5c --- /dev/null +++ b/skia/include/SkShaderExtras.h @@ -0,0 +1,67 @@ +/* include/graphics/SkShaderExtras.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkShaderExtras_DEFINED +#define SkShaderExtras_DEFINED + +#include "SkShader.h" + +class SkXfermode; + +/////////////////////////////////////////////////////////////////////////////////////////// + +/** \class SkComposeShader + This subclass of shader returns the coposition of two other shaders, combined by + a xfermode. +*/ +class SkComposeShader : public SkShader { +public: + /** Create a new compose shader, given shaders A, B, and a combining xfermode mode. + When the xfermode is called, it will be given the result from shader A as its + "dst", and the result of from shader B as its "src". + mode->xfer32(sA_result, sB_result, ...) + @param shaderA The colors from this shader are seen as the "dst" by the xfermode + @param shaderB The colors from this shader are seen as the "src" by the xfermode + @param mode The xfermode that combines the colors from the two shaders. If mode + is null, then SRC_OVER is assumed. + */ + SkComposeShader(SkShader* sA, SkShader* sB, SkXfermode* mode = NULL); + virtual ~SkComposeShader(); + + // override + virtual bool setContext(const SkBitmap& device, const SkPaint& paint, const SkMatrix& matrix); + virtual void shadeSpan(int x, int y, SkPMColor result[], int count); + virtual void beginSession(); + virtual void endSession(); + +protected: + SkComposeShader(SkFlattenableReadBuffer& ); + virtual void flatten(SkFlattenableWriteBuffer& ); + virtual Factory getFactory() { return CreateProc; } + +private: + static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) { + return SkNEW_ARGS(SkComposeShader, (buffer)); } + + SkShader* fShaderA; + SkShader* fShaderB; + SkXfermode* fMode; + + typedef SkShader INHERITED; +}; + +#endif diff --git a/skia/include/SkStackViewLayout.h b/skia/include/SkStackViewLayout.h new file mode 100644 index 0000000..f0bb092 --- /dev/null +++ b/skia/include/SkStackViewLayout.h @@ -0,0 +1,97 @@ +/* include/graphics/SkStackViewLayout.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkStackViewLayout_DEFINED +#define SkStackViewLayout_DEFINED + +#include "SkView.h" + +class SkStackViewLayout : public SkView::Layout { +public: + SkStackViewLayout(); + + enum Orient { + kHorizontal_Orient, + kVertical_Orient, + + kOrientCount + }; + Orient getOrient() const { return (Orient)fOrient; } + void setOrient(Orient); + + void getMargin(SkRect*) const; + void setMargin(const SkRect&); + + SkScalar getSpacer() const { return fSpacer; } + void setSpacer(SkScalar); + + /** Controls the posititioning in the same direction as the orientation + */ + enum Pack { + kStart_Pack, + kCenter_Pack, + kEnd_Pack, + + kPackCount + }; + Pack getPack() const { return (Pack)fPack; } + void setPack(Pack); + + /** Controls the posititioning at right angles to the orientation + */ + enum Align { + kStart_Align, + kCenter_Align, + kEnd_Align, + kStretch_Align, + + kAlignCount + }; + Align getAlign() const { return (Align)fAlign; } + void setAlign(Align); + + bool getRound() const { return SkToBool(fRound); } + void setRound(bool); + +protected: + virtual void onLayoutChildren(SkView* parent); + virtual void onInflate(const SkDOM&, const SkDOM::Node*); + +private: + SkRect fMargin; + SkScalar fSpacer; + uint8_t fOrient, fPack, fAlign, fRound; +}; + +class SkFillViewLayout : public SkView::Layout { +public: + SkFillViewLayout(); + void getMargin(SkRect*) const; + void setMargin(const SkRect&); + +protected: + // overrides; + virtual void onLayoutChildren(SkView* parent); + virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node); + +private: + SkRect fMargin; + typedef SkView::Layout INHERITED; +}; + +#endif + diff --git a/skia/include/SkStream.h b/skia/include/SkStream.h new file mode 100644 index 0000000..4612abb --- /dev/null +++ b/skia/include/SkStream.h @@ -0,0 +1,282 @@ +/* include/graphics/SkStream.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkStream_DEFINED +#define SkStream_DEFINED + +#include "SkRefCnt.h" +#include "SkScalar.h" + +class SkStream : public SkRefCnt { +public: + virtual ~SkStream(); + /** Called to rewind to the beginning of the stream. If this cannot be + done, return false. + */ + virtual bool rewind() = 0; + /** If this stream represents a file, this method returns the file's name. + If it does not, it returns NULL (the default behavior). + */ + virtual const char* getFileName(); + /** Called to read or skip size number of bytes. + If buffer is NULL and size > 0, skip that many bytes, returning how many were skipped. + If buffer is NULL and size == 0, return the total length of the stream. + If buffer != NULL, copy the requested number of bytes into buffer, returning how many were copied. + @param buffer If buffer is NULL, ignore and just skip size bytes, otherwise copy size bytes into buffer + @param size The number of bytes to skip or copy + @return bytes read on success + */ + virtual size_t read(void* buffer, size_t size) = 0; + + /** Return the total length of the stream. + */ + size_t getLength() { return this->read(NULL, 0); } + + /** Skip the specified number of bytes, returning the actual number + of bytes that could be skipped. + */ + size_t skip(size_t bytes); + + /** If the stream is backed by RAM, this method returns the starting + address for the data. If not (i.e. it is backed by a file or other + structure), this method returns NULL. + The default implementation returns NULL. + */ + virtual const void* getMemoryBase(); + + int8_t readS8(); + int16_t readS16(); + int32_t readS32(); + + uint8_t readU8() { return (uint8_t)this->readS8(); } + uint16_t readU16() { return (uint16_t)this->readS16(); } + uint32_t readU32() { return (uint32_t)this->readS32(); } + + bool readBool() { return this->readU8() != 0; } + SkScalar readScalar(); + size_t readPackedUInt(); +}; + +class SkWStream : SkNoncopyable { +public: + virtual ~SkWStream(); + + /** Called to write bytes to a SkWStream. Returns true on success + @param buffer the address of at least size bytes to be written to the stream + @param size The number of bytes in buffer to write to the stream + @return true on success + */ + virtual bool write(const void* buffer, size_t size) = 0; + virtual void newline(); + virtual void flush(); + + // helpers + + bool write8(U8CPU); + bool write16(U16CPU); + bool write32(uint32_t); + + bool writeText(const char text[]); + bool writeDecAsText(int32_t); + bool writeHexAsText(uint32_t, int minDigits = 0); + bool writeScalarAsText(SkScalar); + + bool writeBool(bool v) { return this->write8(v); } + bool writeScalar(SkScalar); + bool writePackedUInt(size_t); + + bool writeStream(SkStream* input, size_t length); + + SkDEBUGCODE(static void UnitTest();) +}; + +//////////////////////////////////////////////////////////////////////////////////////// + +#include "SkString.h" + +struct SkFILE; + +class SkFILEStream : public SkStream { +public: + SkFILEStream(const char path[] = NULL); + virtual ~SkFILEStream(); + + /** Returns true if the current path could be opened. + */ + bool isValid() const { return fFILE != NULL; } + /** Close the current file, and open a new file with the specified + path. If path is NULL, just close the current file. + */ + void setPath(const char path[]); + + SkFILE* getSkFILE() const { return fFILE; } + + virtual bool rewind(); + virtual size_t read(void* buffer, size_t size); + virtual const char* getFileName(); + +private: + SkFILE* fFILE; + SkString fName; +}; + +class SkMemoryStream : public SkStream { +public: + SkMemoryStream(); + /** We allocate (and free) the memory. Write to it via getMemoryBase() + */ + SkMemoryStream(size_t length); + /** if copyData is true, the stream makes a private copy of the data + */ + SkMemoryStream(const void* data, size_t length, bool copyData = false); + virtual ~SkMemoryStream(); + + /** Resets the stream to the specified data and length, + just like the constructor. + if copyData is true, the stream makes a private copy of the data + */ + virtual void setMemory(const void* data, size_t length, + bool copyData = false); + void skipToAlign4(); + virtual bool rewind(); + virtual size_t read(void* buffer, size_t size); + virtual const void* getMemoryBase(); + const void* getAtPos(); + size_t seek(size_t offset); + size_t peek() const { return fOffset; } + +private: + const void* fSrc; + size_t fSize, fOffset; + SkBool8 fWeOwnTheData; +}; + +/** \class SkBufferStream + This is a wrapper class that adds buffering to another stream. + The caller can provide the buffer, or ask SkBufferStream to allocated/free + it automatically. +*/ +class SkBufferStream : public SkStream { +public: + /** Provide the stream to be buffered (proxy), and the size of the buffer that + should be used. This will be allocated and freed automatically. If bufferSize is 0, + a default buffer size will be used. + */ + SkBufferStream(SkStream& proxy, size_t bufferSize = 0); + /** Provide the stream to be buffered (proxy), and a buffer and size to be used. + This buffer is owned by the caller, and must be at least bufferSize bytes big. + Passing NULL for buffer will cause the buffer to be allocated/freed automatically. + If buffer is not NULL, it is an error for bufferSize to be 0. + */ + SkBufferStream(SkStream& proxy, void* buffer, size_t bufferSize); + virtual ~SkBufferStream(); + + virtual bool rewind(); + virtual const char* getFileName(); + virtual size_t read(void* buffer, size_t size); + virtual const void* getMemoryBase(); + +private: + enum { + kDefaultBufferSize = 128 + }; + // illegal + SkBufferStream(const SkBufferStream&); + SkBufferStream& operator=(const SkBufferStream&); + + SkStream& fProxy; + char* fBuffer; + size_t fOrigBufferSize, fBufferSize, fBufferOffset; + bool fWeOwnTheBuffer; + + void init(void*, size_t); +}; + +///////////////////////////////////////////////////////////////////////////////////////////// + +class SkFILEWStream : public SkWStream { +public: + SkFILEWStream(const char path[]); + virtual ~SkFILEWStream(); + + /** Returns true if the current path could be opened. + */ + bool isValid() const { return fFILE != NULL; } + + virtual bool write(const void* buffer, size_t size); + virtual void flush(); +private: + SkFILE* fFILE; +}; + +class SkMemoryWStream : public SkWStream { +public: + SkMemoryWStream(void* buffer, size_t size); + virtual bool write(const void* buffer, size_t size); + +private: + char* fBuffer; + size_t fMaxLength; + size_t fBytesWritten; +}; + +class SkDynamicMemoryWStream : public SkWStream { +public: + SkDynamicMemoryWStream(); + virtual ~SkDynamicMemoryWStream(); + virtual bool write(const void* buffer, size_t size); + // random access write + // modifies stream and returns true if offset + size is less than or equal to getOffset() + bool write(const void* buffer, size_t offset, size_t size); + bool read(void* buffer, size_t offset, size_t size); + size_t getOffset() { return fBytesWritten; } + + // copy what has been written to the stream into dst + void copyTo(void* dst) const; + /* return a cache of the flattened data returned by copyTo(). + This copy is only valid until the next call to write(). + The memory is managed by the stream class. + */ + const char* getStream() const; + + // same as getStream, but additionally detach the flattened datat + const char* detach(); + + // reset the stream to its original state + void reset(); + void padToAlign4(); +private: + struct Block; + Block* fHead; + Block* fTail; + size_t fBytesWritten; + mutable char* fCopyToCache; +}; + + +class SkDebugWStream : public SkWStream { +public: + // overrides + virtual bool write(const void* buffer, size_t size); + virtual void newline(); +}; + +// for now +typedef SkFILEStream SkURLStream; + +#endif + diff --git a/skia/include/SkStream_Win.h b/skia/include/SkStream_Win.h new file mode 100644 index 0000000..02ab5b1 --- /dev/null +++ b/skia/include/SkStream_Win.h @@ -0,0 +1,54 @@ +/* include/graphics/SkStream_Win.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkStream_Win_DEFINED +#define SkStream_Win_DEFINED + +#ifndef SK_BUILD_FOR_WIN +#error "only valid for windows and wince builds" +#endif + +#ifndef SkStream_DEFINED +#include "SkStream.h" +#endif +#include "SkString.h" +#include "Wininet.h" + +/** \cond ZERO */ +class SkURLStream : public SkStream { +public: + SkURLStream(const char url[] = NULL); + virtual ~SkURLStream(); + + /** Close the current URL, and open a new URL. + If URL is null, just close the current URL. + */ + void setURL(const char url[]); + + // overrides + virtual bool rewind(); + virtual size_t read(void* buffer, size_t size); + +private: + SkString fURL; + HINTERNET fConnection; + HINTERNET fURLStream; +}; + +/** \endcond */ +#endif // SkStream_Win_DEFINED + diff --git a/skia/include/SkString.h b/skia/include/SkString.h new file mode 100644 index 0000000..3c02b22 --- /dev/null +++ b/skia/include/SkString.h @@ -0,0 +1,169 @@ +/* include/graphics/SkString.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkString_DEFINED +#define SkString_DEFINED + +#include "SkScalar.h" + +/* Some helper functions for C strings +*/ + +bool SkStrStartsWith(const char string[], const char prefix[]); +bool SkStrEndsWith(const char string[], const char suffix[]); +int SkStrStartsWithOneOf(const char string[], const char prefixes[]); + +#define SkStrAppendS32_MaxSize 11 +char* SkStrAppendS32(char buffer[], int32_t); +#define SkStrAppendScalar_MaxSize 11 +char* SkStrAppendScalar(char buffer[], SkScalar); + +/** \class SkString + + Light weight class for managing strings. Uses reference + counting to make string assignments and copies very fast + with no extra RAM cost. Assumes UTF8 encoding. +*/ +class SkString { +public: + SkString(); + explicit SkString(size_t len); + explicit SkString(const char text[]); + SkString(const char text[], size_t len); + explicit SkString(const SkString&); + ~SkString(); + + bool isEmpty() const { return fRec->fLength == 0; } + size_t size() const { return (size_t) fRec->fLength; } + const char* c_str() const { return fRec->data(); } + + bool equals(const SkString&) const; + bool equals(const char text[]) const; + bool equals(const char text[], size_t len) const; + + bool startsWith(const char prefix[]) const + { + return SkStrStartsWith(fRec->data(), prefix); + } + bool endsWith(const char suffix[]) const + { + return SkStrEndsWith(fRec->data(), suffix); + } + + friend int operator==(const SkString& a, const SkString& b) + { + return a.equals(b); + } + friend int operator!=(const SkString& a, const SkString& b) + { + return !a.equals(b); + } + + // these methods edit the string + + SkString& operator=(const SkString&); + + char* writable_str(); + + void reset(); + void resize(size_t len) { this->set(NULL, len); } + void set(const SkString& src) { *this = src; } + void set(const char text[]); + void set(const char text[], size_t len); + void setUTF16(const uint16_t[]); + void setUTF16(const uint16_t[], size_t len); + + void insert(size_t offset, const SkString& src) { this->insert(offset, src.c_str(), src.size()); } + void insert(size_t offset, const char text[]); + void insert(size_t offset, const char text[], size_t len); + void insertUnichar(size_t offset, SkUnichar); + void insertS32(size_t offset, int32_t value); + void insertHex(size_t offset, uint32_t value, int minDigits = 0); + void insertScalar(size_t offset, SkScalar); + + void append(const SkString& str) { this->insert((size_t)-1, str); } + void append(const char text[]) { this->insert((size_t)-1, text); } + void append(const char text[], size_t len) { this->insert((size_t)-1, text, len); } + void appendUnichar(SkUnichar uni) { this->insertUnichar((size_t)-1, uni); } + void appendS32(int32_t value) { this->insertS32((size_t)-1, value); } + void appendHex(uint32_t value, int minDigits = 0) { this->insertHex((size_t)-1, value, minDigits); } + void appendScalar(SkScalar value) { this->insertScalar((size_t)-1, value); } + + void prepend(const SkString& str) { this->insert(0, str); } + void prepend(const char text[]) { this->insert(0, text); } + void prepend(const char text[], size_t len) { this->insert(0, text, len); } + void prependUnichar(SkUnichar uni) { this->insertUnichar(0, uni); } + void prependS32(int32_t value) { this->insertS32(0, value); } + void prependHex(uint32_t value, int minDigits = 0) { this->insertHex(0, value, minDigits); } + void prependScalar(SkScalar value) { this->insertScalar((size_t)-1, value); } + + void printf(const char format[], ...); + + void remove(size_t offset, size_t length); + + /** Swap contents between this and other. This function is guaranteed + to never fail or throw. + */ + void swap(SkString& other); + + /** @cond UNIT_TEST */ + SkDEBUGCODE(static void UnitTest();) + /** @endcond */ + +private: + struct Rec { + public: + uint16_t fLength; + uint16_t fRefCnt; + char fBeginningOfData; + + char* data() { return &fBeginningOfData; } + const char* data() const { return &fBeginningOfData; } + }; + Rec* fRec; + +#ifdef SK_DEBUG + const char* fStr; + void validate() const; +#else + void validate() const {} +#endif + + static const Rec gEmptyRec; + static Rec* AllocRec(const char text[], U16CPU len); + static Rec* RefRec(Rec*); +}; + +class SkAutoUCS2 { +public: + SkAutoUCS2(const char utf8[]); + ~SkAutoUCS2(); + + /** This returns the number of ucs2 characters + */ + int count() const { return fCount; } + /** This returns a null terminated ucs2 string + */ + const uint16_t* getUCS2() const { return fUCS2; } + +private: + int fCount; + uint16_t* fUCS2; +}; + +#endif + diff --git a/skia/include/SkStroke.h b/skia/include/SkStroke.h new file mode 100644 index 0000000..bc1ca4a --- /dev/null +++ b/skia/include/SkStroke.h @@ -0,0 +1,72 @@ +/* include/graphics/SkStroke.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkStroke_DEFINED +#define SkStroke_DEFINED + +#include "SkPoint.h" +#include "SkPaint.h" + +struct SkRect; +class SkPath; + +#define SK_DefaultStrokeWidth SK_Scalar1 +#define SK_DefaultMiterLimit SkIntToScalar(4) + + +/** \class SkStroke + SkStroke is the utility class that constructs paths by stroking + geometries (lines, rects, ovals, roundrects, paths). This is + invoked when a geometry or text is drawn in a canvas with the + kStroke_Mask bit set in the paint. +*/ +class SkStroke { +public: + SkStroke(); + SkStroke(const SkPaint&); + SkStroke(const SkPaint&, SkScalar width); // width overrides paint.getStrokeWidth() + + SkPaint::Cap getCap() const { return (SkPaint::Cap)fCap; } + void setCap(SkPaint::Cap); + + SkPaint::Join getJoin() const { return (SkPaint::Join)fJoin; } + void setJoin(SkPaint::Join); + + void setMiterLimit(SkScalar); + void setWidth(SkScalar); + + bool getDoFill() const { return SkToBool(fDoFill); } + void setDoFill(bool doFill) { fDoFill = SkToU8(doFill); } + + void strokeLine(const SkPoint& start, const SkPoint& end, SkPath*) const; + void strokeRect(const SkRect& rect, SkPath*) const; + void strokeOval(const SkRect& oval, SkPath*) const; + void strokeRRect(const SkRect& rect, SkScalar rx, SkScalar ry, SkPath*) const; + void strokePath(const SkPath& path, SkPath*) const; + + //////////////////////////////////////////////////////////////// + +private: + SkScalar fWidth, fMiterLimit; + uint8_t fCap, fJoin; + SkBool8 fDoFill; + + friend class SkPaint; +}; + +#endif + diff --git a/skia/include/SkSystemEventTypes.h b/skia/include/SkSystemEventTypes.h new file mode 100644 index 0000000..726d880 --- /dev/null +++ b/skia/include/SkSystemEventTypes.h @@ -0,0 +1,33 @@ +/* include/graphics/SkSystemEventTypes.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkSystemEventTypes_DEFINED +#define SkSystemEventTypes_DEFINED + +/* + The goal of these strings is two-fold: + 1) make funny strings (containing at least one char < 32) to avoid colliding with "user" strings + 2) keep them <= 4 bytes, so we can avoid an allocation in SkEvent::setType() +*/ +#define SK_EventType_Delay "\xd" "lay" +#define SK_EventType_Inval "nv" "\xa" "l" +#define SK_EventType_Key "key" "\x1" +#define SK_EventType_OnEnd "on" "\xe" "n" +#define SK_EventType_Unichar "\xc" "har" +#define SK_EventType_KeyUp "key" "\xf" + +#endif diff --git a/skia/include/SkTDArray.h b/skia/include/SkTDArray.h new file mode 100644 index 0000000..620ecc5 --- /dev/null +++ b/skia/include/SkTDArray.h @@ -0,0 +1,294 @@ +/* include/graphics/SkTDArray.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkTDArray_DEFINED +#define SkTDArray_DEFINED + +#include "SkTypes.h" + +template <typename T> class SkTDArray { +public: + SkTDArray() { + fReserve = fCount = 0; + fArray = NULL; +#ifdef SK_DEBUG + fData = NULL; +#endif + } + SkTDArray(const T src[], size_t count) { + SkASSERT(src || count == 0); + + fReserve = fCount = 0; + fArray = NULL; +#ifdef SK_DEBUG + fData = NULL; +#endif + if (count) { + fArray = (T*)sk_malloc_throw(count * sizeof(T)); +#ifdef SK_DEBUG + fData = (ArrayT*)fArray; +#endif + memcpy(fArray, src, sizeof(T) * count); + fReserve = fCount = count; + } + } + SkTDArray(const SkTDArray<T>& src) { + fReserve = fCount = 0; + fArray = NULL; +#ifdef SK_DEBUG + fData = NULL; +#endif + SkTDArray<T> tmp(src.fArray, src.fCount); + this->swap(tmp); + } + ~SkTDArray() { + sk_free(fArray); + } + + SkTDArray<T>& operator=(const SkTDArray<T>& src) { + if (this != &src) { + if (src.fCount > fReserve) { + SkTDArray<T> tmp(src.fArray, src.fCount); + this->swap(tmp); + } else { + memcpy(fArray, src.fArray, sizeof(T) * src.fCount); + fCount = src.fCount; + } + } + return *this; + } + + friend int operator==(const SkTDArray<T>& a, const SkTDArray<T>& b) { + return a.fCount == b.fCount && + (a.fCount == 0 || + !memcmp(a.fArray, b.fArray, a.fCount * sizeof(T))); + } + + void swap(SkTDArray<T>& other) { + SkTSwap(fArray, other.fArray); +#ifdef SK_DEBUG + SkTSwap(fData, other.fData); +#endif + SkTSwap(fReserve, other.fReserve); + SkTSwap(fCount, other.fCount); + } + + bool isEmpty() const { return fCount == 0; } + size_t count() const { return fCount; } + T* begin() const { return fArray; } + T* end() const { return fArray ? fArray + fCount : NULL; } + T& operator[](int index) const { + SkASSERT((unsigned)index < fCount); + return fArray[index]; + } + + void reset() { + if (fArray) { + sk_free(fArray); + fArray = NULL; +#ifdef SK_DEBUG + fData = NULL; +#endif + fReserve = fCount = 0; + } else { + SkASSERT(fReserve == 0 && fCount == 0); + } + } + + void rewind() { + // same as setCount(0) + fCount = 0; + } + + void setCount(size_t count) { + if (count > fReserve) { + this->growBy(count - fCount); + } else { + fCount = count; + } + } + + void setReserve(size_t reserve) { + if (reserve > fReserve) { + SkASSERT(reserve > fCount); + size_t count = fCount; + this->growBy(reserve - fCount); + fCount = count; + } + } + + T* prepend() { + this->growBy(1); + memmove(fArray + 1, fArray, (fCount - 1) * sizeof(T)); + return fArray; + } + + T* append() { + return this->append(1, NULL); + } + T* append(size_t count, const T* src = NULL) { + unsigned oldCount = fCount; + if (count) { + SkASSERT(src == NULL || fArray == NULL || + src + count <= fArray || fArray + oldCount <= src); + + this->growBy(count); + if (src) { + memcpy(fArray + oldCount, src, sizeof(T) * count); + } + } + return fArray + oldCount; + } + + T* appendClear() { + T* result = this->append(); + *result = 0; + return result; + } + + T* insert(size_t index) { + return this->insert(index, 1, NULL); + } + T* insert(size_t index, size_t count, const T* src = NULL) { + SkASSERT(count); + SkASSERT(index <= fCount); + int oldCount = fCount; + this->growBy(count); + T* dst = fArray + index; + memmove(dst + count, dst, sizeof(T) * (oldCount - index)); + if (src) { + memcpy(dst, src, sizeof(T) * count); + } + return dst; + } + + void remove(size_t index, size_t count = 1) { + SkASSERT(index + count <= fCount); + fCount = fCount - count; + memmove(fArray + index, fArray + index + count, sizeof(T) * (fCount - index)); + } + + void removeShuffle(size_t index) { + SkASSERT(index < fCount); + unsigned newCount = fCount - 1; + fCount = newCount; + if (index != newCount) { + memcpy(fArray + index, fArray + newCount, sizeof(T)); + } + } + + int find(const T& elem) const { + const T* iter = fArray; + const T* stop = fArray + fCount; + + for (; iter < stop; iter++) { + if (*iter == elem) { + return (int) (iter - fArray); + } + } + return -1; + } + + int rfind(const T& elem) const { + const T* iter = fArray + fCount; + const T* stop = fArray; + + while (iter > stop) { + if (*--iter == elem) { + return iter - stop; + } + } + return -1; + } + + // routines to treat the array like a stack + T* push() { return this->append(); } + void push(const T& elem) { *this->append() = elem; } + const T& top() const { return (*this)[fCount - 1]; } + T& top() { return (*this)[fCount - 1]; } + void pop(T* elem) { if (elem) *elem = (*this)[fCount - 1]; --fCount; } + void pop() { --fCount; } + + void deleteAll() { + T* iter = fArray; + T* stop = fArray + fCount; + while (iter < stop) { + delete (*iter); + iter += 1; + } + this->reset(); + } + + void freeAll() { + T* iter = fArray; + T* stop = fArray + fCount; + while (iter < stop) { + sk_free(*iter); + iter += 1; + } + this->reset(); + } + + void unrefAll() { + T* iter = fArray; + T* stop = fArray + fCount; + while (iter < stop) { + (*iter)->unref(); + iter += 1; + } + this->reset(); + } + +#ifdef SK_DEBUG + void validate() const { + SkASSERT((fReserve == 0 && fArray == NULL) || + (fReserve > 0 && fArray != NULL)); + SkASSERT(fCount <= fReserve); + SkASSERT(fData == (ArrayT*)fArray); + } +#endif + +private: +#ifdef SK_DEBUG + enum { + kDebugArraySize = 16 + }; + typedef T ArrayT[kDebugArraySize]; + ArrayT* fData; +#endif + T* fArray; + size_t fReserve, fCount; + + void growBy(size_t extra) { + SkASSERT(extra); + + if (fCount + extra > fReserve) { + size_t size = fCount + extra + 4; + size += size >> 2; + + fArray = (T*)sk_realloc_throw(fArray, size * sizeof(T)); +#ifdef SK_DEBUG + fData = (ArrayT*)fArray; +#endif + fReserve = size; + } + fCount += extra; + } +}; + +#endif + diff --git a/skia/include/SkTDStack.h b/skia/include/SkTDStack.h new file mode 100644 index 0000000..e070cb9 --- /dev/null +++ b/skia/include/SkTDStack.h @@ -0,0 +1,121 @@ +/* include/graphics/SkTDStack.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkTDStack_DEFINED +#define SkTDStack_DEFINED + +#include "SkTypes.h" + +template <typename T> class SkTDStack : SkNoncopyable { +public: + SkTDStack() : fCount(0), fTotalCount(0) + { + fInitialRec.fNext = NULL; + fRec = &fInitialRec; + + // fCount = kSlotCount; + } + ~SkTDStack() + { + Rec* rec = fRec; + while (rec != &fInitialRec) + { + Rec* next = rec->fNext; + sk_free(rec); + rec = next; + } + } + + int count() const { return fTotalCount; } + + T* push() + { + SkASSERT(fCount <= kSlotCount); + if (fCount == kSlotCount) + { + Rec* rec = (Rec*)sk_malloc_throw(sizeof(Rec)); + rec->fNext = fRec; + fRec = rec; + fCount = 0; + } + ++fTotalCount; + return &fRec->fSlots[fCount++]; + } + void push(const T& elem) { *this->push() = elem; } + const T& index(int idx) const + { + SkASSERT(fRec && fCount > idx); + return fRec->fSlots[fCount - idx - 1]; + } + T& index(int idx) + { + SkASSERT(fRec && fCount > idx); + return fRec->fSlots[fCount - idx - 1]; + } + const T& top() const + { + SkASSERT(fRec && fCount > 0); + return fRec->fSlots[fCount - 1]; + } + T& top() + { + SkASSERT(fRec && fCount > 0); + return fRec->fSlots[fCount - 1]; + } + void pop(T* elem) + { + if (elem) + *elem = fRec->fSlots[fCount - 1]; + this->pop(); + } + void pop() + { + SkASSERT(fCount > 0 && fRec); + --fTotalCount; + if (--fCount == 0) + { + if (fRec != &fInitialRec) + { + Rec* rec = fRec->fNext; + sk_free(fRec); + fCount = kSlotCount; + fRec = rec; + } + else + SkASSERT(fTotalCount == 0); + } + } + +private: + enum { + kSlotCount = 8 + }; + + struct Rec; + friend struct Rec; + + struct Rec { + Rec* fNext; + T fSlots[kSlotCount]; + }; + Rec fInitialRec; + Rec* fRec; + int fCount, fTotalCount; +}; + +#endif + diff --git a/skia/include/SkTDict.h b/skia/include/SkTDict.h new file mode 100644 index 0000000..cd7d555 --- /dev/null +++ b/skia/include/SkTDict.h @@ -0,0 +1,170 @@ +/* include/graphics/SkTDict.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkTDict_DEFINED +#define SkTDict_DEFINED + +#include "SkChunkAlloc.h" +#include "SkTSearch.h" +#include "SkTDArray.h" + +template <typename T> class SkTDict : SkNoncopyable { +public: + SkTDict(size_t minStringAlloc) : fStrings(minStringAlloc) {} + + void reset() + { + fArray.reset(); + fStrings.reset(); + } + + int count() const { return fArray.count(); } + + bool set(const char name[], const T& value) + { + return set(name, strlen(name), value); + } + + bool set(const char name[], size_t len, const T& value) + { + SkASSERT(name); + + int index = this->find_index(name, len); + + if (index >= 0) + { + fArray[index].fValue = value; + return false; + } + else + { + Pair* pair = fArray.insert(~index); + char* copy = (char*)fStrings.alloc(len + 1, SkChunkAlloc::kThrow_AllocFailType); + memcpy(copy, name, len); + copy[len] = '\0'; + pair->fName = copy; + pair->fValue = value; + return true; + } + } + + bool find(const char name[]) const + { + return this->find_index(name) >= 0; + } + + bool find(const char name[], size_t len) const + { + return this->find_index(name, len) >= 0; + } + + bool find(const char name[], T* value) const + { + return find(name, strlen(name), value); + } + + bool find(const char name[], size_t len, T* value) const + { + int index = this->find_index(name, len); + + if (index >= 0) + { + if (value) + *value = fArray[index].fValue; + return true; + } + return false; + } + + bool findKey(T& value, const char** name) const + { + Pair* end = fArray.end(); + for (Pair* pair = fArray.begin(); pair < end; pair++) { + if (pair->fValue != value) + continue; + *name = pair->fName; + return true; + } + return false; + } + +public: + struct Pair { + const char* fName; + T fValue; + + friend int operator<(const Pair& a, const Pair& b) + { + return strcmp(a.fName, b.fName); + } + friend int operator!=(const Pair& a, const Pair& b) + { + return strcmp(a.fName, b.fName); + } + }; + friend class Iter; + +public: + class Iter { + public: + Iter(const SkTDict<T>& dict) + { + fIter = dict.fArray.begin(); + fStop = dict.fArray.end(); + } + const char* next(T* value) + { + const char* name = NULL; + if (fIter < fStop) + { + name = fIter->fName; + if (value) + *value = fIter->fValue; + fIter += 1; + } + return name; + } + private: + Pair* fIter; + Pair* fStop; + }; + +private: + SkTDArray<Pair> fArray; + SkChunkAlloc fStrings; + + int find_index(const char name[]) const + { + return find_index(name, strlen(name)); + } + + int find_index(const char name[], size_t len) const + { + SkASSERT(name); + + int count = fArray.count(); + int index = ~0; + + if (count) + index = SkStrSearch(&fArray.begin()->fName, count, name, len, sizeof(Pair)); + return index; + } + friend class Iter; +}; + +#endif + diff --git a/skia/include/SkTextBox.h b/skia/include/SkTextBox.h new file mode 100644 index 0000000..1d7f46c --- /dev/null +++ b/skia/include/SkTextBox.h @@ -0,0 +1,78 @@ +/* include/graphics/SkTextBox.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkTextBox_DEFINED +#define SkTextBox_DEFINED + +#include "SkCanvas.h" + +/** \class SkTextBox + + SkTextBox is a helper class for drawing 1 or more lines of text + within a rectangle. The textbox is positioned and clipped by its Frame. + The Margin rectangle controls where the text is drawn relative to + the Frame. Line-breaks occur inside the Margin rectangle. + + Spacing is a linear equation used to compute the distance between lines + of text. Spacing consists of two scalars: mul and add, and the spacing + between lines is computed as: spacing = paint.getTextSize() * mul + add +*/ +class SkTextBox { +public: + SkTextBox(); + + enum Mode { + kOneLine_Mode, + kLineBreak_Mode, + + kModeCount + }; + Mode getMode() const { return (Mode)fMode; } + void setMode(Mode); + + enum SpacingAlign { + kStart_SpacingAlign, + kCenter_SpacingAlign, + kEnd_SpacingAlign, + + kSpacingAlignCount + }; + SpacingAlign getSpacingAlign() const { return (SpacingAlign)fSpacingAlign; } + void setSpacingAlign(SpacingAlign); + + void getBox(SkRect*) const; + void setBox(const SkRect&); + void setBox(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom); + + void getSpacing(SkScalar* mul, SkScalar* add) const; + void setSpacing(SkScalar mul, SkScalar add); + + void draw(SkCanvas*, const char text[], size_t len, const SkPaint&); + +private: + SkRect fBox; + SkScalar fSpacingMul, fSpacingAdd; + uint8_t fMode, fSpacingAlign; +}; + +class SkTextLineBreaker { +public: + static int CountLines(const char text[], size_t len, const SkPaint&, SkScalar width); +}; + +#endif + diff --git a/skia/include/SkTime.h b/skia/include/SkTime.h new file mode 100644 index 0000000..6733501 --- /dev/null +++ b/skia/include/SkTime.h @@ -0,0 +1,73 @@ +/* include/graphics/SkTime.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkTime_DEFINED +#define SkTime_DEFINED + +#include "SkTypes.h" + +/** \class SkTime + Platform-implemented utilities to return time of day, and millisecond counter. +*/ +class SkTime { +public: + struct DateTime { + uint16_t fYear; //!< e.g. 2005 + uint8_t fMonth; //!< 1..12 + uint8_t fDayOfWeek; //!< 0..6, 0==Sunday + uint8_t fDay; //!< 1..31 + uint8_t fHour; //!< 0..23 + uint8_t fMinute; //!< 0..59 + uint8_t fSecond; //!< 0..59 + }; + static void GetDateTime(DateTime*); + + static SkMSec GetMSecs(); +}; + +#if defined(SK_DEBUG) && defined(SK_BUILD_FOR_WIN32) + extern SkMSec gForceTickCount; +#endif + +#define SK_TIME_FACTOR 1 + +/////////////////////////////////////////////////////////////////////////////// + +class SkAutoTime { +public: + // The label is not deep-copied, so its address must remain valid for the + // lifetime of this object + SkAutoTime(const char* label = NULL, SkMSec minToDump = 0) : fLabel(label) + { + fNow = SkTime::GetMSecs(); + fMinToDump = minToDump; + } + ~SkAutoTime() + { + SkMSec dur = SkTime::GetMSecs() - fNow; + if (dur >= fMinToDump) { + SkDebugf("%s %d\n", fLabel ? fLabel : "", dur); + } + } +private: + const char* fLabel; + SkMSec fNow; + SkMSec fMinToDump; +}; + +#endif + diff --git a/skia/include/SkTransparentShader.h b/skia/include/SkTransparentShader.h new file mode 100644 index 0000000..82b56da --- /dev/null +++ b/skia/include/SkTransparentShader.h @@ -0,0 +1,56 @@ +/* include/graphics/SkTransparentShader.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkTransparentShader_DEFINED +#define SkTransparentShader_DEFINED + +#include "SkShader.h" + +class SkTransparentShader : public SkShader { +public: + SkTransparentShader() {} + virtual uint32_t getFlags(); + virtual bool setContext( const SkBitmap& device, + const SkPaint& paint, + const SkMatrix& matrix); + virtual void shadeSpan(int x, int y, SkPMColor[], int count); + virtual void shadeSpan16(int x, int y, uint16_t span[], int count); + + // overrides for SkFlattenable + virtual Factory getFactory() { return Create; } + virtual void flatten(SkFlattenableWriteBuffer& buffer) + { + this->INHERITED::flatten(buffer); + } + +private: + // these are a cache from the call to setContext() + const SkBitmap* fDevice; + uint8_t fAlpha; + + SkTransparentShader(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {} + + static SkFlattenable* Create(SkFlattenableReadBuffer& buffer) + { + return SkNEW_ARGS(SkTransparentShader, (buffer)); + } + + typedef SkShader INHERITED; +}; + +#endif + diff --git a/skia/include/SkTypeface.h b/skia/include/SkTypeface.h new file mode 100644 index 0000000..bcdb021 --- /dev/null +++ b/skia/include/SkTypeface.h @@ -0,0 +1,130 @@ +/* Copyright 2006-2008, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkTypeface_DEFINED +#define SkTypeface_DEFINED + +#include "SkRefCnt.h" + +class SkStream; +class SkWStream; + +/** \class SkTypeface + + The SkTypeface class specifies the typeface and intrinsic style of a font. + This is used in the paint, along with optionally algorithmic settings like + textSize, textSkewX, textScaleX, kFakeBoldText_Mask, to specify + how text appears when drawn (and measured). + + Typeface objects are immutable, and so they can be shred between threads. + To enable this, Typeface inherits from the thread-safe version of SkRefCnt. +*/ +class SkTypeface : public SkRefCnt { +public: + /** Style specifies the intrinsic style attributes of a given typeface + */ + enum Style { + kNormal = 0, + kBold = 0x01, + kItalic = 0x02, + + // helpers + kBoldItalic = 0x03 + }; + + /** Returns the typeface's intrinsic style attributes + */ + Style style() const { return fStyle; } + + /** DEPRECATED */ + Style getStyle() const { return this->style(); } + + /** Returns true if getStyle() has the kBold bit set. + */ + bool isBold() const { return (fStyle & kBold) != 0; } + + /** Returns true if getStyle() has the kItalic bit set. + */ + bool isItalic() const { return (fStyle & kItalic) != 0; } + + uint32_t uniqueID() const { return fUniqueID; } + + /** Return the uniqueID for the specified typeface. If the face is null, + resolve it to the default font and return its uniqueID. + */ + static uint32_t UniqueID(const SkTypeface* face); + + /** Return a new reference to the typeface that most closely matches the + requested familyName and style. Pass null as the familyName to return + the default font for the requested style. Will never return null + + @param familyName May be NULL. The name of the font family. + @param style The style (normal, bold, italic) of the typeface. + @return reference to the closest-matching typeface. Call must call + unref() when they are done. + */ + static SkTypeface* Create(const char familyName[], Style style = kNormal); + + /** Return a new reference to the typeface that most closely matches the + requested typeface and specified Style. Use this call if you want to + pick a new style from the same family of the existing typeface. + If family is NULL, this selects from the default font's family. + + @param family May be NULL. The name of the existing type face. + @param s The style (normal, bold, italic) of the type face. + @return reference to the closest-matching typeface. Call must call + unref() when they are done. + */ + static SkTypeface* CreateFromTypeface(const SkTypeface* family, Style s); + + /** Returns true if the two typefaces reference the same underlying font, + even if one is null (which maps to the default font). + */ + static bool Equal(const SkTypeface* facea, const SkTypeface* faceb); + + /** Returns a 32bit hash value for the typeface. Takes care of mapping null + to the default typeface. + */ + static uint32_t Hash(const SkTypeface* face); + + /** Return a new typeface given a file. If the file does not exist, or is + not a valid font file, returns null. + */ + static SkTypeface* CreateFromFile(const char path[]); + + /** Return a new typeface given a stream. If the stream is + not a valid font file, returns null. Ownership of the stream is + transferred, so the caller must not reference it again. + */ + static SkTypeface* CreateFromStream(SkStream* stream); + + // Serialization + void serialize(SkWStream*) const; + static SkTypeface* Deserialize(SkStream*); + +protected: + /** uniqueID must be unique (please!) and non-zero + */ + SkTypeface(Style style, uint32_t uniqueID) + : fUniqueID(uniqueID), fStyle(style) {} + +private: + uint32_t fUniqueID; + Style fStyle; + + typedef SkRefCnt INHERITED; +}; + +#endif diff --git a/skia/include/SkUnPreMultiply.h b/skia/include/SkUnPreMultiply.h new file mode 100644 index 0000000..bbbcea1 --- /dev/null +++ b/skia/include/SkUnPreMultiply.h @@ -0,0 +1,47 @@ + + + +#ifndef SkUnPreMultiply_DEFINED +#define SkUnPreMultiply_DEFINED + +#include "SkColor.h" + +class SkUnPreMultiply { +public: + typedef uint32_t Scale; + + // index this table with alpha [0..255] + static const Scale* GetScaleTable() { + return gTable; + } + + static Scale GetScale(U8CPU alpha) { + SkASSERT(alpha <= 255); + return gTable[alpha]; + } + + /** Usage: + + const Scale* table = SkUnPreMultiply::GetScaleTable(); + + for (...) { + unsigned a = ... + SkUnPreMultiply::Scale scale = table[a]; + + red = SkUnPreMultiply::ApplyScale(scale, red); + ... + // now red is unpremultiplied + } + */ + static U8CPU ApplyScale(Scale scale, U8CPU component) { + SkASSERT(component <= 255); + return (scale * component + (1 << 23)) >> 24; + } + + static SkColor PMColorToColor(SkPMColor c); + +private: + static const uint32_t gTable[256]; +}; + +#endif diff --git a/skia/include/SkUnitMapper.h b/skia/include/SkUnitMapper.h new file mode 100644 index 0000000..9d7c65b --- /dev/null +++ b/skia/include/SkUnitMapper.h @@ -0,0 +1,39 @@ +/* include/graphics/SkUnitMapper.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkUnitMapper_DEFINED +#define SkUnitMapper_DEFINED + +#include "SkRefCnt.h" +#include "SkScalar.h" + +#include "SkFlattenable.h" + +class SkUnitMapper : public SkFlattenable { +public: + SkUnitMapper() {} + + /** Given a value in [0..0xFFFF], return a value in the same range. + */ + virtual uint16_t mapUnit16(uint16_t x) = 0; + +protected: + SkUnitMapper(SkFlattenableReadBuffer& rb) : SkFlattenable(rb) {} +}; + +#endif + diff --git a/skia/include/SkUnitMappers.h b/skia/include/SkUnitMappers.h new file mode 100644 index 0000000..a1331bb --- /dev/null +++ b/skia/include/SkUnitMappers.h @@ -0,0 +1,49 @@ +#ifndef SkUnitMappers_DEFINED +#define SkUnitMappers_DEFINED + +#include "SkUnitMapper.h" + +/** This discretizes the range [0...1) into N discret values. +*/ +class SkDiscreteMapper : public SkUnitMapper { +public: + SkDiscreteMapper(int segments); + // override from SkUnitMapper + virtual uint16_t mapUnit16(uint16_t x); + +protected: + SkDiscreteMapper(SkFlattenableReadBuffer& ); + // overrides from SkFlattenable + virtual void flatten(SkFlattenableWriteBuffer& ); + virtual Factory getFactory(); +private: + int fSegments; + SkFract fScale; // computed from fSegments + + static SkFlattenable* Create(SkFlattenableReadBuffer& buffer); + + typedef SkUnitMapper INHERITED; +}; + +/** This returns cos(x), to simulate lighting a sphere, where 0 maps to the + center of the sphere, and 1 maps to the edge. +*/ +class SkCosineMapper : public SkUnitMapper { +public: + SkCosineMapper() {} + // override from SkUnitMapper + virtual uint16_t mapUnit16(uint16_t x); + +protected: + SkCosineMapper(SkFlattenableReadBuffer&); + // overrides from SkFlattenable + virtual Factory getFactory(); + +private: + static SkFlattenable* Create(SkFlattenableReadBuffer&); + + typedef SkUnitMapper INHERITED; +}; + +#endif + diff --git a/skia/include/SkUtils.h b/skia/include/SkUtils.h new file mode 100644 index 0000000..5ec3caa --- /dev/null +++ b/skia/include/SkUtils.h @@ -0,0 +1,141 @@ +/* include/graphics/SkUtils.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkUtils_DEFINED +#define SkUtils_DEFINED + +#include "SkTypes.h" + +/////////////////////////////////////////////////////////////////////////// + +/** Similar to memset(), but it assigns a 16bit value into the buffer. + @param buffer The memory to have value copied into it + @param value The 16bit value to be copied into buffer + @param count The number of times value should be copied into the buffer. +*/ +void sk_memset16_portable(uint16_t dst[], uint16_t value, int count); + +/** Similar to memset(), but it assigns a 32bit value into the buffer. + @param buffer The memory to have value copied into it + @param value The 32bit value to be copied into buffer + @param count The number of times value should be copied into the buffer. +*/ +void sk_memset32_portable(uint32_t dst[], uint32_t value, int count); + +#ifdef ANDROID + #include "cutils/memory.h" + + #define sk_memset16(dst, value, count) android_memset16(dst, value, (count) << 1) + #define sk_memset32(dst, value, count) android_memset32(dst, value, (count) << 2) +#endif + +#ifndef sk_memset16 + #define sk_memset16(dst, value, count) sk_memset16_portable(dst, value, count) +#endif + +#ifndef sk_memset32 + #define sk_memset32(dst, value, count) sk_memset32_portable(dst, value, count) +#endif + + +/////////////////////////////////////////////////////////////////////////// + +#define kMaxBytesInUTF8Sequence 4 + +#ifdef SK_DEBUG + int SkUTF8_LeadByteToCount(unsigned c); +#else + #define SkUTF8_LeadByteToCount(c) ((((0xE5 << 24) >> ((unsigned)c >> 4 << 1)) & 3) + 1) +#endif + +inline int SkUTF8_CountUTF8Bytes(const char utf8[]) +{ + SkASSERT(utf8); + return SkUTF8_LeadByteToCount(*(const uint8_t*)utf8); +} + +int SkUTF8_CountUnichars(const char utf8[]); +int SkUTF8_CountUnichars(const char utf8[], size_t byteLength); +SkUnichar SkUTF8_ToUnichar(const char utf8[]); +SkUnichar SkUTF8_NextUnichar(const char**); +SkUnichar SkUTF8_PrevUnichar(const char**); + +/** Return the number of bytes need to convert a unichar + into a utf8 sequence. Will be 1..kMaxBytesInUTF8Sequence, + or 0 if uni is illegal. +*/ +size_t SkUTF8_FromUnichar(SkUnichar uni, char utf8[] = NULL); + +/////////////////////////////////////////////////////////////////////////////// + +#define SkUTF16_IsHighSurrogate(c) (((c) & 0xFC00) == 0xD800) +#define SkUTF16_IsLowSurrogate(c) (((c) & 0xFC00) == 0xDC00) + +int SkUTF16_CountUnichars(const uint16_t utf16[]); +int SkUTF16_CountUnichars(const uint16_t utf16[], + int numberOf16BitValues); +// returns the current unichar and then moves past it (*p++) +SkUnichar SkUTF16_NextUnichar(const uint16_t**); +// this guy backs up to the previus unichar value, and returns it (*--p) +SkUnichar SkUTF16_PrevUnichar(const uint16_t**); +size_t SkUTF16_FromUnichar(SkUnichar uni, uint16_t utf16[] = NULL); + +size_t SkUTF16_ToUTF8(const uint16_t utf16[], int numberOf16BitValues, + char utf8[] = NULL); + +class SkUtils { +public: +#ifdef SK_DEBUG + static void UnitTest(); +#endif +}; + +/////////////////////////////////////////////////////////////////////////////// + +class SkAutoTrace { +public: + /** NOTE: label contents are not copied, just the ptr is + retained, so DON'T DELETE IT. + */ + SkAutoTrace(const char label[]) : fLabel(label) { + SkDebugf("--- trace: %s Enter\n", fLabel); + } + ~SkAutoTrace() { + SkDebugf("--- trace: %s Leave\n", fLabel); + } +private: + const char* fLabel; +}; + +/////////////////////////////////////////////////////////////////////////////// + +class SkAutoMemoryUsageProbe { +public: + /** Record memory usage in constructor, and dump the result + (delta and current total) in the destructor, with the optional + label. NOTE: label contents are not copied, just the ptr is + retained, so DON'T DELETE IT. + */ + SkAutoMemoryUsageProbe(const char label[]); + ~SkAutoMemoryUsageProbe(); +private: + const char* fLabel; + size_t fBytesAllocated; +}; + +#endif + diff --git a/skia/include/SkView.h b/skia/include/SkView.h new file mode 100644 index 0000000..feb186d --- /dev/null +++ b/skia/include/SkView.h @@ -0,0 +1,346 @@ +/* include/graphics/SkView.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkView_DEFINED +#define SkView_DEFINED + +#include "SkEventSink.h" +#include "SkRect.h" +#include "SkDOM.h" +#include "SkTDict.h" + +class SkCanvas; +class SkLayerView; + +/** \class SkView + + SkView is the base class for screen management. All widgets and controls inherit + from SkView. +*/ +class SkView : public SkEventSink { +public: + enum Flag_Shift { + kVisible_Shift, + kEnabled_Shift, + kFocusable_Shift, + kFlexH_Shift, + kFlexV_Shift, + + kFlagShiftCount + }; + enum Flag_Mask { + kVisible_Mask = 1 << kVisible_Shift, //!< set if the view is visible + kEnabled_Mask = 1 << kEnabled_Shift, //!< set if the view is enabled + kFocusable_Mask = 1 << kFocusable_Shift, //!< set if the view can receive focus + kFlexH_Mask = 1 << kFlexH_Shift, //!< set if the view's width is stretchable + kFlexV_Mask = 1 << kFlexV_Shift, //!< set if the view's height is stretchable + + kAllFlagMasks = (uint32_t)(0 - 1) >> (32 - kFlagShiftCount) + }; + + SkView(uint32_t flags = 0); + virtual ~SkView(); + + /** Return the flags associated with the view + */ + uint32_t getFlags() const { return fFlags; } + /** Set the flags associated with the view + */ + void setFlags(uint32_t flags); + + /** Helper that returns non-zero if the kVisible_Mask bit is set in the view's flags + */ + int isVisible() const { return fFlags & kVisible_Mask; } + int isEnabled() const { return fFlags & kEnabled_Mask; } + int isFocusable() const { return fFlags & kFocusable_Mask; } + /** Helper to set/clear the view's kVisible_Mask flag */ + void setVisibleP(bool); + void setEnabledP(bool); + void setFocusableP(bool); + + /** Return the view's width */ + SkScalar width() const { return fWidth; } + /** Return the view's height */ + SkScalar height() const { return fHeight; } + /** Set the view's width and height. These must both be >= 0. This does not affect the view's loc */ + void setSize(SkScalar width, SkScalar height); + void setSize(const SkPoint& size) { this->setSize(size.fX, size.fY); } + void setWidth(SkScalar width) { this->setSize(width, fHeight); } + void setHeight(SkScalar height) { this->setSize(fWidth, height); } + /** Return a rectangle set to [0, 0, width, height] */ + void getLocalBounds(SkRect* bounds) const; + + /** Return the view's left edge */ + SkScalar locX() const { return fLoc.fX; } + /** Return the view's top edge */ + SkScalar locY() const { return fLoc.fY; } + /** Set the view's left and top edge. This does not affect the view's size */ + void setLoc(SkScalar x, SkScalar y); + void setLoc(const SkPoint& loc) { this->setLoc(loc.fX, loc.fY); } + void setLocX(SkScalar x) { this->setLoc(x, fLoc.fY); } + void setLocY(SkScalar y) { this->setLoc(fLoc.fX, y); } + /** Offset (move) the view by the specified dx and dy. This does not affect the view's size */ + void offset(SkScalar dx, SkScalar dy); + + /** Call this to have the view draw into the specified canvas. */ + void draw(SkCanvas* canvas); + /** Call this to invalidate part of all of a view, requesting that the view's + draw method be called. The rectangle parameter specifies the part of the view + that should be redrawn. If it is null, it specifies the entire view bounds. + */ + void inval(SkRect* rectOrNull); + + // Focus management + + SkView* getFocusView() const; + bool hasFocus() const; + + enum FocusDirection { + kNext_FocusDirection, + kPrev_FocusDirection, + + kFocusDirectionCount + }; + bool acceptFocus(); + SkView* moveFocus(FocusDirection); + + // Click handling + + class Click { + public: + Click(SkView* target); + virtual ~Click(); + + const char* getType() const { return fType; } + bool isType(const char type[]) const; + void setType(const char type[]); // does NOT make a copy of the string + void copyType(const char type[]); // makes a copy of the string + + enum State { + kDown_State, + kMoved_State, + kUp_State + }; + SkPoint fOrig, fPrev, fCurr; + SkIPoint fIOrig, fIPrev, fICurr; + State fState; + private: + SkEventSinkID fTargetID; + char* fType; + bool fWeOwnTheType; + + void resetType(); + + friend class SkView; + }; + Click* findClickHandler(SkScalar x, SkScalar y); + + static void DoClickDown(Click*, int x, int y); + static void DoClickMoved(Click*, int x, int y); + static void DoClickUp(Click*, int x, int y); + + /** Send the event to the view's parent, and its parent etc. until one of them + returns true from its onEvent call. This view is returned. If no parent handles + the event, null is returned. + */ + SkView* sendEventToParents(const SkEvent&); + /** Depricated helper function. Just call event->post(sinkID, delay); + */ + bool postEvent(SkEvent* evt, SkEventSinkID sinkID, SkMSec delay) { return evt->post(sinkID, delay); } + + // View hierarchy management + + /** Return the view's parent, or null if it has none. This does not affect the parent's reference count. */ + SkView* getParent() const { return fParent; } + SkView* attachChildToFront(SkView* child); + /** Attach the child view to this view, and increment the child's reference count. The child view is added + such that it will be drawn before all other child views. + The child view parameter is returned. + */ + SkView* attachChildToBack(SkView* child); + /** If the view has a parent, detach the view from its parent and decrement the view's reference count. + If the parent was the only owner of the view, this will cause the view to be deleted. + */ + void detachFromParent(); + /** Attach the child view to this view, and increment the child's reference count. The child view is added + such that it will be drawn after all other child views. + The child view parameter is returned. + */ + /** Detach all child views from this view. */ + void detachAllChildren(); + + /** Convert the specified point from global coordinates into view-local coordinates + */ + void globalToLocal(SkPoint* pt) const { if (pt) this->globalToLocal(pt->fX, pt->fY, pt); } + /** Convert the specified x,y from global coordinates into view-local coordinates, returning + the answer in the local parameter. + */ + void globalToLocal(SkScalar globalX, SkScalar globalY, SkPoint* local) const; + + /** \class F2BIter + + Iterator that will return each of this view's children, in + front-to-back order (the order used for clicking). The first + call to next() returns the front-most child view. When + next() returns null, there are no more child views. + */ + class F2BIter { + public: + F2BIter(const SkView* parent); + SkView* next(); + private: + SkView* fFirstChild, *fChild; + }; + + /** \class B2FIter + + Iterator that will return each of this view's children, in + back-to-front order (the order they are drawn). The first + call to next() returns the back-most child view. When + next() returns null, there are no more child views. + */ + class B2FIter { + public: + B2FIter(const SkView* parent); + SkView* next(); + private: + SkView* fFirstChild, *fChild; + }; + + /** \class Artist + + Install a subclass of this in a view (calling setArtist()), and then the + default implementation of that view's onDraw() will invoke this object + automatically. + */ + class Artist : public SkRefCnt { + public: + void draw(SkView*, SkCanvas*); + void inflate(const SkDOM&, const SkDOM::Node*); + protected: + virtual void onDraw(SkView*, SkCanvas*) = 0; + virtual void onInflate(const SkDOM&, const SkDOM::Node*); + }; + /** Return the artist attached to this view (or null). The artist's reference + count is not affected. + */ + Artist* getArtist() const; + /** Attach the specified artist (or null) to the view, replacing any existing + artist. If the new artist is not null, its reference count is incremented. + The artist parameter is returned. + */ + Artist* setArtist(Artist* artist); + + /** \class Layout + + Install a subclass of this in a view (calling setLayout()), and then the + default implementation of that view's onLayoutChildren() will invoke + this object automatically. + */ + class Layout : public SkRefCnt { + public: + void layoutChildren(SkView* parent); + void inflate(const SkDOM&, const SkDOM::Node*); + protected: + virtual void onLayoutChildren(SkView* parent) = 0; + virtual void onInflate(const SkDOM&, const SkDOM::Node*); + }; + + /** Return the layout attached to this view (or null). The layout's reference + count is not affected. + */ + Layout* getLayout() const; + /** Attach the specified layout (or null) to the view, replacing any existing + layout. If the new layout is not null, its reference count is incremented. + The layout parameter is returned. + */ + Layout* setLayout(Layout*, bool invokeLayoutNow = true); + /** If a layout is attached to this view, call its layoutChildren() method + */ + void invokeLayout(); + + /** Call this to initialize this view based on the specified XML node + */ + void inflate(const SkDOM& dom, const SkDOM::Node* node); + /** After a view hierarchy is inflated, this may be called with a dictionary + containing pairs of <name, view*>, where the name string was the view's + "id" attribute when it was inflated. + + This will call the virtual onPostInflate for this view, and the recursively + call postInflate on all of the view's children. + */ + void postInflate(const SkTDict<SkView*>& ids); + + SkDEBUGCODE(void dump(bool recurse) const;) + +protected: + /** Override this to draw inside the view. Be sure to call the inherited version too */ + virtual void onDraw(SkCanvas*); + /** Override this to be notified when the view's size changes. Be sure to call the inherited version too */ + virtual void onSizeChange(); + /** Override this if you want to handle an inval request from this view or one of its children. + Tyically this is only overridden by the by the "window". If your subclass does handle the + request, return true so the request will not continue to propogate to the parent. + */ + virtual bool handleInval(const SkRect&); + virtual SkCanvas* beforeChildren(SkCanvas* c) { return c; } + virtual void afterChildren(SkCanvas* orig) {} + /** Override this if you might handle the click + */ + virtual Click* onFindClickHandler(SkScalar x, SkScalar y); + /** Override this to track clicks, returning true as long as you want to track + the pen/mouse. + */ + virtual bool onClick(Click*); + /** Override this to initialize your subclass from the XML node. Be sure to call the inherited version too */ + virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node); + /** Override this if you want to perform post initialization work based on the ID dictionary built + during XML parsing. Be sure to call the inherited version too. + */ + virtual void onPostInflate(const SkTDict<SkView*>&); + +public: + // default action is to inval the view + virtual void onFocusChange(bool gainFocusP); +protected: + + // override these if you're acting as a layer/host + virtual bool onGetFocusView(SkView**) const { return false; } + virtual bool onSetFocusView(SkView*) { return false; } + +private: + SkScalar fWidth, fHeight; + SkPoint fLoc; + SkView* fParent; + SkView* fFirstChild; + SkView* fNextSibling; + SkView* fPrevSibling; + uint8_t fFlags; + uint8_t fContainsFocus; + + friend class B2FIter; + friend class F2BIter; + + friend class SkLayerView; + + bool setFocusView(SkView* fvOrNull); + SkView* acceptFocus(FocusDirection); + void detachFromParent_NoLayout(); +}; + +#endif + diff --git a/skia/include/SkViewInflate.h b/skia/include/SkViewInflate.h new file mode 100644 index 0000000..354aed4 --- /dev/null +++ b/skia/include/SkViewInflate.h @@ -0,0 +1,80 @@ +/* include/graphics/SkViewInflate.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkViewInflate_DEFINED +#define SkViewInflate_DEFINED + +#include "SkDOM.h" +#include "SkTDict.h" +#include "SkEvent.h" + +class SkView; + +class SkViewInflate { +public: + SkViewInflate(); + virtual ~SkViewInflate(); + + /** Return the tree of inflated views. If root is null, create the root element + as a view, otherwise assume root is that view, and just "inflate" it. + + Returns null if the tree cannot be built. + */ + SkView* inflate(const SkDOM& dom, const SkDOM::Node* node, SkView* root = NULL); + SkView* inflate(const char xml[], size_t len, SkView* root = NULL); + + /** Given an id attribute value, return the corresponding view, or null + if no match is found. + */ + SkView* findViewByID(const char id[]) const; + + SkDEBUGCODE(void dump() const;) + +protected: + /* Override this in your subclass to handle instantiating views + Call the inherited version for nodes you don't recognize. + + Do not call "inflate" on the view, just return it. This will + get called automatically after createView returns. + */ + virtual SkView* createView(const SkDOM& dom, const SkDOM::Node* node); + /** Base implementation calls view->inflate(dom, node). Subclasses may override this + to perform additional initializations to view, either before or after calling + the inherited version. + */ + virtual void inflateView(SkView* view, const SkDOM& dom, const SkDOM::Node* node); + +private: + enum { + kMinIDStrAlloc = 64 + }; + SkTDict<SkView*> fIDs; + + struct IDStr { + SkView* fView; + char* fStr; + }; + SkTDArray<IDStr> fListenTo, fBroadcastTo; + SkChunkAlloc fStrings; + + void addIDStr(SkTDArray<IDStr>* list, SkView*, const char* str); + + void rInflate(const SkDOM& dom, const SkDOM::Node* node, SkView* parent); +}; + +#endif + diff --git a/skia/include/SkWidget.h b/skia/include/SkWidget.h new file mode 100644 index 0000000..9087330 --- /dev/null +++ b/skia/include/SkWidget.h @@ -0,0 +1,477 @@ +/* include/graphics/SkWidget.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkWidget_DEFINED +#define SkWidget_DEFINED + +#include "SkView.h" +#include "SkBitmap.h" +#include "SkDOM.h" +#include "SkPaint.h" +#include "SkString.h" +#include "SkTDArray.h" + +////////////////////////////////////////////////////////////////////////////// + +class SkWidget : public SkView { +public: + SkWidget(uint32_t flags = 0) : SkView(flags | kFocusable_Mask | kEnabled_Mask) {} + + /** Call this to post the widget's event to its listeners */ + void postWidgetEvent(); + + static void Init(); + static void Term(); +protected: + // override to add slots to an event before posting + virtual void prepareWidgetEvent(SkEvent*); + virtual void onEnabledChange(); + + // <event ...> to initialize the event from XML + virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node); + +private: + SkEvent fEvent; + typedef SkView INHERITED; +}; + +class SkHasLabelWidget : public SkWidget { +public: + SkHasLabelWidget(uint32_t flags = 0) : SkWidget(flags) {} + + size_t getLabel(SkString* label = NULL) const; + size_t getLabel(char lable[] = NULL) const; + void setLabel(const SkString&); + void setLabel(const char label[]); + void setLabel(const char label[], size_t len); + +protected: + // called when the label changes + virtual void onLabelChange(); + + // overrides + virtual void onInflate(const SkDOM& dom, const SkDOM::Node*); + +private: + SkString fLabel; + typedef SkWidget INHERITED; +}; + +class SkButtonWidget : public SkHasLabelWidget { +public: + SkButtonWidget(uint32_t flags = 0) : SkHasLabelWidget(flags), fState(kOff_State) {} + + enum State { + kOff_State, //!< XML: buttonState="off" + kOn_State, //!< XML: buttonState="on" + kUnknown_State //!< XML: buttonState="unknown" + }; + State getButtonState() const { return fState; } + void setButtonState(State); + +protected: + /** called when the label changes. default behavior is to inval the widget */ + virtual void onButtonStateChange(); + + // overrides + virtual void onInflate(const SkDOM& dom, const SkDOM::Node*); + +private: + State fState; + typedef SkHasLabelWidget INHERITED; +}; + +class SkPushButtonWidget : public SkButtonWidget { +public: + SkPushButtonWidget(uint32_t flags = 0) : SkButtonWidget(flags) {} + +protected: + virtual bool onEvent(const SkEvent&); + virtual void onDraw(SkCanvas*); + virtual Click* onFindClickHandler(SkScalar x, SkScalar y); + virtual bool onClick(Click* click); + +private: + typedef SkButtonWidget INHERITED; +}; + +class SkCheckBoxWidget : public SkButtonWidget { +public: + SkCheckBoxWidget(uint32_t flags = 0); + +protected: + virtual bool onEvent(const SkEvent&); + virtual void onDraw(SkCanvas*); + virtual void onInflate(const SkDOM& dom, const SkDOM::Node*); + +private: + typedef SkButtonWidget INHERITED; +}; + +#include "SkTextBox.h" + +class SkStaticTextView : public SkView { +public: + SkStaticTextView(uint32_t flags = 0); + virtual ~SkStaticTextView(); + + enum Mode { + kFixedSize_Mode, + kAutoWidth_Mode, + kAutoHeight_Mode, + + kModeCount + }; + Mode getMode() const { return (Mode)fMode; } + void setMode(Mode); + + SkTextBox::SpacingAlign getSpacingAlign() const { return (SkTextBox::SpacingAlign)fSpacingAlign; } + void setSpacingAlign(SkTextBox::SpacingAlign); + + void getMargin(SkPoint* margin) const; + void setMargin(SkScalar dx, SkScalar dy); + + size_t getText(SkString* text = NULL) const; + size_t getText(char text[] = NULL) const; + void setText(const SkString&); + void setText(const char text[]); + void setText(const char text[], size_t len); + + void getPaint(SkPaint*) const; + void setPaint(const SkPaint&); + +protected: + // overrides + virtual void onDraw(SkCanvas*); + virtual void onInflate(const SkDOM& dom, const SkDOM::Node*); + +private: + SkPoint fMargin; + SkString fText; + SkPaint fPaint; + uint8_t fMode; + uint8_t fSpacingAlign; + + void computeSize(); + + typedef SkView INHERITED; +}; + +class SkBitmapView : public SkView { +public: + SkBitmapView(uint32_t flags = 0); + virtual ~SkBitmapView(); + + bool getBitmap(SkBitmap*) const; + void setBitmap(const SkBitmap*, bool viewOwnsPixels); + bool loadBitmapFromFile(const char path[]); + +protected: + virtual void onDraw(SkCanvas*); + virtual void onInflate(const SkDOM&, const SkDOM::Node*); + +private: + SkBitmap fBitmap; + typedef SkView INHERITED; +}; + +///////////////////////////////////////////////////////////////////////////// + +class SkShader; +class SkInterpolator; + +class SkWidgetView : public SkView { +public: + SkWidgetView(uint32_t flags = 0); + virtual ~SkWidgetView(); + + static const char* GetEventType(); +}; + +class SkSliderView : public SkWidgetView { +public: + SkSliderView(uint32_t flags = 0); + + uint16_t getValue() const { return fValue; } + uint16_t getMax() const { return fMax; } + + void setMax(U16CPU max); + void setValue(U16CPU value); + +protected: + virtual void onDraw(SkCanvas*); + virtual Click* onFindClickHandler(SkScalar x, SkScalar y); + virtual bool onClick(Click*); + +private: + uint16_t fValue, fMax; + + typedef SkWidgetView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +class SkHasLabelView : public SkView { +public: + void getLabel(SkString*) const; + void setLabel(const SkString&); + void setLabel(const char label[]); + +protected: + SkString fLabel; + + // called when the label changes + virtual void onLabelChange(); + + // overrides + virtual void onInflate(const SkDOM& dom, const SkDOM::Node*); +}; + +class SkPushButtonView : public SkHasLabelView { +public: + SkPushButtonView(uint32_t flags = 0); + +protected: + virtual void onDraw(SkCanvas*); + virtual void onInflate(const SkDOM& dom, const SkDOM::Node*); +}; + +class SkCheckBoxView : public SkHasLabelView { +public: + SkCheckBoxView(uint32_t flags = 0); + + enum State { + kOff_State, + kOn_State, + kMaybe_State + }; + State getState() const { return fState; } + void setState(State); + +protected: + virtual void onDraw(SkCanvas*); + virtual void onInflate(const SkDOM& dom, const SkDOM::Node*); + +private: + State fState; +}; + +class SkProgressView : public SkView { +public: + SkProgressView(uint32_t flags = 0); + virtual ~SkProgressView(); + + uint16_t getValue() const { return fValue; } + uint16_t getMax() const { return fMax; } + + void setMax(U16CPU max); + void setValue(U16CPU value); + +protected: + virtual void onDraw(SkCanvas*); + virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node); + +private: + uint16_t fValue, fMax; + SkShader* fOnShader, *fOffShader; + SkInterpolator* fInterp; + bool fDoInterp; + + typedef SkView INHERITED; +}; + +class SkTextView : public SkView { +public: + SkTextView(uint32_t flags = 0); + virtual ~SkTextView(); + + enum AnimaDir { + kNeutral_AnimDir, + kForward_AnimDir, + kBackward_AnimDir, + kAnimDirCount + }; + + void getText(SkString*) const; + void setText(const SkString&, AnimaDir dir = kNeutral_AnimDir); + void setText(const char text[], AnimaDir dir = kNeutral_AnimDir); + void setText(const char text[], size_t len, AnimaDir dir = kNeutral_AnimDir); + + void getMargin(SkPoint* margin) const; + void setMargin(const SkPoint&); + + SkPaint& paint() { return fPaint; } + +protected: + virtual void onDraw(SkCanvas*); + virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node); + +private: + SkString fText; + SkPaint fPaint; + SkPoint fMargin; + + class Interp; + Interp* fInterp; + bool fDoInterp; + // called by the other setText methods. This guy does not check for != + // before doing the assign, so the caller must check for us + void privSetText(const SkString&, AnimaDir dir); + + typedef SkView INHERITED; +}; + +////////////////////////////////////////////////////////// + +class SkEvent; + +class SkListSource : public SkEventSink { +public: + virtual int countRows() = 0; + virtual void getRow(int index, SkString* left, SkString* right) = 0; + virtual SkEvent* getEvent(int index); + + static SkListSource* CreateFromDir(const char path[], const char suffix[], + const char targetPrefix[]); + static SkListSource* CreateFromDOM(const SkDOM& dom, const SkDOM::Node* node); +}; + +class SkListView : public SkWidgetView { +public: + SkListView(uint32_t flags = 0); + virtual ~SkListView(); + + SkScalar getRowHeight() const { return fRowHeight; } + void setRowHeight(SkScalar); + + /** Return the index of the selected row, or -1 if none + */ + int getSelection() const { return fCurrIndex; } + /** Set the index of the selected row, or -1 for none + */ + void setSelection(int); + + void moveSelectionUp(); + void moveSelectionDown(); + + enum Attr { + kBG_Attr, + kNormalText_Attr, + kHiliteText_Attr, + kHiliteCell_Attr, + kAttrCount + }; + SkPaint& paint(Attr); + + SkListSource* getListSource() const { return fSource; } + SkListSource* setListSource(SkListSource*); + +#if 0 + enum Action { + kSelectionChange_Action, + kSelectionPicked_Action, + kActionCount + }; + /** If event is not null, it is retained by the view, and a copy + of the event will be posted to its listeners when the specified + action occurs. If event is null, then no event will be posted for + the specified action. + */ + void setActionEvent(Action, SkEvent* event); +#endif + +protected: + virtual void onDraw(SkCanvas*); + virtual void onSizeChange(); + virtual bool onEvent(const SkEvent&); + virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node); + +private: + SkPaint fPaint[kAttrCount]; + SkListSource* fSource; + SkScalar fRowHeight; + int fCurrIndex; // logical index + int fScrollIndex; // logical index of top-most visible row + int fVisibleRowCount; + SkString* fStrCache; + + void dirtyStrCache(); + void ensureStrCache(int visibleCount); + + int logicalToVisualIndex(int index) const { return index - fScrollIndex; } + void invalSelection(); + bool getRowRect(int index, SkRect*) const; + void ensureSelectionIsVisible(); + + typedef SkWidgetView INHERITED; +}; + +////////////////////////////////////////////////////////// + +class SkGridView : public SkWidgetView { +public: + SkGridView(uint32_t flags = 0); + virtual ~SkGridView(); + + void getCellSize(SkPoint*) const; + void setCellSize(SkScalar x, SkScalar y); + + /** Return the index of the selected item, or -1 if none + */ + int getSelection() const { return fCurrIndex; } + /** Set the index of the selected row, or -1 for none + */ + void setSelection(int); + + void moveSelectionUp(); + void moveSelectionDown(); + + enum Attr { + kBG_Attr, + kHiliteCell_Attr, + kAttrCount + }; + SkPaint& paint(Attr); + + SkListSource* getListSource() const { return fSource; } + SkListSource* setListSource(SkListSource*); + +protected: + virtual void onDraw(SkCanvas*); + virtual void onSizeChange(); + virtual bool onEvent(const SkEvent&); + virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node); + +private: + SkView* fScrollBar; + SkPaint fPaint[kAttrCount]; + SkListSource* fSource; + int fCurrIndex; // logical index + + SkPoint fCellSize; + SkIPoint fVisibleCount; + + int logicalToVisualIndex(int index) const { return index; } + void invalSelection(); + bool getCellRect(int index, SkRect*) const; + void ensureSelectionIsVisible(); + + typedef SkWidgetView INHERITED; +}; + +#endif + diff --git a/skia/include/SkWidgetViews.h b/skia/include/SkWidgetViews.h new file mode 100644 index 0000000..0b74156 --- /dev/null +++ b/skia/include/SkWidgetViews.h @@ -0,0 +1,311 @@ +/* include/graphics/SkWidgetViews.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkWidgetViews_DEFINED +#define SkWidgetViews_DEFINED + +#include "SkView.h" + + +enum SkWidgetEnum { + kBorder_WidgetEnum, //!< <sk-border> + kButton_WidgetEnum, //!< <sk-button> + kImage_WidgetEnum, //!< <sk-image> + kList_WidgetEnum, //!< <sk-list> + kProgress_WidgetEnum, //!< <sk-progress> + kScroll_WidgetEnum, //!< <sk-scroll> + kText_WidgetEnum, //!< <sk-text> + + kWidgetEnumCount +}; + +//determines which skin to use +enum SkinEnum { + kBorder_SkinEnum, + kButton_SkinEnum, + kProgress_SkinEnum, + kScroll_SkinEnum, + kStaticText_SkinEnum, + + kSkinEnumCount +}; + +#include "SkAnimator.h" +//used for inflates +const char* get_skin_enum_path(SkinEnum se); +void init_skin_anim(const char path[], SkAnimator* anim); +void init_skin_anim(SkinEnum se, SkAnimator* anim); +void init_skin_paint(SkinEnum se, SkPaint* paint); +void inflate_paint(const SkDOM& dom, const SkDOM::Node* node, SkPaint* paint); + +/** Given an enum value, return an instance of the specified widget. + If the enum is out of range, returns null +*/ +SkView* SkWidgetFactory(SkWidgetEnum); +/** Given the inflate/element name of a widget, return an instance of + the specified widget, or null if name does not match any known + widget type. +*/ +SkView* SkWidgetFactory(const char name[]); + +//////////////////////////////////////////////////////////////////////////////////////////////// + +class SkWidgetView : public SkView { +public: + SkWidgetView(); + + const char* getLabel() const; + void getLabel(SkString* label) const; + + void setLabel(const char[]); + void setLabel(const char[], size_t len); + void setLabel(const SkString&); + + SkEvent& event() { return fEvent; } + const SkEvent& event() const { return fEvent; } + + /** Returns true if the widget can post its event to its listeners. + */ + bool postWidgetEvent(); + + /** Returns the sinkID of the widgetview that posted the event, or 0 + */ + static SkEventSinkID GetWidgetEventSinkID(const SkEvent&); + +protected: + /** called when the label changes. override in subclasses. default action invals the view's bounds. + called with the old and new labels, before the label has actually changed. + */ + virtual void onLabelChange(const char oldLabel[], const char newLabel[]); + /** called before posting the event to our listeners. Override to add slots to the event + before posting. Return true to proceed with posting, or false to not post the event to any + listener. Note: the event passed in may not be the same as calling this->event(). + Be sure to call your INHERITED method as well, so that all classes in the hierarchy get a shot + at modifying the event (and possibly returning false to abort). + */ + virtual bool onPrepareWidgetEvent(SkEvent* evt); + + // overrides + virtual void onInflate(const SkDOM& dom, const SkDOM::Node*); + +private: + SkString fLabel; + SkEvent fEvent; + + typedef SkView INHERITED; +}; + +//////////////////////////////////////////////////////////////////////////////////////////////// + +class SkButtonView : public SkWidgetView { +public: + // inflate: "sk-button" + +protected: + // overrides + virtual bool onEvent(const SkEvent&); +}; + +//////////////////////////////////////////////////////////////////////////////////////////////// + +class SkCheckButtonView : public SkWidgetView { +public: + SkCheckButtonView(); + + // inflate: "sk-checkbutton" + + enum CheckState { + kOff_CheckState, //!< inflate: check-state="off" + kOn_CheckState, //!< inflate: check-state="on" + kUnknown_CheckState //!< inflate: check-state="unknown" + }; + CheckState getCheckState() const { return (CheckState)fCheckState; } + void setCheckState(CheckState); + + /** use this to extract the CheckState from an event (i.e. one that as posted + by a SkCheckButtonView). Returns true if the proper slot was present in the event, + and sets state to that value. If no proper slot is found, returns false and does not + modify state. + */ + static bool GetWidgetEventCheckState(const SkEvent&, CheckState* state); + +protected: + // called when the check-state is about to change, but before it actually has + virtual void onCheckStateChange(CheckState oldState, CheckState newState); + + // overrides + virtual void onInflate(const SkDOM& dom, const SkDOM::Node*); + virtual bool onPrepareWidgetEvent(SkEvent* evt); + +private: + uint8_t fCheckState; + + typedef SkWidgetView INHERITED; +}; + +//////////////////////////////////////////////////////////////////////////////////////////////// +#include "SkTextBox.h" + +class SkStaticTextView : public SkView { +public: + SkStaticTextView(); + virtual ~SkStaticTextView(); + + enum Mode { + kFixedSize_Mode, + kAutoWidth_Mode, + kAutoHeight_Mode, + + kModeCount + }; + Mode getMode() const { return (Mode)fMode; } + void setMode(Mode); + + SkTextBox::SpacingAlign getSpacingAlign() const { return (SkTextBox::SpacingAlign)fSpacingAlign; } + void setSpacingAlign(SkTextBox::SpacingAlign); + + void getMargin(SkPoint* margin) const; + void setMargin(SkScalar dx, SkScalar dy); + + size_t getText(SkString* text = NULL) const; + size_t getText(char text[] = NULL) const; + void setText(const SkString&); + void setText(const char text[]); + void setText(const char text[], size_t len); + + void getPaint(SkPaint*) const; + void setPaint(const SkPaint&); + +protected: + // overrides + virtual void onDraw(SkCanvas*); + virtual void onInflate(const SkDOM& dom, const SkDOM::Node*); + +private: + SkPoint fMargin; + SkString fText; + SkPaint fPaint; + uint8_t fMode; + uint8_t fSpacingAlign; + + void computeSize(); + + typedef SkView INHERITED; +}; + +//////////////////////////////////////////////////////////////////////////////////////////////// + +class SkAnimator; +class SkListSource; +class SkScrollBarView; + +class SkListView : public SkWidgetView { +public: + SkListView(); + virtual ~SkListView(); + + bool hasScrollBar() const { return fScrollBar != NULL; } + void setHasScrollBar(bool); + + /** Return the number of visible rows + */ + int getVisibleRowCount() const { return fVisibleRowCount; } + /** Return the index of the selected row, or -1 if none + */ + int getSelection() const { return fCurrIndex; } + /** Set the index of the selected row, or -1 for none + */ + void setSelection(int); + /** If possible, move the selection up and return true, + else do nothing and return false + If nothing is selected, select the last item (unless there are no items). + */ + bool moveSelectionUp(); + /** If possible, move the selection down and return true, + else do nothing and return false. + If nothing is selected, select the first item (unless there are no items). + */ + bool moveSelectionDown(); + + SkListSource* getListSource() const { return fSource; } + SkListSource* setListSource(SkListSource*); + + /** Call this in your event handler. If the specified event is from a SkListView, + then it returns the index of the selected item in this list, otherwise it + returns -1 + */ + static int GetWidgetEventListIndex(const SkEvent&); + +protected: + // overrides + virtual void onDraw(SkCanvas*); + virtual void onSizeChange(); + virtual bool onEvent(const SkEvent&); + virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node); + virtual bool onPrepareWidgetEvent(SkEvent*); + +private: + enum DirtyFlags { + kAnimCount_DirtyFlag = 0x01, + kAnimContent_DirtyFlag = 0x02 + }; + void dirtyCache(unsigned dirtyFlags); + bool ensureCache(); + + int logicalToVisualIndex(int index) const { return index - fScrollIndex; } + void invalSelection(); + SkScalar getContentWidth() const; + bool getRowRect(int index, SkRect*) const; + void ensureSelectionIsVisible(); + void ensureVisibleRowCount(); + + struct BindingRec; + + enum Heights { + kNormal_Height, + kSelected_Height + }; + SkListSource* fSource; + SkScrollBarView* fScrollBar; + SkAnimator* fAnims; + BindingRec* fBindings; + SkString fSkinName; + SkScalar fHeights[2]; + int16_t fScrollIndex, fCurrIndex; + uint16_t fVisibleRowCount, fBindingCount; + SkBool8 fAnimContentDirty; + SkBool8 fAnimFocusDirty; + + typedef SkWidgetView INHERITED; +}; + +class SkListSource : public SkRefCnt { +public: + virtual int countFields(); + virtual void getFieldName(int index, SkString* field); + /** Return the index of the named field, or -1 if not found */ + virtual int findFieldIndex(const char field[]); + + virtual int countRecords(); + virtual void getRecord(int rowIndex, int fieldIndex, SkString* data); + + virtual bool prepareWidgetEvent(SkEvent*, int rowIndex); + + static SkListSource* Factory(const char name[]); +}; + +#endif diff --git a/skia/include/SkWindow.h b/skia/include/SkWindow.h new file mode 100644 index 0000000..e70e04b --- /dev/null +++ b/skia/include/SkWindow.h @@ -0,0 +1,103 @@ +/* include/graphics/SkWindow.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkWindow_DEFINED +#define SkWindow_DEFINED + +#include "SkView.h" +#include "SkBitmap.h" +#include "SkRegion.h" +#include "SkEvent.h" +#include "SkKey.h" +#include "SkTDArray.h" + +#ifdef SK_BUILD_FOR_WINCEx + #define SHOW_FPS +#endif +//#define USE_GX_SCREEN + +class SkOSMenu; + +class SkWindow : public SkView { +public: + SkWindow(); + virtual ~SkWindow(); + + const SkBitmap& getBitmap() const { return fBitmap; } + + void setConfig(SkBitmap::Config); + void resize(int width, int height, SkBitmap::Config config = SkBitmap::kNo_Config); + void eraseARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b); + void eraseRGB(U8CPU r, U8CPU g, U8CPU b); + + bool isDirty() const { return !fDirtyRgn.isEmpty(); } + bool update(SkIRect* updateArea); + bool handleClick(int x, int y, Click::State); + bool handleChar(SkUnichar); + bool handleKey(SkKey); + bool handleKeyUp(SkKey); + bool handleMenu(uint32_t os_cmd); + + void addMenu(SkOSMenu*); + void setTitle(const char title[]); + +protected: + virtual bool onEvent(const SkEvent&); + + // called if part of our bitmap is invalidated + virtual void onHandleInval(const SkIRect&); + virtual bool onHandleChar(SkUnichar); + virtual bool onHandleKey(SkKey); + virtual bool onHandleKeyUp(SkKey); + virtual void onAddMenu(const SkOSMenu*) {} + virtual void onSetTitle(const char title[]) {} + + // overrides from SkView + virtual bool handleInval(const SkRect&); + virtual bool onGetFocusView(SkView** focus) const; + virtual bool onSetFocusView(SkView* focus); + +private: + SkBitmap::Config fConfig; + SkBitmap fBitmap; + SkRegion fDirtyRgn; + Click* fClick; // to track clicks + + SkTDArray<SkOSMenu*> fMenus; + + SkView* fFocusView; + bool fWaitingOnInval; + + typedef SkView INHERITED; +}; + +/////////////////////////////////////////////////////////// + +#ifndef SK_USE_WXWIDGETS +#ifdef SK_BUILD_FOR_MAC + #include "SkOSWindow_Mac.h" +#elif defined(SK_BUILD_FOR_WIN) + #include "SkOSWindow_Win.h" +#elif defined(SK_BUILD_FOR_UNIXx) + #include "SkOSWindow_Unix.h" +#endif +#else + #include "SkOSWindow_wxwidgets.h" +#endif + +#endif + diff --git a/skia/include/SkWriter32.h b/skia/include/SkWriter32.h new file mode 100644 index 0000000..66910d1 --- /dev/null +++ b/skia/include/SkWriter32.h @@ -0,0 +1,96 @@ +#ifndef SkWriter32_DEFINED +#define SkWriter32_DEFINED + +#include "SkTypes.h" + +#include "SkScalar.h" +#include "SkPoint.h" +#include "SkRect.h" + +class SkStream; +class SkWStream; + +class SkWriter32 : SkNoncopyable { +public: + SkWriter32(size_t minSize) { + fMinSize = minSize; + fSize = 0; + fHead = fTail = NULL; + } + ~SkWriter32(); + + bool writeBool(bool value) { + this->writeInt(value); + return value; + } + + void writeInt(int32_t value) { + *(int32_t*)this->reserve(sizeof(value)) = value; + } + + void write8(int32_t value) { + *(int32_t*)this->reserve(sizeof(value)) = value & 0xFF; + } + + void write16(int32_t value) { + *(int32_t*)this->reserve(sizeof(value)) = value & 0xFFFF; + } + + void write32(int32_t value) { + *(int32_t*)this->reserve(sizeof(value)) = value; + } + + void writeScalar(SkScalar value) { + *(SkScalar*)this->reserve(sizeof(value)) = value; + } + + void writePoint(const SkPoint& pt) { + *(SkPoint*)this->reserve(sizeof(pt)) = pt; + } + + void writeRect(const SkRect& rect) { + *(SkRect*)this->reserve(sizeof(rect)) = rect; + } + + // write count bytes (must be a multiple of 4) + void writeMul4(const void* values, size_t size) { + SkASSERT(SkAlign4(size) == size); + // if we could query how much is avail in the current block, we might + // copy that much, and then alloc the rest. That would reduce the waste + // in the current block + memcpy(this->reserve(size), values, size); + } + + void writePad(const void* src, size_t size); + + // return the current offset (will always be a multiple of 4) + uint32_t size() const { return fSize; } + void reset(); + uint32_t* reserve(size_t size); // size MUST be multiple of 4 + + // return the address of the 4byte int at the specified offset (which must + // be a multiple of 4. This does not allocate any new space, so the returned + // address is only valid for 1 int. + uint32_t* peek32(size_t offset); + + // copy into a single buffer (allocated by caller). Must be at least size() + void flatten(void* dst) const; + + // read from the stream, and write up to length bytes. Return the actual + // number of bytes written. + size_t readFromStream(SkStream*, size_t length); + + bool writeToStream(SkWStream*); + +private: + size_t fMinSize; + uint32_t fSize; + + struct Block; + Block* fHead; + Block* fTail; + + Block* newBlock(size_t bytes); +}; + +#endif diff --git a/skia/include/SkXMLParser.h b/skia/include/SkXMLParser.h new file mode 100644 index 0000000..98f106c --- /dev/null +++ b/skia/include/SkXMLParser.h @@ -0,0 +1,162 @@ +/* include/graphics/SkXMLParser.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkXMLParser_DEFINED +#define SkXMLParser_DEFINED + +#include "SkMath.h" +#include "SkString.h" + +class SkStream; + +class SkDOM; +struct SkDOMNode; + +class SkXMLParserError { +public: + enum ErrorCode { + kNoError, + kEmptyFile, + kUnknownElement, + kUnknownAttributeName, + kErrorInAttributeValue, + kDuplicateIDs, + kUnknownError + }; + + SkXMLParserError(); + virtual ~SkXMLParserError(); + ErrorCode getErrorCode() const { return fCode; } + virtual void getErrorString(SkString* str) const; + int getLineNumber() const { return fLineNumber; } + int getNativeCode() const { return fNativeCode; } + bool hasError() const { return fCode != kNoError || fNativeCode != -1; } + bool hasNoun() const { return fNoun.size() > 0; } + void reset(); + void setCode(ErrorCode code) { fCode = code; } + void setNoun(const SkString& str) { fNoun.set(str); } + void setNoun(const char* ch) { fNoun.set(ch); } + void setNoun(const char* ch, size_t len) { fNoun.set(ch, len); } +protected: + ErrorCode fCode; +private: + int fLineNumber; + int fNativeCode; + SkString fNoun; + friend class SkXMLParser; +}; + +class SkXMLParser { +public: + SkXMLParser(SkXMLParserError* parserError = NULL); + virtual ~SkXMLParser(); + + /** Returns true for success + */ + bool parse(const char doc[], size_t len); + bool parse(SkStream& docStream); + bool parse(const SkDOM&, const SkDOMNode*); + + static void GetNativeErrorString(int nativeErrorCode, SkString* str); + +protected: + // override in subclasses; return true to stop parsing + virtual bool onStartElement(const char elem[]); + virtual bool onAddAttribute(const char name[], const char value[]); + virtual bool onEndElement(const char elem[]); + virtual bool onText(const char text[], int len); + +public: + // public for ported implementation, not meant for clients to call + virtual bool startElement(const char elem[]); + virtual bool addAttribute(const char name[], const char value[]); + virtual bool endElement(const char elem[]); + virtual bool text(const char text[], int len); + void* fParser; +protected: + SkXMLParserError* fError; +private: + void reportError(void* parser); +}; + +class SkXMLPullParser { +public: + SkXMLPullParser(); + explicit SkXMLPullParser(SkStream*); + virtual ~SkXMLPullParser(); + + SkStream* getStream() const { return fStream; } + SkStream* setStream(SkStream* stream); + + enum EventType { + ERROR = -1, + START_DOCUMENT, + END_DOCUMENT, + START_TAG, + END_TAG, + TEXT, + CDSECT, + ENTITY_REF, + IGNORABLE_WHITESPACE, + PROCESSING_INSTRUCTION, + COMMENT, + DOCDECL + }; + + EventType nextToken(); + EventType getEventType() const { return fCurr.fEventType; } + + struct AttrInfo { + const char* fName; + const char* fValue; + }; + + int getDepth() const { return fDepth; } + const char* getName(); + int getAttributeCount(); + void getAttributeInfo(int, AttrInfo*); + const char* getText(); + bool isWhitespace(); + +protected: + virtual bool onEntityReplacement(const char name[], + SkString* replacement); + +public: + struct Curr { + EventType fEventType; + const char* fName; + AttrInfo* fAttrInfos; + int fAttrInfoCount; + bool fIsWhitespace; + }; + +private: + // implemented in the porting layer + bool onInit(); // return false on failure + EventType onNextToken(); + void onExit(); + + SkStream* fStream; + Curr fCurr; + int fDepth; + + struct Impl; + Impl* fImpl; +}; + +#endif diff --git a/skia/include/SkXMLWriter.h b/skia/include/SkXMLWriter.h new file mode 100644 index 0000000..f60aef4 --- /dev/null +++ b/skia/include/SkXMLWriter.h @@ -0,0 +1,94 @@ +/* include/graphics/SkXMLWriter.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkXMLWriter_DEFINED +#define SkXMLWriter_DEFINED + +#include "SkTDArray.h" +#include "SkString.h" +#include "SkDOM.h" + +class SkWStream; +class SkXMLParser; + +class SkXMLWriter { +public: + SkXMLWriter(bool doEscapeMarkup = true); + virtual ~SkXMLWriter(); + + void addS32Attribute(const char name[], int32_t value); + void addAttribute(const char name[], const char value[]); + void addAttributeLen(const char name[], const char value[], size_t length); + void addHexAttribute(const char name[], uint32_t value, int minDigits = 0); + void addScalarAttribute(const char name[], SkScalar value); + void endElement() { this->onEndElement(); } + void startElement(const char elem[]); + void startElementLen(const char elem[], size_t length); + void writeDOM(const SkDOM&, const SkDOM::Node*, bool skipRoot); + void flush(); + virtual void writeHeader(); + +protected: + virtual void onStartElementLen(const char elem[], size_t length) = 0; + virtual void onAddAttributeLen(const char name[], const char value[], size_t length) = 0; + virtual void onEndElement() = 0; + + struct Elem { + SkString fName; + bool fHasChildren; + }; + void doEnd(Elem* elem); + bool doStart(const char name[], size_t length); + Elem* getEnd(); + const char* getHeader(); + SkTDArray<Elem*> fElems; + +private: + bool fDoEscapeMarkup; + // illegal + SkXMLWriter& operator=(const SkXMLWriter&); +}; + +class SkXMLStreamWriter : public SkXMLWriter { +public: + SkXMLStreamWriter(SkWStream*); + virtual ~SkXMLStreamWriter(); + virtual void writeHeader(); + SkDEBUGCODE(static void UnitTest();) +protected: + virtual void onStartElementLen(const char elem[], size_t length); + virtual void onEndElement(); + virtual void onAddAttributeLen(const char name[], const char value[], size_t length); +private: + SkWStream& fStream; +}; + +class SkXMLParserWriter : public SkXMLWriter { +public: + SkXMLParserWriter(SkXMLParser*); + virtual ~SkXMLParserWriter(); +protected: + virtual void onStartElementLen(const char elem[], size_t length); + virtual void onEndElement(); + virtual void onAddAttributeLen(const char name[], const char value[], size_t length); +private: + SkXMLParser& fParser; +}; + + +#endif + diff --git a/skia/include/SkXfermode.h b/skia/include/SkXfermode.h new file mode 100644 index 0000000..8de5e65d --- /dev/null +++ b/skia/include/SkXfermode.h @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2006 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SkXfermode_DEFINED +#define SkXfermode_DEFINED + +#include "SkFlattenable.h" +#include "SkColor.h" + +/** \class SkXfermode + + SkXfermode is the base class for objects that are called to implement custom + "transfer-modes" in the drawing pipeline. The static function Create(Modes) + can be called to return an instance of any of the predefined subclasses as + specified in the Modes enum. When an SkXfermode is assigned to an SkPaint, + then objects drawn with that paint have the xfermode applied. +*/ +class SkXfermode : public SkFlattenable { +public: + SkXfermode() {} + + virtual void xfer32(SkPMColor dst[], const SkPMColor src[], int count, + const SkAlpha aa[]); + virtual void xfer16(uint16_t dst[], const SkPMColor src[], int count, + const SkAlpha aa[]); + virtual void xfer4444(uint16_t dst[], const SkPMColor src[], int count, + const SkAlpha aa[]); + virtual void xferA8(SkAlpha dst[], const SkPMColor src[], int count, + const SkAlpha aa[]); + + enum Coeff { + kZero_Coeff, + kOne_Coeff, + kSC_Coeff, + kISC_Coeff, + kDC_Coeff, + kIDC_Coeff, + kSA_Coeff, + kISA_Coeff, + kDA_Coeff, + kIDA_Coeff, + + kCoeffCount + }; + virtual bool asCoeff(Coeff* src, Coeff* dst); + +protected: + SkXfermode(SkFlattenableReadBuffer& rb) : SkFlattenable(rb) {} + + /** The default implementation of xfer32/xfer16/xferA8 in turn call this + method, 1 color at a time (upscaled to a SkPMColor). The default + implmentation of this method just returns dst. If performance is + important, your subclass should override xfer32/xfer16/xferA8 directly. + + This method will not be called directly by the client, so it need not + be implemented if your subclass has overridden xfer32/xfer16/xferA8 + */ + virtual SkPMColor xferColor(SkPMColor src, SkPMColor dst); + +private: + typedef SkFlattenable INHERITED; +}; + +/////////////////////////////////////////////////////////////////////////////// + +/** \class SkProcXfermode + + SkProcXfermode is a xfermode that applies the specified proc to its colors. + This class is not exported to java. +*/ +class SkProcXfermode : public SkXfermode { +public: + SkProcXfermode(SkXfermodeProc proc) : fProc(proc) {} + + // overrides from SkXfermode + virtual void xfer32(SkPMColor dst[], const SkPMColor src[], int count, + const SkAlpha aa[]); + virtual void xfer16(uint16_t dst[], const SkPMColor src[], int count, + const SkAlpha aa[]); + virtual void xfer4444(uint16_t dst[], const SkPMColor src[], int count, + const SkAlpha aa[]); + virtual void xferA8(SkAlpha dst[], const SkPMColor src[], int count, + const SkAlpha aa[]); + + // overrides from SkFlattenable + virtual Factory getFactory() { return CreateProc; } + virtual void flatten(SkFlattenableWriteBuffer&); + +protected: + SkProcXfermode(SkFlattenableReadBuffer&); + +private: + SkXfermodeProc fProc; + + static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) { + return SkNEW_ARGS(SkProcXfermode, (buffer)); } + + typedef SkXfermode INHERITED; +}; + +#endif + diff --git a/skia/include/corecg/Sk64.h b/skia/include/corecg/Sk64.h new file mode 100644 index 0000000..4d5343c --- /dev/null +++ b/skia/include/corecg/Sk64.h @@ -0,0 +1,245 @@ +/* + * Copyright (C) 2006-2008 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef Sk64_DEFINED +#define Sk64_DEFINED + +#include "SkFixed.h" +#include "SkMath.h" + +/** \class Sk64 + + Sk64 is a 64-bit math package that does not require long long support from the compiler. +*/ +struct Sk64 { + int32_t fHi; //!< the high 32 bits of the number (including sign) + uint32_t fLo; //!< the low 32 bits of the number + + /** Returns non-zero if the Sk64 can be represented as a signed 32 bit integer + */ + SkBool is32() const { return fHi == ((int32_t)fLo >> 31); } + + /** Returns non-zero if the Sk64 cannot be represented as a signed 32 bit integer + */ + SkBool is64() const { return fHi != ((int32_t)fLo >> 31); } + + /** Returns non-zero if the Sk64 can be represented as a signed 48 bit integer. Used to know + if we can shift the value down by 16 to treat it as a SkFixed. + */ + SkBool isFixed() const; + + /** Return the signed 32 bit integer equivalent. Asserts that is32() returns non-zero. + */ + int32_t get32() const { SkASSERT(this->is32()); return (int32_t)fLo; } + + /** Return the number >> 16. Asserts that this does not loose any significant high bits. + */ + SkFixed getFixed() const { + SkASSERT(this->isFixed()); + + uint32_t sum = fLo + (1 << 15); + int32_t hi = fHi; + if (sum < fLo) { + hi += 1; + } + return (hi << 16) | (sum >> 16); + } + + /** Return the number >> 30. Asserts that this does not loose any + significant high bits. + */ + SkFract getFract() const; + + /** Returns the square-root of the number as a signed 32 bit value. */ + int32_t getSqrt() const; + + /** Returns the number of leading zeros of the absolute value of this. + Will return in the range [0..64] + */ + int getClzAbs() const; + + /** Returns non-zero if the number is zero */ + SkBool isZero() const { return (fHi | fLo) == 0; } + + /** Returns non-zero if the number is non-zero */ + SkBool nonZero() const { return fHi | fLo; } + + /** Returns non-zero if the number is negative (number < 0) */ + SkBool isNeg() const { return (uint32_t)fHi >> 31; } + + /** Returns non-zero if the number is positive (number > 0) */ + SkBool isPos() const { return ~(fHi >> 31) & (fHi | fLo); } + + /** Returns -1,0,+1 based on the sign of the number */ + int sign() const { return (fHi >> 31) | Sk32ToBool(fHi | fLo); } + + /** Negate the number */ + void negate(); + + /** If the number < 0, negate the number + */ + void abs(); + + /** Returns the number of bits needed to shift the Sk64 to the right + in order to make it fit in a signed 32 bit integer. + */ + int shiftToMake32() const; + + /** Set the number to zero */ + void setZero() { fHi = fLo = 0; } + + /** Set the high and low 32 bit values of the number */ + void set(int32_t hi, uint32_t lo) { fHi = hi; fLo = lo; } + + /** Set the number to the specified 32 bit integer */ + void set(int32_t a) { fHi = a >> 31; fLo = a; } + + /** Set the number to the product of the two 32 bit integers */ + void setMul(int32_t a, int32_t b); + + /** extract 32bits after shifting right by bitCount. + Note: itCount must be [0..63]. + Asserts that no significant high bits were lost. + */ + int32_t getShiftRight(unsigned bitCount) const; + + /** Shift the number left by the specified number of bits. + @param bits How far to shift left, must be [0..63] + */ + void shiftLeft(unsigned bits); + + /** Shift the number right by the specified number of bits. + @param bits How far to shift right, must be [0..63]. This + performs an arithmetic right-shift (sign extending). + */ + void shiftRight(unsigned bits); + + /** Shift the number right by the specified number of bits, but + round the result. + @param bits How far to shift right, must be [0..63]. This + performs an arithmetic right-shift (sign extending). + */ + void roundRight(unsigned bits); + + /** Add the specified 32 bit integer to the number */ + void add(int32_t lo) { + int32_t hi = lo >> 31; // 0 or -1 + uint32_t sum = fLo + (uint32_t)lo; + + fHi = fHi + hi + (sum < fLo); + fLo = sum; + } + + /** Add the specified Sk64 to the number */ + void add(int32_t hi, uint32_t lo) { + uint32_t sum = fLo + lo; + + fHi = fHi + hi + (sum < fLo); + fLo = sum; + } + + /** Add the specified Sk64 to the number */ + void add(const Sk64& other) { this->add(other.fHi, other.fLo); } + + /** Subtract the specified Sk64 from the number. (*this) = (*this) - num + */ + void sub(const Sk64& num); + + /** Subtract the number from the specified Sk64. (*this) = num - (*this) + */ + void rsub(const Sk64& num); + + /** Multiply the number by the specified 32 bit integer + */ + void mul(int32_t); + + enum DivOptions { + kTrunc_DivOption, //!< truncate the result when calling div() + kRound_DivOption //!< round the result when calling div() + }; + + /** Divide the number by the specified 32 bit integer, using the specified + divide option (either truncate or round). + */ + void div(int32_t, DivOptions); + + /** return (this + other >> 16) as a 32bit result */ + SkFixed addGetFixed(const Sk64& other) const { + return this->addGetFixed(other.fHi, other.fLo); + } + + /** return (this + Sk64(hi, lo) >> 16) as a 32bit result */ + SkFixed addGetFixed(int32_t hi, uint32_t lo) const { +#ifdef SK_DEBUG + Sk64 tmp(*this); + tmp.add(hi, lo); +#endif + + uint32_t sum = fLo + lo; + hi += fHi + (sum < fLo); + lo = sum; + + sum = lo + (1 << 15); + if (sum < lo) + hi += 1; + + hi = (hi << 16) | (sum >> 16); + SkASSERT(hi == tmp.getFixed()); + return hi; + } + + /** Return the result of dividing the number by denom, treating the answer + as a SkFixed. (*this) << 16 / denom. It is an error for denom to be 0. + */ + SkFixed getFixedDiv(const Sk64& denom) const; + + friend bool operator==(const Sk64& a, const Sk64& b) { + return a.fHi == b.fHi && a.fLo == b.fLo; + } + + friend bool operator!=(const Sk64& a, const Sk64& b) { + return a.fHi != b.fHi || a.fLo != b.fLo; + } + + friend bool operator<(const Sk64& a, const Sk64& b) { + return a.fHi < b.fHi || (a.fHi == b.fHi && a.fLo < b.fLo); + } + + friend bool operator<=(const Sk64& a, const Sk64& b) { + return a.fHi < b.fHi || (a.fHi == b.fHi && a.fLo <= b.fLo); + } + + friend bool operator>(const Sk64& a, const Sk64& b) { + return a.fHi > b.fHi || (a.fHi == b.fHi && a.fLo > b.fLo); + } + + friend bool operator>=(const Sk64& a, const Sk64& b) { + return a.fHi > b.fHi || (a.fHi == b.fHi && a.fLo >= b.fLo); + } + +#ifdef SkLONGLONG + SkLONGLONG getLongLong() const; +#endif + +#ifdef SK_DEBUG + /** @cond UNIT_TEST */ + static void UnitTest(); + /** @endcond */ +#endif +}; + +#endif + diff --git a/skia/include/corecg/SkBuffer.h b/skia/include/corecg/SkBuffer.h new file mode 100644 index 0000000..b1cfefc --- /dev/null +++ b/skia/include/corecg/SkBuffer.h @@ -0,0 +1,142 @@ +/* include/corecg/SkBuffer.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkBuffer_DEFINED +#define SkBuffer_DEFINED + +#include "SkScalar.h" + +/** \class SkRBuffer + + Light weight class for reading data from a memory block. + The RBuffer is given the buffer to read from, with either a specified size + or no size (in which case no range checking is performed). It is iillegal + to attempt to read a value from an empty RBuffer (data == null). +*/ +class SkRBuffer : SkNoncopyable { +public: + SkRBuffer() : fData(0), fPos(0), fStop(0) {} + /** Initialize RBuffer with a data pointer, but no specified length. + This signals the RBuffer to not perform range checks during reading. + */ + SkRBuffer(const void* data) + { + fData = (const char*)data; + fPos = (const char*)data; + fStop = 0; // no bounds checking + } + /** Initialize RBuffer with a data point and length. + */ + SkRBuffer(const void* data, size_t size) + { + SkASSERT(data != 0 || size == 0); + fData = (const char*)data; + fPos = (const char*)data; + fStop = (const char*)data + size; + } + + /** Return the number of bytes that have been read from the beginning + of the data pointer. + */ + size_t pos() const { return fPos - fData; } + /** Return the total size of the data pointer. Only defined if the length was + specified in the constructor or in a call to reset(). + */ + size_t size() const { return fStop - fData; } + /** Return true if the buffer has read to the end of the data pointer. + Only defined if the length was specified in the constructor or in a call + to reset(). Always returns true if the length was not specified. + */ + bool eof() const { return fPos >= fStop; } + + /** Read the specified number of bytes from the data pointer. If buffer is not + null, copy those bytes into buffer. + */ + void read(void* buffer, size_t size) { if (size) this->readNoSizeCheck(buffer, size); } + const void* skip(size_t size); // return start of skipped data + size_t skipToAlign4(); + + void* readPtr() { void* ptr; read(&ptr, sizeof(ptr)); return ptr; } + SkScalar readScalar() { SkScalar x; read(&x, 4); return x; } + uint32_t readU32() { uint32_t x; read(&x, 4); return x; } + int32_t readS32() { int32_t x; read(&x, 4); return x; } + uint16_t readU16() { uint16_t x; read(&x, 2); return x; } + int16_t readS16() { int16_t x; read(&x, 2); return x; } + uint8_t readU8() { uint8_t x; read(&x, 1); return x; } + bool readBool() { return this->readU8() != 0; } + +protected: + void readNoSizeCheck(void* buffer, size_t size); + + const char* fData; + const char* fPos; + const char* fStop; +}; + +/** \class SkWBuffer + + Light weight class for writing data to a memory block. + The WBuffer is given the buffer to write into, with either a specified size + or no size, in which case no range checking is performed. An empty WBuffer + is legal, in which case no data is ever written, but the relative pos() + is updated. +*/ +class SkWBuffer : SkNoncopyable { +public: + SkWBuffer() : fData(0), fPos(0), fStop(0) {} + SkWBuffer(void* data) { reset(data); } + SkWBuffer(void* data, size_t size) { reset(data, size); } + + void reset(void* data) + { + fData = (char*)data; + fPos = (char*)data; + fStop = 0; // no bounds checking + } + void reset(void* data, size_t size) + { + SkASSERT(data != 0 || size == 0); + fData = (char*)data; + fPos = (char*)data; + fStop = (char*)data + size; + } + + void* data() const { return fData; } + size_t pos() const { return fPos - fData; } + size_t size() const { return fStop - fData; } + bool eof() const { return fPos >= fStop; } + void* skip(size_t size); // return start of skipped data + void write(const void* buffer, size_t size) { if (size) this->writeNoSizeCheck(buffer, size); } + size_t padToAlign4(); + + void writePtr(const void* x) { this->writeNoSizeCheck(&x, sizeof(x)); } + void writeScalar(SkScalar x) { this->writeNoSizeCheck(&x, 4); } + void write32(int32_t x) { this->writeNoSizeCheck(&x, 4); } + void write16(int16_t x) { this->writeNoSizeCheck(&x, 2); } + void write8(int8_t x) { this->writeNoSizeCheck(&x, 1); } + void writeBool(bool x) { this->write8(x); } + +protected: + void writeNoSizeCheck(const void* buffer, size_t size); + + char* fData; + char* fPos; + char* fStop; +}; + +#endif + diff --git a/skia/include/corecg/SkChunkAlloc.h b/skia/include/corecg/SkChunkAlloc.h new file mode 100644 index 0000000..aa4ebde --- /dev/null +++ b/skia/include/corecg/SkChunkAlloc.h @@ -0,0 +1,62 @@ +/* include/corecg/SkChunkAlloc.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkChunkAlloc_DEFINED +#define SkChunkAlloc_DEFINED + +#include "SkTypes.h" + +class SkChunkAlloc : SkNoncopyable { +public: + SkChunkAlloc(size_t minSize); + ~SkChunkAlloc(); + + /** Free up all allocated blocks. This invalidates all returned + pointers. + */ + void reset(); + + /** Reuse all allocated blocks. This invalidates all returned + pointers (like reset) but doesn't necessarily free up all + of the privately allocated blocks. This is more efficient + if you plan to reuse the allocator multiple times. + */ + void reuse(); + + enum AllocFailType { + kReturnNil_AllocFailType, + kThrow_AllocFailType + }; + + void* alloc(size_t bytes, AllocFailType); + void* allocThrow(size_t bytes) { + return this->alloc(bytes, kThrow_AllocFailType); + } + + size_t totalCapacity() const { return fTotalCapacity; } + +private: + struct Block; + Block* fBlock; + size_t fMinSize; + Block* fPool; + size_t fTotalCapacity; + + Block* newBlock(size_t bytes, AllocFailType ftype); +}; + +#endif diff --git a/skia/include/corecg/SkEndian.h b/skia/include/corecg/SkEndian.h new file mode 100644 index 0000000..0897914 --- /dev/null +++ b/skia/include/corecg/SkEndian.h @@ -0,0 +1,99 @@ +/* include/corecg/SkEndian.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkEndian_DEFINED +#define SkEndian_DEFINED + +#include "SkTypes.h" + +/** \file SkEndian.h + + Macros and helper functions for handling 16 and 32 bit values in + big and little endian formats. +*/ + +#if defined(SK_CPU_LENDIAN) && defined(SK_CPU_BENDIAN) + #error "can't have both LENDIAN and BENDIAN defined" +#endif + +#if !defined(SK_CPU_LENDIAN) && !defined(SK_CPU_BENDIAN) + #error "need either LENDIAN or BENDIAN defined" +#endif + +/** Swap the two bytes in the low 16bits of the parameters. + e.g. 0x1234 -> 0x3412 +*/ +inline uint16_t SkEndianSwap16(U16CPU value) +{ + SkASSERT(value == (uint16_t)value); + return (uint16_t)((value >> 8) | (value << 8)); +} + +/** Vector version of SkEndianSwap16(), which swaps the + low two bytes of each value in the array. +*/ +inline void SkEndianSwap16s(uint16_t array[], int count) +{ + SkASSERT(count == 0 || array != NULL); + + while (--count >= 0) + { + *array = SkEndianSwap16(*array); + array += 1; + } +} + +/** Reverse all 4 bytes in a 32bit value. + e.g. 0x12345678 -> 0x78563412 +*/ +inline uint32_t SkEndianSwap32(uint32_t value) +{ + return ((value & 0xFF) << 24) | + ((value & 0xFF00) << 8) | + ((value & 0xFF0000) >> 8) | + (value >> 24); +} + +/** Vector version of SkEndianSwap16(), which swaps the + bytes of each value in the array. +*/ +inline void SkEndianSwap32s(uint32_t array[], int count) +{ + SkASSERT(count == 0 || array != NULL); + + while (--count >= 0) + { + *array = SkEndianSwap32(*array); + array += 1; + } +} + +#ifdef SK_CPU_LENDIAN + #define SkEndian_SwapBE16(n) SkEndianSwap16(n) + #define SkEndian_SwapBE32(n) SkEndianSwap32(n) + #define SkEndian_SwapLE16(n) (n) + #define SkEndian_SwapLE32(n) (n) +#else // SK_CPU_BENDIAN + #define SkEndian_SwapBE16(n) (n) + #define SkEndian_SwapBE32(n) (n) + #define SkEndian_SwapLE16(n) SkEndianSwap16(n) + #define SkEndian_SwapLE32(n) SkEndianSwap32(n) +#endif + + +#endif + diff --git a/skia/include/corecg/SkFDot6.h b/skia/include/corecg/SkFDot6.h new file mode 100644 index 0000000..57d4516 --- /dev/null +++ b/skia/include/corecg/SkFDot6.h @@ -0,0 +1,68 @@ +/* include/corecg/SkFDot6.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkFDot6_DEFINED +#define SkFDot6_DEFINED + +#include "SkMath.h" + +typedef int32_t SkFDot6; + +#define SK_FDot61 (64) +#define SK_FDot6Half (32) + +#ifdef SK_DEBUG + inline SkFDot6 SkIntToFDot6(S16CPU x) + { + SkASSERT(SkToS16(x) == x); + return x << 6; + } +#else + #define SkIntToFDot6(x) ((x) << 6) +#endif + +#define SkFDot6Floor(x) ((x) >> 6) +#define SkFDot6Ceil(x) (((x) + 63) >> 6) +#define SkFDot6Round(x) (((x) + 32) >> 6) + +#define SkFixedToFDot6(x) ((x) >> 10) + +inline SkFixed SkFDot6ToFixed(SkFDot6 x) +{ + SkASSERT((x << 10 >> 10) == x); + + return x << 10; +} + +#ifdef SK_SCALAR_IS_FLOAT + #define SkScalarToFDot6(x) (SkFDot6)((x) * 64) +#else + #define SkScalarToFDot6(x) ((x) >> 10) +#endif + +inline SkFixed SkFDot6Div(SkFDot6 a, SkFDot6 b) +{ + SkASSERT(b != 0); + + if (a == (int16_t)a) + return (a << 16) / b; + else + return SkFixedDiv(a, b); +} + +#endif + diff --git a/skia/include/corecg/SkFixed.h b/skia/include/corecg/SkFixed.h new file mode 100644 index 0000000..e77d455 --- /dev/null +++ b/skia/include/corecg/SkFixed.h @@ -0,0 +1,254 @@ +/* include/corecg/SkFixed.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkFixed_DEFINED +#define SkFixed_DEFINED + +#include "SkTypes.h" + +/** \file SkFixed.h + + Types and macros for 16.16 fixed point +*/ + +/** 32 bit signed integer used to represent fractions values with 16 bits to the right of the decimal point +*/ +typedef int32_t SkFixed; +#define SK_Fixed1 (1 << 16) +#define SK_FixedHalf (1 << 15) +#define SK_FixedMax (0x7FFFFFFF) +#define SK_FixedMin (0x1) +#define SK_FixedNaN ((int) 0x80000000) +#define SK_FixedPI (0x3243F) +#define SK_FixedSqrt2 (92682) +#define SK_FixedTanPIOver8 (0x6A0A) +#define SK_FixedRoot2Over2 (0xB505) + +#ifdef SK_CAN_USE_FLOAT + #define SkFixedToFloat(x) ((x) * 1.5258789e-5f) + #define SkFloatToFixed(x) ((SkFixed)((x) * SK_Fixed1)) + + #define SkFixedToDouble(x) ((x) * 1.5258789e-5) + #define SkDoubleToFixed(x) ((SkFixed)((x) * SK_Fixed1)) +#endif + +/** 32 bit signed integer used to represent fractions values with 30 bits to the right of the decimal point +*/ +typedef int32_t SkFract; +#define SK_Fract1 (1 << 30) +#define Sk_FracHalf (1 << 29) +#define SK_FractPIOver180 (0x11DF46A) + +#ifdef SK_CAN_USE_FLOAT + #define SkFractToFloat(x) ((float)(x) * 0.00000000093132257f) + #define SkFloatToFract(x) ((SkFract)((x) * SK_Fract1)) +#endif + +/** Converts an integer to a SkFixed, asserting that the result does not overflow + a 32 bit signed integer +*/ +#ifdef SK_DEBUG + inline SkFixed SkIntToFixed(int n) + { + SkASSERT(n >= -32768 && n <= 32767); + return n << 16; + } +#else + // force the cast to SkFixed to ensure that the answer is signed (like the debug version) + #define SkIntToFixed(n) (SkFixed)((n) << 16) +#endif + +/** Converts a SkFixed to a SkFract, asserting that the result does not overflow + a 32 bit signed integer +*/ +#ifdef SK_DEBUG + inline SkFract SkFixedToFract(SkFixed x) + { + SkASSERT(x >= (-2 << 16) && x <= (2 << 16) - 1); + return x << 14; + } +#else + #define SkFixedToFract(x) ((x) << 14) +#endif + +/** Returns the signed fraction of a SkFixed +*/ +inline SkFixed SkFixedFraction(SkFixed x) +{ + SkFixed mask = x >> 31 << 16; + return (x & 0xFFFF) | mask; +} + +/** Converts a SkFract to a SkFixed +*/ +#define SkFractToFixed(x) ((x) >> 14) +/** Round a SkFixed to an integer +*/ +#define SkFixedRound(x) (((x) + SK_FixedHalf) >> 16) +#define SkFixedCeil(x) (((x) + SK_Fixed1 - 1) >> 16) +#define SkFixedFloor(x) ((x) >> 16) +#define SkFixedAbs(x) SkAbs32(x) +#define SkFixedAve(a, b) (((a) + (b)) >> 1) + +SkFixed SkFixedMul_portable(SkFixed, SkFixed); +SkFract SkFractMul_portable(SkFract, SkFract); +inline SkFixed SkFixedSquare_portable(SkFixed value) +{ + uint32_t a = SkAbs32(value); + uint32_t ah = a >> 16; + uint32_t al = a & 0xFFFF; + return ah * a + al * ah + (al * al >> 16); +} + +#define SkFixedDiv(numer, denom) SkDivBits(numer, denom, 16) +SkFixed SkFixedDivInt(int32_t numer, int32_t denom); +SkFixed SkFixedMod(SkFixed numer, SkFixed denom); +#define SkFixedInvert(n) SkDivBits(SK_Fixed1, n, 16) +SkFixed SkFixedFastInvert(SkFixed n); +#define SkFixedSqrt(n) SkSqrtBits(n, 23) +SkFixed SkFixedMean(SkFixed a, SkFixed b); //*< returns sqrt(x*y) +int SkFixedMulCommon(SkFixed, int , int bias); // internal used by SkFixedMulFloor, SkFixedMulCeil, SkFixedMulRound + +#define SkFractDiv(numer, denom) SkDivBits(numer, denom, 30) +#define SkFractSqrt(n) SkSqrtBits(n, 30) + +SkFixed SkFixedSinCos(SkFixed radians, SkFixed* cosValueOrNull); +#define SkFixedSin(radians) SkFixedSinCos(radians, NULL) +inline SkFixed SkFixedCos(SkFixed radians) +{ + SkFixed cosValue; + (void)SkFixedSinCos(radians, &cosValue); + return cosValue; +} +SkFixed SkFixedTan(SkFixed radians); +SkFixed SkFixedASin(SkFixed); +SkFixed SkFixedACos(SkFixed); +SkFixed SkFixedATan2(SkFixed y, SkFixed x); +SkFixed SkFixedExp(SkFixed); +SkFixed SkFixedLog(SkFixed); + +#define SK_FixedNearlyZero (SK_Fixed1 >> 12) + +inline bool SkFixedNearlyZero(SkFixed x, SkFixed tolerance = SK_FixedNearlyZero) +{ + SkASSERT(tolerance > 0); + return SkAbs32(x) < tolerance; +} + +////////////////////////////////////////////////////////////////////////////////////////////////////// +// Now look for ASM overrides for our portable versions (should consider putting this in its own file) + +#ifdef SkLONGLONG + inline SkFixed SkFixedMul_longlong(SkFixed a, SkFixed b) + { + return (SkFixed)((SkLONGLONG)a * b >> 16); + } + inline SkFract SkFractMul_longlong(SkFract a, SkFract b) + { + return (SkFixed)((SkLONGLONG)a * b >> 30); + } + inline SkFixed SkFixedSquare_longlong(SkFixed value) + { + return (SkFixed)((SkLONGLONG)value * value >> 16); + } + #define SkFixedMul(a,b) SkFixedMul_longlong(a,b) + #define SkFractMul(a,b) SkFractMul_longlong(a,b) + #define SkFixedSquare(a) SkFixedSquare_longlong(a) +#endif + +#if defined(__arm__) && !defined(__thumb__) + /* This guy does not handle NaN or other obscurities, but is faster than + than (int)(x*65536) when we only have software floats + */ + inline SkFixed SkFloatToFixed_arm(float x) + { + register int32_t y, z; + asm("movs %1, %3, lsl #1 \n" + "mov %2, #0x8E \n" + "sub %1, %2, %1, lsr #24 \n" + "mov %2, %3, lsl #8 \n" + "orr %2, %2, #0x80000000 \n" + "mov %1, %2, lsr %1 \n" + "rsbcs %1, %1, #0 \n" + : "=r"(x), "=&r"(y), "=&r"(z) + : "r"(x) + : "cc" + ); + return y; + } + inline SkFixed SkFixedMul_arm(SkFixed x, SkFixed y) + { + register int32_t t; + asm("smull %0, %2, %1, %3 \n" + "mov %0, %0, lsr #16 \n" + "orr %0, %0, %2, lsl #16 \n" + : "=r"(x), "=&r"(y), "=r"(t) + : "r"(x), "1"(y) + : + ); + return x; + } + inline SkFixed SkFixedMulAdd_arm(SkFixed x, SkFixed y, SkFixed a) + { + register int32_t t; + asm("smull %0, %3, %1, %4 \n" + "add %0, %2, %0, lsr #16 \n" + "add %0, %0, %3, lsl #16 \n" + : "=r"(x), "=&r"(y), "=&r"(a), "=r"(t) + : "%r"(x), "1"(y), "2"(a) + : + ); + return x; + } + inline SkFixed SkFractMul_arm(SkFixed x, SkFixed y) + { + register int32_t t; + asm("smull %0, %2, %1, %3 \n" + "mov %0, %0, lsr #30 \n" + "orr %0, %0, %2, lsl #2 \n" + : "=r"(x), "=&r"(y), "=r"(t) + : "r"(x), "1"(y) + : + ); + return x; + } + #undef SkFixedMul + #undef SkFractMul + #define SkFixedMul(x, y) SkFixedMul_arm(x, y) + #define SkFractMul(x, y) SkFractMul_arm(x, y) + #define SkFixedMulAdd(x, y, a) SkFixedMulAdd_arm(x, y, a) + + #undef SkFloatToFixed + #define SkFloatToFixed(x) SkFloatToFixed_arm(x) +#endif + +/////////////////////// Now define our macros to the portable versions if they weren't overridden + +#ifndef SkFixedSquare + #define SkFixedSquare(x) SkFixedSquare_portable(x) +#endif +#ifndef SkFixedMul + #define SkFixedMul(x, y) SkFixedMul_portable(x, y) +#endif +#ifndef SkFractMul + #define SkFractMul(x, y) SkFractMul_portable(x, y) +#endif +#ifndef SkFixedMulAdd + #define SkFixedMulAdd(x, y, a) (SkFixedMul(x, y) + (a)) +#endif + +#endif diff --git a/skia/include/corecg/SkFloatingPoint.h b/skia/include/corecg/SkFloatingPoint.h new file mode 100644 index 0000000..b7c0435 --- /dev/null +++ b/skia/include/corecg/SkFloatingPoint.h @@ -0,0 +1,65 @@ +/* include/corecg/SkFloatingPoint.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkFloatingPoint_DEFINED +#define SkFloatingPoint_DEFINED + +#include "SkTypes.h" + +#ifdef SK_CAN_USE_FLOAT + +#include <math.h> +#include <float.h> + +#ifdef SK_BUILD_FOR_WINCE + #define sk_float_sqrt(x) (float)::sqrt(x) + #define sk_float_sin(x) (float)::sin(x) + #define sk_float_cos(x) (float)::cos(x) + #define sk_float_tan(x) (float)::tan(x) + #define sk_float_acos(x) (float)::acos(x) + #define sk_float_asin(x) (float)::asin(x) + #define sk_float_atan2(y,x) (float)::atan2(y,x) + #define sk_float_abs(x) (float)::fabs(x) + #define sk_float_mod(x,y) (float)::fmod(x,y) + #define sk_float_exp(x) (float)::exp(x) + #define sk_float_log(x) (float)::log(x) + #define sk_float_floor(x) (float)::floor(x) + #define sk_float_ceil(x) (float)::ceil(x) +#else + #define sk_float_sqrt(x) sqrtf(x) + #define sk_float_sin(x) sinf(x) + #define sk_float_cos(x) cosf(x) + #define sk_float_tan(x) tanf(x) + #define sk_float_floor(x) floorf(x) + #define sk_float_ceil(x) ceilf(x) +#ifdef SK_BUILD_FOR_MAC + #define sk_float_acos(x) acos(x) + #define sk_float_asin(x) asin(x) +#else + #define sk_float_acos(x) acosf(x) + #define sk_float_asin(x) asinf(x) +#endif + #define sk_float_atan2(y,x) atan2f(y,x) + #define sk_float_abs(x) fabsf(x) + #define sk_float_mod(x,y) fmodf(x,y) + #define sk_float_exp(x) expf(x) + #define sk_float_log(x) logf(x) + #define sk_float_isNaN(x) _isnan(x) +#endif + +#endif +#endif diff --git a/skia/include/corecg/SkInterpolator.h b/skia/include/corecg/SkInterpolator.h new file mode 100644 index 0000000..d98196d --- /dev/null +++ b/skia/include/corecg/SkInterpolator.h @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2006-2008 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SkInterpolator_DEFINED +#define SkInterpolator_DEFINED + +#include "SkScalar.h" + +class SkInterpolatorBase : SkNoncopyable { +public: + enum Result { + kNormal_Result, + kFreezeStart_Result, + kFreezeEnd_Result + }; +protected: + SkInterpolatorBase(); + ~SkInterpolatorBase(); +public: + void reset(int elemCount, int frameCount); + + /** Return the start and end time for this interpolator. + If there are no key frames, return false. + @param startTime If not null, returns the time (in milliseconds) of the + first keyframe. If there are no keyframes, this param + is ignored (left unchanged). + @param endTime If not null, returns the time (in milliseconds) of the + last keyframe. If there are no keyframes, this parameter + is ignored (left unchanged). + @return True if there are key frames, or false if there are none. + */ + bool getDuration(SkMSec* startTime, SkMSec* endTime) const; + + + /** Set the whether the repeat is mirrored. + @param mirror If true, the odd repeats interpolate from the last key + frame and the first. + */ + void setMirror(bool mirror) { + fFlags = SkToU8((fFlags & ~kMirror) | (int)mirror); + } + + /** Set the repeat count. The repeat count may be fractional. + @param repeatCount Multiplies the total time by this scalar. + */ + void setRepeatCount(SkScalar repeatCount) { fRepeat = repeatCount; } + + /** Set the whether the repeat is mirrored. + @param reset If true, the odd repeats interpolate from the last key + frame and the first. + */ + void setReset(bool reset) { + fFlags = SkToU8((fFlags & ~kReset) | (int)reset); + } + + Result timeToT(SkMSec time, SkScalar* T, int* index, SkBool* exact) const; + +protected: + enum Flags { + kMirror = 1, + kReset = 2, + kHasBlend = 4 + }; + static SkScalar ComputeRelativeT(SkMSec time, SkMSec prevTime, + SkMSec nextTime, const SkScalar blend[4] = NULL); + int16_t fFrameCount; + uint8_t fElemCount; + uint8_t fFlags; + SkScalar fRepeat; + struct SkTimeCode { + SkMSec fTime; + SkScalar fBlend[4]; + }; + SkTimeCode* fTimes; // pointer into fStorage + void* fStorage; +#ifdef SK_DEBUG + SkTimeCode(* fTimesArray)[10]; +#endif +}; + +class SkInterpolator : public SkInterpolatorBase { +public: + SkInterpolator(); + SkInterpolator(int elemCount, int frameCount); + void reset(int elemCount, int frameCount); + + /** Add or replace a key frame, copying the values[] data into the + interpolator. + @param index The index of this frame (frames must be ordered by time) + @param time The millisecond time for this frame + @param values The array of values [elemCount] for this frame. The data + is copied into the interpolator. + @param blend A positive scalar specifying how to blend between this + and the next key frame. [0...1) is a cubic lag/log/lag + blend (slow to change at the beginning and end) + 1 is a linear blend (default) + */ + bool setKeyFrame(int index, SkMSec time, const SkScalar values[], + const SkScalar blend[4] = NULL); + + /** Return the computed values given the specified time. Return whether + those values are the result of pinning to either the first + (kFreezeStart) or last (kFreezeEnd), or from interpolated the two + nearest key values (kNormal). + @param time The time to sample (in milliseconds) + @param (may be null) where to write the computed values. + */ + Result timeToValues(SkMSec time, SkScalar values[] = NULL) const; + + SkDEBUGCODE(static void UnitTest();) +private: + SkScalar* fValues; // pointer into fStorage +#ifdef SK_DEBUG + SkScalar(* fScalarsArray)[10]; +#endif + typedef SkInterpolatorBase INHERITED; +}; + +/** Given all the parameters are [0...1], apply the cubic specified by (0,0) + (bx,by) (cx,cy) (1,1) to value, returning the answer, also [0...1]. +*/ +SkScalar SkUnitCubicInterp(SkScalar value, SkScalar bx, SkScalar by, + SkScalar cx, SkScalar cy); + +#endif + diff --git a/skia/include/corecg/SkMath.h b/skia/include/corecg/SkMath.h new file mode 100644 index 0000000..7519dee --- /dev/null +++ b/skia/include/corecg/SkMath.h @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2006-2008 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SkMath_DEFINED +#define SkMath_DEFINED + +#include "SkTypes.h" + +//! Returns the number of leading zero bits (0...32) +int SkCLZ_portable(uint32_t); + +/** Computes the 64bit product of a * b, and then shifts the answer down by + shift bits, returning the low 32bits. shift must be [0..63] + e.g. to perform a fixedmul, call SkMulShift(a, b, 16) +*/ +int32_t SkMulShift(int32_t a, int32_t b, unsigned shift); + +/** Computes numer1 * numer2 / denom in full 64 intermediate precision. + It is an error for denom to be 0. There is no special handling if + the result overflows 32bits. +*/ +int32_t SkMulDiv(int32_t numer1, int32_t numer2, int32_t denom); + +/** Computes (numer1 << shift) / denom in full 64 intermediate precision. + It is an error for denom to be 0. There is no special handling if + the result overflows 32bits. +*/ +int32_t SkDivBits(int32_t numer, int32_t denom, int shift); + +/** Return the integer square root of value, with a bias of bitBias +*/ +int32_t SkSqrtBits(int32_t value, int bitBias); + +/** Return the integer square root of n, treated as a SkFixed (16.16) +*/ +#define SkSqrt32(n) SkSqrtBits(n, 15) + +/** Return the integer cube root of value, with a bias of bitBias + */ +int32_t SkCubeRootBits(int32_t value, int bitBias); + +/** Returns -1 if n < 0, else returns 0 +*/ +#define SkExtractSign(n) ((int32_t)(n) >> 31) + +/** If sign == -1, returns -n, else sign must be 0, and returns n. + Typically used in conjunction with SkExtractSign(). +*/ +static inline int32_t SkApplySign(int32_t n, int32_t sign) { + SkASSERT(sign == 0 || sign == -1); + return (n ^ sign) - sign; +} + +/** Returns (value < 0 ? 0 : value) efficiently (i.e. no compares or branches) +*/ +static inline int SkClampPos(int value) { + return value & ~(value >> 31); +} + +/** Given an integer and a positive (max) integer, return the value + pinned against 0 and max, inclusive. + Note: only works as long as max - value doesn't wrap around + @param value The value we want returned pinned between [0...max] + @param max The positive max value + @return 0 if value < 0, max if value > max, else value +*/ +static inline int SkClampMax(int value, int max) { + // ensure that max is positive + SkASSERT(max >= 0); + // ensure that if value is negative, max - value doesn't wrap around + SkASSERT(value >= 0 || max - value > 0); + +#ifdef SK_CPU_HAS_CONDITIONAL_INSTR + if (value < 0) { + value = 0; + } + if (value > max) { + value = max; + } + return value; +#else + + int diff = max - value; + // clear diff if diff is positive + diff &= diff >> 31; + + // clear the result if value < 0 + return (value + diff) & ~(value >> 31); +#endif +} + +/** Given a positive value and a positive max, return the value + pinned against max. + Note: only works as long as max - value doesn't wrap around + @return max if value >= max, else value +*/ +static inline unsigned SkClampUMax(unsigned value, unsigned max) { +#ifdef SK_CPU_HAS_CONDITIONAL_INSTR + if (value > max) { + value = max; + } + return value; +#else + int diff = max - value; + // clear diff if diff is positive + diff &= diff >> 31; + + return value + diff; +#endif +} + +/////////////////////////////////////////////////////////////////////////////// + +#if defined(__arm__) && !defined(__thumb__) + #define SkCLZ(x) __builtin_clz(x) +#endif + +#ifndef SkCLZ + #define SkCLZ(x) SkCLZ_portable(x) +#endif + +/////////////////////////////////////////////////////////////////////////////// + +/** Returns the smallest power-of-2 that is >= the specified value. If value + is already a power of 2, then it is returned unchanged. It is undefined + if value is <= 0. +*/ +static inline int SkNextPow2(int value) { + SkASSERT(value > 0); + return 1 << (32 - SkCLZ(value - 1)); +} + +/** Returns the log2 of the specified value, were that value to be rounded up + to the next power of 2. It is undefined to pass 0. Examples: + SkNextLog2(1) -> 0 + SkNextLog2(2) -> 1 + SkNextLog2(3) -> 2 + SkNextLog2(4) -> 2 + SkNextLog2(5) -> 3 +*/ +static inline int SkNextLog2(uint32_t value) { + SkASSERT(value != 0); + return 32 - SkCLZ(value - 1); +} + +/////////////////////////////////////////////////////////////////////////////// + +/** SkMulS16(a, b) multiplies a * b, but requires that a and b are both int16_t. + With this requirement, we can generate faster instructions on some + architectures. +*/ +#if defined(__arm__) && !defined(__thumb__) + static inline int32_t SkMulS16(S16CPU x, S16CPU y) { + SkASSERT((int16_t)x == x); + SkASSERT((int16_t)y == y); + int32_t product; + asm("smulbb %0, %1, %2 \n" + : "=r"(product) + : "r"(x), "r"(y) + : + ); + return product; + } +#else + #ifdef SK_DEBUG + static inline int32_t SkMulS16(S16CPU x, S16CPU y) { + SkASSERT((int16_t)x == x); + SkASSERT((int16_t)y == y); + return x * y; + } + #else + #define SkMulS16(x, y) ((x) * (y)) + #endif +#endif + +/** Return a*b/255, truncating away any fractional bits. Only valid if both + a and b are 0..255 +*/ +static inline U8CPU SkMulDiv255Trunc(U8CPU a, U8CPU b) { + SkASSERT((uint8_t)a == a); + SkASSERT((uint8_t)b == b); + unsigned prod = SkMulS16(a, b) + 1; + return (prod + (prod >> 8)) >> 8; +} + +/** Return a*b/255, rounding any fractional bits. Only valid if both + a and b are 0..255 + */ +static inline U8CPU SkMulDiv255Round(U8CPU a, U8CPU b) { + SkASSERT((uint8_t)a == a); + SkASSERT((uint8_t)b == b); + unsigned prod = SkMulS16(a, b) + 128; + return (prod + (prod >> 8)) >> 8; +} + +/** Return a*b/((1 << shift) - 1), rounding any fractional bits. + Only valid if a and b are unsigned and <= 32767 and shift is > 0 and <= 8 +*/ +static unsigned SkMul16ShiftRound(unsigned a, unsigned b, int shift) { + SkASSERT(a <= 32767); + SkASSERT(b <= 32767); + SkASSERT(shift > 0 && shift <= 8); + unsigned prod = SkMulS16(a, b) + (1 << (shift - 1)); + return (prod + (prod >> shift)) >> shift; +} + +/////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_DEBUG + class SkMath { + public: + static void UnitTest(); + }; +#endif + +#endif + diff --git a/skia/include/corecg/SkMatrix.h b/skia/include/corecg/SkMatrix.h new file mode 100644 index 0000000..60fdd42 --- /dev/null +++ b/skia/include/corecg/SkMatrix.h @@ -0,0 +1,476 @@ +/* + * Copyright (C) 2006-2008 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SkMatrix_DEFINED +#define SkMatrix_DEFINED + +#include "SkRect.h" + +/** \class SkMatrix + + The SkMatrix class holds a 3x3 matrix for transforming coordinates. + SkMatrix does not have a constructor, so it must be explicitly initialized + using either reset() - to construct an identity matrix, or one of the set + functions (e.g. setTranslate, setRotate, etc.). +*/ +class SkMatrix { +public: + /** Enum of bit fields for the mask return by getType(). + Use this to identify the complexity of the matrix. + */ + enum TypeMask { + kIdentity_Mask = 0, + kTranslate_Mask = 0x01, //!< set if the matrix has translation + kScale_Mask = 0x02, //!< set if the matrix has X or Y scale + kAffine_Mask = 0x04, //!< set if the matrix skews or rotates + kPerspective_Mask = 0x08 //!< set if the matrix is in perspective + }; + + /** Returns a mask bitfield describing the types of transformations + that the matrix will perform. This information is used by routines + like mapPoints, to optimize its inner loops to only perform as much + arithmetic as is necessary. + */ + TypeMask getType() const { + if (fTypeMask & kUnknown_Mask) { + fTypeMask = this->computeTypeMask(); + } + // only return the public masks + return (TypeMask)(fTypeMask & 0xF); + } + + /** Returns true if the matrix is identity. + */ + bool isIdentity() const { + return this->getType() == 0; + } + + /** Returns true if will map a rectangle to another rectangle. This can be + true if the matrix is identity, scale-only, or rotates a multiple of + 90 degrees. + */ + bool rectStaysRect() const { + if (fTypeMask & kUnknown_Mask) { + fTypeMask = this->computeTypeMask(); + } + return (fTypeMask & kRectStaysRect_Mask) != 0; + } + + enum { + kMScaleX, + kMSkewX, + kMTransX, + kMSkewY, + kMScaleY, + kMTransY, + kMPersp0, + kMPersp1, + kMPersp2 + }; + + SkScalar operator[](int index) const { + SkASSERT((unsigned)index < 9); + return fMat[index]; + } + + SkScalar get(int index) const { + SkASSERT((unsigned)index < 9); + return fMat[index]; + } + + SkScalar getScaleX() const { return fMat[kMScaleX]; } + SkScalar getScaleY() const { return fMat[kMScaleY]; } + SkScalar getSkewY() const { return fMat[kMSkewY]; } + SkScalar getSkewX() const { return fMat[kMSkewX]; } + SkScalar getTranslateX() const { return fMat[kMTransX]; } + SkScalar getTranslateY() const { return fMat[kMTransY]; } + SkScalar getPerspX() const { return fMat[kMPersp0]; } + SkScalar getPerspY() const { return fMat[kMPersp1]; } + + void set(int index, SkScalar value) { + SkASSERT((unsigned)index < 9); + fMat[index] = value; + this->setTypeMask(kUnknown_Mask); + } + + void setScaleX(SkScalar v) { this->set(kMScaleX, v); } + void setScaleY(SkScalar v) { this->set(kMScaleY, v); } + void setSkewY(SkScalar v) { this->set(kMSkewY, v); } + void setSkewX(SkScalar v) { this->set(kMSkewX, v); } + void setTranslateX(SkScalar v) { this->set(kMTransX, v); } + void setTranslateY(SkScalar v) { this->set(kMTransY, v); } + void setPerspX(SkScalar v) { this->set(kMPersp0, v); } + void setPerspY(SkScalar v) { this->set(kMPersp1, v); } + + /** Set the matrix to identity + */ + void reset(); + + /** Set the matrix to translate by (dx, dy). + */ + void setTranslate(SkScalar dx, SkScalar dy); + /** Set the matrix to scale by sx and sy, with a pivot point at (px, py). + The pivot point is the coordinate that should remain unchanged by the + specified transformation. + */ + void setScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py); + /** Set the matrix to scale by sx and sy. + */ + void setScale(SkScalar sx, SkScalar sy); + /** Set the matrix to rotate by the specified number of degrees, with a + pivot point at (px, py). The pivot point is the coordinate that should + remain unchanged by the specified transformation. + */ + void setRotate(SkScalar degrees, SkScalar px, SkScalar py); + /** Set the matrix to rotate about (0,0) by the specified number of degrees. + */ + void setRotate(SkScalar degrees); + /** Set the matrix to rotate by the specified sine and cosine values, with + a pivot point at (px, py). The pivot point is the coordinate that + should remain unchanged by the specified transformation. + */ + void setSinCos(SkScalar sinValue, SkScalar cosValue, + SkScalar px, SkScalar py); + /** Set the matrix to rotate by the specified sine and cosine values. + */ + void setSinCos(SkScalar sinValue, SkScalar cosValue); + /** Set the matrix to skew by sx and sy, with a pivot point at (px, py). + The pivot point is the coordinate that should remain unchanged by the + specified transformation. + */ + void setSkew(SkScalar kx, SkScalar ky, SkScalar px, SkScalar py); + /** Set the matrix to skew by sx and sy. + */ + void setSkew(SkScalar kx, SkScalar ky); + /** Set the matrix to the concatenation of the two specified matrices, + returning true if the the result can be represented. Either of the + two matrices may also be the target matrix. *this = a * b; + */ + bool setConcat(const SkMatrix& a, const SkMatrix& b); + + /** Preconcats the matrix with the specified translation. + M' = M * T(dx, dy) + */ + bool preTranslate(SkScalar dx, SkScalar dy); + /** Preconcats the matrix with the specified scale. + M' = M * S(sx, sy, px, py) + */ + bool preScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py); + /** Preconcats the matrix with the specified scale. + M' = M * S(sx, sy) + */ + bool preScale(SkScalar sx, SkScalar sy); + /** Preconcats the matrix with the specified rotation. + M' = M * R(degrees, px, py) + */ + bool preRotate(SkScalar degrees, SkScalar px, SkScalar py); + /** Preconcats the matrix with the specified rotation. + M' = M * R(degrees) + */ + bool preRotate(SkScalar degrees); + /** Preconcats the matrix with the specified skew. + M' = M * K(kx, ky, px, py) + */ + bool preSkew(SkScalar kx, SkScalar ky, SkScalar px, SkScalar py); + /** Preconcats the matrix with the specified skew. + M' = M * K(kx, ky) + */ + bool preSkew(SkScalar kx, SkScalar ky); + /** Preconcats the matrix with the specified matrix. + M' = M * other + */ + bool preConcat(const SkMatrix& other); + + /** Postconcats the matrix with the specified translation. + M' = T(dx, dy) * M + */ + bool postTranslate(SkScalar dx, SkScalar dy); + /** Postconcats the matrix with the specified scale. + M' = S(sx, sy, px, py) * M + */ + bool postScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py); + /** Postconcats the matrix with the specified scale. + M' = S(sx, sy) * M + */ + bool postScale(SkScalar sx, SkScalar sy); + /** Postconcats the matrix by dividing it by the specified integers. + M' = S(1/divx, 1/divy, 0, 0) * M + */ + bool postIDiv(int divx, int divy); + /** Postconcats the matrix with the specified rotation. + M' = R(degrees, px, py) * M + */ + bool postRotate(SkScalar degrees, SkScalar px, SkScalar py); + /** Postconcats the matrix with the specified rotation. + M' = R(degrees) * M + */ + bool postRotate(SkScalar degrees); + /** Postconcats the matrix with the specified skew. + M' = K(kx, ky, px, py) * M + */ + bool postSkew(SkScalar kx, SkScalar ky, SkScalar px, SkScalar py); + /** Postconcats the matrix with the specified skew. + M' = K(kx, ky) * M + */ + bool postSkew(SkScalar kx, SkScalar ky); + /** Postconcats the matrix with the specified matrix. + M' = other * M + */ + bool postConcat(const SkMatrix& other); + + enum ScaleToFit { + /** + * Scale in X and Y independently, so that src matches dst exactly. + * This may change the aspect ratio of the src. + */ + kFill_ScaleToFit, + /** + * Compute a scale that will maintain the original src aspect ratio, + * but will also ensure that src fits entirely inside dst. At least one + * axis (X or Y) will fit exactly. kStart aligns the result to the + * left and top edges of dst. + */ + kStart_ScaleToFit, + /** + * Compute a scale that will maintain the original src aspect ratio, + * but will also ensure that src fits entirely inside dst. At least one + * axis (X or Y) will fit exactly. The result is centered inside dst. + */ + kCenter_ScaleToFit, + /** + * Compute a scale that will maintain the original src aspect ratio, + * but will also ensure that src fits entirely inside dst. At least one + * axis (X or Y) will fit exactly. kEnd aligns the result to the + * right and bottom edges of dst. + */ + kEnd_ScaleToFit + }; + + /** Set the matrix to the scale and translate values that map the source + rectangle to the destination rectangle, returning true if the the result + can be represented. + @param src the source rectangle to map from. + @param dst the destination rectangle to map to. + @param stf the ScaleToFit option + @return true if the matrix can be represented by the rectangle mapping. + */ + bool setRectToRect(const SkRect& src, const SkRect& dst, ScaleToFit stf); + + /** Set the matrix such that the specified src points would map to the + specified dst points. count must be within [0..4]. + @param src The array of src points + @param dst The array of dst points + @param count The number of points to use for the transformation + @return true if the matrix was set to the specified transformation + */ + bool setPolyToPoly(const SkPoint src[], const SkPoint dst[], int count); + + /** If this matrix can be inverted, return true and if inverse is not null, + set inverse to be the inverse of this matrix. If this matrix cannot be + inverted, ignore inverse and return false + */ + bool invert(SkMatrix* inverse) const; + + /** Apply this matrix to the array of points specified by src, and write + the transformed points into the array of points specified by dst. + dst[] = M * src[] + @param dst Where the transformed coordinates are written. It must + contain at least count entries + @param src The original coordinates that are to be transformed. It + must contain at least count entries + @param count The number of points in src to read, and then transform + into dst. + */ + void mapPoints(SkPoint dst[], const SkPoint src[], int count) const; + + /** Apply this matrix to the array of points, overwriting it with the + transformed values. + dst[] = M * pts[] + @param pts The points to be transformed. It must contain at least + count entries + @param count The number of points in pts. + */ + void mapPoints(SkPoint pts[], int count) const { + this->mapPoints(pts, pts, count); + } + + void mapXY(SkScalar x, SkScalar y, SkPoint* result) const { + SkASSERT(result); + this->getMapXYProc()(*this, x, y, result); + } + + /** Apply this matrix to the array of vectors specified by src, and write + the transformed vectors into the array of vectors specified by dst. + This is similar to mapPoints, but ignores any translation in the matrix. + @param dst Where the transformed coordinates are written. It must + contain at least count entries + @param src The original coordinates that are to be transformed. It + must contain at least count entries + @param count The number of vectors in src to read, and then transform + into dst. + */ + void mapVectors(SkVector dst[], const SkVector src[], int count) const; + + /** Apply this matrix to the array of vectors specified by src, and write + the transformed vectors into the array of vectors specified by dst. + This is similar to mapPoints, but ignores any translation in the matrix. + @param vecs The vectors to be transformed. It must contain at least + count entries + @param count The number of vectors in vecs. + */ + void mapVectors(SkVector vecs[], int count) const { + this->mapVectors(vecs, vecs, count); + } + + /** Apply this matrix to the src rectangle, and write the transformed + rectangle into dst. This is accomplished by transforming the 4 corners + of src, and then setting dst to the bounds of those points. + @param dst Where the transformed rectangle is written. + @param src The original rectangle to be transformed. + @return the result of calling rectStaysRect() + */ + bool mapRect(SkRect* dst, const SkRect& src) const; + + /** Apply this matrix to the rectangle, and write the transformed rectangle + back into it. This is accomplished by transforming the 4 corners of + rect, and then setting it to the bounds of those points + @param rect The rectangle to transform. + @return the result of calling rectStaysRect() + */ + bool mapRect(SkRect* rect) const { + return this->mapRect(rect, *rect); + } + + /** Return the mean radius of a circle after it has been mapped by + this matrix. NOTE: in perspective this value assumes the circle + has its center at the origin. + */ + SkScalar mapRadius(SkScalar radius) const; + + typedef void (*MapXYProc)(const SkMatrix& mat, SkScalar x, SkScalar y, + SkPoint* result); + + static MapXYProc GetMapXYProc(TypeMask mask) { + SkASSERT((mask & ~kAllMasks) == 0); + return gMapXYProcs[mask & kAllMasks]; + } + + MapXYProc getMapXYProc() const { + return GetMapXYProc(this->getType()); + } + + typedef void (*MapPtsProc)(const SkMatrix& mat, SkPoint dst[], + const SkPoint src[], int count); + + static MapPtsProc GetMapPtsProc(TypeMask mask) { + SkASSERT((mask & ~kAllMasks) == 0); + return gMapPtsProcs[mask & kAllMasks]; + } + + MapPtsProc getMapPtsProc() const { + return GetMapPtsProc(this->getType()); + } + + /** If the matrix can be stepped in X (not complex perspective) + then return true and if step[XY] is not null, return the step[XY] value. + If it cannot, return false and ignore step. + */ + bool fixedStepInX(SkScalar y, SkFixed* stepX, SkFixed* stepY) const; + + friend bool operator==(const SkMatrix& a, const SkMatrix& b) { + return memcmp(a.fMat, b.fMat, sizeof(a.fMat)) == 0; + } + + friend bool operator!=(const SkMatrix& a, const SkMatrix& b) { + return memcmp(a.fMat, b.fMat, sizeof(a.fMat)) != 0; + } + + void dump() const; + +#ifdef SK_DEBUG + /** @cond UNIT_TEST */ + + static void UnitTest(); + /** @endcond */ +#endif + +private: + enum { + /** Set if the matrix will map a rectangle to another rectangle. This + can be true if the matrix is scale-only, or rotates a multiple of + 90 degrees. This bit is not set if the matrix is identity. + + This bit will be set on identity matrices + */ + kRectStaysRect_Mask = 0x10, + + kUnknown_Mask = 0x80, + + kAllMasks = kTranslate_Mask | + kScale_Mask | + kAffine_Mask | + kPerspective_Mask | + kRectStaysRect_Mask + }; + + SkScalar fMat[9]; + mutable uint8_t fTypeMask; + + uint8_t computeTypeMask() const; + + void setTypeMask(int mask) { + // allow kUnknown or a valid mask + SkASSERT(kUnknown_Mask == mask || (mask & kAllMasks) == mask); + fTypeMask = SkToU8(mask); + } + + void clearTypeMask(int mask) { + // only allow a valid mask + SkASSERT((mask & kAllMasks) == mask); + fTypeMask &= ~mask; + } + + static bool Poly2Proc(const SkPoint[], SkMatrix*, const SkPoint& scale); + static bool Poly3Proc(const SkPoint[], SkMatrix*, const SkPoint& scale); + static bool Poly4Proc(const SkPoint[], SkMatrix*, const SkPoint& scale); + + static void Identity_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*); + static void Trans_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*); + static void Scale_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*); + static void ScaleTrans_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*); + static void Rot_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*); + static void RotTrans_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*); + static void Persp_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*); + + static const MapXYProc gMapXYProcs[]; + + static void Identity_pts(const SkMatrix&, SkPoint[], const SkPoint[], int); + static void Trans_pts(const SkMatrix&, SkPoint dst[], const SkPoint[], int); + static void Scale_pts(const SkMatrix&, SkPoint dst[], const SkPoint[], int); + static void ScaleTrans_pts(const SkMatrix&, SkPoint dst[], const SkPoint[], + int count); + static void Rot_pts(const SkMatrix&, SkPoint dst[], const SkPoint[], int); + static void RotTrans_pts(const SkMatrix&, SkPoint dst[], const SkPoint[], + int count); + static void Persp_pts(const SkMatrix&, SkPoint dst[], const SkPoint[], int); + + static const MapPtsProc gMapPtsProcs[]; + + friend class SkPerspIter; +}; + +#endif + diff --git a/skia/include/corecg/SkPerspIter.h b/skia/include/corecg/SkPerspIter.h new file mode 100644 index 0000000..1a5d82f --- /dev/null +++ b/skia/include/corecg/SkPerspIter.h @@ -0,0 +1,56 @@ +/* include/corecg/SkPerspIter.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkPerspIter_DEFINED +#define SkPerspIter_DEFINED + +#include "SkMatrix.h" + +class SkPerspIter { +public: + /** Iterate a line through the matrix [x,y] ... [x+count-1, y]. + @param m The matrix we will be iterating a line through + @param x The initial X coordinate to be mapped through the matrix + @param y The initial Y coordinate to be mapped through the matrix + @param count The number of points (x,y) (x+1,y) (x+2,y) ... we will eventually map + */ + SkPerspIter(const SkMatrix& m, SkScalar x, SkScalar y, int count); + + /** Return the buffer of [x,y] fixed point values we will be filling. + This always returns the same value, so it can be saved across calls to + next(). + */ + const SkFixed* getXY() const { return fStorage; } + + /** Return the number of [x,y] pairs that have been filled in the getXY() buffer. + When this returns 0, the iterator is finished. + */ + int next(); + +private: + enum { + kShift = 4, + kCount = (1 << kShift) + }; + const SkMatrix& fMatrix; + SkFixed fStorage[kCount * 2]; + SkFixed fX, fY; + SkScalar fSX, fSY; + int fCount; +}; + +#endif diff --git a/skia/include/corecg/SkPoint.h b/skia/include/corecg/SkPoint.h new file mode 100644 index 0000000..4493ce3 --- /dev/null +++ b/skia/include/corecg/SkPoint.h @@ -0,0 +1,288 @@ +/* + * Copyright (C) 2006-2008 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SkPoint_DEFINED +#define SkPoint_DEFINED + +#include "SkMath.h" +#include "SkScalar.h" + +/** \struct SkIPoint + + SkIPoint holds two 32 bit integer coordinates +*/ +struct SkIPoint { + int32_t fX, fY; + + /** Set the x and y values of the point. */ + void set(int32_t x, int32_t y) { fX = x; fY = y; } + + /** Rotate the point clockwise, writing the new point into dst + It is legal for dst == this + */ + void rotateCW(SkIPoint* dst) const; + + /** Rotate the point clockwise, writing the new point back into the point + */ + + void rotateCW() { this->rotateCW(this); } + + /** Rotate the point counter-clockwise, writing the new point into dst. + It is legal for dst == this + */ + void rotateCCW(SkIPoint* dst) const; + + /** Rotate the point counter-clockwise, writing the new point back into + the point + */ + void rotateCCW() { this->rotateCCW(this); } + + /** Negate the X and Y coordinates of the point. + */ + void negate() { fX = -fX; fY = -fY; } + + /** Return a new point whose X and Y coordinates are the negative of the + original point's + */ + SkIPoint operator-() const { + SkIPoint neg; + neg.fX = -fX; + neg.fY = -fY; + return neg; + } + + /** Add v's coordinates to this point's */ + void operator+=(const SkIPoint& v) { + fX += v.fX; + fY += v.fY; + } + + /** Subtract v's coordinates from this point's */ + void operator-=(const SkIPoint& v) { + fX -= v.fX; + fY -= v.fY; + } + + /** Returns true if the point's coordinates equal (x,y) */ + bool equals(int32_t x, int32_t y) const { + return fX == x && fY == y; + } + + friend bool operator==(const SkIPoint& a, const SkIPoint& b) { + return a.fX == b.fX && a.fY == b.fY; + } + + friend bool operator!=(const SkIPoint& a, const SkIPoint& b) { + return a.fX != b.fX || a.fY != b.fY; + } + + /** Returns a new point whose coordinates are the difference between + a and b (i.e. a - b) + */ + friend SkIPoint operator-(const SkIPoint& a, const SkIPoint& b) { + SkIPoint v; + v.set(a.fX - b.fX, a.fY - b.fY); + return v; + } + + /** Returns a new point whose coordinates are the sum of a and b (a + b) + */ + friend SkIPoint operator+(const SkIPoint& a, const SkIPoint& b) { + SkIPoint v; + v.set(a.fX + b.fX, a.fY + b.fY); + return v; + } + + /** Returns the dot product of a and b, treating them as 2D vectors + */ + static int32_t DotProduct(const SkIPoint& a, const SkIPoint& b) { + return a.fX * b.fX + a.fY * b.fY; + } + + /** Returns the cross product of a and b, treating them as 2D vectors + */ + static int32_t CrossProduct(const SkIPoint& a, const SkIPoint& b) { + return a.fX * b.fY - a.fY * b.fX; + } +}; + +struct SkPoint { + SkScalar fX, fY; + + /** Set the point's X and Y coordinates */ + void set(SkScalar x, SkScalar y) { fX = x; fY = y; } + + /** Set the point's X and Y coordinates by automatically promoting (x,y) to + SkScalar values. + */ + void iset(int32_t x, int32_t y) { + fX = SkIntToScalar(x); + fY = SkIntToScalar(y); + } + + /** Set the point's X and Y coordinates by automatically promoting p's + coordinates to SkScalar values. + */ + void iset(const SkIPoint& p) { + fX = SkIntToScalar(p.fX); + fY = SkIntToScalar(p.fY); + } + + /** Return the euclidian distance from (0,0) to the point + */ + SkScalar length() const { return SkPoint::Length(fX, fY); } + + /** Set the point (vector) to be unit-length in the same direction as it + currently is, and return its old length. If the old length is + degenerately small (nearly zero), do nothing and return false, otherwise + return true. + */ + bool normalize(); + + /** Set the point (vector) to be unit-length in the same direction as the + x,y params. If the vector (x,y) has a degenerate length (i.e. nearly 0) + then return false and do nothing, otherwise return true. + */ + bool setNormalize(SkScalar x, SkScalar y); + + /** Scale the point (vector) to have the specified length, and return that + length. If the original length is degenerately small (nearly zero), + do nothing and return false, otherwise return true. + */ + bool setLength(SkScalar length); + + /** Set the point (vector) to have the specified length in the same + direction as (x,y). If the vector (x,y) has a degenerate length + (i.e. nearly 0) then return false and do nothing, otherwise return true. + */ + bool setLength(SkScalar x, SkScalar y, SkScalar length); + + /** Scale the point's coordinates by scale, writing the answer into dst. + It is legal for dst == this. + */ + void scale(SkScalar scale, SkPoint* dst) const; + + /** Scale the point's coordinates by scale, writing the answer back into + the point. + */ + void scale(SkScalar scale) { this->scale(scale, this); } + + /** Rotate the point clockwise by 90 degrees, writing the answer into dst. + It is legal for dst == this. + */ + void rotateCW(SkPoint* dst) const; + + /** Rotate the point clockwise by 90 degrees, writing the answer back into + the point. + */ + void rotateCW() { this->rotateCW(this); } + + /** Rotate the point counter-clockwise by 90 degrees, writing the answer + into dst. It is legal for dst == this. + */ + void rotateCCW(SkPoint* dst) const; + + /** Rotate the point counter-clockwise by 90 degrees, writing the answer + back into the point. + */ + void rotateCCW() { this->rotateCCW(this); } + + /** Negate the point's coordinates + */ + void negate() { + fX = -fX; + fY = -fY; + } + + /** Returns a new point whose coordinates are the negative of the point's + */ + SkPoint operator-() const { + SkPoint neg; + neg.fX = -fX; + neg.fY = -fY; + return neg; + } + + /** Add v's coordinates to the point's + */ + void operator+=(const SkPoint& v) { + fX += v.fX; + fY += v.fY; + } + + /** Subtract v's coordinates from the point's + */ + void operator-=(const SkPoint& v) { + fX -= v.fX; + fY -= v.fY; + } + + /** Returns true if the point's coordinates equal (x,y) + */ + bool equals(SkScalar x, SkScalar y) const { return fX == x && fY == y; } + + friend bool operator==(const SkPoint& a, const SkPoint& b) { + return a.fX == b.fX && a.fY == b.fY; + } + + friend bool operator!=(const SkPoint& a, const SkPoint& b) { + return a.fX != b.fX || a.fY != b.fY; + } + + /** Returns a new point whose coordinates are the difference between + a's and b's (a - b) + */ + friend SkPoint operator-(const SkPoint& a, const SkPoint& b) { + SkPoint v; + v.set(a.fX - b.fX, a.fY - b.fY); + return v; + } + + /** Returns a new point whose coordinates are the sum of a's and b's (a + b) + */ + friend SkPoint operator+(const SkPoint& a, const SkPoint& b) { + SkPoint v; + v.set(a.fX + b.fX, a.fY + b.fY); + return v; + } + + /** Returns the euclidian distance from (0,0) to (x,y) + */ + static SkScalar Length(SkScalar x, SkScalar y); + + /** Returns the euclidian distance between a and b + */ + static SkScalar Distance(const SkPoint& a, const SkPoint& b) { + return Length(a.fX - b.fX, a.fY - b.fY); + } + + /** Returns the dot product of a and b, treating them as 2D vectors + */ + static SkScalar DotProduct(const SkPoint& a, const SkPoint& b) { + return SkScalarMul(a.fX, b.fX) + SkScalarMul(a.fY, b.fY); + } + + /** Returns the cross product of a and b, treating them as 2D vectors + */ + static SkScalar CrossProduct(const SkPoint& a, const SkPoint& b) { + return SkScalarMul(a.fX, b.fY) - SkScalarMul(a.fY, b.fX); + } +}; + +typedef SkPoint SkVector; + +#endif + diff --git a/skia/include/corecg/SkPostConfig.h b/skia/include/corecg/SkPostConfig.h new file mode 100644 index 0000000..3ca5f75 --- /dev/null +++ b/skia/include/corecg/SkPostConfig.h @@ -0,0 +1,211 @@ +/* include/corecg/SkPostConfig.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkPostConfig_DEFINED +#define SkPostConfig_DEFINED + +#if defined(SK_BUILD_FOR_WIN32) || defined(SK_BUILD_FOR_WINCE) + #define SK_BUILD_FOR_WIN +#endif + +#if defined(SK_DEBUG) && defined(SK_RELEASE) + #error "cannot define both SK_DEBUG and SK_RELEASE" +#elif !defined(SK_DEBUG) && !defined(SK_RELEASE) + #error "must define either SK_DEBUG or SK_RELEASE" +#endif + +#if defined SK_SUPPORT_UNITTEST && !defined(SK_DEBUG) + #error "can't have unittests without debug" +#endif + +#if defined(SK_SCALAR_IS_FIXED) && defined(SK_SCALAR_IS_FLOAT) + #error "cannot define both SK_SCALAR_IS_FIXED and SK_SCALAR_IS_FLOAT" +#elif !defined(SK_SCALAR_IS_FIXED) && !defined(SK_SCALAR_IS_FLOAT) + #ifdef SK_CAN_USE_FLOAT + #define SK_SCALAR_IS_FLOAT + #else + #define SK_SCALAR_IS_FIXED + #endif +#endif + +#if defined(SK_SCALAR_IS_FLOAT) && !defined(SK_CAN_USE_FLOAT) + #define SK_CAN_USE_FLOAT + // we do nothing in the else case: fixed-scalars can have floats or not +#endif + +#if defined(SK_CPU_LENDIAN) && defined(SK_CPU_BENDIAN) + #error "cannot define both SK_CPU_LENDIAN and SK_CPU_BENDIAN" +#elif !defined(SK_CPU_LENDIAN) && !defined(SK_CPU_BENDIAN) + #error "must define either SK_CPU_LENDIAN or SK_CPU_BENDIAN" +#endif + +// ensure the port has defined all of these, or none of them +#ifdef SK_A32_SHIFT + #if !defined(SK_R32_SHIFT) || !defined(SK_G32_SHIFT) || !defined(SK_B32_SHIFT) + #error "all or none of the 32bit SHIFT amounts must be defined" + #endif +#else + #if defined(SK_R32_SHIFT) || defined(SK_G32_SHIFT) || defined(SK_B32_SHIFT) + #error "all or none of the 32bit SHIFT amounts must be defined" + #endif +#endif + +#ifndef SkNEW + #define SkNEW(type_name) new type_name + #define SkNEW_ARGS(type_name, args) new type_name args + #define SkNEW_ARRAY(type_name, count) new type_name[count] + #define SkDELETE(obj) delete obj + #define SkDELETE_ARRAY(array) delete[] array +#endif + +#ifndef SK_CRASH +#if 1 // set to 0 for infinite loop, which can help connecting gdb + #define SK_CRASH() *(int *)(uintptr_t)0xbbadbeef = 0 +#else + #define SK_CRASH() do {} while (true) +#endif +#endif + +/////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_BUILD_FOR_WIN + ////////////////////////////////////////////////////////////////////// + // Begin Chrome-specific changes + // Chrome already defines WIN32_LEAN_AND_MEAN so no need to define it here. + + #include <windows.h> + // End Chrome-specific changes + + #ifndef SK_DEBUGBREAK + #define SK_DEBUGBREAK(cond) do { if (!(cond)) DebugBreak(); } while (false) + #endif + + #ifdef SK_BUILD_FOR_WIN32 + #define strcasecmp(a, b) stricmp(a, b) + #define strncasecmp(a, b, c) strnicmp(a, b, c) + #elif defined(SK_BUILD_FOR_WINCE) + #define strcasecmp(a, b) _stricmp(a, b) + #define strncasecmp(a, b, c) _strnicmp(a, b, c) + #endif +#elif defined(SK_BUILD_FOR_MAC) + #ifndef SK_DEBUGBREAK + #define SK_DEBUGBREAK(cond) do { if (!(cond)) SK_CRASH(); } while (false) + #endif +#else + #ifdef SK_DEBUG + #include <stdio.h> + #ifndef SK_DEBUGBREAK + #define SK_DEBUGBREAK(cond) do { if (cond) break; \ + SkDebugf("%s:%d: failed assertion \"%s\"\n", \ + __FILE__, __LINE__, #cond); SK_CRASH(); } while (false) + #endif + #endif +#endif + +// stdlib macros + +#if 0 +#if !defined(strlen) && defined(SK_DEBUG) + extern size_t sk_strlen(const char*); + #define strlen(s) sk_strlen(s) +#endif +#ifndef sk_strcpy + #define sk_strcpy(dst, src) strcpy(dst, src) +#endif +#ifndef sk_strchr + #define sk_strchr(s, c) strchr(s, c) +#endif +#ifndef sk_strrchr + #define sk_strrchr(s, c) strrchr(s, c) +#endif +#ifndef sk_strcmp + #define sk_strcmp(s, t) strcmp(s, t) +#endif +#ifndef sk_strncmp + #define sk_strncmp(s, t, n) strncmp(s, t, n) +#endif +#ifndef sk_memcpy + #define sk_memcpy(dst, src, n) memcpy(dst, src, n) +#endif +#ifndef memmove + #define memmove(dst, src, n) memmove(dst, src, n) +#endif +#ifndef sk_memset + #define sk_memset(dst, val, n) memset(dst, val, n) +#endif +#ifndef sk_memcmp + #define sk_memcmp(s, t, n) memcmp(s, t, n) +#endif + +#define sk_strequal(s, t) (!sk_strcmp(s, t)) +#define sk_strnequal(s, t, n) (!sk_strncmp(s, t, n)) +#endif + +////////////////////////////////////////////////////////////////////////////////////////////// +#ifndef SK_BUILD_FOR_WINCE +#include <string.h> +#include <stdlib.h> +#else +#define _CMNINTRIN_DECLARE_ONLY +#include "cmnintrin.h" +#endif + +#if defined SK_DEBUG && defined SK_BUILD_FOR_WIN32 +//#define _CRTDBG_MAP_ALLOC +#ifdef free +#undef free +#endif +#include <crtdbg.h> +#undef free + +#ifdef SK_DEBUGx +#if defined(SK_SIMULATE_FAILED_MALLOC) && defined(__cplusplus) + void * operator new( + size_t cb, + int nBlockUse, + const char * szFileName, + int nLine, + int foo + ); + void * operator new[]( + size_t cb, + int nBlockUse, + const char * szFileName, + int nLine, + int foo + ); + void operator delete( + void *pUserData, + int, const char*, int, int + ); + void operator delete( + void *pUserData + ); + void operator delete[]( void * p ); + #define DEBUG_CLIENTBLOCK new( _CLIENT_BLOCK, __FILE__, __LINE__, 0) +#else + #define DEBUG_CLIENTBLOCK new( _CLIENT_BLOCK, __FILE__, __LINE__) +#endif + #define new DEBUG_CLIENTBLOCK +#else +#define DEBUG_CLIENTBLOCK +#endif // _DEBUG + +#endif + +#endif + diff --git a/skia/include/corecg/SkPreConfig.h b/skia/include/corecg/SkPreConfig.h new file mode 100644 index 0000000..c651dd4 --- /dev/null +++ b/skia/include/corecg/SkPreConfig.h @@ -0,0 +1,111 @@ +/* include/corecg/SkPreConfig.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkPreConfig_DEFINED +#define SkPreConfig_DEFINED + +#ifdef ANDROID + #define SK_BUILD_FOR_UNIX + #define SkLONGLONG int64_t +#endif + +////////////////////////////////////////////////////////////////////// + +#if !defined(SK_BUILD_FOR_PALM) && !defined(SK_BUILD_FOR_WINCE) && !defined(SK_BUILD_FOR_WIN32) && !defined(SK_BUILD_FOR_SYMBIAN) && !defined(SK_BUILD_FOR_UNIX) && !defined(SK_BUILD_FOR_MAC) + + #if defined(PALMOS_SDK_VERSION) + #define SK_BUILD_FOR_PALM + #elif defined(UNDER_CE) + #define SK_BUILD_FOR_WINCE + #elif defined(WIN32) + #define SK_BUILD_FOR_WIN32 + #elif defined(__SYMBIAN32__) + #define SK_BUILD_FOR_WIN32 + #elif defined(linux) + #define SK_BUILD_FOR_UNIX + #else + #define SK_BUILD_FOR_MAC + #endif + +#endif + +////////////////////////////////////////////////////////////////////// + +#if !defined(SK_DEBUG) && !defined(SK_RELEASE) + #ifdef NDEBUG + #define SK_RELEASE + #else + #define SK_DEBUG + #endif +#endif + +////////////////////////////////////////////////////////////////////// + +// define to blank or change this in SkUserConfig.h as needed +#define SK_RESTRICT __restrict__ + +////////////////////////////////////////////////////////////////////// + +#if defined(SK_BUILD_FOR_WIN32) || defined(SK_BUILD_FOR_MAC) + #ifndef SK_CAN_USE_FLOAT + #define SK_CAN_USE_FLOAT + #endif + #if !defined(SK_SCALAR_IS_FIXED) && !defined(SK_SCALAR_IS_FLOAT) + #define SK_SCALAR_IS_FIXED + #endif + + #ifndef SkLONGLONG + #ifdef SK_BUILD_FOR_WIN32 + #define SkLONGLONG __int64 + #else + #define SkLONGLONG long long + #endif + #endif +#endif + +////////////////////////////////////////////////////////////////////// + +#if !defined(SK_CPU_BENDIAN) && !defined(SK_CPU_LENDIAN) + #if defined (__ppc__) || defined(__ppc64__) + #define SK_CPU_BENDIAN + #else + #define SK_CPU_LENDIAN + #endif +#endif + +////////////////////////////////////////////////////////////////////// + +#if (defined(__arm__) && !defined(__thumb__)) || defined(SK_BUILD_FOR_BREW) || defined(SK_BUILD_FOR_WINCE) || (defined(SK_BUILD_FOR_SYMBIAN) && !defined(__MARM_THUMB__)) + /* e.g. the ARM instructions have conditional execution, making tiny branches cheap */ + #define SK_CPU_HAS_CONDITIONAL_INSTR +#endif + +////////////////////////////////////////////////////////////////////// +// Conditional features based on build target + +#if defined(SK_BUILD_FOR_WIN32) || defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_UNIX) + #ifndef SK_BUILD_NO_IMAGE_ENCODE + #define SK_SUPPORT_IMAGE_ENCODE + #endif +#endif + +#ifdef SK_BUILD_FOR_SYMBIAN + #define SK_USE_RUNTIME_GLOBALS +#endif + +#endif + diff --git a/skia/include/corecg/SkRandom.h b/skia/include/corecg/SkRandom.h new file mode 100644 index 0000000..8994fc4 --- /dev/null +++ b/skia/include/corecg/SkRandom.h @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2006-2008 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SkRandom_DEFINED +#define SkRandom_DEFINED + +#include "Sk64.h" +#include "SkScalar.h" + +/** \class SkRandom + + Utility class that implements pseudo random 32bit numbers using a fast + linear equation. Unlike rand(), this class holds its own seed (initially + set to 0), so that multiple instances can be used with no side-effects. +*/ +class SkRandom { +public: + SkRandom() : fSeed(0) {} + SkRandom(uint32_t seed) : fSeed(seed) {} + + /** Return the next pseudo random number as an unsigned 32bit value. + */ + uint32_t nextU() { uint32_t r = fSeed * kMul + kAdd; fSeed = r; return r; } + + /** Return the next pseudo random number as a signed 32bit value. + */ + int32_t nextS() { return (int32_t)this->nextU(); } + + /** Return the next pseudo random number as an unsigned 16bit value. + */ + U16CPU nextU16() { return this->nextU() >> 16; } + + /** Return the next pseudo random number as a signed 16bit value. + */ + S16CPU nextS16() { return this->nextS() >> 16; } + + /** Return the next pseudo random number, as an unsigned value of + at most bitCount bits. + @param bitCount The maximum number of bits to be returned + */ + uint32_t nextBits(unsigned bitCount) { + SkASSERT(bitCount > 0 && bitCount <= 32); + return this->nextU() >> (32 - bitCount); + } + + /** Return the next pseudo random unsigned number, mapped to lie within + [min, max] inclusive. + */ + uint32_t nextRangeU(uint32_t min, uint32_t max) { + SkASSERT(min <= max); + return min + this->nextU() % (max - min + 1); + } + + /** Return the next pseudo random number expressed as an unsigned SkFixed + in the range [0..SK_Fixed1). + */ + SkFixed nextUFixed1() { return this->nextU() >> 16; } + + /** Return the next pseudo random number expressed as a signed SkFixed + in the range (-SK_Fixed1..SK_Fixed1). + */ + SkFixed nextSFixed1() { return this->nextS() >> 15; } + + /** Return the next pseudo random number expressed as a SkScalar + in the range [0..SK_Scalar1). + */ + SkScalar nextUScalar1() { return SkFixedToScalar(this->nextUFixed1()); } + + /** Return the next pseudo random number expressed as a SkScalar + in the range (-SK_Scalar1..SK_Scalar1). + */ + SkScalar nextSScalar1() { return SkFixedToScalar(this->nextSFixed1()); } + + /** Return the next pseudo random number as a signed 64bit value. + */ + void next64(Sk64* a) { + SkASSERT(a); + a->set(this->nextS(), this->nextU()); + } + + /** Set the seed of the random object. The seed is initialized to 0 when the + object is first created, and is updated each time the next pseudo random + number is requested. + */ + void setSeed(int32_t seed) { fSeed = (uint32_t)seed; } + +private: + // See "Numerical Recipes in C", 1992 page 284 for these constants + enum { + kMul = 1664525, + kAdd = 1013904223 + }; + uint32_t fSeed; +}; + +#endif + diff --git a/skia/include/corecg/SkRect.h b/skia/include/corecg/SkRect.h new file mode 100644 index 0000000..65a562e --- /dev/null +++ b/skia/include/corecg/SkRect.h @@ -0,0 +1,426 @@ +/* include/corecg/SkRect.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkRect_DEFINED +#define SkRect_DEFINED + +#include "SkPoint.h" + +/** \struct SkIRect + + SkIRect holds four 32 bit integer coordinates for a rectangle +*/ +struct SkIRect { + int32_t fLeft, fTop, fRight, fBottom; + + /** Return true if the rectangle's width or height are <= 0 + */ + bool isEmpty() const { return fLeft >= fRight || fTop >= fBottom; } + + /** Returns the rectangle's width. This does not check for a valid rectangle (i.e. left <= right) + so the result may be negative. + */ + int width() const { return fRight - fLeft; } + + /** Returns the rectangle's height. This does not check for a valid rectangle (i.e. top <= bottom) + so the result may be negative. + */ + int height() const { return fBottom - fTop; } + + friend int operator==(const SkIRect& a, const SkIRect& b) + { + return !memcmp(&a, &b, sizeof(a)); + } + friend int operator!=(const SkIRect& a, const SkIRect& b) + { + return memcmp(&a, &b, sizeof(a)); + } + + /** Set the rectangle to (0,0,0,0) + */ + void setEmpty() { memset(this, 0, sizeof(*this)); } + + void set(int32_t left, int32_t top, int32_t right, int32_t bottom) + { + fLeft = left; + fTop = top; + fRight = right; + fBottom = bottom; + } + + /** Offset set the rectangle by adding dx to its left and right, + and adding dy to its top and bottom. + */ + void offset(int32_t dx, int32_t dy) + { + fLeft += dx; + fTop += dy; + fRight += dx; + fBottom += dy; + } + + /** Inset the rectangle by (dx,dy). If dx is positive, then the sides are moved inwards, + making the rectangle narrower. If dx is negative, then the sides are moved outwards, + making the rectangle wider. The same hods true for dy and the top and bottom. + */ + void inset(int32_t dx, int32_t dy) + { + fLeft += dx; + fTop += dy; + fRight -= dx; + fBottom -= dy; + } + /** Returns true if (x,y) is inside the rectangle and the rectangle is not + empty. The left and top are considered to be inside, while the right + and bottom are not. Thus for the rectangle (0, 0, 5, 10), the + points (0,0) and (0,9) are inside, while (-1,0) and (5,9) are not. + */ + bool contains(int32_t x, int32_t y) const + { + return (unsigned)(x - fLeft) < (unsigned)(fRight - fLeft) && + (unsigned)(y - fTop) < (unsigned)(fBottom - fTop); + } + + /** Returns true if the 4 specified sides of a rectangle are inside or equal to this rectangle. + If either rectangle is empty, contains() returns false. + */ + bool contains(int32_t left, int32_t top, int32_t right, int32_t bottom) const + { + return left < right && top < bottom && !this->isEmpty() && // check for empties + fLeft <= left && fTop <= top && + fRight >= right && fBottom >= bottom; + } + + /** Returns true if the specified rectangle r is inside or equal to this rectangle. + */ + bool contains(const SkIRect& r) const + { + return !r.isEmpty() && !this->isEmpty() && // check for empties + fLeft <= r.fLeft && fTop <= r.fTop && + fRight >= r.fRight && fBottom >= r.fBottom; + } + + /** Return true if this rectangle contains the specified rectangle. + For speed, this method does not check if either this or the specified + rectangles are empty, and if either is, its return value is undefined. + In the debugging build however, we assert that both this and the + specified rectangles are non-empty. + */ + bool containsNoEmptyCheck(int32_t left, int32_t top, + int32_t right, int32_t bottom) const + { + SkASSERT(fLeft < fRight && fTop < fBottom); + SkASSERT(left < right && top < bottom); + + return fLeft <= left && fTop <= top && + fRight >= right && fBottom >= bottom; + } + + /** If r intersects this rectangle, return true and set this rectangle to that + intersection, otherwise return false and do not change this rectangle. + If either rectangle is empty, do nothing and return false. + */ + bool intersect(const SkIRect& r) + { + SkASSERT(&r); + return this->intersect(r.fLeft, r.fTop, r.fRight, r.fBottom); + } + + /** If rectangles a and b intersect, return true and set this rectangle to + that intersection, otherwise return false and do not change this + rectangle. If either rectangle is empty, do nothing and return false. + */ + bool intersect(const SkIRect& a, const SkIRect& b) + { + SkASSERT(&a && &b); + + if (!a.isEmpty() && !b.isEmpty() && + a.fLeft < b.fRight && b.fLeft < a.fRight && + a.fTop < b.fBottom && b.fTop < a.fBottom) + { + fLeft = SkMax32(a.fLeft, b.fLeft); + fTop = SkMax32(a.fTop, b.fTop); + fRight = SkMin32(a.fRight, b.fRight); + fBottom = SkMin32(a.fBottom, b.fBottom); + return true; + } + return false; + } + + /** If rectangles a and b intersect, return true and set this rectangle to + that intersection, otherwise return false and do not change this + rectangle. For speed, no check to see if a or b are empty is performed. + If either is, then the return result is undefined. In the debug build, + we assert that both rectangles are non-empty. + */ + bool intersectNoEmptyCheck(const SkIRect& a, const SkIRect& b) + { + SkASSERT(&a && &b); + SkASSERT(!a.isEmpty() && !b.isEmpty()); + + if (a.fLeft < b.fRight && b.fLeft < a.fRight && + a.fTop < b.fBottom && b.fTop < a.fBottom) + { + fLeft = SkMax32(a.fLeft, b.fLeft); + fTop = SkMax32(a.fTop, b.fTop); + fRight = SkMin32(a.fRight, b.fRight); + fBottom = SkMin32(a.fBottom, b.fBottom); + return true; + } + return false; + } + + /** If the rectangle specified by left,top,right,bottom intersects this rectangle, + return true and set this rectangle to that intersection, + otherwise return false and do not change this rectangle. + If either rectangle is empty, do nothing and return false. + */ + bool intersect(int32_t left, int32_t top, int32_t right, int32_t bottom) + { + if (left < right && top < bottom && !this->isEmpty() && + fLeft < right && left < fRight && fTop < bottom && top < fBottom) + { + if (fLeft < left) fLeft = left; + if (fTop < top) fTop = top; + if (fRight > right) fRight = right; + if (fBottom > bottom) fBottom = bottom; + return true; + } + return false; + } + + /** Returns true if a and b are not empty, and they intersect + */ + static bool Intersects(const SkIRect& a, const SkIRect& b) + { + return !a.isEmpty() && !b.isEmpty() && // check for empties + a.fLeft < b.fRight && b.fLeft < a.fRight && + a.fTop < b.fBottom && b.fTop < a.fBottom; + } + + /** Update this rectangle to enclose itself and the specified rectangle. + If this rectangle is empty, just set it to the specified rectangle. If the specified + rectangle is empty, do nothing. + */ + void join(int32_t left, int32_t top, int32_t right, int32_t bottom); + + /** Update this rectangle to enclose itself and the specified rectangle. + If this rectangle is empty, just set it to the specified rectangle. If the specified + rectangle is empty, do nothing. + */ + void join(const SkIRect& r) + { + this->join(r.fLeft, r.fTop, r.fRight, r.fBottom); + } + + /** Swap top/bottom or left/right if there are flipped. + This can be called if the edges are computed separately, + and may have crossed over each other. + When this returns, left <= right && top <= bottom + */ + void sort(); +}; + +/** \struct SkRect +*/ +struct SkRect { + SkScalar fLeft, fTop, fRight, fBottom; + + /** Return true if the rectangle's width or height are <= 0 + */ + bool isEmpty() const { return fLeft >= fRight || fTop >= fBottom; } + SkScalar width() const { return fRight - fLeft; } + SkScalar height() const { return fBottom - fTop; } + SkScalar centerX() const { return SkScalarHalf(fLeft + fRight); } + SkScalar centerY() const { return SkScalarHalf(fTop + fBottom); } + + friend int operator==(const SkRect& a, const SkRect& b) + { + return !memcmp(&a, &b, sizeof(a)); + } + friend int operator!=(const SkRect& a, const SkRect& b) + { + return memcmp(&a, &b, sizeof(a)); + } + + /** return the 4 points that enclose the rectangle + */ + void toQuad(SkPoint quad[4]) const; + + /** Set this rectangle to the empty rectangle (0,0,0,0) + */ + void setEmpty() { memset(this, 0, sizeof(*this)); } + + void set(const SkIRect& src) + { + fLeft = SkIntToScalar(src.fLeft); + fTop = SkIntToScalar(src.fTop); + fRight = SkIntToScalar(src.fRight); + fBottom = SkIntToScalar(src.fBottom); + } + + void set(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) + { + fLeft = left; + fTop = top; + fRight = right; + fBottom = bottom; + } + + /** Set this rectangle to be the bounds of the array of points. + If the array is empty (count == 0), then set this rectangle + to the empty rectangle (0,0,0,0) + */ + void set(const SkPoint pts[], int count); + + /** Offset set the rectangle by adding dx to its left and right, + and adding dy to its top and bottom. + */ + void offset(SkScalar dx, SkScalar dy) + { + fLeft += dx; + fTop += dy; + fRight += dx; + fBottom += dy; + } + + /** Inset the rectangle by (dx,dy). If dx is positive, then the sides are moved inwards, + making the rectangle narrower. If dx is negative, then the sides are moved outwards, + making the rectangle wider. The same hods true for dy and the top and bottom. + */ + void inset(SkScalar dx, SkScalar dy) + { + fLeft += dx; + fTop += dy; + fRight -= dx; + fBottom -= dy; + } + + /** If this rectangle intersects r, return true and set this rectangle to that + intersection, otherwise return false and do not change this rectangle. + If either rectangle is empty, do nothing and return false. + */ + bool intersect(const SkRect& r); + + /** If this rectangle intersects the rectangle specified by left, top, right, bottom, + return true and set this rectangle to that intersection, otherwise return false + and do not change this rectangle. + If either rectangle is empty, do nothing and return false. + */ + bool intersect(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom); + + /** Return true if this rectangle is not empty, and the specified sides of + a rectangle are not empty, and they intersect. + */ + bool intersects(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) const + { + return // first check that both are not empty + left < right && top < bottom && + fLeft < fRight && fTop < fBottom && + // now check for intersection + fLeft < right && left < fRight && + fTop < bottom && top < fBottom; + } + + /** Return true if rectangles a and b are not empty and intersect. + */ + static bool Intersects(const SkRect& a, const SkRect& b) + { + return !a.isEmpty() && !b.isEmpty() && // check for empties + a.fLeft < b.fRight && b.fLeft < a.fRight && + a.fTop < b.fBottom && b.fTop < a.fBottom; + } + + /** Update this rectangle to enclose itself and the specified rectangle. + If this rectangle is empty, just set it to the specified rectangle. If the specified + rectangle is empty, do nothing. + */ + void join(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom); + + /** Update this rectangle to enclose itself and the specified rectangle. + If this rectangle is empty, just set it to the specified rectangle. If the specified + rectangle is empty, do nothing. + */ + void join(const SkRect& r) + { + this->join(r.fLeft, r.fTop, r.fRight, r.fBottom); + } + + /** Returns true if (p.fX,p.fY) is inside the rectangle. The left and top coordinates of + the rectangle are considered to be inside, while the right and bottom coordinates + are not. Thus for the rectangle (0, 0, 5, 10), the points (0,0) and (0,9) are inside, + while (-1,0) and (5,9) are not. + If this rectangle is empty, return false. + */ + bool contains(const SkPoint& p) const + { + return !this->isEmpty() && + fLeft <= p.fX && p.fX < fRight && + fTop <= p.fY && p.fY < fBottom; + } + + /** Returns true if (x,y) is inside the rectangle. The left and top coordinates of + the rectangle are considered to be inside, while the right and bottom coordinates + are not. Thus for the rectangle (0, 0, 5, 10), the points (0,0) and (0,9) are inside, + while (-1,0) and (5,9) are not. + If this rectangle is empty, return false. + */ + bool contains(SkScalar x, SkScalar y) const + { + return !this->isEmpty() && + fLeft <= x && x < fRight && + fTop <= y && y < fBottom; + } + + /** Return true if this rectangle contains r. + If either rectangle is empty, return false. + */ + bool contains(const SkRect& r) const + { + return !r.isEmpty() && !this->isEmpty() && // check for empties + fLeft <= r.fLeft && fTop <= r.fTop && + fRight >= r.fRight && fBottom >= r.fBottom; + } + + /** Set the dst integer rectangle by rounding this rectangle's coordinates + to their nearest integer values. + */ + void round(SkIRect* dst) const + { + SkASSERT(dst); + dst->set(SkScalarRound(fLeft), SkScalarRound(fTop), SkScalarRound(fRight), SkScalarRound(fBottom)); + } + + /** Set the dst integer rectangle by rounding "out" this rectangle, choosing the floor of top and left, + and the ceiling of right and bototm. + */ + void roundOut(SkIRect* dst) const + { + SkASSERT(dst); + dst->set(SkScalarFloor(fLeft), SkScalarFloor(fTop), SkScalarCeil(fRight), SkScalarCeil(fBottom)); + } + + /** Swap top/bottom or left/right if there are flipped. + This can be called if the edges are computed separately, + and may have crossed over each other. + When this returns, left <= right && top <= bottom + */ + void sort(); +}; + +#endif + diff --git a/skia/include/corecg/SkRegion.h b/skia/include/corecg/SkRegion.h new file mode 100644 index 0000000..238524a --- /dev/null +++ b/skia/include/corecg/SkRegion.h @@ -0,0 +1,339 @@ +/* + * Copyright (C) 2005-2007 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SkRegion_DEFINED +#define SkRegion_DEFINED + +#include "SkRect.h" + +class SkPath; +class SkRgnBuilder; + +namespace android { + class Region; +} + +#define SkRegion_gEmptyRunHeadPtr ((SkRegion::RunHead*)-1) +#define SkRegion_gRectRunHeadPtr 0 + +/** \class SkRegion + + The SkRegion class encapsulates the geometric region used to specify + clipping areas for drawing. +*/ +class SkRegion { +public: + typedef int32_t RunType; + enum { + kRunTypeSentinel = 0x7FFFFFFF + }; + + SkRegion(); + SkRegion(const SkRegion&); + explicit SkRegion(const SkIRect&); + ~SkRegion(); + + SkRegion& operator=(const SkRegion&); + + friend int operator==(const SkRegion& a, const SkRegion& b); + friend int operator!=(const SkRegion& a, const SkRegion& b) { + return !(a == b); + } + + /** Replace this region with the specified region, and return true if the + resulting region is non-empty. + */ + bool set(const SkRegion& src) { + SkASSERT(&src); + *this = src; + return !this->isEmpty(); + } + + /** Swap the contents of this and the specified region. This operation + is gauarenteed to never fail. + */ + void swap(SkRegion&); + + /** Return true if this region is empty */ + bool isEmpty() const { return fRunHead == SkRegion_gEmptyRunHeadPtr; } + /** Return true if this region is a single, non-empty rectangle */ + bool isRect() const { return fRunHead == SkRegion_gRectRunHeadPtr; } + /** Return true if this region consists of more than 1 rectangular area */ + bool isComplex() const { return !this->isEmpty() && !this->isRect(); } + /** Return the bounds of this region. If the region is empty, returns an + empty rectangle. + */ + const SkIRect& getBounds() const { return fBounds; } + + /** Returns true if the region is non-empty, and if so, sets the specified + path to the boundary(s) of the region. + */ + bool getBoundaryPath(SkPath* path) const; + + /** Set the region to be empty, and return false, since the resulting + region is empty + */ + bool setEmpty(); + + /** If rect is non-empty, set this region to that rectangle and return true, + otherwise set this region to empty and return false. + */ + bool setRect(const SkIRect&); + + /** If left < right and top < bottom, set this region to that rectangle and + return true, otherwise set this region to empty and return false. + */ + bool setRect(int32_t left, int32_t top, int32_t right, int32_t bottom); + + /** Set this region to the specified region, and return true if it is + non-empty. */ + bool setRegion(const SkRegion&); + + /** Set this region to the area described by the path, clipped. + Return true if the resulting region is non-empty. + This produces a region that is identical to the pixels that would be + drawn by the path (with no antialiasing) with the specified clip. + */ + bool setPath(const SkPath&, const SkRegion& clip); + + /** Return true if the specified x,y coordinate is inside the region. + */ + bool contains(int32_t x, int32_t y) const; + + /** Return true if the specified rectangle is completely inside the region. + This works for simple (rectangular) and complex regions, and always + returns the correct result. Note: if either this region or the rectangle + is empty, contains() returns false. + */ + bool contains(const SkIRect&) const; + + /** Return true if the specified region is completely inside the region. + This works for simple (rectangular) and complex regions, and always + returns the correct result. Note: if either region is empty, contains() + returns false. + */ + bool contains(const SkRegion&) const; + + /** Return true if this region is a single rectangle (not complex) and the + specified rectangle is contained by this region. Returning false is not + a guarantee that the rectangle is not contained by this region, but + return true is a guarantee that the rectangle is contained by this region. + */ + bool quickContains(const SkIRect& r) const { + return this->quickContains(r.fLeft, r.fTop, r.fRight, r.fBottom); + } + + /** Return true if this region is a single rectangle (not complex) and the + specified rectangle is contained by this region. Returning false is not + a guarantee that the rectangle is not contained by this region, but + return true is a guarantee that the rectangle is contained by this + region. + */ + bool quickContains(int32_t left, int32_t top, int32_t right, + int32_t bottom) const { + SkASSERT(this->isEmpty() == fBounds.isEmpty()); // valid region + + return left < right && top < bottom && + fRunHead == SkRegion_gRectRunHeadPtr && // this->isRect() + /* fBounds.contains(left, top, right, bottom); */ + fBounds.fLeft <= left && fBounds.fTop <= top && + fBounds.fRight >= right && fBounds.fBottom >= bottom; + } + + /** Return true if this region is empty, or if the specified rectangle does + not intersect the region. Returning false is not a guarantee that they + intersect, but returning true is a guarantee that they do not. + */ + bool quickReject(const SkIRect& rect) const + { + return this->isEmpty() || rect.isEmpty() || + !SkIRect::Intersects(fBounds, rect); + } + + /** Return true if this region, or rgn, is empty, or if their bounds do not + intersect. Returning false is not a guarantee that they intersect, but + returning true is a guarantee that they do not. + */ + bool quickReject(const SkRegion& rgn) const { + return this->isEmpty() || rgn.isEmpty() || + !SkIRect::Intersects(fBounds, rgn.fBounds); + } + + /** Translate the region by the specified (dx, dy) amount. + */ + void translate(int dx, int dy) { this->translate(dx, dy, this); } + + /** Translate the region by the specified (dx, dy) amount, writing the + resulting region into dst. Note: it is legal to pass this region as the + dst parameter, effectively translating the region in place. If dst is + null, nothing happens. + */ + void translate(int dx, int dy, SkRegion* dst) const; + + /** The logical operations that can be performed when combining two regions. + */ + enum Op { + kDifference_Op, //!< subtract the op region from the first region + kIntersect_Op, //!< intersect the two regions + kUnion_Op, //!< union (inclusive-or) the two regions + kXOR_Op, //!< exclusive-or the two regions + /** subtract the first region from the op region */ + kReverseDifference_Op, + kReplace_Op //!< replace the dst region with the op region + }; + + /** Set this region to the result of applying the Op to this region and the + specified rectangle: this = (this op rect). + Return true if the resulting region is non-empty. + */ + bool op(const SkIRect& rect, Op op) { return this->op(*this, rect, op); } + + /** Set this region to the result of applying the Op to this region and the + specified rectangle: this = (this op rect). + Return true if the resulting region is non-empty. + */ + bool op(int left, int top, int right, int bottom, Op op) { + SkIRect rect; + rect.set(left, top, right, bottom); + return this->op(*this, rect, op); + } + + /** Set this region to the result of applying the Op to this region and the + specified region: this = (this op rgn). + Return true if the resulting region is non-empty. + */ + bool op(const SkRegion& rgn, Op op) { return this->op(*this, rgn, op); } + /** Set this region to the result of applying the Op to the specified + rectangle and region: this = (rect op rgn). + Return true if the resulting region is non-empty. + */ + bool op(const SkIRect& rect, const SkRegion& rgn, Op); + /** Set this region to the result of applying the Op to the specified + region and rectangle: this = (rgn op rect). + Return true if the resulting region is non-empty. + */ + bool op(const SkRegion& rgn, const SkIRect& rect, Op); + /** Set this region to the result of applying the Op to the specified + regions: this = (rgna op rgnb). + Return true if the resulting region is non-empty. + */ + bool op(const SkRegion& rgna, const SkRegion& rgnb, Op op); + + /** Returns the sequence of rectangles, sorted in Y and X, that make up + this region. + */ + class Iterator { + public: + Iterator() : fRgn(NULL), fDone(true) {} + Iterator(const SkRegion&); + // if we have a region, reset to it and return true, else return false + bool rewind(); + // reset the iterator, using the new region + void reset(const SkRegion&); + bool done() { return fDone; } + void next(); + const SkIRect& rect() const { return fRect; } + + private: + const SkRegion* fRgn; + const RunType* fRuns; + SkIRect fRect; + bool fDone; + }; + + /** Returns the sequence of rectangles, sorted in Y and X, that make up + this region intersected with the specified clip rectangle. + */ + class Cliperator { + public: + Cliperator(const SkRegion&, const SkIRect& clip); + bool done() { return fDone; } + void next(); + const SkIRect& rect() const { return fRect; } + + private: + Iterator fIter; + SkIRect fClip; + SkIRect fRect; + bool fDone; + }; + + /** Returns the sequence of runs that make up this region for the specified + Y scanline, clipped to the specified left and right X values. + */ + class Spanerator { + public: + Spanerator(const SkRegion&, int y, int left, int right); + bool next(int* left, int* right); + + private: + const SkRegion::RunType* fRuns; + int fLeft, fRight; + bool fDone; + }; + + /** Write the region to the buffer, and return the number of bytes written. + If buffer is NULL, it still returns the number of bytes. + */ + uint32_t flatten(void* buffer) const; + /** Initialized the region from the buffer, returning the number + of bytes actually read. + */ + uint32_t unflatten(const void* buffer); + + SkDEBUGCODE(void dump() const;) + SkDEBUGCODE(void validate() const;) + SkDEBUGCODE(static void UnitTest();) + +private: + enum { + kOpCount = kReplace_Op + 1 + }; + + enum { + kRectRegionRuns = 6 // need to store a region of a rect [T B L R S S] + }; + + friend class android::Region; // needed for marshalling efficiently + void allocateRuns(int count); // allocate space for count runs + + struct RunHead; + + SkIRect fBounds; + RunHead* fRunHead; + + void freeRuns(); + const RunType* getRuns(RunType tmpStorage[], int* count) const; + bool setRuns(RunType runs[], int count); + + int count_runtype_values(int* itop, int* ibot) const; + + static void BuildRectRuns(const SkIRect& bounds, + RunType runs[kRectRegionRuns]); + // returns true if runs are just a rect + static bool ComputeRunBounds(const RunType runs[], int count, + SkIRect* bounds); + + friend struct RunHead; + friend class Iterator; + friend class Spanerator; + friend class SkRgnBuilder; + friend class SkFlatRegion; +}; + + +#endif + diff --git a/skia/include/corecg/SkScalar.h b/skia/include/corecg/SkScalar.h new file mode 100644 index 0000000..3e737ae --- /dev/null +++ b/skia/include/corecg/SkScalar.h @@ -0,0 +1,261 @@ +/* include/corecg/SkScalar.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkScalar_DEFINED +#define SkScalar_DEFINED + +#include "SkFixed.h" + +/** \file SkScalar.h + + Types and macros for the data type SkScalar. This is the fractional numeric type + that, depending on the compile-time flag SK_SCALAR_IS_FLOAT, may be implemented + either as an IEEE float, or as a 16.16 SkFixed. The macros in this file are written + to allow the calling code to manipulate SkScalar values without knowing which representation + is in effect. +*/ + +#ifdef SK_SCALAR_IS_FLOAT + #include "SkFloatingPoint.h" + + /** SkScalar is our type for fractional values and coordinates. Depending on + compile configurations, it is either represented as an IEEE float, or + as a 16.16 fixed point integer. + */ + typedef float SkScalar; + extern const uint32_t gIEEENotANumber; + extern const uint32_t gIEEEInfinity; + + /** SK_Scalar1 is defined to be 1.0 represented as an SkScalar + */ + #define SK_Scalar1 (1.0f) + /** SK_Scalar1 is defined to be 1/2 represented as an SkScalar + */ + #define SK_ScalarHalf (0.5f) + /** SK_ScalarInfinity is defined to be infinity as an SkScalar + */ + #define SK_ScalarInfinity (*(const float*)&gIEEEInfinity) + /** SK_ScalarMax is defined to be the largest value representable as an SkScalar + */ + #define SK_ScalarMax (3.4028235e+38f) + /** SK_ScalarMin is defined to be the smallest value representable as an SkScalar + */ + #define SK_ScalarMin (1.1754944e-38f) + /** SK_ScalarNaN is defined to be 'Not a Number' as an SkScalar + */ + #define SK_ScalarNaN (*(const float*)(const void*)&gIEEENotANumber) + /** SkScalarIsNaN(n) returns true if argument is not a number + */ + static inline bool SkScalarIsNaN(float x) { return x != x; } + /** SkIntToScalar(n) returns its integer argument as an SkScalar + */ + #define SkIntToScalar(n) ((float)(n)) + /** SkFixedToScalar(n) returns its SkFixed argument as an SkScalar + */ + #define SkFixedToScalar(x) SkFixedToFloat(x) + /** SkScalarToFixed(n) returns its SkScalar argument as an SkFixed + */ + #define SkScalarToFixed(x) SkFloatToFixed(x) + + #define SkScalarToFloat(n) (n) + #define SkFloatToScalar(n) (n) + + #define SkScalarToDouble(n) (double)(n) + #define SkDoubleToScalar(n) (float)(n) + + /** SkScalarFraction(x) returns the signed fractional part of the argument + */ + #define SkScalarFraction(x) sk_float_mod(x, 1.0f) + /** Rounds the SkScalar to the nearest integer value + */ + #define SkScalarRound(x) (int)sk_float_floor((x) + 0.5f) + /** Returns the smallest integer that is >= the specified SkScalar + */ + #define SkScalarCeil(x) (int)sk_float_ceil(x) + /** Returns the largest integer that is <= the specified SkScalar + */ + #define SkScalarFloor(x) (int)sk_float_floor(x) + /** Returns the absolute value of the specified SkScalar + */ + #define SkScalarAbs(x) sk_float_abs(x) + /** Returns the value pinned between 0 and max inclusive + */ + inline SkScalar SkScalarClampMax(SkScalar x, SkScalar max) { + return x < 0 ? 0 : x > max ? max : x; + } + /** Returns the value pinned between min and max inclusive + */ + inline SkScalar SkScalarPin(SkScalar x, SkScalar min, SkScalar max) { + return x < min ? min : x > max ? max : x; + } + /** Returns the specified SkScalar squared (x*x) + */ + inline SkScalar SkScalarSquare(SkScalar x) { return x * x; } + /** Returns the product of two SkScalars + */ + #define SkScalarMul(a, b) ((float)(a) * (b)) + /** Returns the product of two SkScalars plus a third SkScalar + */ + #define SkScalarMulAdd(a, b, c) ((float)(a) * (b) + (c)) + /** Returns the product of a SkScalar and an int rounded to the nearest integer value + */ + #define SkScalarMulRound(a, b) SkScalarRound((float)(a) * (b)) + /** Returns the product of a SkScalar and an int promoted to the next larger int + */ + #define SkScalarMulCeil(a, b) SkScalarCeil((float)(a) * (b)) + /** Returns the product of a SkScalar and an int truncated to the next smaller int + */ + #define SkScalarMulFloor(a, b) SkScalarFloor((float)(a) * (b)) + /** Returns the quotient of two SkScalars (a/b) + */ + #define SkScalarDiv(a, b) ((float)(a) / (b)) + /** Returns the mod of two SkScalars (a mod b) + */ + #define SkScalarMod(x,y) sk_float_mod(x,y) + /** Returns the product of the first two arguments, divided by the third argument + */ + #define SkScalarMulDiv(a, b, c) ((float)(a) * (b) / (c)) + /** Returns the multiplicative inverse of the SkScalar (1/x) + */ + #define SkScalarInvert(x) (SK_Scalar1 / (x)) + #define SkScalarFastInvert(x) (SK_Scalar1 / (x)) + /** Returns the square root of the SkScalar + */ + #define SkScalarSqrt(x) sk_float_sqrt(x) + /** Returns the average of two SkScalars (a+b)/2 + */ + #define SkScalarAve(a, b) (((a) + (b)) * 0.5f) + /** Returns the geometric mean of two SkScalars + */ + #define SkScalarMean(a, b) sk_float_sqrt((float)(a) * (b)) + /** Returns one half of the specified SkScalar + */ + #define SkScalarHalf(a) ((a) * 0.5f) + + #define SK_ScalarSqrt2 1.41421356f + #define SK_ScalarPI 3.14159265f + #define SK_ScalarTanPIOver8 0.414213562f + #define SK_ScalarRoot2Over2 0.707106781f + + #define SkDegreesToRadians(degrees) ((degrees) * (SK_ScalarPI / 180)) + float SkScalarSinCos(SkScalar radians, SkScalar* cosValue); + #define SkScalarSin(radians) (float)sk_float_sin(radians) + #define SkScalarCos(radians) (float)sk_float_cos(radians) + #define SkScalarTan(radians) (float)sk_float_tan(radians) + #define SkScalarASin(val) (float)sk_float_asin(val) + #define SkScalarACos(val) (float)sk_float_acos(val) + #define SkScalarATan2(y, x) (float)sk_float_atan2(y,x) + #define SkScalarExp(x) (float)sk_float_exp(x) + #define SkScalarLog(x) (float)sk_float_log(x) + + inline SkScalar SkMaxScalar(SkScalar a, SkScalar b) { return a > b ? a : b; } + inline SkScalar SkMinScalar(SkScalar a, SkScalar b) { return a < b ? a : b; } + +#else + typedef SkFixed SkScalar; + + #define SK_Scalar1 SK_Fixed1 + #define SK_ScalarHalf SK_FixedHalf + #define SK_ScalarInfinity SK_FixedMax + #define SK_ScalarMax SK_FixedMax + #define SK_ScalarMin SK_FixedMin + #define SK_ScalarNaN SK_FixedNaN + #define SkScalarIsNaN(x) ((x) == SK_FixedNaN) + #define SkIntToScalar(n) SkIntToFixed(n) + #define SkFixedToScalar(x) (x) + #define SkScalarToFixed(x) (x) + #ifdef SK_CAN_USE_FLOAT + #define SkScalarToFloat(n) SkFixedToFloat(n) + #define SkFloatToScalar(n) SkFloatToFixed(n) + + #define SkScalarToDouble(n) SkFixedToDouble(n) + #define SkDoubleToScalar(n) SkDoubleToFixed(n) + #endif + #define SkScalarFraction(x) SkFixedFraction(x) + #define SkScalarRound(x) SkFixedRound(x) + #define SkScalarCeil(x) SkFixedCeil(x) + #define SkScalarFloor(x) SkFixedFloor(x) + #define SkScalarAbs(x) SkFixedAbs(x) + #define SkScalarClampMax(x, max) SkClampMax(x, max) + #define SkScalarPin(x, min, max) SkPin32(x, min, max) + #define SkScalarSquare(x) SkFixedSquare(x) + #define SkScalarMul(a, b) SkFixedMul(a, b) + #define SkScalarMulAdd(a, b, c) SkFixedMulAdd(a, b, c) + #define SkScalarMulRound(a, b) SkFixedMulCommon(a, b, SK_FixedHalf) + #define SkScalarMulCeil(a, b) SkFixedMulCommon(a, b, SK_Fixed1 - 1) + #define SkScalarMulFloor(a, b) SkFixedMulCommon(a, b, 0) + #define SkScalarDiv(a, b) SkFixedDiv(a, b) + #define SkScalarMod(a, b) SkFixedMod(a, b) + #define SkScalarMulDiv(a, b, c) SkMulDiv(a, b, c) + #define SkScalarInvert(x) SkFixedInvert(x) + #define SkScalarFastInvert(x) SkFixedFastInvert(x) + #define SkScalarSqrt(x) SkFixedSqrt(x) + #define SkScalarAve(a, b) SkFixedAve(a, b) + #define SkScalarMean(a, b) SkFixedMean(a, b) + #define SkScalarHalf(a) ((a) >> 1) + + #define SK_ScalarSqrt2 SK_FixedSqrt2 + #define SK_ScalarPI SK_FixedPI + #define SK_ScalarTanPIOver8 SK_FixedTanPIOver8 + #define SK_ScalarRoot2Over2 SK_FixedRoot2Over2 + + #define SkDegreesToRadians(degrees) SkFractMul(degrees, SK_FractPIOver180) + #define SkScalarSinCos(radians, cosPtr) SkFixedSinCos(radians, cosPtr) + #define SkScalarSin(radians) SkFixedSin(radians) + #define SkScalarCos(radians) SkFixedCos(radians) + #define SkScalarTan(val) SkFixedTan(val) + #define SkScalarASin(val) SkFixedASin(val) + #define SkScalarACos(val) SkFixedACos(val) + #define SkScalarATan2(y, x) SkFixedATan2(y,x) + #define SkScalarExp(x) SkFixedExp(x) + #define SkScalarLog(x) SkFixedLog(x) + + #define SkMaxScalar(a, b) SkMax32(a, b) + #define SkMinScalar(a, b) SkMin32(a, b) +#endif + +#ifndef SK_SCALAR_IS_FLOAT +#define SK_ScalarNearlyZero SK_FixedNearlyZero +#else +/* Allow a little more flexibility for floating-point scalars + */ +#define SK_ScalarNearlyZero (SK_Scalar1 / (1<<15)) +#endif + +/* <= is slower than < for floats, so we use < for our tolerance test +*/ + +inline bool SkScalarNearlyZero(SkScalar x, SkScalar tolerance = SK_ScalarNearlyZero) +{ + SkASSERT(tolerance > 0); + return SkScalarAbs(x) < tolerance; +} + +/** Linearly interpolate between A and B, based on t. + If t is 0, return A + If t is 1, return B + else interpolate. + t must be [0..SK_Scalar1] +*/ +inline SkScalar SkScalarInterp(SkScalar A, SkScalar B, SkScalar t) +{ + SkASSERT(t >= 0 && t <= SK_Scalar1); + return A + SkScalarMul(B - A, t); +} + +#endif + diff --git a/skia/include/corecg/SkTSearch.h b/skia/include/corecg/SkTSearch.h new file mode 100644 index 0000000..ca1e467 --- /dev/null +++ b/skia/include/corecg/SkTSearch.h @@ -0,0 +1,168 @@ +/* include/corecg/SkTSearch.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkTSearch_DEFINED +#define SkTSearch_DEFINED + +#include "SkTypes.h" + +template <typename T> +int SkTSearch(const T* base, int count, const T& target, size_t elemSize) +{ + SkASSERT(count >= 0); + if (count <= 0) + return ~0; + + SkASSERT(base != NULL); // base may be NULL if count is zero + + int lo = 0; + int hi = count - 1; + + while (lo < hi) + { + int mid = (hi + lo) >> 1; + const T* elem = (const T*)((const char*)base + mid * elemSize); + + if (*elem < target) + lo = mid + 1; + else + hi = mid; + } + + const T* elem = (const T*)((const char*)base + hi * elemSize); + if (*elem != target) + { + if (*elem < target) + hi += 1; + hi = ~hi; + } + return hi; +} + +template <typename T> +int SkTSearch(const T* base, int count, const T& target, size_t elemSize, + int (*compare)(const T&, const T&)) +{ + SkASSERT(count >= 0); + if (count <= 0) { + return ~0; + } + + SkASSERT(base != NULL); // base may be NULL if count is zero + + int lo = 0; + int hi = count - 1; + + while (lo < hi) { + int mid = (hi + lo) >> 1; + const T* elem = (const T*)((const char*)base + mid * elemSize); + + if ((*compare)(*elem, target) < 0) + lo = mid + 1; + else + hi = mid; + } + + const T* elem = (const T*)((const char*)base + hi * elemSize); + int pred = (*compare)(*elem, target); + if (pred != 0) { + if (pred < 0) + hi += 1; + hi = ~hi; + } + return hi; +} + +template <typename T> +int SkTSearch(const T** base, int count, const T* target, size_t elemSize, + int (*compare)(const T*, const T*)) +{ + SkASSERT(count >= 0); + if (count <= 0) + return ~0; + + SkASSERT(base != NULL); // base may be NULL if count is zero + + int lo = 0; + int hi = count - 1; + + while (lo < hi) + { + int mid = (hi + lo) >> 1; + const T* elem = *(const T**)((const char*)base + mid * elemSize); + + if ((*compare)(elem, target) < 0) + lo = mid + 1; + else + hi = mid; + } + + const T* elem = *(const T**)((const char*)base + hi * elemSize); + int pred = (*compare)(elem, target); + if (pred != 0) + { + if (pred < 0) + hi += 1; + hi = ~hi; + } + return hi; +} + +int SkStrSearch(const char*const* base, int count, const char target[], + size_t target_len, size_t elemSize); +int SkStrSearch(const char*const* base, int count, const char target[], + size_t elemSize); + +/** Like SkStrSearch, but treats target as if it were all lower-case. Assumes that + base points to a table of lower-case strings. +*/ +int SkStrLCSearch(const char*const* base, int count, const char target[], + size_t target_len, size_t elemSize); +int SkStrLCSearch(const char*const* base, int count, const char target[], + size_t elemSize); + +/** Helper class to convert a string to lower-case, but only modifying the ascii + characters. This makes the routine very fast and never changes the string + length, but it is not suitable for linguistic purposes. Normally this is + used for buiding and searching string tables. +*/ +class SkAutoAsciiToLC { +public: + SkAutoAsciiToLC(const char str[], size_t len = (size_t)-1); + ~SkAutoAsciiToLC(); + + const char* lc() const { return fLC; } + size_t length() const { return fLength; } + +private: + char* fLC; // points to either the heap or fStorage + size_t fLength; + enum { + STORAGE = 64 + }; + char fStorage[STORAGE+1]; +}; + +extern "C" { + typedef int (*SkQSortCompareProc)(const void*, const void*); + void SkQSort(void* base, size_t count, size_t elemSize, SkQSortCompareProc); +} + +SkDEBUGCODE(void SkQSort_UnitTest();) + +#endif + diff --git a/skia/include/corecg/SkTemplates.h b/skia/include/corecg/SkTemplates.h new file mode 100644 index 0000000..cfe42d9 --- /dev/null +++ b/skia/include/corecg/SkTemplates.h @@ -0,0 +1,188 @@ +/* include/corecg/SkTemplates.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkTemplates_DEFINED +#define SkTemplates_DEFINED + +#include "SkTypes.h" + +/** \file SkTemplates.h + + This file contains light-weight template classes for type-safe and exception-safe + resource management. +*/ + +/** \class SkAutoTCallVProc + + Call a function when this goes out of scope. The template uses two + parameters, the object, and a function that is to be called in the destructor. + If detach() is called, the object reference is set to null. If the object + reference is null when the destructor is called, we do not call the + function. +*/ +template <typename T, void (*P)(T*)> class SkAutoTCallVProc : SkNoncopyable { +public: + SkAutoTCallVProc(T* obj): fObj(obj) {} + ~SkAutoTCallVProc() { if (fObj) P(fObj); } + T* detach() { T* obj = fObj; fObj = NULL; return obj; } +private: + T* fObj; +}; + +/** \class SkAutoTCallIProc + +Call a function when this goes out of scope. The template uses two +parameters, the object, and a function that is to be called in the destructor. +If detach() is called, the object reference is set to null. If the object +reference is null when the destructor is called, we do not call the +function. +*/ +template <typename T, int (*P)(T*)> class SkAutoTCallIProc : SkNoncopyable { +public: + SkAutoTCallIProc(T* obj): fObj(obj) {} + ~SkAutoTCallIProc() { if (fObj) P(fObj); } + T* detach() { T* obj = fObj; fObj = NULL; return obj; } +private: + T* fObj; +}; + +template <typename T> class SkAutoTDelete : SkNoncopyable { +public: + SkAutoTDelete(T* obj) : fObj(obj) {} + ~SkAutoTDelete() { delete fObj; } + + T* get() const { return fObj; } + void free() { delete fObj; fObj = NULL; } + T* detach() { T* obj = fObj; fObj = NULL; return obj; } + +private: + T* fObj; +}; + +template <typename T> class SkAutoTDeleteArray : SkNoncopyable { +public: + SkAutoTDeleteArray(T array[]) : fArray(array) {} + ~SkAutoTDeleteArray() { delete[] fArray; } + + T* get() const { return fArray; } + void free() { delete[] fArray; fArray = NULL; } + T* detach() { T* array = fArray; fArray = NULL; return array; } + +private: + T* fArray; +}; + +template <typename T> class SkAutoTArray : SkNoncopyable { +public: + SkAutoTArray(size_t count) + { + fArray = NULL; // init first in case we throw + if (count) + fArray = new T[count]; +#ifdef SK_DEBUG + fCount = count; +#endif + } + ~SkAutoTArray() + { + delete[] fArray; + } + + T* get() const { return fArray; } + T& operator[](int index) const { SkASSERT((unsigned)index < fCount); return fArray[index]; } + + void reset() + { + if (fArray) + { + delete[] fArray; + fArray = NULL; + } + } + + void replace(T* array) + { + if (fArray != array) + { + delete[] fArray; + fArray = array; + } + } + + /** Call swap to exchange your pointer to an array of T with the SkAutoTArray object. + After this call, the SkAutoTArray object will be responsible for deleting your + array, and you will be responsible for deleting its. + */ + void swap(T*& other) + { + T* tmp = fArray; + fArray = other; + other = tmp; + } + +private: +#ifdef SK_DEBUG + size_t fCount; +#endif + T* fArray; +}; + +/** Allocate a temp array on the stack/heap. + Does NOT call any constructors/destructors on T (i.e. T must be POD) +*/ +template <typename T> class SkAutoTMalloc : SkNoncopyable { +public: + SkAutoTMalloc(size_t count) + { + fPtr = (T*)sk_malloc_flags(count * sizeof(T), SK_MALLOC_THROW | SK_MALLOC_TEMP); + } + ~SkAutoTMalloc() + { + sk_free(fPtr); + } + T* get() const { return fPtr; } + +private: + T* fPtr; +}; + +template <size_t N, typename T> class SkAutoSTMalloc : SkNoncopyable { +public: + SkAutoSTMalloc(size_t count) + { + if (count <= N) + fPtr = fTStorage; + else + fPtr = (T*)sk_malloc_flags(count * sizeof(T), SK_MALLOC_THROW | SK_MALLOC_TEMP); + } + ~SkAutoSTMalloc() + { + if (fPtr != fTStorage) + sk_free(fPtr); + } + T* get() const { return fPtr; } + +private: + T* fPtr; + union { + uint32_t fStorage32[(N*sizeof(T) + 3) >> 2]; + T fTStorage[1]; // do NOT want to invoke T::T() + }; +}; + +#endif + diff --git a/skia/include/corecg/SkThread.h b/skia/include/corecg/SkThread.h new file mode 100644 index 0000000..8dc2402 --- /dev/null +++ b/skia/include/corecg/SkThread.h @@ -0,0 +1,69 @@ +/* include/corecg/SkThread.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkThread_DEFINED +#define SkThread_DEFINED + +#include "SkTypes.h" +#include "SkThread_platform.h" + +/****** SkThread_platform needs to define the following... + +int32_t sk_atomic_inc(int32_t*); +int32_t sk_atomic_dec(int32_t*); + +class SkMutex { +public: + SkMutex(); + ~SkMutex(); + + void acquire(); + void release(); +}; + +****************/ + +class SkAutoMutexAcquire : SkNoncopyable { +public: + explicit SkAutoMutexAcquire(SkMutex& mutex) : fMutex(&mutex) + { + SkASSERT(fMutex != NULL); + mutex.acquire(); + } + /** If the mutex has not been release, release it now. + */ + ~SkAutoMutexAcquire() + { + if (fMutex) + fMutex->release(); + } + /** If the mutex has not been release, release it now. + */ + void release() + { + if (fMutex) + { + fMutex->release(); + fMutex = NULL; + } + } + +private: + SkMutex* fMutex; +}; + +#endif diff --git a/skia/include/corecg/SkThread_platform.h b/skia/include/corecg/SkThread_platform.h new file mode 100644 index 0000000..b94bc42 --- /dev/null +++ b/skia/include/corecg/SkThread_platform.h @@ -0,0 +1,72 @@ +/* include/corecg/SkThread_platform.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkThread_platform_DEFINED +#define SkThread_platform_DEFINED + +#ifdef ANDROID + +#include <utils/threads.h> +#include <utils/Atomic.h> + +#define sk_atomic_inc(addr) android_atomic_inc(addr) +#define sk_atomic_dec(addr) android_atomic_dec(addr) + +class SkMutex : android::Mutex { +public: + // if isGlobal is true, then ignore any errors in the platform-specific + // destructor + SkMutex(bool isGlobal = true) {} + ~SkMutex() {} + + void acquire() { this->lock(); } + void release() { this->unlock(); } +}; + +#else + +/** Implemented by the porting layer, this function adds 1 to the int specified + by the address (in a thread-safe manner), and returns the previous value. +*/ +int32_t sk_atomic_inc(int32_t* addr); +/** Implemented by the porting layer, this function subtracts 1 to the int + specified by the address (in a thread-safe manner), and returns the previous + value. +*/ +int32_t sk_atomic_dec(int32_t* addr); + +class SkMutex { +public: + // if isGlobal is true, then ignore any errors in the platform-specific + // destructor + SkMutex(bool isGlobal = true); + ~SkMutex(); + + void acquire(); + void release(); + +private: + bool fIsGlobal; + enum { + kStorageIntCount = 12 + }; + uint32_t fStorage[kStorageIntCount]; +}; + +#endif + +#endif diff --git a/skia/include/corecg/SkTypes.h b/skia/include/corecg/SkTypes.h new file mode 100644 index 0000000..b67cf1d --- /dev/null +++ b/skia/include/corecg/SkTypes.h @@ -0,0 +1,377 @@ +/* + * Copyright 2006, Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SkTypes_DEFINED +#define SkTypes_DEFINED + +#include "SkPreConfig.h" +#include "SkUserConfig.h" +#include "SkPostConfig.h" + +#ifndef SK_IGNORE_STDINT_DOT_H + #include <stdint.h> +#endif + +#include <stdio.h> + +/** \file SkTypes.h +*/ + +/* + memory wrappers to be implemented by the porting layer (platform) +*/ + +/** Called internally if we run out of memory. The platform implementation must + not return, but should either throw an exception or otherwise exit. +*/ +extern void sk_out_of_memory(void); +/** Called internally if we hit an unrecoverable error. + The platform implementation must not return, but should either throw + an exception or otherwise exit. +*/ +extern void sk_throw(void); + +enum { + SK_MALLOC_TEMP = 0x01, //!< hint to sk_malloc that the requested memory will be freed in the scope of the stack frame + SK_MALLOC_THROW = 0x02 //!< instructs sk_malloc to call sk_throw if the memory cannot be allocated. +}; +/** Return a block of memory (at least 4-byte aligned) of at least the + specified size. If the requested memory cannot be returned, either + return null (if SK_MALLOC_TEMP bit is clear) or call sk_throw() + (if SK_MALLOC_TEMP bit is set). To free the memory, call sk_free(). +*/ +extern void* sk_malloc_flags(size_t size, unsigned flags); +/** Same as sk_malloc(), but hard coded to pass SK_MALLOC_THROW as the flag +*/ +extern void* sk_malloc_throw(size_t size); +/** Same as standard realloc(), but this one never returns null on failure. It will throw + an exception if it fails. +*/ +extern void* sk_realloc_throw(void* buffer, size_t size); +/** Free memory returned by sk_malloc(). It is safe to pass null. +*/ +extern void sk_free(void*); + +/////////////////////////////////////////////////////////////////////// + +#define SK_INIT_TO_AVOID_WARNING = 0 + +#ifndef SkDebugf + void SkDebugf(const char format[], ...); +#endif + +#ifdef SK_DEBUG + #define SkASSERT(cond) SK_DEBUGBREAK(cond) + #define SkDEBUGCODE(code) code + #define SkDECLAREPARAM(type, var) , type var + #define SkPARAM(var) , var +// #define SkDEBUGF(args ) SkDebugf##args + #define SkDEBUGF(args ) SkDebugf args + #define SkAssertResult(cond) SkASSERT(cond) +#else + #define SkASSERT(cond) + #define SkDEBUGCODE(code) + #define SkDEBUGF(args) + #define SkDECLAREPARAM(type, var) + #define SkPARAM(var) + + // unlike SkASSERT, this guy executes its condition in the non-debug build + #define SkAssertResult(cond) cond +#endif + +/////////////////////////////////////////////////////////////////////// + +/** Fast type for signed 8 bits. Use for parameter passing and local variables, not for storage +*/ +typedef int S8CPU; +/** Fast type for unsigned 8 bits. Use for parameter passing and local variables, not for storage +*/ +typedef int S16CPU; +/** Fast type for signed 16 bits. Use for parameter passing and local variables, not for storage +*/ +typedef unsigned U8CPU; +/** Fast type for unsigned 16 bits. Use for parameter passing and local variables, not for storage +*/ +typedef unsigned U16CPU; + +/** Meant to be faster than bool (doesn't promise to be 0 or 1, just 0 or non-zero +*/ +typedef int SkBool; +/** Meant to be a small version of bool, for storage purposes. Will be 0 or 1 +*/ +typedef uint8_t SkBool8; + +#ifdef SK_DEBUG + int8_t SkToS8(long); + uint8_t SkToU8(size_t); + int16_t SkToS16(long); + uint16_t SkToU16(size_t); + int32_t SkToS32(long); + uint32_t SkToU32(size_t); +#else + #define SkToS8(x) ((int8_t)(x)) + #define SkToU8(x) ((uint8_t)(x)) + #define SkToS16(x) ((int16_t)(x)) + #define SkToU16(x) ((uint16_t)(x)) + #define SkToS32(x) ((int32_t)(x)) + #define SkToU32(x) ((uint32_t)(x)) +#endif + +/** Returns 0 or 1 based on the condition +*/ +#define SkToBool(cond) ((cond) != 0) + +#define SK_MaxS16 32767 +#define SK_MinS16 -32767 +#define SK_MaxU16 0xFFFF +#define SK_MinU16 0 +#define SK_MaxS32 0x7FFFFFFF +#define SK_MinS32 0x80000001 +#define SK_MaxU32 0xFFFFFFFF +#define SK_MinU32 0 +#define SK_NaN32 0x80000000 + +#ifndef SK_OFFSETOF + #define SK_OFFSETOF(type, field) ((char*)&(((type*)1)->field) - (char*)1) +#endif + +/** Returns the number of entries in an array (not a pointer) +*/ +#define SK_ARRAY_COUNT(array) (sizeof(array) / sizeof(array[0])) + +/** Returns x rounded up to a multiple of 2 +*/ +#define SkAlign2(x) (((x) + 1) >> 1 << 1) +/** Returns x rounded up to a multiple of 4 +*/ +#define SkAlign4(x) (((x) + 3) >> 2 << 2) + +typedef uint32_t SkFourByteTag; +#define SkSetFourByteTag(a, b, c, d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d)) + +/** 32 bit integer to hold a unicode value +*/ +typedef int32_t SkUnichar; +/** 32 bit value to hold a millisecond count +*/ +typedef uint32_t SkMSec; +/** 1 second measured in milliseconds +*/ +#define SK_MSec1 1000 +/** maximum representable milliseconds +*/ +#define SK_MSecMax 0x7FFFFFFF +/** Returns a < b for milliseconds, correctly handling wrap-around from 0xFFFFFFFF to 0 +*/ +#define SkMSec_LT(a, b) ((int32_t)(a) - (int32_t)(b) < 0) +/** Returns a <= b for milliseconds, correctly handling wrap-around from 0xFFFFFFFF to 0 +*/ +#define SkMSec_LE(a, b) ((int32_t)(a) - (int32_t)(b) <= 0) + + +/**************************************************************************** + The rest of these only build with C++ +*/ +#ifdef __cplusplus + +/** Faster than SkToBool for integral conditions. Returns 0 or 1 +*/ +inline int Sk32ToBool(uint32_t n) +{ + return (n | (0-n)) >> 31; +} + +template <typename T> inline void SkTSwap(T& a, T& b) +{ + T c(a); + a = b; + b = c; +} + +inline int32_t SkAbs32(int32_t value) +{ +#ifdef SK_CPU_HAS_CONDITIONAL_INSTR + if (value < 0) + value = -value; + return value; +#else + int32_t mask = value >> 31; + return (value ^ mask) - mask; +#endif +} + +inline int32_t SkMax32(int32_t a, int32_t b) +{ + if (a < b) + a = b; + return a; +} + +inline int32_t SkMin32(int32_t a, int32_t b) +{ + if (a > b) + a = b; + return a; +} + +inline int32_t SkSign32(int32_t a) +{ + return (a >> 31) | ((unsigned) -a >> 31); +} + +inline int32_t SkFastMin32(int32_t value, int32_t max) +{ +#ifdef SK_CPU_HAS_CONDITIONAL_INSTR + if (value > max) + value = max; + return value; +#else + int diff = max - value; + // clear diff if it is negative (clear if value > max) + diff &= (diff >> 31); + return value + diff; +#endif +} + +/** Returns signed 32 bit value pinned between min and max, inclusively +*/ +inline int32_t SkPin32(int32_t value, int32_t min, int32_t max) +{ +#ifdef SK_CPU_HAS_CONDITIONAL_INSTR + if (value < min) + value = min; + if (value > max) + value = max; +#else + if (value < min) + value = min; + else if (value > max) + value = max; +#endif + return value; +} + +inline uint32_t SkSetClearShift(uint32_t bits, bool cond, unsigned shift) +{ + SkASSERT((int)cond == 0 || (int)cond == 1); + return (bits & ~(1 << shift)) | ((int)cond << shift); +} + +inline uint32_t SkSetClearMask(uint32_t bits, bool cond, uint32_t mask) +{ + return cond ? bits | mask : bits & ~mask; +} + +////////////////////////////////////////////////////////////////////////////// + +/** \class SkNoncopyable + +SkNoncopyable is the base class for objects that may do not want to +be copied. It hides its copy-constructor and its assignment-operator. +*/ +class SkNoncopyable { +public: + SkNoncopyable() {} + +private: + SkNoncopyable(const SkNoncopyable&); + SkNoncopyable& operator=(const SkNoncopyable&); +}; + +class SkAutoFree : SkNoncopyable { +public: + SkAutoFree() : fPtr(NULL) {} + explicit SkAutoFree(void* ptr) : fPtr(ptr) {} + ~SkAutoFree() { sk_free(fPtr); } + + /** Return the currently allocate buffer, or null + */ + void* get() const { return fPtr; } + + /** Assign a new ptr allocated with sk_malloc (or null), and return the + previous ptr. Note it is the caller's responsibility to sk_free the + returned ptr. + */ + void* set(void* ptr) { + void* prev = fPtr; + fPtr = ptr; + return prev; + } + + /** Transfer ownership of the current ptr to the caller, setting the + internal reference to null. Note the caller is reponsible for calling + sk_free on the returned address. + */ + void* detach() { return this->set(NULL); } + + /** Free the current buffer, and set the internal reference to NULL. Same + as calling sk_free(detach()) + */ + void free() { + sk_free(fPtr); + fPtr = NULL; + } + +private: + void* fPtr; + // illegal + SkAutoFree(const SkAutoFree&); + SkAutoFree& operator=(const SkAutoFree&); +}; + +class SkAutoMalloc : public SkAutoFree { +public: + explicit SkAutoMalloc(size_t size) + : SkAutoFree(sk_malloc_flags(size, SK_MALLOC_THROW | SK_MALLOC_TEMP)) {} + + SkAutoMalloc(size_t size, unsigned flags) + : SkAutoFree(sk_malloc_flags(size, flags)) {} + SkAutoMalloc() {} + + void* alloc(size_t size, + unsigned flags = (SK_MALLOC_THROW | SK_MALLOC_TEMP)) { + sk_free(set(sk_malloc_flags(size, flags))); + return get(); + } +}; + +template <size_t kSize> class SkAutoSMalloc : SkNoncopyable { +public: + explicit SkAutoSMalloc(size_t size) + { + if (size <= kSize) + fPtr = fStorage; + else + fPtr = sk_malloc_flags(size, SK_MALLOC_THROW | SK_MALLOC_TEMP); + } + ~SkAutoSMalloc() + { + if (fPtr != (void*)fStorage) + sk_free(fPtr); + } + void* get() const { return fPtr; } +private: + void* fPtr; + uint32_t fStorage[(kSize + 3) >> 2]; + // illegal + SkAutoSMalloc(const SkAutoSMalloc&); + SkAutoSMalloc& operator=(const SkAutoSMalloc&); +}; + +#endif /* C++ */ + +#endif + diff --git a/skia/include/corecg/SkUserConfig.h b/skia/include/corecg/SkUserConfig.h new file mode 100644 index 0000000..be7622f --- /dev/null +++ b/skia/include/corecg/SkUserConfig.h @@ -0,0 +1,118 @@ +/* include/corecg/SkUserConfig.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkUserConfig_DEFINED +#define SkUserConfig_DEFINED + +// remove the x if you want to force us into SK_DEBUG mode +#ifdef SK_RELEASEx + #undef SK_RELEASE + #define SK_DEBUG +#endif + +#ifdef ANDROID + #include <utils/misc.h> + +#if 0 + // force fixed + #define SK_SCALAR_IS_FIXED + #undef SK_SCALAR_IS_FLOAT +#else + // force floats + #ifdef SK_SCALAR_IS_FIXED + #undef SK_SCALAR_IS_FIXED + #endif + #define SK_SCALAR_IS_FLOAT +#endif + + #define SK_CAN_USE_FLOAT + #define SkLONGLONG int64_t + + #if __BYTE_ORDER == __BIG_ENDIAN + #define SK_CPU_BENDIAN + #undef SK_CPU_LENDIAN + #else + #define SK_CPU_LENDIAN + #undef SK_CPU_BENDIAN + #endif + + // define SkDebugf to record file/line + #define SkDebugf(...) Android_SkDebugf(__FILE__, __LINE__, \ + __FUNCTION__, __VA_ARGS__) + void Android_SkDebugf(const char* file, int line, + const char* function, const char* format, ...); +#endif + +/* This file is included before all other headers, except for SkPreConfig.h. + That file uses various heuristics to make a "best guess" at settings for + the following build defines. + + However, in this file you can override any of those decisions by either + defining new symbols, or #undef symbols that were already set. +*/ + +// experimental for now +#define SK_SUPPORT_MIPMAP + +#ifdef SK_DEBUG + #define SK_SUPPORT_UNITTEST + /* Define SK_SIMULATE_FAILED_MALLOC to have + * sk_malloc throw an exception. Use this to + * detect unhandled memory leaks. */ + //#define SK_SIMULATE_FAILED_MALLOC + //#define SK_FIND_MEMORY_LEAKS +#endif + +#ifdef SK_BUILD_FOR_BREW + #include "SkBrewUserConfig.h" +#endif + +// ===== Begin Chrome-specific definitions ===== + +#define SK_SCALAR_IS_FLOAT +#undef SK_SCALAR_IS_FIXED + +#define SK_BUILD_FOR_WIN + +// VC8 doesn't support stdint.h, so we define those types here. +#define SK_IGNORE_STDINT_DOT_H +typedef signed char int8_t; +typedef unsigned char uint8_t; +typedef short int16_t; +typedef unsigned short uint16_t; +typedef int int32_t; +typedef unsigned uint32_t; + +// Don't use skia debug mode even when compiled as debug, because we don't +// care about debugging this library, only our app. +#undef SK_DEBUG +#undef SK_SUPPORT_UNITTEST +#define SK_RELEASE +#undef SK_RESTRICT +#define SK_RESTRICT +// Skia uses this deprecated bzero function to fill zeros into a string. +#define bzero(str, len) memset(str, 0, len) +#define SkDebugf(...) ((void)0) +#define SK_A32_SHIFT 24 +#define SK_R32_SHIFT 16 +#define SK_G32_SHIFT 8 +#define SK_B32_SHIFT 0 + +// ===== End Chrome-specific definitions ===== + +#endif + diff --git a/skia/picture/SkPictureFlat.cpp b/skia/picture/SkPictureFlat.cpp new file mode 100644 index 0000000..ba7119e --- /dev/null +++ b/skia/picture/SkPictureFlat.cpp @@ -0,0 +1,266 @@ +#include "SkPictureFlat.h" + +#include "SkColorFilter.h" +#include "SkDrawLooper.h" +#include "SkMaskFilter.h" +#include "SkRasterizer.h" +#include "SkShader.h" +#include "SkTypeface.h" +#include "SkXfermode.h" + +SkFlatData* SkFlatData::Alloc(SkChunkAlloc* heap, int32_t size, int index) { + SkFlatData* result = (SkFlatData*) heap->allocThrow(size + sizeof(SkFlatData)); + result->fIndex = index; + result->fAllocSize = size + sizeof(result->fAllocSize); + return result; +} + +SkFlatBitmap* SkFlatBitmap::Flatten(SkChunkAlloc* heap, const SkBitmap& bitmap, + int index, SkRefCntRecorder* rec) { + SkFlattenableWriteBuffer buffer(1024); + buffer.setRefCntRecorder(rec); + + bitmap.flatten(buffer); + size_t size = buffer.size(); + SkFlatBitmap* result = (SkFlatBitmap*) INHERITED::Alloc(heap, size, index); + buffer.flatten(result->fBitmapData); + return result; +} + +SkFlatMatrix* SkFlatMatrix::Flatten(SkChunkAlloc* heap, const SkMatrix& matrix, int index) { + int32_t size = sizeof(SkMatrix); + SkFlatMatrix* result = (SkFlatMatrix*) INHERITED::Alloc(heap, size, index); + memcpy(&result->fMatrixData, &matrix, sizeof(SkMatrix)); + return result; +} + +#ifdef SK_DEBUG_DUMP +void SkFlatMatrix::dump() const { + const SkMatrix* matrix = (const SkMatrix*) fMatrixData; + char pBuffer[DUMP_BUFFER_SIZE]; + char* bufferPtr = pBuffer; + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "matrix: "); + SkScalar scaleX = matrix->getScaleX(); + SkMatrix defaultMatrix; + defaultMatrix.reset(); + if (scaleX != defaultMatrix.getScaleX()) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "scaleX:%g ", SkScalarToFloat(scaleX)); + SkScalar scaleY = matrix->getScaleY(); + if (scaleY != defaultMatrix.getScaleY()) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "scaleY:%g ", SkScalarToFloat(scaleY)); + SkScalar skewX = matrix->getSkewX(); + if (skewX != defaultMatrix.getSkewX()) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "skewX:%g ", SkScalarToFloat(skewX)); + SkScalar skewY = matrix->getSkewY(); + if (skewY != defaultMatrix.getSkewY()) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "skewY:%g ", SkScalarToFloat(skewY)); + SkScalar translateX = matrix->getTranslateX(); + if (translateX != defaultMatrix.getTranslateX()) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "translateX:%g ", SkScalarToFloat(translateX)); + SkScalar translateY = matrix->getTranslateY(); + if (translateY != defaultMatrix.getTranslateY()) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "translateY:%g ", SkScalarToFloat(translateY)); + SkScalar perspX = matrix->getPerspX(); + if (perspX != defaultMatrix.getPerspX()) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "perspX:%g ", SkFractToFloat(perspX)); + SkScalar perspY = matrix->getPerspY(); + if (perspY != defaultMatrix.getPerspY()) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "perspY:%g ", SkFractToFloat(perspY)); + SkDebugf("%s\n", pBuffer); +} +#endif + +/////////////////////////////////////////////////////////////////////////////// + +SkFlatPaint* SkFlatPaint::Flatten(SkChunkAlloc* heap, const SkPaint& paint, + int index, SkRefCntRecorder* rec, + SkRefCntRecorder* faceRecorder) { + SkFlattenableWriteBuffer buffer(2*sizeof(SkPaint)); + buffer.setRefCntRecorder(rec); + buffer.setTypefaceRecorder(faceRecorder); + + paint.flatten(buffer); + uint32_t size = buffer.size(); + SkFlatPaint* result = (SkFlatPaint*) INHERITED::Alloc(heap, size, index); + buffer.flatten(&result->fPaintData); + return result; +} + +void SkFlatPaint::Read(const void* storage, SkPaint* paint, + SkRefCntPlayback* rcp, SkTypefacePlayback* facePlayback) { + SkFlattenableReadBuffer buffer(storage); + if (rcp) { + rcp->setupBuffer(buffer); + } + if (facePlayback) { + facePlayback->setupBuffer(buffer); + } + paint->unflatten(buffer); +} + +#ifdef SK_DEBUG_DUMP +void SkFlatPaint::dump() const { + SkPaint defaultPaint; + SkFlattenableReadBuffer buffer(fPaintData); + SkTypeface* typeface = (SkTypeface*) buffer.readPtr(); + char pBuffer[DUMP_BUFFER_SIZE]; + char* bufferPtr = pBuffer; + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "paint: "); + if (typeface != defaultPaint.getTypeface()) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "typeface:%p ", typeface); + SkScalar textSize = buffer.readScalar(); + if (textSize != defaultPaint.getTextSize()) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "textSize:%g ", SkScalarToFloat(textSize)); + SkScalar textScaleX = buffer.readScalar(); + if (textScaleX != defaultPaint.getTextScaleX()) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "textScaleX:%g ", SkScalarToFloat(textScaleX)); + SkScalar textSkewX = buffer.readScalar(); + if (textSkewX != defaultPaint.getTextSkewX()) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "textSkewX:%g ", SkScalarToFloat(textSkewX)); + const SkPathEffect* pathEffect = (const SkPathEffect*) buffer.readFlattenable(); + if (pathEffect != defaultPaint.getPathEffect()) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "pathEffect:%p ", pathEffect); + SkDELETE(pathEffect); + const SkShader* shader = (const SkShader*) buffer.readFlattenable(); + if (shader != defaultPaint.getShader()) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "shader:%p ", shader); + SkDELETE(shader); + const SkXfermode* xfermode = (const SkXfermode*) buffer.readFlattenable(); + if (xfermode != defaultPaint.getXfermode()) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "xfermode:%p ", xfermode); + SkDELETE(xfermode); + const SkMaskFilter* maskFilter = (const SkMaskFilter*) buffer.readFlattenable(); + if (maskFilter != defaultPaint.getMaskFilter()) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "maskFilter:%p ", maskFilter); + SkDELETE(maskFilter); + const SkColorFilter* colorFilter = (const SkColorFilter*) buffer.readFlattenable(); + if (colorFilter != defaultPaint.getColorFilter()) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "colorFilter:%p ", colorFilter); + SkDELETE(colorFilter); + const SkRasterizer* rasterizer = (const SkRasterizer*) buffer.readFlattenable(); + if (rasterizer != defaultPaint.getRasterizer()) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "rasterizer:%p ", rasterizer); + SkDELETE(rasterizer); + const SkDrawLooper* drawLooper = (const SkDrawLooper*) buffer.readFlattenable(); + if (drawLooper != defaultPaint.getLooper()) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "drawLooper:%p ", drawLooper); + SkDELETE(drawLooper); + unsigned color = buffer.readU32(); + if (color != defaultPaint.getColor()) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "color:0x%x ", color); + SkScalar strokeWidth = buffer.readScalar(); + if (strokeWidth != defaultPaint.getStrokeWidth()) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "strokeWidth:%g ", SkScalarToFloat(strokeWidth)); + SkScalar strokeMiter = buffer.readScalar(); + if (strokeMiter != defaultPaint.getStrokeMiter()) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "strokeMiter:%g ", SkScalarToFloat(strokeMiter)); + unsigned flags = buffer.readU16(); + if (flags != defaultPaint.getFlags()) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "flags:0x%x ", flags); + int align = buffer.readU8(); + if (align != defaultPaint.getTextAlign()) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "align:0x%x ", align); + int strokeCap = buffer.readU8(); + if (strokeCap != defaultPaint.getStrokeCap()) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "strokeCap:0x%x ", strokeCap); + int strokeJoin = buffer.readU8(); + if (strokeJoin != defaultPaint.getStrokeJoin()) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "align:0x%x ", strokeJoin); + int style = buffer.readU8(); + if (style != defaultPaint.getStyle()) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "style:0x%x ", style); + int textEncoding = buffer.readU8(); + if (textEncoding != defaultPaint.getTextEncoding()) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "textEncoding:0x%x ", textEncoding); + SkDebugf("%s\n", pBuffer); +} +#endif + +SkFlatPath* SkFlatPath::Flatten(SkChunkAlloc* heap, const SkPath& path, int index) { + SkFlattenableWriteBuffer buffer(1024); + path.flatten(buffer); + uint32_t size = buffer.size(); + SkFlatPath* result = (SkFlatPath*) INHERITED::Alloc(heap, size, index); + buffer.flatten(&result->fPathData); + return result; +} + +SkFlatRegion* SkFlatRegion::Flatten(SkChunkAlloc* heap, const SkRegion& region, int index) { + uint32_t size = region.flatten(NULL); + SkFlatRegion* result = (SkFlatRegion*) INHERITED::Alloc(heap, size, index); + region.flatten(&result->fRegionData); + return result; +} + +/////////////////////////////////////////////////////////////////////////////// + +SkRefCntPlayback::SkRefCntPlayback() : fCount(0), fArray(NULL) {} + +SkRefCntPlayback::~SkRefCntPlayback() { + this->reset(NULL); +} + +void SkRefCntPlayback::reset(const SkRefCntRecorder* rec) { + for (int i = 0; i < fCount; i++) { + SkASSERT(fArray[i]); + fArray[i]->unref(); + } + SkDELETE_ARRAY(fArray); + + if (rec) { + fCount = rec->count(); + fArray = SkNEW_ARRAY(SkRefCnt*, fCount); + rec->get(fArray); + for (int i = 0; i < fCount; i++) { + fArray[i]->ref(); + } + } else { + fCount = 0; + fArray = NULL; + } +} + +void SkRefCntPlayback::setCount(int count) { + this->reset(NULL); + + fCount = count; + fArray = SkNEW_ARRAY(SkRefCnt*, count); + bzero(fArray, count * sizeof(SkRefCnt*)); +} + +SkRefCnt* SkRefCntPlayback::set(int index, SkRefCnt* obj) { + SkASSERT((unsigned)index < (unsigned)fCount); + SkRefCnt_SafeAssign(fArray[index], obj); + return obj; +} + diff --git a/skia/picture/SkPictureFlat.h b/skia/picture/SkPictureFlat.h new file mode 100644 index 0000000..b4ce899 --- /dev/null +++ b/skia/picture/SkPictureFlat.h @@ -0,0 +1,225 @@ +#ifndef SkPictureFlat_DEFINED +#define SkPictureFlat_DEFINED + +#include "SkChunkAlloc.h" +#include "SkBitmap.h" +#include "SkPicture.h" +#include "SkMatrix.h" +#include "SkPaint.h" +#include "SkPath.h" +#include "SkRegion.h" + +enum DrawType { + UNUSED, + CLIP_PATH, + CLIP_REGION, + CLIP_RECT, + CONCAT, + DRAW_BITMAP, + DRAW_BITMAP_MATRIX, + DRAW_BITMAP_RECT, + DRAW_PAINT, + DRAW_PATH, + DRAW_PICTURE, + DRAW_POINTS, + DRAW_POS_TEXT, + DRAW_POS_TEXT_H, + DRAW_RECT_GENERAL, + DRAW_RECT_SIMPLE, + DRAW_SPRITE, + DRAW_TEXT, + DRAW_TEXT_ON_PATH, + DRAW_VERTICES, + RESTORE, + ROTATE, + SAVE, + SAVE_LAYER, + SCALE, + SKEW, + TRANSLATE +}; + +enum DrawVertexFlags { + DRAW_VERTICES_HAS_TEXS = 0x01, + DRAW_VERTICES_HAS_COLORS = 0x02, + DRAW_VERTICES_HAS_INDICES = 0x04 +}; + +class SkRefCntPlayback { +public: + SkRefCntPlayback(); + ~SkRefCntPlayback(); + + int count() const { return fCount; } + + void reset(const SkRefCntRecorder*); + + void setCount(int count); + SkRefCnt* set(int index, SkRefCnt*); + + virtual void setupBuffer(SkFlattenableReadBuffer& buffer) const { + buffer.setRefCntArray(fArray, fCount); + } + +protected: + int fCount; + SkRefCnt** fArray; +}; + +class SkTypefacePlayback : public SkRefCntPlayback { +public: + virtual void setupBuffer(SkFlattenableReadBuffer& buffer) const { + buffer.setTypefaceArray((SkTypeface**)fArray, fCount); + } +}; + +class SkFactoryPlayback { +public: + SkFactoryPlayback(int count) : fCount(count) { + fArray = SkNEW_ARRAY(SkFlattenable::Factory, count); + } + + ~SkFactoryPlayback() { + SkDELETE_ARRAY(fArray); + } + + SkFlattenable::Factory* base() const { return fArray; } + + void setupBuffer(SkFlattenableReadBuffer& buffer) const { + buffer.setFactoryPlayback(fArray, fCount); + } + +private: + int fCount; + SkFlattenable::Factory* fArray; +}; + +class SkFlatData { +public: + static int Compare(const SkFlatData* a, const SkFlatData* b) { + return memcmp(&a->fAllocSize, &b->fAllocSize, a->fAllocSize); + } + + int index() const { return fIndex; } + +#ifdef SK_DEBUG_SIZE + size_t size() const { return sizeof(fIndex) + fAllocSize; } +#endif + +protected: + static SkFlatData* Alloc(SkChunkAlloc* heap, int32_t size, int index); + + int fIndex; + int32_t fAllocSize; +}; + +class SkFlatBitmap : public SkFlatData { +public: + static SkFlatBitmap* Flatten(SkChunkAlloc*, const SkBitmap&, int index, + SkRefCntRecorder*); + + void unflatten(SkBitmap* bitmap, SkRefCntPlayback* rcp) const { + SkFlattenableReadBuffer buffer(fBitmapData); + if (rcp) { + rcp->setupBuffer(buffer); + } + bitmap->unflatten(buffer); + } + +#ifdef SK_DEBUG_VALIDATE + void validate() const { + // to be written + } +#endif + +private: + char fBitmapData[1]; + typedef SkFlatData INHERITED; +}; + +class SkFlatMatrix : public SkFlatData { +public: + static SkFlatMatrix* Flatten(SkChunkAlloc* heap, const SkMatrix& matrix, int index); + + void unflatten(SkMatrix* result) const { + memcpy(result, fMatrixData, sizeof(SkMatrix)); + } + +#ifdef SK_DEBUG_DUMP + void dump() const; +#endif + +#ifdef SK_DEBUG_VALIDATE + void validate() const { + // to be written + } +#endif + +private: + char fMatrixData[1]; + typedef SkFlatData INHERITED; +}; + +class SkFlatPaint : public SkFlatData { +public: + static SkFlatPaint* Flatten(SkChunkAlloc* heap, const SkPaint& paint, + int index, SkRefCntRecorder*, + SkRefCntRecorder* faceRecorder); + + void unflatten(SkPaint* result, SkRefCntPlayback* rcp, + SkTypefacePlayback* facePlayback) const { + Read(fPaintData, result, rcp, facePlayback); + } + + static void Read(const void* storage, SkPaint* paint, SkRefCntPlayback*, + SkTypefacePlayback* facePlayback); + +#ifdef SK_DEBUG_DUMP + void dump() const; +#endif + +private: + char fPaintData[1]; + typedef SkFlatData INHERITED; +}; + +class SkFlatPath : public SkFlatData { +public: + static SkFlatPath* Flatten(SkChunkAlloc* heap, const SkPath& path, int index); + + void unflatten(SkPath* path) const { + SkFlattenableReadBuffer buffer(fPathData); + path->unflatten(buffer); + } + +#ifdef SK_DEBUG_VALIDATE + void validate() const { + // to be written + } +#endif + +private: + char fPathData[1]; + typedef SkFlatData INHERITED; +}; + +class SkFlatRegion : public SkFlatData { +public: + static SkFlatRegion* Flatten(SkChunkAlloc* heap, const SkRegion& region, int index); + + void unflatten(SkRegion* result) const { + result->unflatten(fRegionData); + } + +#ifdef SK_DEBUG_VALIDATE + void validate() const { + // to be written + } +#endif + +private: + char fRegionData[1]; + typedef SkFlatData INHERITED; +}; + +#endif diff --git a/skia/picture/SkPicturePlayback.cpp b/skia/picture/SkPicturePlayback.cpp new file mode 100644 index 0000000..3bf162e --- /dev/null +++ b/skia/picture/SkPicturePlayback.cpp @@ -0,0 +1,1366 @@ +#include "SkPicturePlayback.h" +#include "SkPictureRecord.h" +#include "SkTypeface.h" +#include <new> + +SkPicturePlayback::SkPicturePlayback() { + this->init(); +} + +SkPicturePlayback::SkPicturePlayback(const SkPictureRecord& record) { +#ifdef SK_DEBUG_SIZE + size_t overallBytes, bitmapBytes, matricesBytes, + paintBytes, pathBytes, pictureBytes, regionBytes; + int bitmaps = record.bitmaps(&bitmapBytes); + int matrices = record.matrices(&matricesBytes); + int paints = record.paints(&paintBytes); + int paths = record.paths(&pathBytes); + int pictures = record.pictures(&pictureBytes); + int regions = record.regions(®ionBytes); + SkDebugf("picture record mem used %zd (stream %zd) ", record.size(), + record.streamlen()); + if (bitmaps != 0) + SkDebugf("bitmaps size %zd (bitmaps:%d) ", bitmapBytes, bitmaps); + if (matrices != 0) + SkDebugf("matrices size %zd (matrices:%d) ", matricesBytes, matrices); + if (paints != 0) + SkDebugf("paints size %zd (paints:%d) ", paintBytes, paints); + if (paths != 0) + SkDebugf("paths size %zd (paths:%d) ", pathBytes, paths); + if (pictures != 0) + SkDebugf("pictures size %zd (pictures:%d) ", pictureBytes, pictures); + if (regions != 0) + SkDebugf("regions size %zd (regions:%d) ", regionBytes, regions); + if (record.fPointWrites != 0) + SkDebugf("points size %zd (points:%d) ", record.fPointBytes, record.fPointWrites); + if (record.fRectWrites != 0) + SkDebugf("rects size %zd (rects:%d) ", record.fRectBytes, record.fRectWrites); + if (record.fTextWrites != 0) + SkDebugf("text size %zd (text strings:%d) ", record.fTextBytes, record.fTextWrites); + + SkDebugf("\n"); +#endif +#ifdef SK_DEBUG_DUMP + record.dumpMatrices(); + record.dumpPaints(); +#endif + + record.validate(); + const SkWriter32& writer = record.writeStream(); + init(); + if (writer.size() == 0) + return; + + { + size_t size = writer.size(); + void* buffer = sk_malloc_throw(size); + writer.flatten(buffer); + fReader.setMemory(buffer, size); // fReader owns buffer now + } + + // copy over the refcnt dictionary to our reader + // + fRCPlayback.reset(&record.fRCRecorder); + fRCPlayback.setupBuffer(fReader); + + fTFPlayback.reset(&record.fTFRecorder); + fTFPlayback.setupBuffer(fReader); + + const SkTDArray<const SkFlatBitmap* >& bitmaps = record.getBitmaps(); + fBitmapCount = bitmaps.count(); + if (fBitmapCount > 0) { + fBitmaps = SkNEW_ARRAY(SkBitmap, fBitmapCount); + for (const SkFlatBitmap** flatBitmapPtr = bitmaps.begin(); + flatBitmapPtr != bitmaps.end(); flatBitmapPtr++) { + const SkFlatBitmap* flatBitmap = *flatBitmapPtr; + int index = flatBitmap->index() - 1; + flatBitmap->unflatten(&fBitmaps[index], &fRCPlayback); + } + } + + const SkTDArray<const SkFlatMatrix* >& matrices = record.getMatrices(); + fMatrixCount = matrices.count(); + if (fMatrixCount > 0) { + fMatrices = SkNEW_ARRAY(SkMatrix, fMatrixCount); + for (const SkFlatMatrix** matrixPtr = matrices.begin(); + matrixPtr != matrices.end(); matrixPtr++) { + const SkFlatMatrix* flatMatrix = *matrixPtr; + flatMatrix->unflatten(&fMatrices[flatMatrix->index() - 1]); + } + } + + const SkTDArray<const SkFlatPaint* >& paints = record.getPaints(); + fPaintCount = paints.count(); + if (fPaintCount > 0) { + fPaints = SkNEW_ARRAY(SkPaint, fPaintCount); + for (const SkFlatPaint** flatPaintPtr = paints.begin(); + flatPaintPtr != paints.end(); flatPaintPtr++) { + const SkFlatPaint* flatPaint = *flatPaintPtr; + int index = flatPaint->index() - 1; + SkASSERT((unsigned)index < (unsigned)fPaintCount); + flatPaint->unflatten(&fPaints[index], &fRCPlayback, &fTFPlayback); + } + } + + const SkTDArray<const SkFlatPath* >& paths = record.getPaths(); + fPathCount = paths.count(); + if (fPathCount > 0) { + fPaths = SkNEW_ARRAY(SkPath, fPathCount); + for (const SkFlatPath** flatPathPtr = paths.begin(); + flatPathPtr != paths.end(); flatPathPtr++) { + const SkFlatPath* flatPath = *flatPathPtr; + flatPath->unflatten(&fPaths[flatPath->index() - 1]); + } + } + + const SkTDArray<SkPicture* >& pictures = record.getPictureRefs(); + fPictureCount = pictures.count(); + if (fPictureCount > 0) { + fPictureRefs = SkNEW_ARRAY(SkPicture*, fPictureCount); + for (int i = 0; i < fPictureCount; i++) { + fPictureRefs[i] = pictures[i]; + fPictureRefs[i]->ref(); + } + } + + const SkTDArray<const SkFlatRegion* >& regions = record.getRegions(); + fRegionCount = regions.count(); + if (fRegionCount > 0) { + fRegions = SkNEW_ARRAY(SkRegion, fRegionCount); + for (const SkFlatRegion** flatRegionPtr = regions.begin(); + flatRegionPtr != regions.end(); flatRegionPtr++) { + const SkFlatRegion* flatRegion = *flatRegionPtr; + flatRegion->unflatten(&fRegions[flatRegion->index() - 1]); + } + } + +#ifdef SK_DEBUG_SIZE + int overall = fPlayback->size(&overallBytes); + bitmaps = fPlayback->bitmaps(&bitmapBytes); + paints = fPlayback->paints(&paintBytes); + paths = fPlayback->paths(&pathBytes); + pictures = fPlayback->pictures(&pictureBytes); + regions = fPlayback->regions(®ionBytes); + SkDebugf("playback size %zd (objects:%d) ", overallBytes, overall); + if (bitmaps != 0) + SkDebugf("bitmaps size %zd (bitmaps:%d) ", bitmapBytes, bitmaps); + if (paints != 0) + SkDebugf("paints size %zd (paints:%d) ", paintBytes, paints); + if (paths != 0) + SkDebugf("paths size %zd (paths:%d) ", pathBytes, paths); + if (pictures != 0) + SkDebugf("pictures size %zd (pictures:%d) ", pictureBytes, pictures); + if (regions != 0) + SkDebugf("regions size %zd (regions:%d) ", regionBytes, regions); + SkDebugf("\n"); +#endif +} + +SkPicturePlayback::SkPicturePlayback(const SkPicturePlayback& src) { + this->init(); + + // copy the data from fReader + { + size_t size = src.fReader.size(); + void* buffer = sk_malloc_throw(size); + memcpy(buffer, src.fReader.base(), size); + fReader.setMemory(buffer, size); + } + + int i; + + fBitmapCount = src.fBitmapCount; + fBitmaps = SkNEW_ARRAY(SkBitmap, fBitmapCount); + for (i = 0; i < fBitmapCount; i++) { + fBitmaps[i] = src.fBitmaps[i]; + } + + fMatrixCount = src.fMatrixCount; + fMatrices = SkNEW_ARRAY(SkMatrix, fMatrixCount); + memcpy(fMatrices, src.fMatrices, fMatrixCount * sizeof(SkMatrix)); + + fPaintCount = src.fPaintCount; + fPaints = SkNEW_ARRAY(SkPaint, fPaintCount); + for (i = 0; i < fPaintCount; i++) { + fPaints[i] = src.fPaints[i]; + } + + fPathCount = src.fPathCount; + fPaths = SkNEW_ARRAY(SkPath, fPathCount); + for (i = 0; i < fPathCount; i++) { + fPaths[i] = src.fPaths[i]; + } + + fPictureCount = src.fPictureCount; + fPictureRefs = SkNEW_ARRAY(SkPicture*, fPictureCount); + for (int i = 0; i < fPictureCount; i++) { + fPictureRefs[i] = src.fPictureRefs[i]; + fPictureRefs[i]->ref(); + } + + fRegionCount = src.fRegionCount; + fRegions = SkNEW_ARRAY(SkRegion, fRegionCount); + for (i = 0; i < fRegionCount; i++) { + fRegions[i] = src.fRegions[i]; + } +} + +void SkPicturePlayback::init() { + fBitmaps = NULL; + fMatrices = NULL; + fPaints = NULL; + fPaths = NULL; + fPictureRefs = NULL; + fRegions = NULL; + fBitmapCount = fMatrixCount = fPaintCount = fPathCount = fPictureCount = + fRegionCount = 0; + + fFactoryPlayback = NULL; +} + +SkPicturePlayback::~SkPicturePlayback() { + sk_free((void*) fReader.base()); + + SkDELETE_ARRAY(fBitmaps); + SkDELETE_ARRAY(fMatrices); + SkDELETE_ARRAY(fPaints); + SkDELETE_ARRAY(fPaths); + SkDELETE_ARRAY(fRegions); + + for (int i = 0; i < fPictureCount; i++) { + fPictureRefs[i]->unref(); + } + SkDELETE_ARRAY(fPictureRefs); + + SkDELETE(fFactoryPlayback); +} + +void SkPicturePlayback::dumpSize() const { + SkDebugf("--- picture size: ops=%d bitmaps=%d [%d] matrices=%d [%d] paints=%d [%d] paths=%d regions=%d\n", + fReader.size(), + fBitmapCount, fBitmapCount * sizeof(SkBitmap), + fMatrixCount, fMatrixCount * sizeof(SkMatrix), + fPaintCount, fPaintCount * sizeof(SkPaint), + fPathCount, + fRegionCount); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +// The chunks are writte/read in this order... + +#define PICT_READER_TAG SkSetFourByteTag('r', 'e', 'a', 'd') +#define PICT_FACTORY_TAG SkSetFourByteTag('f', 'a', 'c', 't') +#define PICT_TYPEFACE_TAG SkSetFourByteTag('t', 'p', 'f', 'c') +#define PICT_PICTURE_TAG SkSetFourByteTag('p', 'c', 't', 'r') +#define PICT_ARRAYS_TAG SkSetFourByteTag('a', 'r', 'a', 'y') +// these are all inside the ARRAYS tag +#define PICT_BITMAP_TAG SkSetFourByteTag('b', 't', 'm', 'p') +#define PICT_MATRIX_TAG SkSetFourByteTag('m', 't', 'r', 'x') +#define PICT_PAINT_TAG SkSetFourByteTag('p', 'n', 't', ' ') +#define PICT_PATH_TAG SkSetFourByteTag('p', 't', 'h', ' ') +#define PICT_REGION_TAG SkSetFourByteTag('r', 'g', 'n', ' ') + + +#include "SkStream.h" + +static void writeTagSize(SkFlattenableWriteBuffer& buffer, uint32_t tag, + uint32_t size) { + buffer.write32(tag); + buffer.write32(size); +} + +static void writeTagSize(SkWStream* stream, uint32_t tag, + uint32_t size) { + stream->write32(tag); + stream->write32(size); +} + +static void writeFactories(SkWStream* stream, const SkFactoryRecorder& rec) { + int count = rec.count(); + + writeTagSize(stream, PICT_FACTORY_TAG, count); + + SkAutoSTMalloc<16, SkFlattenable::Factory> storage(count); + SkFlattenable::Factory* array = (SkFlattenable::Factory*)storage.get(); + rec.get(array); + + for (int i = 0; i < count; i++) { + const char* name = SkFlattenable::FactoryToName(array[i]); +// SkDebugf("---- write factories [%d] %p <%s>\n", i, array[i], name); + if (NULL == name || 0 == *name) { + stream->writePackedUInt(0); + } else { + uint32_t len = strlen(name); + stream->writePackedUInt(len); + stream->write(name, len); + } + } +} + +static void writeTypefaces(SkWStream* stream, const SkRefCntRecorder& rec) { + int count = rec.count(); + + writeTagSize(stream, PICT_TYPEFACE_TAG, count); + + SkAutoSTMalloc<16, SkTypeface*> storage(count); + SkTypeface** array = (SkTypeface**)storage.get(); + rec.get((SkRefCnt**)array); + + for (int i = 0; i < count; i++) { + array[i]->serialize(stream); + } +} + +void SkPicturePlayback::serialize(SkWStream* stream) const { + writeTagSize(stream, PICT_READER_TAG, fReader.size()); + stream->write(fReader.base(), fReader.size()); + + SkRefCntRecorder typefaceRecorder; + SkFactoryRecorder factRecorder; + + SkFlattenableWriteBuffer buffer(1024); + + buffer.setFlags(SkFlattenableWriteBuffer::kCrossProcess_Flag); + buffer.setTypefaceRecorder(&typefaceRecorder); + buffer.setFactoryRecorder(&factRecorder); + + int i; + + writeTagSize(buffer, PICT_BITMAP_TAG, fBitmapCount); + for (i = 0; i < fBitmapCount; i++) { + fBitmaps[i].flatten(buffer); + } + + writeTagSize(buffer, PICT_MATRIX_TAG, fMatrixCount); + buffer.writeMul4(fMatrices, fMatrixCount * sizeof(SkMatrix)); + + writeTagSize(buffer, PICT_PAINT_TAG, fPaintCount); + for (i = 0; i < fPaintCount; i++) { + fPaints[i].flatten(buffer); + } + + writeTagSize(buffer, PICT_PATH_TAG, fPathCount); + for (i = 0; i < fPathCount; i++) { + fPaths[i].flatten(buffer); + } + + writeTagSize(buffer, PICT_REGION_TAG, fRegionCount); + for (i = 0; i < fRegionCount; i++) { + uint32_t size = fRegions[i].flatten(NULL); + buffer.write32(size); + SkAutoSMalloc<512> storage(size); + fRegions[i].flatten(storage.get()); + buffer.writePad(storage.get(), size); + } + + // now we can write to the stream again + + writeFactories(stream, factRecorder); + writeTypefaces(stream, typefaceRecorder); + + writeTagSize(stream, PICT_PICTURE_TAG, fPictureCount); + for (i = 0; i < fPictureCount; i++) { + fPictureRefs[i]->serialize(stream); + } + + writeTagSize(stream, PICT_ARRAYS_TAG, buffer.size()); + buffer.writeToStream(stream); +} + +/////////////////////////////////////////////////////////////////////////////// + +static int readTagSize(SkFlattenableReadBuffer& buffer, uint32_t expectedTag) { + uint32_t tag = buffer.readU32(); + if (tag != expectedTag) { + sk_throw(); + } + return buffer.readU32(); +} + +static int readTagSize(SkStream* stream, uint32_t expectedTag) { + uint32_t tag = stream->readU32(); + if (tag != expectedTag) { + sk_throw(); + } + return stream->readU32(); +} + +SkPicturePlayback::SkPicturePlayback(SkStream* stream) { + this->init(); + + int i; + + { + size_t size = readTagSize(stream, PICT_READER_TAG); + void* storage = sk_malloc_throw(size); + stream->read(storage, size); + fReader.setMemory(storage, size); + } + + int factoryCount = readTagSize(stream, PICT_FACTORY_TAG); + fFactoryPlayback = SkNEW_ARGS(SkFactoryPlayback, (factoryCount)); + for (i = 0; i < factoryCount; i++) { + SkString str; + int len = stream->readPackedUInt(); + str.resize(len); + stream->read(str.writable_str(), len); +// SkDebugf("--- factory playback [%d] <%s>\n", i, str.c_str()); + fFactoryPlayback->base()[i] = SkFlattenable::NameToFactory(str.c_str()); + } + + int typefaceCount = readTagSize(stream, PICT_TYPEFACE_TAG); + fTFPlayback.setCount(typefaceCount); + for (i = 0; i < typefaceCount; i++) { + fTFPlayback.set(i, SkTypeface::Deserialize(stream))->unref(); + } + + fPictureCount = readTagSize(stream, PICT_PICTURE_TAG); + fPictureRefs = SkNEW_ARRAY(SkPicture*, fPictureCount); + for (i = 0; i < fPictureCount; i++) { + fPictureRefs[i] = SkNEW_ARGS(SkPicture, (stream)); + } + + /* + Now read the arrays chunk, and parse using a read buffer + */ + uint32_t size = readTagSize(stream, PICT_ARRAYS_TAG); + SkAutoMalloc storage(size); + stream->read(storage.get(), size); + + SkFlattenableReadBuffer buffer(storage.get(), size); + fFactoryPlayback->setupBuffer(buffer); + fTFPlayback.setupBuffer(buffer); + + fBitmapCount = readTagSize(buffer, PICT_BITMAP_TAG); + fBitmaps = SkNEW_ARRAY(SkBitmap, fBitmapCount); + for (i = 0; i < fBitmapCount; i++) { + fBitmaps[i].unflatten(buffer); + } + + fMatrixCount = readTagSize(buffer, PICT_MATRIX_TAG); + fMatrices = SkNEW_ARRAY(SkMatrix, fMatrixCount); + buffer.read(fMatrices, fMatrixCount * sizeof(SkMatrix)); + + fPaintCount = readTagSize(buffer, PICT_PAINT_TAG); + fPaints = SkNEW_ARRAY(SkPaint, fPaintCount); + for (i = 0; i < fPaintCount; i++) { + fPaints[i].unflatten(buffer); + } + + fPathCount = readTagSize(buffer, PICT_PATH_TAG); + fPaths = SkNEW_ARRAY(SkPath, fPathCount); + for (i = 0; i < fPathCount; i++) { + fPaths[i].unflatten(buffer); + } + + fRegionCount = readTagSize(buffer, PICT_REGION_TAG); + fRegions = SkNEW_ARRAY(SkRegion, fRegionCount); + for (i = 0; i < fRegionCount; i++) { + uint32_t size = buffer.readU32(); + uint32_t bytes = fRegions[i].unflatten(buffer.skip(size)); + SkASSERT(size == bytes); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void SkPicturePlayback::draw(SkCanvas& canvas) { +#ifdef ENABLE_TIME_DRAW + SkAutoTime at("SkPicture::draw", 50); +#endif + + TextContainer text; + fReader.rewind(); + bool clipBoundsDirty = true; + SkRect clipBounds; + + while (!fReader.eof()) { + switch (fReader.readInt()) { + case CLIP_PATH: { + const SkPath& path = getPath(); + SkRegion::Op op = (SkRegion::Op) getInt(); + size_t offsetToRestore = getInt(); + // HACK (false) until I can handle op==kReplace <reed> + if (!canvas.clipPath(path, op) && false) { + //SkDebugf("---- skip clipPath for %d bytes\n", offsetToRestore - fReader.offset()); + fReader.setOffset(offsetToRestore); + } + clipBoundsDirty = true; + } break; + case CLIP_REGION: { + const SkRegion& region = getRegion(); + SkRegion::Op op = (SkRegion::Op) getInt(); + size_t offsetToRestore = getInt(); + if (!canvas.clipRegion(region, op)) { + //SkDebugf("---- skip clipDeviceRgn for %d bytes\n", offsetToRestore - fReader.offset()); + fReader.setOffset(offsetToRestore); + } + clipBoundsDirty = true; + } break; + case CLIP_RECT: { + const SkRect* rect = fReader.skipRect(); + SkRegion::Op op = (SkRegion::Op) getInt(); + size_t offsetToRestore = getInt(); + if (!canvas.clipRect(*rect, op)) { + //SkDebugf("---- skip clipRect for %d bytes\n", offsetToRestore - fReader.offset()); + fReader.setOffset(offsetToRestore); + } + clipBoundsDirty = true; + } break; + case CONCAT: + canvas.concat(*getMatrix()); + clipBoundsDirty = true; + break; + case DRAW_BITMAP: { + const SkPaint* paint = getPaint(); + const SkBitmap& bitmap = getBitmap(); + const SkPoint* loc = fReader.skipPoint(); + canvas.drawBitmap(bitmap, loc->fX, loc->fY, paint); + } break; + case DRAW_BITMAP_RECT: { + const SkPaint* paint = getPaint(); + const SkBitmap& bitmap = getBitmap(); + const SkIRect* src = this->getIRectPtr(); // may be null + const SkRect* dst = fReader.skipRect(); // required + canvas.drawBitmapRect(bitmap, src, *dst, paint); + } break; + case DRAW_BITMAP_MATRIX: { + const SkPaint* paint = getPaint(); + const SkBitmap& bitmap = getBitmap(); + const SkMatrix* matrix = getMatrix(); + canvas.drawBitmapMatrix(bitmap, *matrix, paint); + } break; + case DRAW_PAINT: + canvas.drawPaint(*getPaint()); + break; + case DRAW_PATH: { + const SkPaint& paint = *getPaint(); + const SkPath& path = getPath(); + canvas.drawPath(path, paint); + } break; + case DRAW_PICTURE: + canvas.drawPicture(getPicture()); + break; + case DRAW_POINTS: { + const SkPaint& paint = *getPaint(); + SkCanvas::PointMode mode = (SkCanvas::PointMode)getInt(); + size_t count = getInt(); + const SkPoint* pts = (const SkPoint*)fReader.skip(sizeof(SkPoint) * count); + canvas.drawPoints(mode, count, pts, paint); + } break; + case DRAW_POS_TEXT: { + const SkPaint& paint = *getPaint(); + getText(&text); + size_t points = getInt(); + const SkPoint* pos = (const SkPoint*)fReader.skip(points * sizeof(SkPoint)); + canvas.drawPosText(text.text(), text.length(), pos, paint); + } break; + case DRAW_POS_TEXT_H: { + const SkPaint& paint = *getPaint(); + getText(&text); + size_t points = getInt(); + size_t byteLength = text.length(); + const SkScalar* xpos = (const SkScalar*)fReader.skip((3 + points) * sizeof(SkScalar)); + const SkScalar top = *xpos++; + const SkScalar bottom = *xpos++; + const SkScalar constY = *xpos++; + // would be nice to do this before we load the other fields + // (especially the text block). To do that we'd need to record + // the number of bytes to skip... + if (clipBoundsDirty) { + if (!canvas.getClipBounds(&clipBounds)) { + clipBounds.setEmpty(); + } + clipBoundsDirty = false; + } + if (top < clipBounds.fBottom && bottom > clipBounds.fTop) { + canvas.drawPosTextH(text.text(), byteLength, xpos, constY, + paint); + } + } break; + case DRAW_RECT_GENERAL: { + const SkPaint& paint = *getPaint(); + canvas.drawRect(*fReader.skipRect(), paint); + } break; + case DRAW_RECT_SIMPLE: { + const SkPaint& paint = *getPaint(); + const SkRect* rect = fReader.skipRect(); + if (clipBoundsDirty) { + if (!canvas.getClipBounds(&clipBounds)) { + clipBounds.setEmpty(); + } + clipBoundsDirty = false; + } + if (SkRect::Intersects(clipBounds, *rect)) { + canvas.drawRect(*rect, paint); + } + } break; + case DRAW_SPRITE: { + const SkPaint* paint = getPaint(); + const SkBitmap& bitmap = getBitmap(); + int left = getInt(); + int top = getInt(); + canvas.drawSprite(bitmap, left, top, paint); + } break; + case DRAW_TEXT: { + const SkPaint& paint = *getPaint(); + getText(&text); + const SkScalar* ptr = (const SkScalar*)fReader.skip(4 * sizeof(SkScalar)); + // ptr[0] == x + // ptr[1] == y + // ptr[2] == top + // ptr[3] == bottom + if (clipBoundsDirty) { + if (!canvas.getClipBounds(&clipBounds)) { + clipBounds.setEmpty(); + } + clipBoundsDirty = false; + } + if (ptr[2] < clipBounds.fBottom && ptr[3] > clipBounds.fTop) { + canvas.drawText(text.text(), text.length(), ptr[0], ptr[1], + paint); + } + } break; + case DRAW_TEXT_ON_PATH: { + const SkPaint& paint = *getPaint(); + getText(&text); + const SkPath& path = getPath(); + const SkMatrix* matrix = getMatrix(); + canvas.drawTextOnPath(text.text(), text.length(), path, + matrix, paint); + } break; + case DRAW_VERTICES: { + const SkPaint& paint = *getPaint(); + DrawVertexFlags flags = (DrawVertexFlags)getInt(); + SkCanvas::VertexMode vmode = (SkCanvas::VertexMode)getInt(); + int vCount = getInt(); + const SkPoint* verts = (const SkPoint*)fReader.skip( + vCount * sizeof(SkPoint)); + const SkPoint* texs = NULL; + const SkColor* colors = NULL; + const uint16_t* indices = NULL; + int iCount = 0; + if (flags & DRAW_VERTICES_HAS_TEXS) { + texs = (const SkPoint*)fReader.skip( + vCount * sizeof(SkPoint)); + } + if (flags & DRAW_VERTICES_HAS_COLORS) { + colors = (const SkColor*)fReader.skip( + vCount * sizeof(SkColor)); + } + if (flags & DRAW_VERTICES_HAS_INDICES) { + iCount = getInt(); + indices = (const uint16_t*)fReader.skip( + iCount * sizeof(uint16_t)); + } + canvas.drawVertices(vmode, vCount, verts, texs, colors, NULL, + indices, iCount, paint); + } break; + case RESTORE: + canvas.restore(); + clipBoundsDirty = true; + break; + case ROTATE: + canvas.rotate(getScalar()); + clipBoundsDirty = true; + break; + case SAVE: + canvas.save((SkCanvas::SaveFlags) getInt()); + break; + case SAVE_LAYER: { + const SkRect* boundsPtr = getRectPtr(); + const SkPaint* paint = getPaint(); + canvas.saveLayer(boundsPtr, paint, (SkCanvas::SaveFlags) getInt()); + } break; + case SCALE: { + SkScalar sx = getScalar(); + SkScalar sy = getScalar(); + canvas.scale(sx, sy); + clipBoundsDirty = true; + } break; + case SKEW: { + SkScalar sx = getScalar(); + SkScalar sy = getScalar(); + canvas.skew(sx, sy); + clipBoundsDirty = true; + } break; + case TRANSLATE: { + SkScalar dx = getScalar(); + SkScalar dy = getScalar(); + canvas.translate(dx, dy); + clipBoundsDirty = true; + } break; + default: + SkASSERT(0); + } + } + +// this->dumpSize(); +} + +/////////////////////////////////////////////////////////////////////////////// + +#if 0 +uint32_t SkPicturePlayback::flatten(void* storage) const { + SkWBuffer buffer(storage); + buffer.write32(fBitmapCount); + int index; + for (index = 0; index < fBitmapCount; index++) { + const SkBitmap& bitmap = fBitmaps[index]; + uint32_t size = bitmap.flatten(NULL, true); + buffer.write32(size); + void* local = buffer.skip(size); + bitmap.flatten(local, true); + } + buffer.write32(fPaintCount); + for (index = 0; index < fPaintCount; index++) { + SkFlattenableWriteBuffer flatWrite; + const SkPaint& paint = fPaints[index]; + SkFlatPaint::Write(&flatWrite, paint); + uint32_t size = flatWrite.pos(); + buffer.write32(size); + void* local = buffer.skip(size); + flatWrite.reset(local); + SkFlatPaint::Write(&flatWrite, paint); + } + buffer.write32(fPathCount); + for (index = 0; index < fPathCount; index++) { + const SkPath& path = fPaths[index]; + uint32_t size = path.flatten(NULL); + buffer.write32(size); + void* local = buffer.skip(size); + path.flatten(local); + } + +#if 0 + buffer.write32(fPictureCount); + for (index = 0; index < fPictureCount; index++) { + const SkPicture& picture = fPictures[index]; + uint32_t size = picture.flatten(NULL); + buffer.write32(size); + void* local = buffer.skip(size); + picture.flatten(local); + } +#endif + + buffer.write32(fRegionCount); + for (index = 0; index < fRegionCount; index++) { + const SkRegion& region = fRegions[index]; + size_t size = region.computeBufferSize(); + buffer.write32(size); + void* local = buffer.skip(size); + region.writeToBuffer(local); + } + fReader.rewind(); + size_t length = fReader.size(); + buffer.write32(length); + memcpy(buffer.skip(length), fReader.base(), length); + return (uint32_t) buffer.pos(); +} + +void SkPicturePlayback::unflatten(const void* storage) { + SkRBuffer buffer(storage); + int index; + fBitmapCount = buffer.readU32(); + fBitmaps = new SkBitmap[fBitmapCount]; + for (index = 0; index < fBitmapCount; index++) { + uint32_t size = buffer.readU32(); + const void* local = buffer.skip(size); + fBitmaps[index].unflatten(local); + } + fPaintCount = buffer.readU32(); + fPaints = new SkPaint[fPaintCount]; + for (index = 0; index < fPaintCount; index++) { + uint32_t size = buffer.readU32(); + const void* local = buffer.skip(size); + SkFlatPaint::Read(local, &fPaints[index]); + } + fPathCount = buffer.readU32(); + fPaths = new SkPath[fPathCount]; + for (index = 0; index < fPathCount; index++) { + uint32_t size = buffer.readU32(); + const void* local = buffer.skip(size); + fPaths[index].unflatten(local); + } + +#if 0 + fPictureCount = buffer.readU32(); + fPictures = new SkPicture[fPictureCount]; + for (index = 0; index < fPictureCount; index++) { + uint32_t size = buffer.readU32(); + const void* local = buffer.skip(size); + fPictures[index].unflatten(local); + } +#endif + + fRegionCount = buffer.readU32(); + fRegions = new SkRegion[fRegionCount]; + for (index = 0; index < fRegionCount; index++) { + uint32_t size = buffer.readU32(); + const void* local = buffer.skip(size); + fRegions[index].readFromBuffer(local); + } + int32_t length = buffer.readS32(); + const void* stream = buffer.skip(length); + fReader.setMemory(stream, length); +} +#endif + +/////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_DEBUG_SIZE +int SkPicturePlayback::size(size_t* sizePtr) { + int objects = bitmaps(sizePtr); + objects += paints(sizePtr); + objects += paths(sizePtr); + objects += pictures(sizePtr); + objects += regions(sizePtr); + *sizePtr = fReader.size(); + return objects; +} + +int SkPicturePlayback::bitmaps(size_t* size) { + size_t result = 0; + for (int index = 0; index < fBitmapCount; index++) { + // const SkBitmap& bitmap = fBitmaps[index]; + result += sizeof(SkBitmap); // bitmap->size(); + } + *size = result; + return fBitmapCount; +} + +int SkPicturePlayback::paints(size_t* size) { + size_t result = 0; + for (int index = 0; index < fPaintCount; index++) { + // const SkPaint& paint = fPaints[index]; + result += sizeof(SkPaint); // paint->size(); + } + *size = result; + return fPaintCount; +} + +int SkPicturePlayback::paths(size_t* size) { + size_t result = 0; + for (int index = 0; index < fPathCount; index++) { + const SkPath& path = fPaths[index]; + result += path.flatten(NULL); + } + *size = result; + return fPathCount; +} + +int SkPicturePlayback::regions(size_t* size) { + size_t result = 0; + for (int index = 0; index < fRegionCount; index++) { + // const SkRegion& region = fRegions[index]; + result += sizeof(SkRegion); // region->size(); + } + *size = result; + return fRegionCount; +} +#endif + +#ifdef SK_DEBUG_DUMP +void SkPicturePlayback::dumpBitmap(const SkBitmap& bitmap) const { + char pBuffer[DUMP_BUFFER_SIZE]; + char* bufferPtr = pBuffer; + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "BitmapData bitmap%p = {", &bitmap); + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "{kWidth, %d}, ", bitmap.width()); + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "{kHeight, %d}, ", bitmap.height()); + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "{kRowBytes, %d}, ", bitmap.rowBytes()); +// start here; + SkDebugf("%s{0}};\n", pBuffer); +} + +void dumpMatrix(const SkMatrix& matrix) const { + SkMatrix defaultMatrix; + defaultMatrix.reset(); + char pBuffer[DUMP_BUFFER_SIZE]; + char* bufferPtr = pBuffer; + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "MatrixData matrix%p = {", &matrix); + SkScalar scaleX = matrix.getScaleX(); + if (scaleX != defaultMatrix.getScaleX()) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "{kScaleX, %g}, ", SkScalarToFloat(scaleX)); + SkScalar scaleY = matrix.getScaleY(); + if (scaleY != defaultMatrix.getScaleY()) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "{kScaleY, %g}, ", SkScalarToFloat(scaleY)); + SkScalar skewX = matrix.getSkewX(); + if (skewX != defaultMatrix.getSkewX()) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "{kSkewX, %g}, ", SkScalarToFloat(skewX)); + SkScalar skewY = matrix.getSkewY(); + if (skewY != defaultMatrix.getSkewY()) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "{kSkewY, %g}, ", SkScalarToFloat(skewY)); + SkScalar translateX = matrix.getTranslateX(); + if (translateX != defaultMatrix.getTranslateX()) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "{kTranslateX, %g}, ", SkScalarToFloat(translateX)); + SkScalar translateY = matrix.getTranslateY(); + if (translateY != defaultMatrix.getTranslateY()) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "{kTranslateY, %g}, ", SkScalarToFloat(translateY)); + SkScalar perspX = matrix.getPerspX(); + if (perspX != defaultMatrix.getPerspX()) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "{kPerspX, %g}, ", SkFractToFloat(perspX)); + SkScalar perspY = matrix.getPerspY(); + if (perspY != defaultMatrix.getPerspY()) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "{kPerspY, %g}, ", SkFractToFloat(perspY)); + SkDebugf("%s{0}};\n", pBuffer); +} + +void dumpPaint(const SkPaint& paint) const { + SkPaint defaultPaint; + char pBuffer[DUMP_BUFFER_SIZE]; + char* bufferPtr = pBuffer; + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "PaintPointers paintPtrs%p = {", &paint); + const SkTypeface* typeface = paint.getTypeface(); + if (typeface != defaultPaint.getTypeface()) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "{kTypeface, %p}, ", typeface); + const SkPathEffect* pathEffect = paint.getPathEffect(); + if (pathEffect != defaultPaint.getPathEffect()) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "{kPathEffect, %p}, ", pathEffect); + const SkShader* shader = paint.getShader(); + if (shader != defaultPaint.getShader()) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "{kShader, %p}, ", shader); + const SkXfermode* xfermode = paint.getXfermode(); + if (xfermode != defaultPaint.getXfermode()) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "{kXfermode, %p}, ", xfermode); + const SkMaskFilter* maskFilter = paint.getMaskFilter(); + if (maskFilter != defaultPaint.getMaskFilter()) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "{kMaskFilter, %p}, ", maskFilter); + const SkColorFilter* colorFilter = paint.getColorFilter(); + if (colorFilter != defaultPaint.getColorFilter()) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "{kColorFilter, %p}, ", colorFilter); + const SkRasterizer* rasterizer = paint.getRasterizer(); + if (rasterizer != defaultPaint.getRasterizer()) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "{kRasterizer, %p}, ", rasterizer); + const SkDrawLooper* drawLooper = paint.getLooper(); + if (drawLooper != defaultPaint.getLooper()) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "{kDrawLooper, %p}, ", drawLooper); + SkDebugf("%s{0}};\n", pBuffer); + bufferPtr = pBuffer; + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "PaintScalars paintScalars%p = {", &paint); + SkScalar textSize = paint.getTextSize(); + if (textSize != defaultPaint.getTextSize()) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "{kTextSize, %g}, ", SkScalarToFloat(textSize)); + SkScalar textScaleX = paint.getTextScaleX(); + if (textScaleX != defaultPaint.getTextScaleX()) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "{kTextScaleX, %g}, ", SkScalarToFloat(textScaleX)); + SkScalar textSkewX = paint.getTextSkewX(); + if (textSkewX != defaultPaint.getTextSkewX()) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "{kTextSkewX, %g}, ", SkScalarToFloat(textSkewX)); + SkScalar strokeWidth = paint.getStrokeWidth(); + if (strokeWidth != defaultPaint.getStrokeWidth()) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "{kStrokeWidth, %g}, ", SkScalarToFloat(strokeWidth)); + SkScalar strokeMiter = paint.getStrokeMiter(); + if (strokeMiter != defaultPaint.getStrokeMiter()) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "{kStrokeMiter, %g}, ", SkScalarToFloat(strokeMiter)); + SkDebugf("%s{0}};\n", pBuffer); + bufferPtr = pBuffer; + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "PaintInts = paintInts%p = {", &paint); + unsigned color = paint.getColor(); + if (color != defaultPaint.getColor()) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "{kColor, 0x%x}, ", color); + unsigned flags = paint.getFlags(); + if (flags != defaultPaint.getFlags()) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "{kFlags, 0x%x}, ", flags); + int align = paint.getTextAlign(); + if (align != defaultPaint.getTextAlign()) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "{kAlign, 0x%x}, ", align); + int strokeCap = paint.getStrokeCap(); + if (strokeCap != defaultPaint.getStrokeCap()) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "{kStrokeCap, 0x%x}, ", strokeCap); + int strokeJoin = paint.getStrokeJoin(); + if (strokeJoin != defaultPaint.getStrokeJoin()) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "{kAlign, 0x%x}, ", strokeJoin); + int style = paint.getStyle(); + if (style != defaultPaint.getStyle()) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "{kStyle, 0x%x}, ", style); + int textEncoding = paint.getTextEncoding(); + if (textEncoding != defaultPaint.getTextEncoding()) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "{kTextEncoding, 0x%x}, ", textEncoding); + SkDebugf("%s{0}};\n", pBuffer); + + SkDebugf("PaintData paint%p = {paintPtrs%p, paintScalars%p, paintInts%p};\n", + &paint, &paint, &paint, &paint); +} + +void SkPicturePlayback::dumpPath(const SkPath& path) const { + SkDebugf("path dump unimplemented\n"); +} + +void SkPicturePlayback::dumpPicture(const SkPicture& picture) const { + SkDebugf("picture dump unimplemented\n"); +} + +void SkPicturePlayback::dumpRegion(const SkRegion& region) const { + SkDebugf("region dump unimplemented\n"); +} + +int SkPicturePlayback::dumpDrawType(char* bufferPtr, char* buffer, DrawType drawType) { + return snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - buffer), + "k%s, ", DrawTypeToString(drawType)); +} + +int SkPicturePlayback::dumpInt(char* bufferPtr, char* buffer, char* name) { + return snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - buffer), + "%s:%d, ", name, getInt()); +} + +int SkPicturePlayback::dumpRect(char* bufferPtr, char* buffer, char* name) { + const SkRect* rect = fReader.skipRect(); + return snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - buffer), + "%s:{l:%g t:%g r:%g b:%g}, ", name, SkScalarToFloat(rect.fLeft), + SkScalarToFloat(rect.fTop), + SkScalarToFloat(rect.fRight), SkScalarToFloat(rect.fBottom)); +} + +int SkPicturePlayback::dumpPoint(char* bufferPtr, char* buffer, char* name) { + SkPoint pt; + getPoint(&pt); + return snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - buffer), + "%s:{x:%g y:%g}, ", name, SkScalarToFloat(pt.fX), + SkScalarToFloat(pt.fY)); +} + +void SkPicturePlayback::dumpPointArray(char** bufferPtrPtr, char* buffer, int count) { + char* bufferPtr = *bufferPtrPtr; + const SkPoint* pts = (const SkPoint*)fReadStream.getAtPos(); + fReadStream.skip(sizeof(SkPoint) * count); + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - buffer), + "count:%d {", count); + for (int index = 0; index < count; index++) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - buffer), + "{x:%g y:%g}, ", SkScalarToFloat(pts[index].fX), + SkScalarToFloat(pts[index].fY)); + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - buffer), + "} "); + *bufferPtrPtr = bufferPtr; +} + +int SkPicturePlayback::dumpPtr(char* bufferPtr, char* buffer, char* name, void* ptr) { + return snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - buffer), + "%s:%p, ", name, ptr); +} + +int SkPicturePlayback::dumpRectPtr(char* bufferPtr, char* buffer, char* name) { + char result; + fReadStream.read(&result, sizeof(result)); + if (result) + return dumpRect(bufferPtr, buffer, name); + else + return snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - buffer), + "%s:NULL, ", name); +} + +int SkPicturePlayback::dumpScalar(char* bufferPtr, char* buffer, char* name) { + return snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - buffer), + "%s:%d, ", name, getScalar()); +} + +void SkPicturePlayback::dumpText(char** bufferPtrPtr, char* buffer) { + char* bufferPtr = *bufferPtrPtr; + int length = getInt(); + bufferPtr += dumpDrawType(bufferPtr, buffer); + fReadStream.skipToAlign4(); + char* text = (char*) fReadStream.getAtPos(); + fReadStream.skip(length); + bufferPtr += dumpInt(bufferPtr, buffer, "length"); + int limit = DUMP_BUFFER_SIZE - (bufferPtr - buffer) - 2; + length >>= 1; + if (limit > length) + limit = length; + if (limit > 0) { + *bufferPtr++ = '"'; + for (int index = 0; index < limit; index++) { + *bufferPtr++ = *(unsigned short*) text; + text += sizeof(unsigned short); + } + *bufferPtr++ = '"'; + } + *bufferPtrPtr = bufferPtr; +} + +#define DUMP_DRAWTYPE(drawType) \ + bufferPtr += dumpDrawType(bufferPtr, buffer, drawType) + +#define DUMP_INT(name) \ + bufferPtr += dumpInt(bufferPtr, buffer, #name) + +#define DUMP_RECT_PTR(name) \ + bufferPtr += dumpRectPtr(bufferPtr, buffer, #name) + +#define DUMP_POINT(name) \ + bufferPtr += dumpRect(bufferPtr, buffer, #name) + +#define DUMP_RECT(name) \ + bufferPtr += dumpRect(bufferPtr, buffer, #name) + +#define DUMP_POINT_ARRAY(count) \ + dumpPointArray(&bufferPtr, buffer, count) + +#define DUMP_PTR(name, ptr) \ + bufferPtr += dumpPtr(bufferPtr, buffer, #name, (void*) ptr) + +#define DUMP_SCALAR(name) \ + bufferPtr += dumpScalar(bufferPtr, buffer, #name) + +#define DUMP_TEXT() \ + dumpText(&bufferPtr, buffer) + +void SkPicturePlayback::dumpStream() { + SkDebugf("RecordStream stream = {\n"); + DrawType drawType; + TextContainer text; + fReadStream.rewind(); + char buffer[DUMP_BUFFER_SIZE], * bufferPtr; + while (fReadStream.read(&drawType, sizeof(drawType))) { + bufferPtr = buffer; + DUMP_DRAWTYPE(drawType); + switch (drawType) { + case CLIP_PATH: { + DUMP_PTR(SkPath, &getPath()); + DUMP_INT(SkRegion::Op); + DUMP_INT(offsetToRestore); + } break; + case CLIP_REGION: { + DUMP_PTR(SkRegion, &getRegion()); + DUMP_INT(SkRegion::Op); + DUMP_INT(offsetToRestore); + } break; + case CLIP_RECT: { + DUMP_RECT(rect); + DUMP_INT(SkRegion::Op); + DUMP_INT(offsetToRestore); + } break; + case CONCAT: + DUMP_PTR(SkMatrix, getMatrix()); + break; + case DRAW_BITMAP: { + DUMP_PTR(SkPaint, getPaint()); + DUMP_PTR(SkBitmap, &getBitmap()); + DUMP_SCALAR(left); + DUMP_SCALAR(top); + } break; + case DRAW_PAINT: + DUMP_PTR(SkPaint, getPaint()); + break; + case DRAW_PATH: { + DUMP_PTR(SkPaint, getPaint()); + DUMP_PTR(SkPath, &getPath()); + } break; + case DRAW_PICTURE: { + DUMP_PTR(SkPicture, &getPicture()); + } break; + case DRAW_POINTS: { + DUMP_PTR(SkPaint, getPaint()); + (void)getInt(); // PointMode + size_t count = getInt(); + fReadStream.skipToAlign4(); + DUMP_POINT_ARRAY(count); + } break; + case DRAW_POS_TEXT: { + DUMP_PTR(SkPaint, getPaint()); + DUMP_TEXT(); + size_t points = getInt(); + fReadStream.skipToAlign4(); + DUMP_POINT_ARRAY(points); + } break; + case DRAW_POS_TEXT_H: { + DUMP_PTR(SkPaint, getPaint()); + DUMP_TEXT(); + size_t points = getInt(); + fReadStream.skipToAlign4(); + DUMP_SCALAR(top); + DUMP_SCALAR(bottom); + DUMP_SCALAR(constY); + DUMP_POINT_ARRAY(points); + } break; + case DRAW_RECT_GENERAL: + case DRAW_RECT_SIMPLE: { + DUMP_PTR(SkPaint, getPaint()); + DUMP_RECT(rect); + } break; + case DRAW_SPRITE: { + DUMP_PTR(SkPaint, getPaint()); + DUMP_PTR(SkBitmap, &getBitmap()); + DUMP_SCALAR(left); + DUMP_SCALAR(top); + } break; + case DRAW_TEXT: { + DUMP_PTR(SkPaint, getPaint()); + DUMP_TEXT(); + DUMP_SCALAR(x); + DUMP_SCALAR(y); + } break; + case DRAW_TEXT_ON_PATH: { + DUMP_PTR(SkPaint, getPaint()); + DUMP_TEXT(); + DUMP_PTR(SkPath, &getPath()); + DUMP_PTR(SkMatrix, getMatrix()); + } break; + case RESTORE: + break; + case ROTATE: + DUMP_SCALAR(rotate); + break; + case SAVE: + DUMP_INT(SkCanvas::SaveFlags); + break; + case SAVE_LAYER: { + DUMP_RECT_PTR(layer); + DUMP_PTR(SkPaint, getPaint()); + DUMP_INT(SkCanvas::SaveFlags); + } break; + case SCALE: { + DUMP_SCALAR(sx); + DUMP_SCALAR(sy); + } break; + case SKEW: { + DUMP_SCALAR(sx); + DUMP_SCALAR(sy); + } break; + case TRANSLATE: { + DUMP_SCALAR(dx); + DUMP_SCALAR(dy); + } break; + default: + SkASSERT(0); + } + SkDebugf("%s\n", buffer); + } +} + +void SkPicturePlayback::dump() const { + char pBuffer[DUMP_BUFFER_SIZE]; + char* bufferPtr = pBuffer; + int index; + if (fBitmapCount > 0) + SkDebugf("// bitmaps (%d)\n", fBitmapCount); + for (index = 0; index < fBitmapCount; index++) { + const SkBitmap& bitmap = fBitmaps[index]; + dumpBitmap(bitmap); + } + if (fBitmapCount > 0) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "Bitmaps bitmaps = {"); + for (index = 0; index < fBitmapCount; index++) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "bitmap%p, ", &fBitmaps[index]); + if (fBitmapCount > 0) + SkDebugf("%s0};\n", pBuffer); + + if (fMatrixCount > 0) + SkDebugf("// matrices (%d)\n", fMatrixCount); + for (index = 0; index < fMatrixCount; index++) { + const SkMatrix& matrix = fMatrices[index]; + dumpMatrix(matrix); + } + bufferPtr = pBuffer; + if (fMatrixCount > 0) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "Matrices matrices = {"); + for (index = 0; index < fMatrixCount; index++) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "matrix%p, ", &fMatrices[index]); + if (fMatrixCount > 0) + SkDebugf("%s0};\n", pBuffer); + + if (fPaintCount > 0) + SkDebugf("// paints (%d)\n", fPaintCount); + for (index = 0; index < fPaintCount; index++) { + const SkPaint& paint = fPaints[index]; + dumpPaint(paint); + } + bufferPtr = pBuffer; + if (fPaintCount > 0) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "Paints paints = {"); + for (index = 0; index < fPaintCount; index++) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "paint%p, ", &fPaints[index]); + if (fPaintCount > 0) + SkDebugf("%s0};\n", pBuffer); + + for (index = 0; index < fPathCount; index++) { + const SkPath& path = fPaths[index]; + dumpPath(path); + } + bufferPtr = pBuffer; + if (fPathCount > 0) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "Paths paths = {"); + for (index = 0; index < fPathCount; index++) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "path%p, ", &fPaths[index]); + if (fPathCount > 0) + SkDebugf("%s0};\n", pBuffer); + + for (index = 0; index < fPictureCount; index++) { + dumpPicture(*fPictureRefs[index]); + } + bufferPtr = pBuffer; + if (fPictureCount > 0) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "Pictures pictures = {"); + for (index = 0; index < fPictureCount; index++) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "picture%p, ", fPictureRefs[index]); + if (fPictureCount > 0) + SkDebugf("%s0};\n", pBuffer); + + for (index = 0; index < fRegionCount; index++) { + const SkRegion& region = fRegions[index]; + dumpRegion(region); + } + bufferPtr = pBuffer; + if (fRegionCount > 0) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "Regions regions = {"); + for (index = 0; index < fRegionCount; index++) + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + "region%p, ", &fRegions[index]); + if (fRegionCount > 0) + SkDebugf("%s0};\n", pBuffer); + + const_cast<SkPicturePlayback*>(this)->dumpStream(); +} + +#endif + diff --git a/skia/picture/SkPicturePlayback.h b/skia/picture/SkPicturePlayback.h new file mode 100644 index 0000000..7e99bd0 --- /dev/null +++ b/skia/picture/SkPicturePlayback.h @@ -0,0 +1,166 @@ +#ifndef SkPicturePlayback_DEFINED +#define SkPicturePlayback_DEFINED + +#include "SkPicture.h" +#include "SkReader32.h" + +#include "SkBitmap.h" +#include "SkMatrix.h" +#include "SkPaint.h" +#include "SkPath.h" +#include "SkRegion.h" +#include "SkPictureFlat.h" + +class SkPictureRecord; +class SkStream; +class SkWStream; + +class SkPicturePlayback { +public: + SkPicturePlayback(); + SkPicturePlayback(const SkPicturePlayback& src); + explicit SkPicturePlayback(const SkPictureRecord& record); + explicit SkPicturePlayback(SkStream*); + + virtual ~SkPicturePlayback(); + + void draw(SkCanvas& canvas); + + void serialize(SkWStream*) const; + + void dumpSize() const; + +private: + + class TextContainer { + public: + size_t length() { return fByteLength; } + const void* text() { return (const void*) fText; } + size_t fByteLength; + const char* fText; + }; + + const SkBitmap& getBitmap() { + int index = getInt(); + SkASSERT(index > 0); + return fBitmaps[index - 1]; + } + + int getIndex() { return fReader.readInt(); } + int getInt() { return fReader.readInt(); } + + const SkMatrix* getMatrix() { + int index = getInt(); + if (index == 0) { + return NULL; + } + SkASSERT(index > 0 && index <= fMatrixCount); + return &fMatrices[index - 1]; + } + + const SkPath& getPath() { + int index = getInt(); + SkASSERT(index > 0 && index <= fPathCount); + return fPaths[index - 1]; + } + + SkPicture& getPicture() { + int index = getInt(); + SkASSERT(index > 0 && index <= fPictureCount); + return *fPictureRefs[index - 1]; + } + + const SkPaint* getPaint() { + int index = getInt(); + if (index == 0) { + return NULL; + } + SkASSERT(index > 0 && index <= fPaintCount); + return &fPaints[index - 1]; + } + + const SkRect* getRectPtr() { + if (fReader.readBool()) { + return fReader.skipRect(); + } else { + return NULL; + } + } + + const SkIRect* getIRectPtr() { + if (fReader.readBool()) { + return (const SkIRect*)fReader.skip(sizeof(SkIRect)); + } else { + return NULL; + } + } + + const SkRegion& getRegion() { + int index = getInt(); + SkASSERT(index > 0); + return fRegions[index - 1]; + } + + SkScalar getScalar() { return fReader.readScalar(); } + + void getText(TextContainer* text) { + size_t length = text->fByteLength = getInt(); + text->fText = (const char*)fReader.skip(length); + } + + void init(); + +#ifdef SK_DEBUG_SIZE +public: + int size(size_t* sizePtr); + int bitmaps(size_t* size); + int paints(size_t* size); + int paths(size_t* size); + int regions(size_t* size); +#endif + +#ifdef SK_DEBUG_DUMP +private: + void dumpBitmap(const SkBitmap& bitmap) const; + void dumpMatrix(const SkMatrix& matrix) const; + void dumpPaint(const SkPaint& paint) const; + void dumpPath(const SkPath& path) const; + void dumpPicture(const SkPicture& picture) const; + void dumpRegion(const SkRegion& region) const; + int dumpDrawType(char* bufferPtr, char* buffer, DrawType drawType); + int dumpInt(char* bufferPtr, char* buffer, char* name); + int dumpRect(char* bufferPtr, char* buffer, char* name); + int dumpPoint(char* bufferPtr, char* buffer, char* name); + void dumpPointArray(char** bufferPtrPtr, char* buffer, int count); + int dumpPtr(char* bufferPtr, char* buffer, char* name, void* ptr); + int dumpRectPtr(char* bufferPtr, char* buffer, char* name); + int dumpScalar(char* bufferPtr, char* buffer, char* name); + void dumpText(char** bufferPtrPtr, char* buffer); + void dumpStream(); + +public: + void dump() const; +#endif + +private: + SkBitmap* fBitmaps; + int fBitmapCount; + SkMatrix* fMatrices; + int fMatrixCount; + SkPaint* fPaints; + int fPaintCount; + SkPath* fPaths; + int fPathCount; + SkRegion* fRegions; + int fRegionCount; + mutable SkFlattenableReadBuffer fReader; + + SkPicture** fPictureRefs; + int fPictureCount; + + SkRefCntPlayback fRCPlayback; + SkTypefacePlayback fTFPlayback; + SkFactoryPlayback* fFactoryPlayback; +}; + +#endif diff --git a/skia/picture/SkPictureRecord.cpp b/skia/picture/SkPictureRecord.cpp new file mode 100644 index 0000000..718526b --- /dev/null +++ b/skia/picture/SkPictureRecord.cpp @@ -0,0 +1,688 @@ +#include "SkPictureRecord.h" +#include "SkTSearch.h" + +#define MIN_WRITER_SIZE 16384 +#define HEAP_BLOCK_SIZE 4096 + +SkPictureRecord::SkPictureRecord() : + fHeap(HEAP_BLOCK_SIZE), fWriter(MIN_WRITER_SIZE) { + fBitmapIndex = fMatrixIndex = fPaintIndex = fPathIndex = fRegionIndex = 1; +#ifdef SK_DEBUG_SIZE + fPointBytes = fRectBytes = fTextBytes = 0; + fPointWrites = fRectWrites = fTextWrites = 0; +#endif + + fRestoreOffsetStack.setReserve(32); + fRestoreOffsetStack.push(0); +} + +SkPictureRecord::~SkPictureRecord() { + reset(); +} + +/////////////////////////////////////////////////////////////////////////////// + +int SkPictureRecord::save(SaveFlags flags) { + addDraw(SAVE); + addInt(flags); + + fRestoreOffsetStack.push(0); + + validate(); + return this->INHERITED::save(flags); +} + +int SkPictureRecord::saveLayer(const SkRect* bounds, const SkPaint* paint, + SaveFlags flags) { + addDraw(SAVE_LAYER); + addRectPtr(bounds); + addPaintPtr(paint); + addInt(flags); + + fRestoreOffsetStack.push(0); + + validate(); + return this->INHERITED::saveLayer(bounds, paint, flags); +} + +void SkPictureRecord::restore() { + + // patch up the clip offsets + { + uint32_t restoreOffset = (uint32_t)fWriter.size(); + uint32_t offset = fRestoreOffsetStack.top(); + while (offset) { + uint32_t* peek = fWriter.peek32(offset); + offset = *peek; + *peek = restoreOffset; + } + fRestoreOffsetStack.pop(); + } + + addDraw(RESTORE); + validate(); + return this->INHERITED::restore(); +} + +bool SkPictureRecord::translate(SkScalar dx, SkScalar dy) { + addDraw(TRANSLATE); + addScalar(dx); + addScalar(dy); + validate(); + return this->INHERITED::translate(dx, dy); +} + +bool SkPictureRecord::scale(SkScalar sx, SkScalar sy) { + addDraw(SCALE); + addScalar(sx); + addScalar(sy); + validate(); + return this->INHERITED::scale(sx, sy); +} + +bool SkPictureRecord::rotate(SkScalar degrees) { + addDraw(ROTATE); + addScalar(degrees); + validate(); + return this->INHERITED::rotate(degrees); +} + +bool SkPictureRecord::skew(SkScalar sx, SkScalar sy) { + addDraw(SKEW); + addScalar(sx); + addScalar(sy); + validate(); + return this->INHERITED::skew(sx, sy); +} + +bool SkPictureRecord::concat(const SkMatrix& matrix) { + validate(); + addDraw(CONCAT); + addMatrix(matrix); + validate(); + return this->INHERITED::concat(matrix); +} + +bool SkPictureRecord::clipRect(const SkRect& rect, SkRegion::Op op) { + addDraw(CLIP_RECT); + addRect(rect); + addInt(op); + + size_t offset = fWriter.size(); + addInt(fRestoreOffsetStack.top()); + fRestoreOffsetStack.top() = offset; + + validate(); + return this->INHERITED::clipRect(rect, op); +} + +bool SkPictureRecord::clipPath(const SkPath& path, SkRegion::Op op) { + addDraw(CLIP_PATH); + addPath(path); + addInt(op); + + size_t offset = fWriter.size(); + addInt(fRestoreOffsetStack.top()); + fRestoreOffsetStack.top() = offset; + + validate(); + return this->INHERITED::clipPath(path, op); +} + +bool SkPictureRecord::clipRegion(const SkRegion& region, SkRegion::Op op) { + addDraw(CLIP_REGION); + addRegion(region); + addInt(op); + + size_t offset = fWriter.size(); + addInt(fRestoreOffsetStack.top()); + fRestoreOffsetStack.top() = offset; + + validate(); + return this->INHERITED::clipRegion(region, op); +} + +void SkPictureRecord::drawPaint(const SkPaint& paint) { + addDraw(DRAW_PAINT); + addPaint(paint); + validate(); +} + +void SkPictureRecord::drawPoints(PointMode mode, size_t count, const SkPoint pts[], + const SkPaint& paint) { + addDraw(DRAW_POINTS); + addPaint(paint); + addInt(mode); + addInt(count); + fWriter.writeMul4(pts, count * sizeof(SkPoint)); + validate(); +} + +// return true if geometry drawn with this paint will just be filled +// i.e. its bounding rect will not be larger than the original geometry +static bool is_simple_fill(const SkPaint& paint) { + return paint.getStyle() == SkPaint::kFill_Style && + paint.getPathEffect() == NULL && + paint.getMaskFilter() == NULL && + paint.getRasterizer() == NULL; +} + +void SkPictureRecord::drawRect(const SkRect& rect, const SkPaint& paint) { + addDraw(is_simple_fill(paint) ? DRAW_RECT_SIMPLE : DRAW_RECT_GENERAL); + addPaint(paint); + addRect(rect); + validate(); +} + +void SkPictureRecord::drawPath(const SkPath& path, const SkPaint& paint) { + addDraw(DRAW_PATH); + addPaint(paint); + addPath(path); + validate(); +} + +void SkPictureRecord::drawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top, + const SkPaint* paint = NULL) { + addDraw(DRAW_BITMAP); + addPaintPtr(paint); + addBitmap(bitmap); + addScalar(left); + addScalar(top); + validate(); +} + +void SkPictureRecord::drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src, + const SkRect& dst, const SkPaint* paint) { + addDraw(DRAW_BITMAP_RECT); + addPaintPtr(paint); + addBitmap(bitmap); + addIRectPtr(src); // may be null + addRect(dst); + validate(); +} + +void SkPictureRecord::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix, + const SkPaint* paint) { + addDraw(DRAW_BITMAP_MATRIX); + addPaintPtr(paint); + addBitmap(bitmap); + addMatrix(matrix); + validate(); +} + +void SkPictureRecord::drawSprite(const SkBitmap& bitmap, int left, int top, + const SkPaint* paint = NULL) { + addDraw(DRAW_SPRITE); + addPaintPtr(paint); + addBitmap(bitmap); + addInt(left); + addInt(top); + validate(); +} + +void SkPictureRecord::addFontMetricsTopBottom(const SkPaint& paint, + SkScalar baselineY) { + SkPaint::FontMetrics metrics; + paint.getFontMetrics(&metrics); + addScalar(metrics.fTop + baselineY); + addScalar(metrics.fBottom + baselineY); +} + +void SkPictureRecord::drawText(const void* text, size_t byteLength, SkScalar x, + SkScalar y, const SkPaint& paint) { + addDraw(DRAW_TEXT); + addPaint(paint); + addText(text, byteLength); + addScalar(x); + addScalar(y); + addFontMetricsTopBottom(paint, y); + validate(); +} + +void SkPictureRecord::drawPosText(const void* text, size_t byteLength, + const SkPoint pos[], const SkPaint& paint) { + size_t points = paint.countText(text, byteLength); + if (0 == points) + return; + + bool canUseDrawH = true; + // check if the caller really should have used drawPosTextH() + { + const SkScalar firstY = pos[0].fY; + for (size_t index = 1; index < points; index++) { + if (pos[index].fY != firstY) { + canUseDrawH = false; + break; + } + } + } + + addDraw(canUseDrawH ? DRAW_POS_TEXT_H : DRAW_POS_TEXT); + addPaint(paint); + addText(text, byteLength); + addInt(points); + +#ifdef SK_DEBUG_SIZE + size_t start = fWriter.size(); +#endif + if (canUseDrawH) { + addFontMetricsTopBottom(paint, pos[0].fY); + addScalar(pos[0].fY); + SkScalar* xptr = (SkScalar*)fWriter.reserve(points * sizeof(SkScalar)); + for (size_t index = 0; index < points; index++) + *xptr++ = pos[index].fX; + } + else { + fWriter.writeMul4(pos, points * sizeof(SkPoint)); + } +#ifdef SK_DEBUG_SIZE + fPointBytes += fWriter.size() - start; + fPointWrites += points; +#endif + validate(); +} + +void SkPictureRecord::drawPosTextH(const void* text, size_t byteLength, + const SkScalar xpos[], SkScalar constY, + const SkPaint& paint) { + size_t points = paint.countText(text, byteLength); + if (0 == points) + return; + + addDraw(DRAW_POS_TEXT_H); + addPaint(paint); + addText(text, byteLength); + addInt(points); + +#ifdef SK_DEBUG_SIZE + size_t start = fWriter.size(); +#endif + addFontMetricsTopBottom(paint, constY); + addScalar(constY); + fWriter.writeMul4(xpos, points * sizeof(SkScalar)); +#ifdef SK_DEBUG_SIZE + fPointBytes += fWriter.size() - start; + fPointWrites += points; +#endif + validate(); +} + +void SkPictureRecord::drawTextOnPath(const void* text, size_t byteLength, + const SkPath& path, const SkMatrix* matrix, + const SkPaint& paint) { + addDraw(DRAW_TEXT_ON_PATH); + addPaint(paint); + addText(text, byteLength); + addPath(path); + addMatrixPtr(matrix); + validate(); +} + +void SkPictureRecord::drawPicture(SkPicture& picture) { + addDraw(DRAW_PICTURE); + addPicture(picture); + validate(); +} + +void SkPictureRecord::drawVertices(VertexMode vmode, int vertexCount, + const SkPoint vertices[], const SkPoint texs[], + const SkColor colors[], SkXfermode*, + const uint16_t indices[], int indexCount, + const SkPaint& paint) { + uint32_t flags = 0; + if (texs) { + flags |= DRAW_VERTICES_HAS_TEXS; + } + if (colors) { + flags |= DRAW_VERTICES_HAS_COLORS; + } + if (indexCount > 0) { + flags |= DRAW_VERTICES_HAS_INDICES; + } + + addDraw(DRAW_VERTICES); + addPaint(paint); + addInt(flags); + addInt(vmode); + addInt(vertexCount); + addPoints(vertices, vertexCount); + if (flags & DRAW_VERTICES_HAS_TEXS) { + addPoints(texs, vertexCount); + } + if (flags & DRAW_VERTICES_HAS_COLORS) { + fWriter.writeMul4(colors, vertexCount * sizeof(SkColor)); + } + if (flags & DRAW_VERTICES_HAS_INDICES) { + addInt(indexCount); + fWriter.writePad(indices, indexCount * sizeof(uint16_t)); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void SkPictureRecord::reset() { + fBitmaps.reset(); + fMatrices.reset(); + fPaints.reset(); + fPaths.reset(); + fPictureRefs.unrefAll(); + fRegions.reset(); + fWriter.reset(); + fHeap.reset(); + + fRestoreOffsetStack.setCount(1); + fRestoreOffsetStack.top() = 0; + + fRCRecorder.reset(); + fTFRecorder.reset(); +} + +void SkPictureRecord::addBitmap(const SkBitmap& bitmap) { + addInt(find(fBitmaps, bitmap)); +} + +void SkPictureRecord::addMatrix(const SkMatrix& matrix) { + addMatrixPtr(&matrix); +} + +void SkPictureRecord::addMatrixPtr(const SkMatrix* matrix) { + addInt(find(fMatrices, matrix)); +} + +void SkPictureRecord::addPaint(const SkPaint& paint) { + addPaintPtr(&paint); +} + +void SkPictureRecord::addPaintPtr(const SkPaint* paint) { + addInt(find(fPaints, paint)); +} + +void SkPictureRecord::addPath(const SkPath& path) { + addInt(find(fPaths, path)); +} + +void SkPictureRecord::addPicture(SkPicture& picture) { + int index = fPictureRefs.find(&picture); + if (index < 0) { // not found + index = fPictureRefs.count(); + *fPictureRefs.append() = &picture; + picture.ref(); + } + // follow the convention of recording a 1-based index + addInt(index + 1); +} + +void SkPictureRecord::addPoint(const SkPoint& point) { +#ifdef SK_DEBUG_SIZE + size_t start = fWriter.size(); +#endif + fWriter.writePoint(point); +#ifdef SK_DEBUG_SIZE + fPointBytes += fWriter.size() - start; + fPointWrites++; +#endif +} + +void SkPictureRecord::addPoints(const SkPoint pts[], int count) { + fWriter.writeMul4(pts, count * sizeof(SkPoint)); +#ifdef SK_DEBUG_SIZE + fPointBytes += count * sizeof(SkPoint); + fPointWrites++; +#endif +} + +void SkPictureRecord::addRect(const SkRect& rect) { +#ifdef SK_DEBUG_SIZE + size_t start = fWriter.size(); +#endif + fWriter.writeRect(rect); +#ifdef SK_DEBUG_SIZE + fRectBytes += fWriter.size() - start; + fRectWrites++; +#endif +} + +void SkPictureRecord::addRectPtr(const SkRect* rect) { + if (fWriter.writeBool(rect != NULL)) { + fWriter.writeRect(*rect); + } +} + +void SkPictureRecord::addIRectPtr(const SkIRect* rect) { + if (fWriter.writeBool(rect != NULL)) { + *(SkIRect*)fWriter.reserve(sizeof(SkIRect)) = *rect; + } +} + +void SkPictureRecord::addRegion(const SkRegion& region) { + addInt(find(fRegions, region)); +} + +void SkPictureRecord::addText(const void* text, size_t byteLength) { +#ifdef SK_DEBUG_SIZE + size_t start = fWriter.size(); +#endif + addInt(byteLength); + fWriter.writePad(text, byteLength); +#ifdef SK_DEBUG_SIZE + fTextBytes += fWriter.size() - start; + fTextWrites++; +#endif +} + +/////////////////////////////////////////////////////////////////////////////// + +int SkPictureRecord::find(SkTDArray<const SkFlatBitmap* >& bitmaps, const SkBitmap& bitmap) { + SkFlatBitmap* flat = SkFlatBitmap::Flatten(&fHeap, bitmap, fBitmapIndex, + &fRCRecorder); + int index = SkTSearch<SkFlatData>((const SkFlatData**) bitmaps.begin(), + bitmaps.count(), (SkFlatData*) flat, sizeof(flat), &SkFlatData::Compare); + if (index >= 0) { +// SkBitmap bitmap; +// flat->unflatten(&bitmap); // balance ref count + return bitmaps[index]->index(); + } + index = ~index; + *bitmaps.insert(index) = flat; + return fBitmapIndex++; +} + +int SkPictureRecord::find(SkTDArray<const SkFlatMatrix* >& matrices, const SkMatrix* matrix) { + if (matrix == NULL) + return 0; + SkFlatMatrix* flat = SkFlatMatrix::Flatten(&fHeap, *matrix, fMatrixIndex); + int index = SkTSearch<SkFlatData>((const SkFlatData**) matrices.begin(), + matrices.count(), (SkFlatData*) flat, sizeof(flat), &SkFlatData::Compare); + if (index >= 0) + return matrices[index]->index(); + index = ~index; + *matrices.insert(index) = flat; + return fMatrixIndex++; +} + +int SkPictureRecord::find(SkTDArray<const SkFlatPaint* >& paints, const SkPaint* paint) { + if (paint == NULL) { + return 0; + } + + SkFlatPaint* flat = SkFlatPaint::Flatten(&fHeap, *paint, fPaintIndex, + &fRCRecorder, &fTFRecorder); + int index = SkTSearch<SkFlatData>((const SkFlatData**) paints.begin(), + paints.count(), (SkFlatData*) flat, sizeof(flat), &SkFlatData::Compare); + if (index >= 0) { + return paints[index]->index(); + } + + index = ~index; + *paints.insert(index) = flat; + return fPaintIndex++; +} + +int SkPictureRecord::find(SkTDArray<const SkFlatPath* >& paths, const SkPath& path) { + SkFlatPath* flat = SkFlatPath::Flatten(&fHeap, path, fPathIndex); + int index = SkTSearch<SkFlatData>((const SkFlatData**) paths.begin(), + paths.count(), (SkFlatData*) flat, sizeof(flat), &SkFlatData::Compare); + if (index >= 0) + return paths[index]->index(); + index = ~index; + *paths.insert(index) = flat; + return fPathIndex++; +} + +int SkPictureRecord::find(SkTDArray<const SkFlatRegion* >& regions, const SkRegion& region) { + SkFlatRegion* flat = SkFlatRegion::Flatten(&fHeap, region, fRegionIndex); + int index = SkTSearch<SkFlatData>((const SkFlatData**) regions.begin(), + regions.count(), (SkFlatData*) flat, sizeof(flat), &SkFlatData::Compare); + if (index >= 0) + return regions[index]->index(); + index = ~index; + *regions.insert(index) = flat; + return fRegionIndex++; +} + +#ifdef SK_DEBUG_DUMP +void SkPictureRecord::dumpMatrices() { + int count = fMatrices.count(); + SkMatrix defaultMatrix; + defaultMatrix.reset(); + for (int index = 0; index < count; index++) { + const SkFlatMatrix* flatMatrix = fMatrices[index]; + flatMatrix->dump(); + } +} + +void SkPictureRecord::dumpPaints() { + int count = fPaints.count(); + for (int index = 0; index < count; index++) + fPaints[index]->dump(); +} +#endif + +#ifdef SK_DEBUG_SIZE +size_t SkPictureRecord::size() const { + size_t result = 0; + size_t sizeData; + bitmaps(&sizeData); + result += sizeData; + matrices(&sizeData); + result += sizeData; + paints(&sizeData); + result += sizeData; + paths(&sizeData); + result += sizeData; + pictures(&sizeData); + result += sizeData; + regions(&sizeData); + result += sizeData; + result += streamlen(); + return result; +} + +int SkPictureRecord::bitmaps(size_t* size) const { + size_t result = 0; + int count = fBitmaps.count(); + for (int index = 0; index < count; index++) + result += sizeof(fBitmaps[index]) + fBitmaps[index]->size(); + *size = result; + return count; +} + +int SkPictureRecord::matrices(size_t* size) const { + int count = fMatrices.count(); + *size = sizeof(fMatrices[0]) * count; + return count; +} + +int SkPictureRecord::paints(size_t* size) const { + size_t result = 0; + int count = fPaints.count(); + for (int index = 0; index < count; index++) + result += sizeof(fPaints[index]) + fPaints[index]->size(); + *size = result; + return count; +} + +int SkPictureRecord::paths(size_t* size) const { + size_t result = 0; + int count = fPaths.count(); + for (int index = 0; index < count; index++) + result += sizeof(fPaths[index]) + fPaths[index]->size(); + *size = result; + return count; +} + +int SkPictureRecord::regions(size_t* size) const { + size_t result = 0; + int count = fRegions.count(); + for (int index = 0; index < count; index++) + result += sizeof(fRegions[index]) + fRegions[index]->size(); + *size = result; + return count; +} + +size_t SkPictureRecord::streamlen() const { + return fWriter.size(); +} +#endif + +#ifdef SK_DEBUG_VALIDATE +void SkPictureRecord::validate() const { + validateBitmaps(); + validateMatrices(); + validatePaints(); + validatePaths(); + validatePictures(); + validateRegions(); +} + +void SkPictureRecord::validateBitmaps() const { + int count = fBitmaps.count(); + SkASSERT((unsigned) count < 0x1000); + for (int index = 0; index < count; index++) { + const SkFlatBitmap* bitPtr = fBitmaps[index]; + SkASSERT(bitPtr); + bitPtr->validate(); + } +} + +void SkPictureRecord::validateMatrices() const { + int count = fMatrices.count(); + SkASSERT((unsigned) count < 0x1000); + for (int index = 0; index < count; index++) { + const SkFlatMatrix* matrix = fMatrices[index]; + SkASSERT(matrix); + matrix->validate(); + } +} + +void SkPictureRecord::validatePaints() const { + int count = fPaints.count(); + SkASSERT((unsigned) count < 0x1000); + for (int index = 0; index < count; index++) { + const SkFlatPaint* paint = fPaints[index]; + SkASSERT(paint); +// paint->validate(); + } +} + +void SkPictureRecord::validatePaths() const { + int count = fPaths.count(); + SkASSERT((unsigned) count < 0x1000); + for (int index = 0; index < count; index++) { + const SkFlatPath* path = fPaths[index]; + SkASSERT(path); + path->validate(); + } +} + +void SkPictureRecord::validateRegions() const { + int count = fRegions.count(); + SkASSERT((unsigned) count < 0x1000); + for (int index = 0; index < count; index++) { + const SkFlatRegion* region = fRegions[index]; + SkASSERT(region); + region->validate(); + } +} +#endif + diff --git a/skia/picture/SkPictureRecord.h b/skia/picture/SkPictureRecord.h new file mode 100644 index 0000000..350040e --- /dev/null +++ b/skia/picture/SkPictureRecord.h @@ -0,0 +1,182 @@ +#ifndef SkPictureRecord_DEFINED +#define SkPictureRecord_DEFINED + +#include "SkCanvas.h" +#include "SkFlattenable.h" +#include "SkPicture.h" +#include "SkPictureFlat.h" +#include "SkTemplates.h" +#include "SkWriter32.h" + +class SkPictureRecord : public SkCanvas { +public: + SkPictureRecord(); + virtual ~SkPictureRecord(); + + // overrides from SkCanvas + virtual int save(SaveFlags); + virtual int saveLayer(const SkRect* bounds, const SkPaint*, SaveFlags); + virtual void restore(); + virtual bool translate(SkScalar dx, SkScalar dy); + virtual bool scale(SkScalar sx, SkScalar sy); + virtual bool rotate(SkScalar degrees); + virtual bool skew(SkScalar sx, SkScalar sy); + virtual bool concat(const SkMatrix& matrix); + virtual bool clipRect(const SkRect& rect, SkRegion::Op op); + virtual bool clipPath(const SkPath& path, SkRegion::Op op); + virtual bool clipRegion(const SkRegion& region, SkRegion::Op op); + virtual void drawPaint(const SkPaint& paint); + virtual void drawPoints(PointMode, size_t count, const SkPoint pts[], + const SkPaint&); + virtual void drawRect(const SkRect& rect, const SkPaint&); + virtual void drawPath(const SkPath& path, const SkPaint&); + virtual void drawBitmap(const SkBitmap&, SkScalar left, SkScalar top, + const SkPaint*); + virtual void drawBitmapRect(const SkBitmap&, const SkIRect* src, + const SkRect& dst, const SkPaint*); + virtual void drawBitmapMatrix(const SkBitmap&, const SkMatrix&, + const SkPaint*); + virtual void drawSprite(const SkBitmap&, int left, int top, + const SkPaint*); + virtual void drawText(const void* text, size_t byteLength, SkScalar x, + SkScalar y, const SkPaint&); + virtual void drawPosText(const void* text, size_t byteLength, + const SkPoint pos[], const SkPaint&); + virtual void drawPosTextH(const void* text, size_t byteLength, + const SkScalar xpos[], SkScalar constY, const SkPaint&); + virtual void drawTextOnPath(const void* text, size_t byteLength, + const SkPath& path, const SkMatrix* matrix, + const SkPaint&); + virtual void drawPicture(SkPicture& picture); + virtual void drawVertices(VertexMode, int vertexCount, + const SkPoint vertices[], const SkPoint texs[], + const SkColor colors[], SkXfermode*, + const uint16_t indices[], int indexCount, + const SkPaint&); + + void addFontMetricsTopBottom(const SkPaint& paint, SkScalar baselineY); + + const SkTDArray<const SkFlatBitmap* >& getBitmaps() const { + return fBitmaps; + } + const SkTDArray<const SkFlatMatrix* >& getMatrices() const { + return fMatrices; + } + const SkTDArray<const SkFlatPaint* >& getPaints() const { + return fPaints; + } + const SkTDArray<const SkFlatPath* >& getPaths() const { + return fPaths; + } + const SkTDArray<SkPicture* >& getPictureRefs() const { + return fPictureRefs; + } + const SkTDArray<const SkFlatRegion* >& getRegions() const { + return fRegions; + } + + void reset(); + + const SkWriter32& writeStream() const { + return fWriter; + } + +private: + SkTDArray<uint32_t> fRestoreOffsetStack; + + void addDraw(DrawType drawType) { +#ifdef SK_DEBUG_TRACE + SkDebugf("add %s\n", DrawTypeToString(drawType)); +#endif + fWriter.writeInt(drawType); + } + void addInt(int value) { + fWriter.writeInt(value); + } + void addScalar(SkScalar scalar) { + fWriter.writeScalar(scalar); + } + + void addBitmap(const SkBitmap& bitmap); + void addMatrix(const SkMatrix& matrix); + void addMatrixPtr(const SkMatrix* matrix); + void addPaint(const SkPaint& paint); + void addPaintPtr(const SkPaint* paint); + void addPath(const SkPath& path); + void addPicture(SkPicture& picture); + void addPoint(const SkPoint& point); + void addPoints(const SkPoint pts[], int count); + void addRect(const SkRect& rect); + void addRectPtr(const SkRect* rect); + void addIRectPtr(const SkIRect* rect); + void addRegion(const SkRegion& region); + void addText(const void* text, size_t byteLength); + + int find(SkTDArray<const SkFlatBitmap* >& bitmaps, + const SkBitmap& bitmap); + int find(SkTDArray<const SkFlatMatrix* >& matrices, + const SkMatrix* matrix); + int find(SkTDArray<const SkFlatPaint* >& paints, const SkPaint* paint); + int find(SkTDArray<const SkFlatPath* >& paths, const SkPath& path); + int find(SkTDArray<const SkFlatRegion* >& regions, const SkRegion& region); + +#ifdef SK_DEBUG_DUMP +public: + void dumpMatrices(); + void dumpPaints(); +#endif + +#ifdef SK_DEBUG_SIZE +public: + size_t size() const; + int bitmaps(size_t* size) const; + int matrices(size_t* size) const; + int paints(size_t* size) const; + int paths(size_t* size) const; + int regions(size_t* size) const; + size_t streamlen() const; + + size_t fPointBytes, fRectBytes, fTextBytes; + int fPointWrites, fRectWrites, fTextWrites; +#endif + +#ifdef SK_DEBUG_VALIDATE +public: + void validate() const; +private: + void validateBitmaps() const; + void validateMatrices() const; + void validatePaints() const; + void validatePaths() const; + void validateRegions() const; +#else +public: + void validate() const {} +#endif + +private: + SkChunkAlloc fHeap; + int fBitmapIndex; + SkTDArray<const SkFlatBitmap* > fBitmaps; + int fMatrixIndex; + SkTDArray<const SkFlatMatrix* > fMatrices; + int fPaintIndex; + SkTDArray<const SkFlatPaint* > fPaints; + int fPathIndex; + SkTDArray<const SkFlatPath* > fPaths; + int fRegionIndex; + SkTDArray<const SkFlatRegion* > fRegions; + SkWriter32 fWriter; + + // we ref each item in this array + SkTDArray<SkPicture*> fPictureRefs; + + SkRefCntRecorder fRCRecorder; + SkRefCntRecorder fTFRecorder; + + friend class SkPicturePlayback; + + typedef SkCanvas INHERITED; +}; + +#endif diff --git a/skia/ports/SkFontHost_FONTPATH.cpp b/skia/ports/SkFontHost_FONTPATH.cpp new file mode 100644 index 0000000..94f068c --- /dev/null +++ b/skia/ports/SkFontHost_FONTPATH.cpp @@ -0,0 +1,415 @@ +/* libs/graphics/ports/SkFontHost_android.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkFontHost.h" +#include "SkDescriptor.h" +#include "SkString.h" +#include "SkStream.h" +#include <stdio.h> + +/* define this if we can use mmap() to access fonts from the filesystem */ +#define SK_CAN_USE_MMAP + +#ifndef SK_FONTPATH + #define SK_FONTPATH "the complete path for a font file" +#endif + +struct FontFaceRec { + const char* fFileName; + uint8_t fFamilyIndex; + SkBool8 fBold; + SkBool8 fItalic; + + static const FontFaceRec& FindFace(const FontFaceRec rec[], int count, int isBold, int isItalic); +}; + +struct FontFamilyRec { + const FontFaceRec* fFaces; + int fFaceCount; +}; + +const FontFaceRec& FontFaceRec::FindFace(const FontFaceRec rec[], int count, int isBold, int isItalic) +{ + SkASSERT(count > 0); + + int i; + + // look for an exact match + for (i = 0; i < count; i++) { + if (rec[i].fBold == isBold && rec[i].fItalic == isItalic) + return rec[i]; + } + // look for a match in the bold field + for (i = 0; i < count; i++) { + if (rec[i].fBold == isBold) + return rec[i]; + } + // look for a normal/regular face + for (i = 0; i < count; i++) { + if (!rec[i].fBold && !rec[i].fItalic) + return rec[i]; + } + // give up + return rec[0]; +} + +enum { + DEFAULT_FAMILY_INDEX, + + FAMILY_INDEX_COUNT +}; + +static const FontFaceRec gDefaultFaces[] = { + { SK_FONTPATH, DEFAULT_FAMILY_INDEX, 0, 0 } +}; + +// This table must be in the same order as the ..._FAMILY_INDEX enum specifies +static const FontFamilyRec gFamilies[] = { + { gDefaultFaces, SK_ARRAY_COUNT(gDefaultFaces) } +}; + +#define DEFAULT_FAMILY_INDEX DEFAULT_FAMILY_INDEX +#define DEFAULT_FAMILY_FACE_INDEX 0 + +//////////////////////////////////////////////////////////////////////////////////////// + +/* map common "web" font names to our font list */ + +struct FontFamilyMatchRec { + const char* fLCName; + int fFamilyIndex; +}; + +/* This is a table of synonyms for collapsing font names + down to their pseudo-equivalents (i.e. in terms of fonts + we actually have.) + Keep this sorted by the first field so we can do a binary search. + If this gets big, we could switch to a hash... +*/ +static const FontFamilyMatchRec gMatches[] = { +#if 0 + { "Ahem", Ahem_FAMILY_INDEX }, + { "arial", SANS_FAMILY_INDEX }, + { "courier", MONO_FAMILY_INDEX }, + { "courier new", MONO_FAMILY_INDEX }, + { "cursive", SERIF_FAMILY_INDEX }, + { "fantasy", SERIF_FAMILY_INDEX }, + { "georgia", SERIF_FAMILY_INDEX }, + { "goudy", SERIF_FAMILY_INDEX }, + { "helvetica", SANS_FAMILY_INDEX }, + { "palatino", SERIF_FAMILY_INDEX }, + { "tahoma", SANS_FAMILY_INDEX }, + { "sans-serif", SANS_FAMILY_INDEX }, + { "serif", SERIF_FAMILY_INDEX }, + { "times", SERIF_FAMILY_INDEX }, + { "times new roman", SERIF_FAMILY_INDEX }, + { "verdana", SANS_FAMILY_INDEX } +#endif +}; + +//////////////////////////////////////////////////////////////////////////////////////// + +#include "SkTSearch.h" + +static bool contains_only_ascii(const char s[]) +{ + for (;;) + { + int c = *s++; + if (c == 0) + break; + if ((c >> 7) != 0) + return false; + } + return true; +} + +#define TRACE_FONT_NAME(code) +//#define TRACE_FONT_NAME(code) code + +const FontFamilyRec* find_family_rec(const char target[]) +{ + int index; + + // If we're asked for a font name that contains non-ascii, + // 1) SkStrLCSearch can't handle it + // 2) All of our fonts are have ascii names, so... + +TRACE_FONT_NAME(printf("----------------- font request <%s>", target);) + + if (contains_only_ascii(target)) + { + // Search for the font by matching the entire name + index = SkStrLCSearch(&gMatches[0].fLCName, SK_ARRAY_COUNT(gMatches), target, sizeof(gMatches[0])); + if (index >= 0) + { + TRACE_FONT_NAME(printf(" found %d\n", index);) + return &gFamilies[gMatches[index].fFamilyIndex]; + } + } + + // Sniff for key words... + +#if 0 + if (strstr(target, "sans") || strstr(target, "Sans")) + { + TRACE_FONT_NAME(printf(" found sans\n");) + return &gFamilies[SANS_FAMILY_INDEX]; + } + if (strstr(target, "serif") || strstr(target, "Serif")) + { + TRACE_FONT_NAME(printf(" found serif\n");) + return &gFamilies[SERIF_FAMILY_INDEX]; + } + if (strstr(target, "mono") || strstr(target, "Mono")) + { + TRACE_FONT_NAME(printf(" found mono\n");) + return &gFamilies[MONO_FAMILY_INDEX]; + } +#endif + + TRACE_FONT_NAME(printf(" use default\n");) + // we give up, just give them the default font + return &gFamilies[DEFAULT_FAMILY_INDEX]; +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +static const FontFaceRec* get_default_face() +{ + return &gFamilies[DEFAULT_FAMILY_INDEX].fFaces[DEFAULT_FAMILY_FACE_INDEX]; +} + +class FontFaceRec_Typeface : public SkTypeface { +public: + FontFaceRec_Typeface(const FontFaceRec& face) : fFace(face) + { + int style = 0; + if (face.fBold) + style |= SkTypeface::kBold; + if (face.fItalic) + style |= SkTypeface::kItalic; + this->setStyle((SkTypeface::Style)style); + } + + // This global const reference completely identifies the face + const FontFaceRec& fFace; +}; + +static const FontFaceRec* get_typeface_rec(const SkTypeface* face) +{ + const FontFaceRec_Typeface* f = (FontFaceRec_Typeface*)face; + return f ? &f->fFace : get_default_face(); +} + +static uint32_t ptr2uint32(const void* p) +{ + // cast so we avoid warnings on 64bit machines that a ptr difference + // which might be 64bits is being trucated from 64 to 32 + return (uint32_t)((char*)p - (char*)0); +} + +uint32_t SkFontHost::TypefaceHash(const SkTypeface* face) +{ + // just use our address as the hash value + return ptr2uint32(get_typeface_rec(face)); +} + +bool SkFontHost::TypefaceEqual(const SkTypeface* facea, const SkTypeface* faceb) +{ + return get_typeface_rec(facea) == get_typeface_rec(faceb); +} + +SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace, const char familyName[], SkTypeface::Style style) +{ + const FontFamilyRec* family; + + if (familyFace) + family = &gFamilies[((FontFaceRec_Typeface*)familyFace)->fFace.fFamilyIndex]; + else if (familyName) + family = find_family_rec(familyName); + else + family = &gFamilies[DEFAULT_FAMILY_INDEX]; + + const FontFaceRec& face = FontFaceRec::FindFace(family->fFaces, family->fFaceCount, + (style & SkTypeface::kBold) != 0, + (style & SkTypeface::kItalic) != 0); + + // if we're returning our input parameter, no need to create a new instance + if (familyFace != NULL && &((FontFaceRec_Typeface*)familyFace)->fFace == &face) + { + familyFace->ref(); + return (SkTypeface*)familyFace; + } + return SkNEW_ARGS(FontFaceRec_Typeface, (face)); +} + +uint32_t SkFontHost::FlattenTypeface(const SkTypeface* tface, void* buffer) +{ + const FontFaceRec* face; + + if (tface) + face = &((const FontFaceRec_Typeface*)tface)->fFace; + else + face = get_default_face(); + + size_t size = sizeof(face); + if (buffer) + memcpy(buffer, &face, size); + return size; +} + +void SkFontHost::GetDescriptorKeyString(const SkDescriptor* desc, SkString* key) +{ + key->set(SK_FONTPATH); +} + +#ifdef SK_CAN_USE_MMAP +#include <unistd.h> +#include <sys/mman.h> +#include <fcntl.h> +#include <errno.h> + +class SkMMAPStream : public SkMemoryStream { +public: + SkMMAPStream(const char filename[]); + virtual ~SkMMAPStream(); + + virtual void setMemory(const void* data, size_t length); +private: + int fFildes; + void* fAddr; + size_t fSize; + + void closeMMap(); + + typedef SkMemoryStream INHERITED; +}; + +SkMMAPStream::SkMMAPStream(const char filename[]) +{ + fFildes = -1; // initialize to failure case + + int fildes = open(filename, O_RDONLY); + if (fildes < 0) + { + SkDEBUGF(("---- failed to open(%s) for mmap stream error=%d\n", filename, errno)); + return; + } + + off_t size = lseek(fildes, 0, SEEK_END); // find the file size + if (size == -1) + { + SkDEBUGF(("---- failed to lseek(%s) for mmap stream error=%d\n", filename, errno)); + close(fildes); + return; + } + (void)lseek(fildes, 0, SEEK_SET); // restore file offset to beginning + + void* addr = mmap(NULL, size, PROT_READ, MAP_SHARED, fildes, 0); + if (MAP_FAILED == addr) + { + SkDEBUGF(("---- failed to mmap(%s) for mmap stream error=%d\n", filename, errno)); + close(fildes); + return; + } + + this->INHERITED::setMemory(addr, size); + + fFildes = fildes; + fAddr = addr; + fSize = size; +} + +SkMMAPStream::~SkMMAPStream() +{ + this->closeMMap(); +} + +void SkMMAPStream::setMemory(const void* data, size_t length) +{ + this->closeMMap(); + this->INHERITED::setMemory(data, length); +} + +void SkMMAPStream::closeMMap() +{ + if (fFildes >= 0) + { + munmap(fAddr, fSize); + close(fFildes); + fFildes = -1; + } +} + +#endif + +SkStream* SkFontHost::OpenDescriptorStream(const SkDescriptor* desc, const char keyString[]) +{ + // our key string IS our filename, so we can ignore desc + SkStream* strm; + +#ifdef SK_CAN_USE_MMAP + strm = new SkMMAPStream(keyString); + if (strm->getLength() > 0) + return strm; + + // strm not valid + delete strm; + // fall through to FILEStream attempt +#endif + + strm = new SkFILEStream(keyString); + if (strm->getLength() > 0) + return strm; + + // strm not valid + delete strm; + return NULL; +} + +SkScalerContext* SkFontHost::CreateFallbackScalerContext(const SkScalerContext::Rec& rec) +{ + const FontFaceRec* face = get_default_face(); + + SkAutoDescriptor ad(sizeof(rec) + sizeof(face) + SkDescriptor::ComputeOverhead(2)); + SkDescriptor* desc = ad.getDesc(); + + desc->init(); + desc->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec); + desc->addEntry(kTypeface_SkDescriptorTag, sizeof(face), &face); + desc->computeChecksum(); + + return SkFontHost::CreateScalerContext(desc); +} + +size_t SkFontHost::ShouldPurgeFontCache(size_t sizeAllocatedSoFar) +{ + return 0; // nothing to do (change me if you want to limit the font cache) +} + +int SkFontHost::ComputeGammaFlag(const SkPaint& paint) +{ + return 0; +} + +void SkFontHost::GetGammaTables(const uint8_t* tables[2]) +{ + tables[0] = NULL; // black gamma (e.g. exp=1.4) + tables[1] = NULL; // white gamma (e.g. exp= 1/1.4) +} + diff --git a/skia/ports/SkFontHost_FreeType.cpp b/skia/ports/SkFontHost_FreeType.cpp new file mode 100644 index 0000000..4866f88 --- /dev/null +++ b/skia/ports/SkFontHost_FreeType.cpp @@ -0,0 +1,852 @@ +/* libs/graphics/ports/SkFontHost_FreeType.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkScalerContext.h" +#include "SkBitmap.h" +#include "SkCanvas.h" +#include "SkDescriptor.h" +#include "SkFDot6.h" +#include "SkFontHost.h" +#include "SkMask.h" +#include "SkStream.h" +#include "SkString.h" +#include "SkThread.h" +#include "SkTemplates.h" + +#include <ft2build.h> +#include FT_FREETYPE_H +#include FT_OUTLINE_H +#include FT_SIZES_H + +//#define ENABLE_GLYPH_SPEW // for tracing calls to generateMetrics/generateImage +//#define DUMP_STRIKE_CREATION + +#ifdef SK_DEBUG + #define SkASSERT_CONTINUE(pred) \ + do { \ + if (!(pred)) \ + SkDebugf("file %s:%d: assert failed '" #pred "'\n", __FILE__, __LINE__); \ + } while (false) +#else + #define SkASSERT_CONTINUE(pred) +#endif + +////////////////////////////////////////////////////////////////////////// + +struct SkFaceRec; + +static SkMutex gFTMutex; +static int gFTCount; +static FT_Library gFTLibrary; +static SkFaceRec* gFaceRecHead; + +///////////////////////////////////////////////////////////////////////// + +class SkScalerContext_FreeType : public SkScalerContext { +public: + SkScalerContext_FreeType(const SkDescriptor* desc); + virtual ~SkScalerContext_FreeType(); + +protected: + virtual unsigned generateGlyphCount() const; + virtual uint16_t generateCharToGlyph(SkUnichar uni); + virtual void generateAdvance(SkGlyph* glyph); + virtual void generateMetrics(SkGlyph* glyph); + virtual void generateImage(const SkGlyph& glyph); + virtual void generatePath(const SkGlyph& glyph, SkPath* path); + virtual void generateFontMetrics(SkPaint::FontMetrics* mx, SkPaint::FontMetrics* my); + +private: + SkFaceRec* fFaceRec; + FT_Face fFace; // reference to shared face in gFaceRecHead + FT_Size fFTSize; // our own copy + SkFixed fScaleX, fScaleY; + FT_Matrix fMatrix22; + uint32_t fLoadGlyphFlags; + + FT_Error setupSize(); +}; + +/////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////// + +#include "SkStream.h" + +struct SkFaceRec { + SkFaceRec* fNext; + FT_Face fFace; + FT_StreamRec fFTStream; + SkStream* fSkStream; + uint32_t fRefCnt; + uint32_t fFontID; + + SkFaceRec(SkStream* strm, uint32_t fontID); + ~SkFaceRec() + { + SkFontHost::CloseStream(fFontID, fSkStream); + } +}; + +extern "C" { + static unsigned long sk_stream_read(FT_Stream stream, + unsigned long offset, + unsigned char* buffer, + unsigned long count ) + { + SkStream* str = (SkStream*)stream->descriptor.pointer; + + if (count) + { + if (!str->rewind()) + { + return 0; + } + else + { + unsigned long ret; + if (offset) + { + ret = str->read(NULL, offset); + if (ret != offset) { + return 0; + } + } + ret = str->read(buffer, count); + if (ret != count) { + return 0; + } + count = ret; + } + } + return count; + } + + static void sk_stream_close( FT_Stream stream) + { + } +} + +SkFaceRec::SkFaceRec(SkStream* strm, uint32_t fontID) + : fSkStream(strm), fFontID(fontID) +{ +// SkDEBUGF(("SkFaceRec: opening %s (%p)\n", key.c_str(), strm)); + + memset(&fFTStream, 0, sizeof(fFTStream)); + fFTStream.size = fSkStream->read(NULL, 0); // find out how big the stream is + fFTStream.descriptor.pointer = fSkStream; + fFTStream.read = sk_stream_read; + fFTStream.close = sk_stream_close; +} + +static SkFaceRec* ref_ft_face(uint32_t fontID) +{ + SkFaceRec* rec = gFaceRecHead; + while (rec) + { + if (rec->fFontID == fontID) + { + SkASSERT(rec->fFace); + rec->fRefCnt += 1; + return rec; + } + rec = rec->fNext; + } + + SkStream* strm = SkFontHost::OpenStream(fontID); + if (NULL == strm) + { + SkDEBUGF(("SkFontHost::OpenStream failed opening %x\n", fontID)); + sk_throw(); + return 0; + } + + // this passes ownership of strm to the rec + rec = SkNEW_ARGS(SkFaceRec, (strm, fontID)); + + FT_Open_Args args; + memset(&args, 0, sizeof(args)); + const void* memoryBase = strm->getMemoryBase(); + + if (NULL != memoryBase) + { +//printf("mmap(%s)\n", keyString.c_str()); + args.flags = FT_OPEN_MEMORY; + args.memory_base = (const FT_Byte*)memoryBase; + args.memory_size = strm->getLength(); + } + else + { +//printf("fopen(%s)\n", keyString.c_str()); + args.flags = FT_OPEN_STREAM; + args.stream = &rec->fFTStream; + } + + FT_Error err = FT_Open_Face(gFTLibrary, &args, 0, &rec->fFace); + + if (err) // bad filename, try the default font + { + fprintf(stderr, "ERROR: unable to open font '%x'\n", fontID); + SkDELETE(rec); + sk_throw(); + return 0; + } + else + { + SkASSERT(rec->fFace); + //fprintf(stderr, "Opened font '%s'\n", filename.c_str()); + rec->fNext = gFaceRecHead; + gFaceRecHead = rec; + rec->fRefCnt = 1; + return rec; + } +} + +static void unref_ft_face(FT_Face face) +{ + SkFaceRec* rec = gFaceRecHead; + SkFaceRec* prev = NULL; + while (rec) + { + SkFaceRec* next = rec->fNext; + if (rec->fFace == face) + { + if (--rec->fRefCnt == 0) + { + if (prev) + prev->fNext = next; + else + gFaceRecHead = next; + + FT_Done_Face(face); + SkDELETE(rec); + } + return; + } + prev = rec; + rec = next; + } + SkASSERT("shouldn't get here, face not in list"); +} + +/////////////////////////////////////////////////////////////////////////// + +SkScalerContext_FreeType::SkScalerContext_FreeType(const SkDescriptor* desc) + : SkScalerContext(desc), fFTSize(NULL) +{ + SkAutoMutexAcquire ac(gFTMutex); + + FT_Error err; + + if (gFTCount == 0) + { + err = FT_Init_FreeType(&gFTLibrary); +// SkDEBUGF(("FT_Init_FreeType returned %d\n", err)); + SkASSERT(err == 0); + } + ++gFTCount; + + // load the font file + { + fFaceRec = ref_ft_face(fRec.fFontID); + fFace = fFaceRec ? fFaceRec->fFace : NULL; + } + + // compute our factors from the record + + SkMatrix m; + + fRec.getSingleMatrix(&m); + +#ifdef DUMP_STRIKE_CREATION + SkString keyString; + SkFontHost::GetDescriptorKeyString(desc, &keyString); + printf("========== strike [%g %g %g] [%g %g %g %g] hints %d format %d %s\n", SkScalarToFloat(fRec.fTextSize), + SkScalarToFloat(fRec.fPreScaleX), SkScalarToFloat(fRec.fPreSkewX), + SkScalarToFloat(fRec.fPost2x2[0][0]), SkScalarToFloat(fRec.fPost2x2[0][1]), + SkScalarToFloat(fRec.fPost2x2[1][0]), SkScalarToFloat(fRec.fPost2x2[1][1]), + fRec.fHints, fRec.fMaskFormat, keyString.c_str()); +#endif + + // now compute our scale factors + SkScalar sx = m.getScaleX(); + SkScalar sy = m.getScaleY(); + + if (m.getSkewX() || m.getSkewY() || sx < 0 || sy < 0) // sort of give up on hinting + { + sx = SkMaxScalar(SkScalarAbs(sx), SkScalarAbs(m.getSkewX())); + sy = SkMaxScalar(SkScalarAbs(m.getSkewY()), SkScalarAbs(sy)); + sx = sy = SkScalarAve(sx, sy); + + SkScalar inv = SkScalarInvert(sx); + + // flip the skew elements to go from our Y-down system to FreeType's + fMatrix22.xx = SkScalarToFixed(SkScalarMul(m.getScaleX(), inv)); + fMatrix22.xy = -SkScalarToFixed(SkScalarMul(m.getSkewX(), inv)); + fMatrix22.yx = -SkScalarToFixed(SkScalarMul(m.getSkewY(), inv)); + fMatrix22.yy = SkScalarToFixed(SkScalarMul(m.getScaleY(), inv)); + } + else + { + fMatrix22.xx = fMatrix22.yy = SK_Fixed1; + fMatrix22.xy = fMatrix22.yx = 0; + } + + fScaleX = SkScalarToFixed(sx); + fScaleY = SkScalarToFixed(sy); + + // compute the flags we send to Load_Glyph + { + uint32_t flags = FT_LOAD_DEFAULT; + uint32_t render_flags = FT_LOAD_TARGET_NORMAL; + + // we force autohinting at the moment + // (no patent rights to apple's stuff) + + switch (fRec.fHints) { + case kNo_Hints: + flags |= FT_LOAD_NO_HINTING; + break; + case kSubpixel_Hints: +#if 1 // this is clearer (vertically), but ~2x slower + flags |= FT_LOAD_FORCE_AUTOHINT; + render_flags = FT_LOAD_TARGET_LIGHT; +#else + flags |= FT_LOAD_NO_HINTING; +#endif + break; + case kNormal_Hints: + flags |= FT_LOAD_FORCE_AUTOHINT; + break; + } + + if (SkMask::kBW_Format == fRec.fMaskFormat) + render_flags = FT_LOAD_TARGET_MONO; + else if (SkMask::kLCD_Format == fRec.fMaskFormat) + render_flags = FT_LOAD_TARGET_LCD; + + fLoadGlyphFlags = flags | render_flags; + } + + // now create the FT_Size + + { + FT_Error err; + + err = FT_New_Size(fFace, &fFTSize); + if (err != 0) { + SkDEBUGF(("SkScalerContext_FreeType::FT_New_Size(%x): FT_Set_Char_Size(0x%x, 0x%x) returned 0x%x\n", + fFaceRec->fFontID, fScaleX, fScaleY, err)); + fFace = NULL; + return; + } + + err = FT_Activate_Size(fFTSize); + if (err != 0) { + SkDEBUGF(("SkScalerContext_FreeType::FT_Activate_Size(%x, 0x%x, 0x%x) returned 0x%x\n", + fFaceRec->fFontID, fScaleX, fScaleY, err)); + fFTSize = NULL; + } + + err = FT_Set_Char_Size( fFace, + SkFixedToFDot6(fScaleX), SkFixedToFDot6(fScaleY), + 72, 72); + if (err != 0) { + SkDEBUGF(("SkScalerContext_FreeType::FT_Set_Char_Size(%x, 0x%x, 0x%x) returned 0x%x\n", + fFaceRec->fFontID, fScaleX, fScaleY, err)); + fFace = NULL; + return; + } + + FT_Set_Transform( fFace, &fMatrix22, NULL); + } +} + +SkScalerContext_FreeType::~SkScalerContext_FreeType() +{ + if (fFTSize != NULL) + FT_Done_Size(fFTSize); + + SkAutoMutexAcquire ac(gFTMutex); + + if (fFace != NULL) + unref_ft_face(fFace); + + if (--gFTCount == 0) + { +// SkDEBUGF(("FT_Done_FreeType\n")); + FT_Done_FreeType(gFTLibrary); + SkDEBUGCODE(gFTLibrary = NULL;) + } +} + +/* We call this before each use of the fFace, since we may be sharing + this face with other context (at different sizes). +*/ +FT_Error SkScalerContext_FreeType::setupSize() +{ + if (SkFontHost::ResolveTypeface(fRec.fFontID) == NULL) { + return (FT_Error)-1; + } + + FT_Error err = FT_Activate_Size(fFTSize); + + if (err != 0) { + SkDEBUGF(("SkScalerContext_FreeType::FT_Activate_Size(%x, 0x%x, 0x%x) returned 0x%x\n", + fFaceRec->fFontID, fScaleX, fScaleY, err)); + fFTSize = NULL; + } + else + { + // seems we need to reset this every time (not sure why, but without it + // I get random italics from some other fFTSize) + FT_Set_Transform( fFace, &fMatrix22, NULL); + } + return err; +} + +unsigned SkScalerContext_FreeType::generateGlyphCount() const +{ + return fFace->num_glyphs; +} + +uint16_t SkScalerContext_FreeType::generateCharToGlyph(SkUnichar uni) +{ + return SkToU16(FT_Get_Char_Index( fFace, uni )); +} + +static FT_Pixel_Mode compute_pixel_mode(SkMask::Format format) +{ + switch (format) { + case SkMask::kBW_Format: + return FT_PIXEL_MODE_MONO; + case SkMask::kLCD_Format: + return FT_PIXEL_MODE_LCD; + case SkMask::kA8_Format: + default: + return FT_PIXEL_MODE_GRAY; + } +} + +void SkScalerContext_FreeType::generateAdvance(SkGlyph* glyph) +{ + // insert new freetype API when it is available +#if 1 + this->generateMetrics(glyph); +#else + // hack + SkAutoMutexAcquire ac(gFTMutex); + + glyph->fRsbDelta = 0; + glyph->fLsbDelta = 0; + + glyph->fAdvanceX = fScaleX * 2 / 3; + glyph->fAdvanceY = 0; +#endif + return; +} + +void SkScalerContext_FreeType::generateMetrics(SkGlyph* glyph) +{ + SkAutoMutexAcquire ac(gFTMutex); + + glyph->fRsbDelta = 0; + glyph->fLsbDelta = 0; + + FT_Error err; + + if (this->setupSize()) + goto ERROR; + + err = FT_Load_Glyph( fFace, glyph->getGlyphID(fBaseGlyphCount), fLoadGlyphFlags ); + if (err != 0) + { + SkDEBUGF(("SkScalerContext_FreeType::generateMetrics(%x): FT_Load_Glyph(glyph:%d flags:%d) returned 0x%x\n", + fFaceRec->fFontID, glyph->getGlyphID(fBaseGlyphCount), fLoadGlyphFlags, err)); + ERROR: + glyph->fWidth = 0; + glyph->fHeight = 0; + glyph->fTop = 0; + glyph->fLeft = 0; + glyph->fAdvanceX = 0; + glyph->fAdvanceY = 0; + return; + } + + switch ( fFace->glyph->format ) + { + case FT_GLYPH_FORMAT_OUTLINE: + FT_BBox bbox; + + FT_Outline_Get_CBox(&fFace->glyph->outline, &bbox); + + if (kSubpixel_Hints == fRec.fHints) + { + int dx = glyph->getSubXFixed() >> 10; + int dy = glyph->getSubYFixed() >> 10; + bbox.xMin += dx; + bbox.yMin += dy; + bbox.xMax += dx; + bbox.yMax += dy; + } + + bbox.xMin &= ~63; + bbox.yMin &= ~63; + bbox.xMax = (bbox.xMax + 63) & ~63; + bbox.yMax = (bbox.yMax + 63) & ~63; + + glyph->fWidth = SkToU16((bbox.xMax - bbox.xMin) >> 6); + glyph->fHeight = SkToU16((bbox.yMax - bbox.yMin) >> 6); + glyph->fTop = -SkToS16(bbox.yMax >> 6); + glyph->fLeft = SkToS16(bbox.xMin >> 6); + break; + + case FT_GLYPH_FORMAT_BITMAP: + glyph->fWidth = SkToU16(fFace->glyph->bitmap.width); + glyph->fHeight = SkToU16(fFace->glyph->bitmap.rows); + glyph->fTop = -SkToS16(fFace->glyph->bitmap_top); + glyph->fLeft = SkToS16(fFace->glyph->bitmap_left); + break; + + default: + SkASSERT(!"unknown glyph format"); + goto ERROR; + } + + if (kNormal_Hints == fRec.fHints) + { + glyph->fAdvanceX = SkFDot6ToFixed(fFace->glyph->advance.x); + glyph->fAdvanceY = -SkFDot6ToFixed(fFace->glyph->advance.y); + if (fRec.fFlags & kDevKernText_Flag) + { + glyph->fRsbDelta = SkToS8(fFace->glyph->rsb_delta); + glyph->fLsbDelta = SkToS8(fFace->glyph->lsb_delta); + } + } + else + { + glyph->fAdvanceX = SkFixedMul(fMatrix22.xx, fFace->glyph->linearHoriAdvance); + glyph->fAdvanceY = -SkFixedMul(fMatrix22.yx, fFace->glyph->linearHoriAdvance); + } + +#ifdef ENABLE_GLYPH_SPEW + SkDEBUGF(("FT_Set_Char_Size(this:%p sx:%x sy:%x ", this, fScaleX, fScaleY)); + SkDEBUGF(("Metrics(glyph:%d flags:0x%x) w:%d\n", glyph->getGlyphID(fBaseGlyphCount), fLoadGlyphFlags, glyph->fWidth)); +#endif +} + +void SkScalerContext_FreeType::generateImage(const SkGlyph& glyph) +{ + SkAutoMutexAcquire ac(gFTMutex); + + FT_Error err; + + if (this->setupSize()) + goto ERROR; + + err = FT_Load_Glyph( fFace, glyph.getGlyphID(fBaseGlyphCount), fLoadGlyphFlags); + if (err != 0) { + SkDEBUGF(("SkScalerContext_FreeType::generateImage: FT_Load_Glyph(glyph:%d width:%d height:%d rb:%d flags:%d) returned 0x%x\n", + glyph.getGlyphID(fBaseGlyphCount), glyph.fWidth, glyph.fHeight, glyph.rowBytes(), fLoadGlyphFlags, err)); + ERROR: + memset(glyph.fImage, 0, glyph.rowBytes() * glyph.fHeight); + return; + } + + switch ( fFace->glyph->format ) { + case FT_GLYPH_FORMAT_OUTLINE: + { + FT_Outline* outline = &fFace->glyph->outline; + FT_BBox bbox; + FT_Bitmap target; + + int dx = 0, dy = 0; + if (kSubpixel_Hints == fRec.fHints) + { + dx = glyph.getSubXFixed() >> 10; + dy = glyph.getSubYFixed() >> 10; + } + FT_Outline_Get_CBox(outline, &bbox); + /* + what we really want to do for subpixel is + offset(dx, dy) + compute_bounds + offset(bbox & !63) + but that is two calls to offset, so we do the following, which + achieves the same thing with only one offset call. + */ + FT_Outline_Translate(outline, dx - ((bbox.xMin + dx) & ~63), + dy - ((bbox.yMin + dy) & ~63)); + + target.width = glyph.fWidth; + target.rows = glyph.fHeight; + target.pitch = glyph.rowBytes(); + target.buffer = reinterpret_cast<uint8_t*>(glyph.fImage); + target.pixel_mode = compute_pixel_mode((SkMask::Format)fRec.fMaskFormat); + target.num_grays = 256; + + memset(glyph.fImage, 0, glyph.rowBytes() * glyph.fHeight); + FT_Outline_Get_Bitmap(gFTLibrary, outline, &target); + } + break; + + case FT_GLYPH_FORMAT_BITMAP: + SkASSERT_CONTINUE(glyph.fWidth == fFace->glyph->bitmap.width); + SkASSERT_CONTINUE(glyph.fHeight == fFace->glyph->bitmap.rows); + SkASSERT_CONTINUE(glyph.fTop == -fFace->glyph->bitmap_top); + SkASSERT_CONTINUE(glyph.fLeft == fFace->glyph->bitmap_left); + + { + const uint8_t* src = (const uint8_t*)fFace->glyph->bitmap.buffer; + uint8_t* dst = (uint8_t*)glyph.fImage; + unsigned srcRowBytes = fFace->glyph->bitmap.pitch; + unsigned dstRowBytes = glyph.rowBytes(); + unsigned minRowBytes = SkMin32(srcRowBytes, dstRowBytes); + unsigned extraRowBytes = dstRowBytes - minRowBytes; + + for (int y = fFace->glyph->bitmap.rows - 1; y >= 0; --y) + { + memcpy(dst, src, minRowBytes); + memset(dst + minRowBytes, 0, extraRowBytes); + src += srcRowBytes; + dst += dstRowBytes; + } + } + break; + + default: + SkASSERT(!"unknown glyph format"); + goto ERROR; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////// + +#define ft2sk(x) SkFixedToScalar((x) << 10) + +#if FREETYPE_MAJOR >= 2 && FREETYPE_MINOR >= 3 + #define CONST_PARAM const +#else // older freetype doesn't use const here + #define CONST_PARAM +#endif + +static int move_proc(CONST_PARAM FT_Vector* pt, void* ctx) +{ + SkPath* path = (SkPath*)ctx; + path->close(); // to close the previous contour (if any) + path->moveTo(ft2sk(pt->x), -ft2sk(pt->y)); + return 0; +} + +static int line_proc(CONST_PARAM FT_Vector* pt, void* ctx) +{ + SkPath* path = (SkPath*)ctx; + path->lineTo(ft2sk(pt->x), -ft2sk(pt->y)); + return 0; +} + +static int quad_proc(CONST_PARAM FT_Vector* pt0, CONST_PARAM FT_Vector* pt1, void* ctx) +{ + SkPath* path = (SkPath*)ctx; + path->quadTo(ft2sk(pt0->x), -ft2sk(pt0->y), ft2sk(pt1->x), -ft2sk(pt1->y)); + return 0; +} + +static int cubic_proc(CONST_PARAM FT_Vector* pt0, CONST_PARAM FT_Vector* pt1, CONST_PARAM FT_Vector* pt2, void* ctx) +{ + SkPath* path = (SkPath*)ctx; + path->cubicTo(ft2sk(pt0->x), -ft2sk(pt0->y), ft2sk(pt1->x), -ft2sk(pt1->y), ft2sk(pt2->x), -ft2sk(pt2->y)); + return 0; +} + +void SkScalerContext_FreeType::generatePath(const SkGlyph& glyph, SkPath* path) +{ + SkAutoMutexAcquire ac(gFTMutex); + + SkASSERT(&glyph && path); + + if (this->setupSize()) { + path->reset(); + return; + } + + uint32_t flags = fLoadGlyphFlags; + flags |= FT_LOAD_NO_BITMAP; // ignore embedded bitmaps so we're sure to get the outline + flags &= ~FT_LOAD_RENDER; // don't scan convert (we just want the outline) + + FT_Error err = FT_Load_Glyph( fFace, glyph.getGlyphID(fBaseGlyphCount), flags); + + if (err != 0) { + SkDEBUGF(("SkScalerContext_FreeType::generatePath: FT_Load_Glyph(glyph:%d flags:%d) returned 0x%x\n", + glyph.getGlyphID(fBaseGlyphCount), flags, err)); + path->reset(); + return; + } + + FT_Outline_Funcs funcs; + + funcs.move_to = move_proc; + funcs.line_to = line_proc; + funcs.conic_to = quad_proc; + funcs.cubic_to = cubic_proc; + funcs.shift = 0; + funcs.delta = 0; + + err = FT_Outline_Decompose(&fFace->glyph->outline, &funcs, path); + + if (err != 0) { + SkDEBUGF(("SkScalerContext_FreeType::generatePath: FT_Load_Glyph(glyph:%d flags:%d) returned 0x%x\n", + glyph.getGlyphID(fBaseGlyphCount), flags, err)); + path->reset(); + return; + } + + path->close(); +} + +static void map_y_to_pt(const FT_Matrix& mat, SkFixed y, SkPoint* pt) +{ + SkFixed x = SkFixedMul(mat.xy, y); + y = SkFixedMul(mat.yy, y); + + pt->set(SkFixedToScalar(x), SkFixedToScalar(y)); +} + +void SkScalerContext_FreeType::generateFontMetrics(SkPaint::FontMetrics* mx, SkPaint::FontMetrics* my) +{ + if (NULL == mx && NULL == my) + return; + + SkAutoMutexAcquire ac(gFTMutex); + + if (this->setupSize()) + { + if (mx) + memset(mx, 0, sizeof(SkPaint::FontMetrics)); + if (my) + memset(my, 0, sizeof(SkPaint::FontMetrics)); + return; + } + + SkPoint pts[5]; + SkFixed ys[5]; + FT_Face face = fFace; + int upem = face->units_per_EM; + SkFixed scaleY = fScaleY; + SkFixed mxy = fMatrix22.xy; + SkFixed myy = fMatrix22.yy; + + int leading = face->height - face->ascender + face->descender; + if (leading < 0) + leading = 0; + + ys[0] = -face->bbox.yMax; + ys[1] = -face->ascender; + ys[2] = -face->descender; + ys[3] = -face->bbox.yMin; + ys[4] = leading; + + // convert upem-y values into scalar points + for (int i = 0; i < 5; i++) + { + SkFixed y = SkMulDiv(scaleY, ys[i], upem); + SkFixed x = SkFixedMul(mxy, y); + y = SkFixedMul(myy, y); + pts[i].set(SkFixedToScalar(x), SkFixedToScalar(y)); + } + + if (mx) + { + mx->fTop = pts[0].fX; + mx->fAscent = pts[1].fX; + mx->fDescent = pts[2].fX; + mx->fBottom = pts[3].fX; + mx->fLeading = pts[4].fX; + } + if (my) + { + my->fTop = pts[0].fY; + my->fAscent = pts[1].fY; + my->fDescent = pts[2].fY; + my->fBottom = pts[3].fY; + my->fLeading = pts[4].fY; + } +} + +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// + +SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc) +{ + return SkNEW_ARGS(SkScalerContext_FreeType, (desc)); +} + +/////////////////////////////////////////////////////////////////////////////// + +/* Export this so that other parts of our FonttHost port can make use of our + ability to extract the name+style from a stream, using FreeType's api. +*/ +SkTypeface::Style find_name_and_style(SkStream* stream, SkString* name) +{ + FT_Library library; + if (FT_Init_FreeType(&library)) { + name->set(NULL); + return SkTypeface::kNormal; + } + + FT_Open_Args args; + memset(&args, 0, sizeof(args)); + + const void* memoryBase = stream->getMemoryBase(); + FT_StreamRec streamRec; + + if (NULL != memoryBase) { + args.flags = FT_OPEN_MEMORY; + args.memory_base = (const FT_Byte*)memoryBase; + args.memory_size = stream->getLength(); + } else { + memset(&streamRec, 0, sizeof(streamRec)); + streamRec.size = stream->read(NULL, 0); + streamRec.descriptor.pointer = stream; + streamRec.read = sk_stream_read; + streamRec.close = sk_stream_close; + + args.flags = FT_OPEN_STREAM; + args.stream = &streamRec; + } + + FT_Face face; + if (FT_Open_Face(library, &args, 0, &face)) { + FT_Done_FreeType(library); + name->set(NULL); + return SkTypeface::kNormal; + } + + name->set(face->family_name); + int style = SkTypeface::kNormal; + + if (face->style_flags & FT_STYLE_FLAG_BOLD) { + style |= SkTypeface::kBold; + } + if (face->style_flags & FT_STYLE_FLAG_ITALIC) { + style |= SkTypeface::kItalic; + } + + FT_Done_Face(face); + FT_Done_FreeType(library); + return (SkTypeface::Style)style; +} + diff --git a/skia/ports/SkFontHost_android.cpp b/skia/ports/SkFontHost_android.cpp new file mode 100644 index 0000000..d7613c6 --- /dev/null +++ b/skia/ports/SkFontHost_android.cpp @@ -0,0 +1,578 @@ +/* libs/graphics/ports/SkFontHost_android.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkFontHost.h" +#include "SkDescriptor.h" +#include "SkMMapStream.h" +#include "SkPaint.h" +#include "SkString.h" +#include "SkStream.h" +#include "SkThread.h" +#include "SkTSearch.h" +#include <stdio.h> + +#define FONT_CACHE_MEMORY_BUDGET (768 * 1024) + +#ifndef SK_FONT_FILE_PREFIX + #define SK_FONT_FILE_PREFIX "/fonts/" +#endif + +SkTypeface::Style find_name_and_style(SkStream* stream, SkString* name); + +static void GetFullPathForSysFonts(SkString* full, const char name[]) +{ + full->set(getenv("ANDROID_ROOT")); + full->append(SK_FONT_FILE_PREFIX); + full->append(name); +} + +/////////////////////////////////////////////////////////////////////////////// + +struct FamilyRec; + +/* This guy holds a mapping of a name -> family, used for looking up fonts. + Since it is stored in a stretchy array that doesn't preserve object + semantics, we don't use constructor/destructors, but just have explicit + helpers to manage our internal bookkeeping. +*/ +struct NameFamilyPair { + const char* fName; // we own this + FamilyRec* fFamily; // we don't own this, we just reference it + + void construct(const char name[], FamilyRec* family) + { + fName = strdup(name); + fFamily = family; // we don't own this, so just record the referene + } + void destruct() + { + free((char*)fName); + // we don't own family, so just ignore our reference + } +}; + +// we use atomic_inc to grow this for each typeface we create +static int32_t gUniqueFontID; + +// this is the mutex that protects these globals +static SkMutex gFamilyMutex; +static FamilyRec* gFamilyHead; +static SkTDArray<NameFamilyPair> gNameList; + +struct FamilyRec { + FamilyRec* fNext; + SkTypeface* fFaces[4]; + + FamilyRec() + { + fNext = gFamilyHead; + memset(fFaces, 0, sizeof(fFaces)); + gFamilyHead = this; + } +}; + +static SkTypeface* find_best_face(const FamilyRec* family, + SkTypeface::Style style) +{ + SkTypeface* const* faces = family->fFaces; + + if (faces[style] != NULL) { // exact match + return faces[style]; + } + // look for a matching bold + style = (SkTypeface::Style)(style ^ SkTypeface::kItalic); + if (faces[style] != NULL) { + return faces[style]; + } + // look for the plain + if (faces[SkTypeface::kNormal] != NULL) { + return faces[SkTypeface::kNormal]; + } + // look for anything + for (int i = 0; i < 4; i++) { + if (faces[i] != NULL) { + return faces[i]; + } + } + // should never get here, since the faces list should not be empty + SkASSERT(!"faces list is empty"); + return NULL; +} + +static FamilyRec* find_family(const SkTypeface* member) +{ + FamilyRec* curr = gFamilyHead; + while (curr != NULL) { + for (int i = 0; i < 4; i++) { + if (curr->fFaces[i] == member) { + return curr; + } + } + curr = curr->fNext; + } + return NULL; +} + +static SkTypeface* resolve_uniqueID(uint32_t uniqueID) +{ + FamilyRec* curr = gFamilyHead; + while (curr != NULL) { + for (int i = 0; i < 4; i++) { + SkTypeface* face = curr->fFaces[i]; + if (face != NULL && face->uniqueID() == uniqueID) { + return face; + } + } + curr = curr->fNext; + } + return NULL; +} + +/* Remove reference to this face from its family. If the resulting family + is empty (has no faces), return that family, otherwise return NULL +*/ +static FamilyRec* remove_from_family(const SkTypeface* face) +{ + FamilyRec* family = find_family(face); + SkASSERT(family->fFaces[face->style()] == face); + family->fFaces[face->style()] = NULL; + + for (int i = 0; i < 4; i++) { + if (family->fFaces[i] != NULL) { // family is non-empty + return NULL; + } + } + return family; // return the empty family +} + +// maybe we should make FamilyRec be doubly-linked +static void detach_and_delete_family(FamilyRec* family) +{ + FamilyRec* curr = gFamilyHead; + FamilyRec* prev = NULL; + + while (curr != NULL) { + FamilyRec* next = curr->fNext; + if (curr == family) { + if (prev == NULL) { + gFamilyHead = next; + } else { + prev->fNext = next; + } + SkDELETE(family); + return; + } + prev = curr; + curr = next; + } + SkASSERT(!"Yikes, couldn't find family in our list to remove/delete"); +} + +static SkTypeface* find_typeface(const char name[], SkTypeface::Style style) +{ + NameFamilyPair* list = gNameList.begin(); + int count = gNameList.count(); + + int index = SkStrLCSearch(&list[0].fName, count, name, sizeof(list[0])); + + if (index >= 0) { + return find_best_face(list[index].fFamily, style); + } + return NULL; +} + +static SkTypeface* find_typeface(const SkTypeface* familyMember, + SkTypeface::Style style) +{ + const FamilyRec* family = find_family(familyMember); + return family ? find_best_face(family, style) : NULL; +} + +static void add_name(const char name[], FamilyRec* family) +{ + SkAutoAsciiToLC tolc(name); + name = tolc.lc(); + + NameFamilyPair* list = gNameList.begin(); + int count = gNameList.count(); + + int index = SkStrLCSearch(&list[0].fName, count, name, sizeof(list[0])); + + if (index < 0) { + list = gNameList.insert(~index); + list->construct(name, family); + } +} + +static void remove_from_names(FamilyRec* emptyFamily) +{ +#ifdef SK_DEBUG + for (int i = 0; i < 4; i++) { + SkASSERT(emptyFamily->fFaces[i] == NULL); + } +#endif + + SkTDArray<NameFamilyPair>& list = gNameList; + + // must go backwards when removing + for (int i = list.count() - 1; i >= 0; --i) { + NameFamilyPair* pair = &list[i]; + if (pair->fFamily == emptyFamily) { + pair->destruct(); + list.remove(i); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +class FamilyTypeface : public SkTypeface { +public: + FamilyTypeface(Style style, bool sysFont, SkTypeface* familyMember) + : SkTypeface(style, sk_atomic_inc(&gUniqueFontID) + 1) + { + fIsSysFont = sysFont; + + SkAutoMutexAcquire ac(gFamilyMutex); + + FamilyRec* rec = NULL; + if (familyMember) { + rec = find_family(familyMember); + SkASSERT(rec); + } else { + rec = SkNEW(FamilyRec); + } + rec->fFaces[style] = this; + } + + virtual ~FamilyTypeface() + { + SkAutoMutexAcquire ac(gFamilyMutex); + + // remove us from our family. If the family is now empty, we return + // that and then remove that family from the name list + FamilyRec* family = remove_from_family(this); + if (NULL != family) { + remove_from_names(family); + detach_and_delete_family(family); + } + } + + bool isSysFont() const { return fIsSysFont; } + + virtual SkStream* openStream() = 0; + virtual void closeStream(SkStream*) = 0; + +private: + bool fIsSysFont; + + typedef SkTypeface INHERITED; +}; + +/////////////////////////////////////////////////////////////////////////////// + +class StreamTypeface : public FamilyTypeface { +public: + StreamTypeface(Style style, bool sysFont, SkTypeface* familyMember, + SkStream* stream) + : INHERITED(style, sysFont, familyMember) + { + fStream = stream; + } + virtual ~StreamTypeface() + { + SkDELETE(fStream); + } + + // overrides + virtual SkStream* openStream() { return fStream; } + virtual void closeStream(SkStream*) {} + +private: + SkStream* fStream; + + typedef FamilyTypeface INHERITED; +}; + +class FileTypeface : public FamilyTypeface { +public: + FileTypeface(Style style, bool sysFont, SkTypeface* familyMember, + const char path[]) + : INHERITED(style, sysFont, familyMember) + { + SkString fullpath; + + if (sysFont) { + GetFullPathForSysFonts(&fullpath, path); + path = fullpath.c_str(); + } + fPath.set(path); + } + + // overrides + virtual SkStream* openStream() + { + SkStream* stream = SkNEW_ARGS(SkMMAPStream, (fPath.c_str())); + + // check for failure + if (stream->getLength() <= 0) { + SkDELETE(stream); + // maybe MMAP isn't supported. try FILE + stream = SkNEW_ARGS(SkFILEStream, (fPath.c_str())); + if (stream->getLength() <= 0) { + SkDELETE(stream); + stream = NULL; + } + } + return stream; + } + virtual void closeStream(SkStream* stream) + { + SkDELETE(stream); + } + +private: + SkString fPath; + + typedef FamilyTypeface INHERITED; +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static bool get_name_and_style(const char path[], SkString* name, + SkTypeface::Style* style) +{ + SkString fullpath; + GetFullPathForSysFonts(&fullpath, path); + + SkMMAPStream stream(fullpath.c_str()); + if (stream.getLength() > 0) { + *style = find_name_and_style(&stream, name); + return true; + } + else { + SkFILEStream stream(fullpath.c_str()); + if (stream.getLength() > 0) { + *style = find_name_and_style(&stream, name); + return true; + } + } + + SkDebugf("---- failed to open <%s> as a font\n", fullpath.c_str()); + return false; +} + +struct FontInitRec { + const char* fFileName; + const char* const* fNames; // null-terminated list +}; + +static const char* gSansNames[] = { + "sans-serif", "arial", "helvetica", "tahoma", "verdana", NULL +}; + +static const char* gSerifNames[] = { + "serif", "times", "times new roman", "palatino", "goudy", + "fantasy", "cursive", NULL +}; + +static const char* gMonoNames[] = { + "monospace", "courier", "courier new", "monaco", NULL +}; + +static const char* gFBNames[] = { NULL }; + +/* Fonts must be grouped by family, with the first font in a family having the + list of names (even if that list is empty), and the following members having + null for the list. The names list must be NULL-terminated +*/ +static const FontInitRec gSystemFonts[] = { + { "DroidSans.ttf", gSansNames }, + { "DroidSans-Bold.ttf", NULL }, + { "DroidSerif-Regular.ttf", gSerifNames }, + { "DroidSerif-Bold.ttf", NULL }, + { "DroidSerif-Italic.ttf", NULL }, + { "DroidSerif-BoldItalic.ttf", NULL }, + { "DroidSansMono.ttf", gMonoNames }, + { "DroidSansFallback.ttf", gFBNames } +}; + +#define DEFAULT_NAMES gSansNames + +// these globals are assigned (once) by load_system_fonts() +static SkTypeface* gFallBackTypeface; +static FamilyRec* gDefaultFamily; +static SkTypeface* gDefaultNormal; + +static void load_system_fonts() +{ + // check if we've already be called + if (NULL != gDefaultNormal) { + return; + } + + const FontInitRec* rec = gSystemFonts; + SkTypeface* firstInFamily = NULL; + + for (size_t i = 0; i < SK_ARRAY_COUNT(gSystemFonts); i++) { + // if we're the first in a new family, clear firstInFamily + if (rec[i].fNames != NULL) { + firstInFamily = NULL; + } + + SkString name; + SkTypeface::Style style; + + if (!get_name_and_style(rec[i].fFileName, &name, &style)) { + SkDebugf("------ can't load <%s> as a font\n", rec[i].fFileName); + continue; + } + + SkTypeface* tf = SkNEW_ARGS(FileTypeface, + (style, + true, // system-font (cannot delete) + firstInFamily, // what family to join + rec[i].fFileName) // filename + ); + + if (rec[i].fNames != NULL) { + firstInFamily = tf; + const char* const* names = rec[i].fNames; + + // record the fallback if this is it + if (names == gFBNames) { + gFallBackTypeface = tf; + } + // record the default family if this is it + if (names == DEFAULT_NAMES) { + gDefaultFamily = find_family(tf); + } + // add the names to map to this family + FamilyRec* family = find_family(tf); + while (*names) { + add_name(*names, family); + names += 1; + } + } + + } + + // do this after all fonts are loaded. This is our default font, and it + // acts as a sentinel so we only execute load_system_fonts() once + gDefaultNormal = find_best_face(gDefaultFamily, SkTypeface::kNormal); +} + +/////////////////////////////////////////////////////////////////////////////// + +SkTypeface* SkFontHost::FindTypeface(const SkTypeface* familyFace, + const char familyName[], + SkTypeface::Style style) +{ + load_system_fonts(); + + SkAutoMutexAcquire ac(gFamilyMutex); + + // clip to legal style bits + style = (SkTypeface::Style)(style & SkTypeface::kBoldItalic); + + SkTypeface* tf = NULL; + + if (NULL != familyFace) { + tf = find_typeface(familyFace, style); + } else if (NULL != familyName) { + tf = find_typeface(familyName, style); + } + + if (NULL == tf) { + tf = find_best_face(gDefaultFamily, style); + } + + return tf; +} + +SkTypeface* SkFontHost::ResolveTypeface(uint32_t fontID) +{ + SkAutoMutexAcquire ac(gFamilyMutex); + + return resolve_uniqueID(fontID); +} + +SkStream* SkFontHost::OpenStream(uint32_t fontID) +{ + + FamilyTypeface* tf = (FamilyTypeface*)SkFontHost::ResolveTypeface(fontID); + SkStream* stream = tf ? tf->openStream() : NULL; + + if (NULL == stream || stream->getLength() == 0) { + delete stream; + stream = NULL; + } + return stream; +} + +void SkFontHost::CloseStream(uint32_t fontID, SkStream* stream) +{ + FamilyTypeface* tf = (FamilyTypeface*)SkFontHost::ResolveTypeface(fontID); + if (NULL != tf) { + tf->closeStream(stream); + } +} + +SkScalerContext* SkFontHost::CreateFallbackScalerContext( + const SkScalerContext::Rec& rec) +{ + load_system_fonts(); + + SkAutoDescriptor ad(sizeof(rec) + SkDescriptor::ComputeOverhead(1)); + SkDescriptor* desc = ad.getDesc(); + + desc->init(); + SkScalerContext::Rec* newRec = + (SkScalerContext::Rec*)desc->addEntry(kRec_SkDescriptorTag, + sizeof(rec), &rec); + newRec->fFontID = gFallBackTypeface->uniqueID(); + desc->computeChecksum(); + + return SkFontHost::CreateScalerContext(desc); +} + +/////////////////////////////////////////////////////////////////////////////// + +SkTypeface* SkFontHost::CreateTypeface(SkStream* stream) +{ + if (NULL == stream || stream->getLength() <= 0) { + SkDELETE(stream); + return NULL; + } + + SkString name; + SkTypeface::Style style = find_name_and_style(stream, &name); + + return SkNEW_ARGS(StreamTypeface, (style, false, NULL, stream)); +} + +/////////////////////////////////////////////////////////////////////////////// + +size_t SkFontHost::ShouldPurgeFontCache(size_t sizeAllocatedSoFar) +{ + if (sizeAllocatedSoFar > FONT_CACHE_MEMORY_BUDGET) + return sizeAllocatedSoFar - FONT_CACHE_MEMORY_BUDGET; + else + return 0; // nothing to do +} + diff --git a/skia/ports/SkFontHost_ascender.cpp b/skia/ports/SkFontHost_ascender.cpp new file mode 100644 index 0000000..2148850 --- /dev/null +++ b/skia/ports/SkFontHost_ascender.cpp @@ -0,0 +1,211 @@ +#include "SkScalerContext.h" +#include "SkBitmap.h" +#include "SkCanvas.h" +#include "SkDescriptor.h" +#include "SkFDot6.h" +#include "SkFontHost.h" +#include "SkMask.h" +#include "SkStream.h" +#include "SkString.h" +#include "SkThread.h" +#include "SkTemplates.h" + +#include <acaapi.h> + +////////////////////////////////////////////////////////////////////////// + +#include "SkMMapStream.h" + +class SkScalerContext_Ascender : public SkScalerContext { +public: + SkScalerContext_Ascender(const SkDescriptor* desc); + virtual ~SkScalerContext_Ascender(); + +protected: + virtual unsigned generateGlyphCount() const; + virtual uint16_t generateCharToGlyph(SkUnichar uni); + virtual void generateMetrics(SkGlyph* glyph); + virtual void generateImage(const SkGlyph& glyph); + virtual void generatePath(const SkGlyph& glyph, SkPath* path); + virtual void generateFontMetrics(SkPaint::FontMetrics* mx, SkPaint::FontMetrics* my); + +private: + aca_FontHandle fHandle; + void* fWorkspace; + void* fGlyphWorkspace; + SkStream* fFontStream; + SkStream* fHintStream; +}; + +/////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////// + +SkScalerContext_Ascender::SkScalerContext_Ascender(const SkDescriptor* desc) + : SkScalerContext(desc) +{ + int size = aca_Get_FontHandleRec_Size(); + fHandle = (aca_FontHandle)sk_malloc_throw(size); + + // get the pointer to the font + + fFontStream = new SkMMAPStream("/UcsGB2312-Hei-H.FDL"); + fHintStream = new SkMMAPStream("/genv6-23.bin"); + + void* hints = sk_malloc_throw(fHintStream->getLength()); + memcpy(hints, fHintStream->getMemoryBase(), fHintStream->getLength()); + + aca_Create_Font_Handle(fHandle, + (void*)fFontStream->getMemoryBase(), fFontStream->getLength(), + "fred", + hints, fHintStream->getLength()); + + // compute our factors from the record + + SkMatrix m; + + fRec.getSingleMatrix(&m); + + // now compute our scale factors + SkScalar sx = m.getScaleX(); + SkScalar sy = m.getScaleY(); + + int ppemX = SkScalarRound(sx); + int ppemY = SkScalarRound(sy); + + size = aca_Find_Font_Memory_Required(fHandle, ppemX, ppemY); + size *= 8; // Jeff suggests this :) + fWorkspace = sk_malloc_throw(size); + aca_Set_Font_Memory(fHandle, (uint8_t*)fWorkspace, size); + + aca_GlyphAttribsRec rec; + + memset(&rec, 0, sizeof(rec)); + rec.xSize = ppemX; + rec.ySize = ppemY; + rec.doAdjust = true; + rec.doExceptions = true; + rec.doGlyphHints = true; + rec.doInterpolate = true; + rec.grayMode = 2; + aca_Set_Font_Attributes(fHandle, &rec, &size); + + fGlyphWorkspace = sk_malloc_throw(size); + aca_Set_Glyph_Memory(fHandle, fGlyphWorkspace); +} + +SkScalerContext_Ascender::~SkScalerContext_Ascender() +{ + delete fHintStream; + delete fFontStream; + sk_free(fGlyphWorkspace); + sk_free(fWorkspace); + sk_free(fHandle); +} + +unsigned SkScalerContext_Ascender::generateGlyphCount() const +{ + return 1000; +} + +uint16_t SkScalerContext_Ascender::generateCharToGlyph(SkUnichar uni) +{ + return (uint16_t)(uni & 0xFFFF); +} + +void SkScalerContext_Ascender::generateMetrics(SkGlyph* glyph) +{ + glyph->fRsbDelta = 0; + glyph->fLsbDelta = 0; + + aca_GlyphImageRec rec; + aca_Vector topLeft; + + int adv = aca_Get_Adv_Width(fHandle, glyph->getGlyphID()); + if (aca_GLYPH_NOT_PRESENT == adv) + goto ERROR; + + aca_Rasterize(glyph->getGlyphID(), fHandle, &rec, &topLeft); + + if (false) // error + { +ERROR: + glyph->fWidth = 0; + glyph->fHeight = 0; + glyph->fTop = 0; + glyph->fLeft = 0; + glyph->fAdvanceX = 0; + glyph->fAdvanceY = 0; + return; + } + + glyph->fWidth = rec.width; + glyph->fHeight = rec.rows; + glyph->fRowBytes = rec.width; + glyph->fTop = -topLeft.y; + glyph->fLeft = topLeft.x; + glyph->fAdvanceX = SkIntToFixed(adv); + glyph->fAdvanceY = SkIntToFixed(0); +} + +void SkScalerContext_Ascender::generateImage(const SkGlyph& glyph) +{ + aca_GlyphImageRec rec; + aca_Vector topLeft; + + aca_Rasterize(glyph.getGlyphID(), fHandle, &rec, &topLeft); + + const uint8_t* src = (const uint8_t*)rec.buffer; + uint8_t* dst = (uint8_t*)glyph.fImage; + int height = glyph.fHeight; + + src += rec.y0 * rec.pitch + rec.x0; + while (--height >= 0) + { + memcpy(dst, src, glyph.fWidth); + src += rec.pitch; + dst += glyph.fRowBytes; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////// + +void SkScalerContext_Ascender::generatePath(const SkGlyph& glyph, SkPath* path) +{ + SkRect r; + + r.set(0, 0, SkIntToScalar(4), SkIntToScalar(4)); + path->reset(); + path->addRect(r); +} + +void SkScalerContext_Ascender::generateFontMetrics(SkPaint::FontMetrics* mx, SkPaint::FontMetrics* my) +{ + if (NULL == mx && NULL == my) + return; + + if (mx) + { + mx->fTop = SkIntToScalar(-16); + mx->fAscent = SkIntToScalar(-16); + mx->fDescent = SkIntToScalar(4); + mx->fBottom = SkIntToScalar(4); + mx->fLeading = 0; + } + if (my) + { + my->fTop = SkIntToScalar(-16); + my->fAscent = SkIntToScalar(-16); + my->fDescent = SkIntToScalar(4); + my->fBottom = SkIntToScalar(4); + my->fLeading = 0; + } +} + +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// + +SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc) +{ + return SkNEW_ARGS(SkScalerContext_Ascender, (desc)); +} + diff --git a/skia/ports/SkFontHost_gamma.cpp b/skia/ports/SkFontHost_gamma.cpp new file mode 100644 index 0000000..28c7051 --- /dev/null +++ b/skia/ports/SkFontHost_gamma.cpp @@ -0,0 +1,65 @@ + +#include "SkFontHost.h" +#include <math.h> + +static void build_power_table(uint8_t table[], float ee) +{ +// printf("------ build_power_table %g\n", ee); + for (int i = 0; i < 256; i++) + { + float x = i / 255.f; + // printf(" %d %g", i, x); + x = powf(x, ee); + // printf(" %g", x); + int xx = SkScalarRound(SkFloatToScalar(x * 255)); + // printf(" %d\n", xx); + table[i] = SkToU8(xx); + } +} + +static bool gGammaIsBuilt; +static uint8_t gBlackGamma[256], gWhiteGamma[256]; + +#define ANDROID_BLACK_GAMMA (1.4f) +#define ANDROID_WHITE_GAMMA (1/1.4f) + +void SkFontHost::GetGammaTables(const uint8_t* tables[2]) +{ + // would be cleaner if these tables were precomputed and just linked in + if (!gGammaIsBuilt) + { + build_power_table(gBlackGamma, ANDROID_BLACK_GAMMA); + build_power_table(gWhiteGamma, ANDROID_WHITE_GAMMA); + gGammaIsBuilt = true; + } + tables[0] = gBlackGamma; + tables[1] = gWhiteGamma; +} + +#define BLACK_GAMMA_THRESHOLD 0x40 +#define WHITE_GAMMA_THRESHOLD 0xC0 + +int SkFontHost::ComputeGammaFlag(const SkPaint& paint) +{ + if (paint.getShader() == NULL) + { + SkColor c = paint.getColor(); + int r = SkColorGetR(c); + int g = SkColorGetG(c); + int b = SkColorGetB(c); + int luminance = (r * 2 + g * 5 + b) >> 3; + + if (luminance <= BLACK_GAMMA_THRESHOLD) + { + // printf("------ black gamma for [%d %d %d]\n", r, g, b); + return SkScalerContext::kGammaForBlack_Flag; + } + if (luminance >= WHITE_GAMMA_THRESHOLD) + { + // printf("------ white gamma for [%d %d %d]\n", r, g, b); + return SkScalerContext::kGammaForWhite_Flag; + } + } + return 0; +} + diff --git a/skia/ports/SkFontHost_none.cpp b/skia/ports/SkFontHost_none.cpp new file mode 100644 index 0000000..90bd063 --- /dev/null +++ b/skia/ports/SkFontHost_none.cpp @@ -0,0 +1,80 @@ +/* Copyright 2006-2008, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkFontHost.h" + +SkTypeface* SkFontHost::FindTypeface(const SkTypeface* familyFace, + const char famillyName[], + SkTypeface::Style style) +{ + SkASSERT(!"SkFontHost::FindTypeface unimplemented"); + return NULL; +} + +SkTypeface* SkFontHost::ResolveTypeface(uint32_t uniqueID) +{ + SkASSERT(!"SkFontHost::ResolveTypeface unimplemented"); + return NULL; +} + +SkStream* SkFontHost::OpenStream(uint32_t uniqueID) +{ + SkASSERT(!"SkFontHost::OpenStream unimplemented"); + return NULL; +} + +void SkFontHost::CloseStream(uint32_t uniqueID, SkStream*) +{ + SkASSERT(!"SkFontHost::CloseStream unimplemented"); +} + +SkTypeface* SkFontHost::CreateTypeface(SkStream*) +{ + SkASSERT(!"SkFontHost::CreateTypeface unimplemented"); + return NULL; +} + +/////////////////////////////////////////////////////////////////////////////// + +SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc) +{ + SkASSERT(!"SkFontHost::CreateScalarContext unimplemented"); + return NULL; +} + +SkScalerContext* SkFontHost::CreateFallbackScalerContext(const SkScalerContext::Rec&) +{ + SkASSERT(!"SkFontHost::CreateFallbackScalerContext unimplemented"); + return NULL; +} + +/////////////////////////////////////////////////////////////////////////////// + +size_t SkFontHost::ShouldPurgeFontCache(size_t sizeAllocatedSoFar) +{ + return 0; // nothing to do (change me if you want to limit the font cache) +} + +int SkFontHost::ComputeGammaFlag(const SkPaint& paint) +{ + return 0; +} + +void SkFontHost::GetGammaTables(const uint8_t* tables[2]) +{ + tables[0] = NULL; // black gamma (e.g. exp=1.4) + tables[1] = NULL; // white gamma (e.g. exp= 1/1.4) +} + diff --git a/skia/ports/SkFontHost_win.cpp b/skia/ports/SkFontHost_win.cpp new file mode 100644 index 0000000..d66297a --- /dev/null +++ b/skia/ports/SkFontHost_win.cpp @@ -0,0 +1,908 @@ +/* libs/graphics/ports/SkFontHost_win.cpp + +** + +** Copyright 2006, Google Inc. + +** + +*/ + + + +#include "SkString.h" + +//#include "SkStream.h" + + + +#include "SkFontHost.h" + +#include "SkDescriptor.h" + +#include "SkThread.h" + + + +#ifdef WIN32 + +#include "windows.h" + +#include "tchar.h" + + + +static SkMutex gFTMutex; + + + +static LOGFONT gDefaultFont; + + + +static const uint16_t BUFFERSIZE = (16384 - 32); + +static uint8_t glyphbuf[BUFFERSIZE]; + + + +#ifndef SK_FONTKEY + + #define SK_FONTKEY "Windows Font Key" + +#endif + + + +inline FIXED SkFixedToFIXED(SkFixed x) { + + return *(FIXED*)(&x); + +} + + + +class FontFaceRec_Typeface : public SkTypeface { + +public: + +#if 0 + + FontFaceRec_Typeface(const LOGFONT& face) : fFace(face) + + { + + int style = 0; + + if (face.lfWeight == FW_SEMIBOLD || face.lfWeight == FW_DEMIBOLD || face.lfWeight == FW_BOLD) + + style |= SkTypeface::kBold; + + if (face.lfItalic) + + style |= SkTypeface::kItalic; + + this->setStyle((SkTypeface::Style)style); + + } + +#endif + + ~FontFaceRec_Typeface() {}; + + + + TCHAR* GetFontName() { return fFace.lfFaceName; } + + + + SkTypeface::Style GetFontStyle() { + + int style = SkTypeface::kNormal; + + if (fFace.lfWeight == FW_SEMIBOLD || fFace.lfWeight == FW_DEMIBOLD || fFace.lfWeight == FW_BOLD) + + style |= SkTypeface::kBold; + + if (fFace.lfItalic) + + style |= SkTypeface::kItalic; + + + + return (SkTypeface::Style)style; + + } + + + + long GetFontSize() { return fFace.lfHeight; } + + + + LOGFONT fFace; + +}; + + + + + +static const LOGFONT* get_default_font() + +{ + + // don't hardcode on Windows, Win2000, XP, Vista, and international all have different default + + // and the user could change too + + + + NONCLIENTMETRICS ncm; + + ncm.cbSize = sizeof(NONCLIENTMETRICS); + + SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0); + + + + memcpy(&gDefaultFont, &(ncm.lfMessageFont), sizeof(LOGFONT)); + + + + return &gDefaultFont; + +} + + + +static uint32_t FontFaceChecksum(const LOGFONT& face) + +{ + + uint32_t cs = 0; + + uint32_t bytesize = sizeof(LOGFONT); + + bytesize >>= 2; + + uint32_t *p32 = (uint32_t*)&face; + + + + while (bytesize) { + + bytesize --; + + cs ^= *p32; + + p32 ++; + + } + + + + return cs; + +} + + + +class SkScalerContext_Windows : public SkScalerContext { + +public: + + SkScalerContext_Windows(const SkDescriptor* desc); + + virtual ~SkScalerContext_Windows(); + + + +protected: + + virtual unsigned generateGlyphCount() const; + + virtual uint16_t generateCharToGlyph(SkUnichar uni); + + virtual void generateMetrics(SkGlyph* glyph); + + virtual void generateImage(const SkGlyph& glyph); + + virtual void generatePath(const SkGlyph& glyph, SkPath* path); + + virtual void generateLineHeight(SkPoint* ascent, SkPoint* descent); + + + +private: + + LOGFONT* plf; + + MAT2 mat22; + +}; + + + +SkScalerContext_Windows::SkScalerContext_Windows(const SkDescriptor* desc) + + : SkScalerContext(desc), plf(NULL) + +{ + + SkAutoMutexAcquire ac(gFTMutex); + + + + const LOGFONT** face = (const LOGFONT**)desc->findEntry(kTypeface_SkDescriptorTag, NULL); + + plf = (LOGFONT*)*face; + + SkASSERT(plf); + + + + mat22.eM11 = SkFixedToFIXED(fRec.fPost2x2[0][0]); + + mat22.eM12 = SkFixedToFIXED(-fRec.fPost2x2[0][1]); + + mat22.eM21 = SkFixedToFIXED(fRec.fPost2x2[1][0]); + + mat22.eM22 = SkFixedToFIXED(-fRec.fPost2x2[1][1]); + +} + + + +SkScalerContext_Windows::~SkScalerContext_Windows() { + +} + + + +unsigned SkScalerContext_Windows::generateGlyphCount() const { + + return 0xFFFF; + +// return fFace->num_glyphs; + +} + + + +uint16_t SkScalerContext_Windows::generateCharToGlyph(SkUnichar uni) { + + + + // let's just use the uni as index on Windows + + return SkToU16(uni); + +} + + + +void SkScalerContext_Windows::generateMetrics(SkGlyph* glyph) { + + + + HDC ddc = ::CreateCompatibleDC(NULL); + + SetBkMode(ddc, TRANSPARENT); + + + + SkASSERT(plf); + + plf->lfHeight = -SkFixedFloor(fRec.fTextSize); + + + + HFONT font = CreateFontIndirect(plf); + + HFONT oldfont = (HFONT)SelectObject(ddc, font); + + + + GLYPHMETRICS gm; + + memset(&gm, 0, sizeof(gm)); + + + + glyph->fRsbDelta = 0; + + glyph->fLsbDelta = 0; + + + + // Note: need to use GGO_GRAY8_BITMAP instead of GGO_METRICS because GGO_METRICS returns a smaller + + // BlackBlox; we need the bigger one in case we need the image. fAdvance is the same. + + uint32_t ret = GetGlyphOutlineW(ddc, glyph->f_GlyphID, GGO_GRAY8_BITMAP, &gm, 0, NULL, &mat22); + + + + if (GDI_ERROR != ret) { + + if (ret == 0) { + + // for white space, ret is zero and gmBlackBoxX, gmBlackBoxY are 1 incorrectly! + + gm.gmBlackBoxX = gm.gmBlackBoxY = 0; + + } + + glyph->fWidth = gm.gmBlackBoxX; + + glyph->fHeight = gm.gmBlackBoxY; + + glyph->fTop = gm.gmptGlyphOrigin.y - gm.gmBlackBoxY; + + glyph->fLeft = gm.gmptGlyphOrigin.x; + + glyph->fAdvanceX = SkIntToFixed(gm.gmCellIncX); + + glyph->fAdvanceY = -SkIntToFixed(gm.gmCellIncY); + + } + + + + ::SelectObject(ddc, oldfont); + + ::DeleteObject(font); + + ::DeleteDC(ddc); + +} + + + +void SkScalerContext_Windows::generateImage(const SkGlyph& glyph) { + + + + SkAutoMutexAcquire ac(gFTMutex); + + + + SkASSERT(plf); + + + + HDC ddc = ::CreateCompatibleDC(NULL); + + SetBkMode(ddc, TRANSPARENT); + + + + plf->lfHeight = -SkFixedFloor(fRec.fTextSize); + + + + HFONT font = CreateFontIndirect(plf); + + HFONT oldfont = (HFONT)SelectObject(ddc, font); + + + + GLYPHMETRICS gm; + + memset(&gm, 0, sizeof(gm)); + + + + uint32_t total_size = GetGlyphOutlineW(ddc, glyph.f_GlyphID, GGO_GRAY8_BITMAP, &gm, 0, NULL, &mat22); + + if (GDI_ERROR != total_size && total_size > 0) { + + uint8_t *pBuff = new uint8_t[total_size]; + + if (NULL != pBuff) { + + total_size = GetGlyphOutlineW(ddc, glyph.f_GlyphID, GGO_GRAY8_BITMAP, &gm, total_size, pBuff, &mat22); + + + + SkASSERT(total_size != GDI_ERROR); + + + + uint8_t* dst = (uint8_t*)glyph.fImage; + + uint32_t pitch = (gm.gmBlackBoxX + 3) & ~0x3; + + + + for (int32_t y = gm.gmBlackBoxY - 1; y >= 0; y--) { + + uint8_t* src = pBuff + pitch * y; + + + + for (uint32_t x = 0; x < gm.gmBlackBoxX; x++) { + + if (*src > 63) { + + *dst = 0xFF; + + } + + else { + + *dst = *src << 2; // scale to 0-255 + + } + + dst++; + + src++; + + } + + } + + + + delete[] pBuff; + + } + + } + + + + SkASSERT(GDI_ERROR != total_size && total_size >= 0); + + + + ::SelectObject(ddc, oldfont); + + ::DeleteObject(font); + + ::DeleteDC(ddc); + +} + + + +void SkScalerContext_Windows::generatePath(const SkGlyph& glyph, SkPath* path) { + + + + SkAutoMutexAcquire ac(gFTMutex); + + + + SkASSERT(&glyph && path); + + + + SkASSERT(plf); + + + + path->reset(); + + + + HDC ddc = ::CreateCompatibleDC(NULL); + + SetBkMode(ddc, TRANSPARENT); + + + + plf->lfHeight = -SkFixedFloor(fRec.fTextSize); + + + + HFONT font = CreateFontIndirect(plf); + + HFONT oldfont = (HFONT)SelectObject(ddc, font); + + + + GLYPHMETRICS gm; + + + + uint32_t total_size = GetGlyphOutlineW(ddc, glyph.f_GlyphID, GGO_NATIVE, &gm, BUFFERSIZE, glyphbuf, &mat22); + + + + if (GDI_ERROR != total_size) { + + + + const uint8_t* cur_glyph = glyphbuf; + + const uint8_t* end_glyph = glyphbuf + total_size; + + + + while(cur_glyph < end_glyph) { + + const TTPOLYGONHEADER* th = (TTPOLYGONHEADER*)cur_glyph; + + + + const uint8_t* end_poly = cur_glyph + th->cb; + + const uint8_t* cur_poly = cur_glyph + sizeof(TTPOLYGONHEADER); + + + + path->moveTo(*(SkFixed*)(&th->pfxStart.x), *(SkFixed*)(&th->pfxStart.y)); + + + + while(cur_poly < end_poly) { + + const TTPOLYCURVE* pc = (const TTPOLYCURVE*)cur_poly; + + + + if (pc->wType == TT_PRIM_LINE) { + + for (uint16_t i = 0; i < pc->cpfx; i++) { + + path->lineTo(*(SkFixed*)(&pc->apfx[i].x), *(SkFixed*)(&pc->apfx[i].y)); + + } + + } + + + + if (pc->wType == TT_PRIM_QSPLINE) { + + for (uint16_t u = 0; u < pc->cpfx - 1; u++) { // Walk through points in spline + + POINTFX pnt_b = pc->apfx[u]; // B is always the current point + + POINTFX pnt_c = pc->apfx[u+1]; + + + + if (u < pc->cpfx - 2) { // If not on last spline, compute C + + pnt_c.x = SkFixedToFIXED(SkFixedAve(*(SkFixed*)(&pnt_b.x), *(SkFixed*)(&pnt_c.x))); + + pnt_c.y = SkFixedToFIXED(SkFixedAve(*(SkFixed*)(&pnt_b.y), *(SkFixed*)(&pnt_c.y))); + + } + + + + path->quadTo(*(SkFixed*)(&pnt_b.x), *(SkFixed*)(&pnt_b.y), *(SkFixed*)(&pnt_c.x), *(SkFixed*)(&pnt_c.y)); + + } + + } + + cur_poly += sizeof(uint16_t) * 2 + sizeof(POINTFX) * pc->cpfx; + + } + + cur_glyph += th->cb; + + } + + } + + else { + + SkASSERT(false); + + } + + + + path->close(); + + + + ::SelectObject(ddc, oldfont); + + ::DeleteObject(font); + + ::DeleteDC(ddc); + +} + + + + + +// Note: not sure this is the correct implementation + +void SkScalerContext_Windows::generateLineHeight(SkPoint* ascent, SkPoint* descent) { + + + + HDC ddc = ::CreateCompatibleDC(NULL); + + SetBkMode(ddc, TRANSPARENT); + + + + SkASSERT(plf); + + plf->lfHeight = -SkFixedFloor(fRec.fTextSize); + + + + HFONT font = CreateFontIndirect(plf); + + HFONT oldfont = (HFONT)SelectObject(ddc, font); + + + + OUTLINETEXTMETRIC otm; + + + + uint32_t ret = GetOutlineTextMetrics(ddc, sizeof(otm), &otm); + + + + if (sizeof(otm) == ret) { + + if (ascent) + + ascent->iset(0, otm.otmAscent); + + if (descent) + + descent->iset(0, otm.otmDescent); + + } + + + + ::SelectObject(ddc, oldfont); + + ::DeleteObject(font); + + ::DeleteDC(ddc); + + + + return; + +} + + + +SkTypeface* SkFontHost::CreateTypeface( const SkTypeface* familyFace, const char familyName[], SkTypeface::Style style) { + + + + FontFaceRec_Typeface* ptypeface = new FontFaceRec_Typeface; + + + + if (NULL == ptypeface) { + + SkASSERT(false); + + return NULL; + + } + + + + memset(&ptypeface->fFace, 0, sizeof(LOGFONT)); + + + + // default + + ptypeface->fFace.lfHeight = -11; // default + + ptypeface->fFace.lfWeight = (style & SkTypeface::kBold) != 0 ? FW_BOLD : FW_NORMAL ; + + ptypeface->fFace.lfItalic = ((style & SkTypeface::kItalic) != 0); + + ptypeface->fFace.lfQuality = PROOF_QUALITY; + + + + _tcscpy(ptypeface->fFace.lfFaceName, familyName); + + + + + + return ptypeface; + +} + + + +uint32_t SkFontHost::FlattenTypeface(const SkTypeface* tface, void* buffer) { + + const LOGFONT* face; + + + + if (tface) + + face = &((const FontFaceRec_Typeface*)tface)->fFace; + + else + + face = get_default_font(); + + + + size_t size = sizeof(face); + + + + size += sizeof(uint32_t); + + + + if (buffer) { + + uint8_t* buf = (uint8_t*)buffer; + + memcpy(buf, &face, sizeof(face)); + + uint32_t cs = FontFaceChecksum(*face); + + + + memcpy(buf+sizeof(face), &cs, sizeof(cs)); + + } + + + + return size; + +} + + + +SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc) { + + return SkNEW_ARGS(SkScalerContext_Windows, (desc)); + +} + + + +void SkFontHost::GetDescriptorKeyString(const SkDescriptor* desc, SkString* keyString) { + + const LOGFONT** face = (const LOGFONT**)desc->findEntry(kTypeface_SkDescriptorTag, NULL); + + LOGFONT*lf = (LOGFONT*)*face; + + keyString->set(SK_FONTKEY); + + if (lf) { + + keyString->append(lf->lfFaceName); + + } + +} + + + +SkScalerContext* SkFontHost::CreateFallbackScalerContext(const SkScalerContext::Rec& rec) { + + const LOGFONT* face = get_default_font(); + + + + SkAutoDescriptor ad(sizeof(rec) + sizeof(face) + SkDescriptor::ComputeOverhead(2)); + + SkDescriptor* desc = ad.getDesc(); + + + + desc->init(); + + desc->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec); + + desc->addEntry(kTypeface_SkDescriptorTag, sizeof(face), &face); + + desc->computeChecksum(); + + + + return SkFontHost::CreateScalerContext(desc); + +} + + + +SkStream* SkFontHost::OpenDescriptorStream(const SkDescriptor* desc, const char keyString[]) { + + SkASSERT(!"SkFontHost::OpenDescriptorStream unimplemented"); + + return NULL; + +} + + + +uint32_t SkFontHost::TypefaceHash(const SkTypeface* face) { + + + +// FontFaceRec_Typeface *ptypeface = dynamic_cast<FontFaceRec_Typeface*>(face); + + FontFaceRec_Typeface *ptypeface = (FontFaceRec_Typeface*)(face); + + SkASSERT(ptypeface); + + + + return FontFaceChecksum(ptypeface->fFace); + +} + + + +bool SkFontHost::TypefaceEqual(const SkTypeface* facea, const SkTypeface* faceb) { + + + + FontFaceRec_Typeface *ptypefaceA = (FontFaceRec_Typeface*)facea; + + SkASSERT(ptypefaceA); + + + + FontFaceRec_Typeface *ptypefaceB = (FontFaceRec_Typeface*)faceb; + + SkASSERT(ptypefaceB); + + + + if (_tcscmp(ptypefaceA->GetFontName(), ptypefaceB->GetFontName())) return false; + + if (ptypefaceA->GetFontStyle() != ptypefaceB->GetFontStyle()) return false; + + if (ptypefaceA->GetFontSize() != ptypefaceB->GetFontSize()) return false; + + + + return true; + +} + + + +int SkFontHost::ComputeGammaFlag(const SkPaint& paint) + +{ + + return 0; + +} + + + +void SkFontHost::GetGammaTables(const uint8_t* tables[2]) + +{ + + tables[0] = NULL; // black gamma (e.g. exp=1.4) + + tables[1] = NULL; // white gamma (e.g. exp= 1/1.4) + +} + + + +#endif // WIN32 + + + diff --git a/skia/ports/SkGlobals_global.cpp b/skia/ports/SkGlobals_global.cpp new file mode 100644 index 0000000..f5f48ef --- /dev/null +++ b/skia/ports/SkGlobals_global.cpp @@ -0,0 +1,28 @@ +/* libs/graphics/ports/SkGlobals_global.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkGlobals.h" +#include "SkThread.h" + +static SkGlobals::BootStrap gBootStrap; + +SkGlobals::BootStrap& SkGlobals::GetBootStrap() +{ + return gBootStrap; +} + + diff --git a/skia/ports/SkImageDecoder_Factory.cpp b/skia/ports/SkImageDecoder_Factory.cpp new file mode 100644 index 0000000..67d4dcb --- /dev/null +++ b/skia/ports/SkImageDecoder_Factory.cpp @@ -0,0 +1,135 @@ +/* libs/graphics/ports/SkImageDecoder_Factory.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkImageDecoder.h" +#include "SkMovie.h" +#include "SkStream.h" + +//#define USE_PV_FOR_JPEG + +extern SkImageDecoder* SkImageDecoder_GIF_Factory(SkStream*); +extern SkImageDecoder* SkImageDecoder_BMP_Factory(SkStream*); +extern SkImageDecoder* SkImageDecoder_ICO_Factory(SkStream*); +extern SkImageDecoder* SkImageDecoder_PNG_Factory(SkStream*); +extern SkImageDecoder* SkImageDecoder_WBMP_Factory(SkStream*); +#ifdef USE_PV_FOR_JPEG + extern SkImageDecoder* SkImageDecoder_PVJPEG_Factory(SkStream*); +#else + extern SkImageDecoder* SkImageDecoder_JPEG_Factory(SkStream*); +#endif + + +typedef SkImageDecoder* (*SkImageDecoderFactoryProc)(SkStream*); + +struct CodecFormat { + SkImageDecoderFactoryProc fProc; + SkImageDecoder::Format fFormat; +}; + +static const CodecFormat gPairs[] = { + { SkImageDecoder_GIF_Factory, SkImageDecoder::kGIF_Format }, + { SkImageDecoder_PNG_Factory, SkImageDecoder::kPNG_Format }, + { SkImageDecoder_ICO_Factory, SkImageDecoder::kICO_Format }, + { SkImageDecoder_WBMP_Factory, SkImageDecoder::kWBMP_Format }, + { SkImageDecoder_BMP_Factory, SkImageDecoder::kBMP_Format }, + // jpeg must be last, as it doesn't have a good sniffer yet +#ifdef USE_PV_FOR_JPEG + { SkImageDecoder_PVJPEG_Factory, SkImageDecoder::kJPEG_Format } +#else + { SkImageDecoder_JPEG_Factory, SkImageDecoder::kJPEG_Format } +#endif +}; + +SkImageDecoder* SkImageDecoder::Factory(SkStream* stream) { + for (size_t i = 0; i < SK_ARRAY_COUNT(gPairs); i++) { + SkImageDecoder* codec = gPairs[i].fProc(stream); + stream->rewind(); + if (NULL != codec) { + return codec; + } + } + return NULL; +} + +bool SkImageDecoder::SupportsFormat(Format format) { + for (size_t i = 0; i < SK_ARRAY_COUNT(gPairs); i++) { + if (gPairs[i].fFormat == format) { + return true; + } + } + return false; +} + +///////////////////////////////////////////////////////////////////////// + +// the movie may hold onto the stream (by calling ref()) +typedef SkMovie* (*SkMovieStreamProc)(SkStream*); +// the movie may NOT hold onto the pointer +typedef SkMovie* (*SkMovieMemoryProc)(const void*, size_t); + +extern SkMovie* SkMovie_GIF_StreamFactory(SkStream*); +extern SkMovie* SkMovie_GIF_MemoryFactory(const void*, size_t); + +static const SkMovieStreamProc gStreamProc[] = { + SkMovie_GIF_StreamFactory +}; + +static const SkMovieMemoryProc gMemoryProc[] = { + SkMovie_GIF_MemoryFactory +}; + +SkMovie* SkMovie::DecodeStream(SkStream* stream) { + for (unsigned i = 0; i < SK_ARRAY_COUNT(gStreamProc); i++) { + SkMovie* movie = gStreamProc[i](stream); + if (NULL != movie) { + return movie; + } + stream->rewind(); + } + return NULL; +} + +SkMovie* SkMovie::DecodeMemory(const void* data, size_t length) +{ + for (unsigned i = 0; i < SK_ARRAY_COUNT(gMemoryProc); i++) { + SkMovie* movie = gMemoryProc[i](data, length); + if (NULL != movie) { + return movie; + } + } + return NULL; +} + +///////////////////////////////////////////////////////////////////////// + +#ifdef SK_SUPPORT_IMAGE_ENCODE + +extern SkImageEncoder* SkImageEncoder_JPEG_Factory(); +extern SkImageEncoder* SkImageEncoder_PNG_Factory(); + +SkImageEncoder* SkImageEncoder::Create(Type t) { + switch (t) { + case kJPEG_Type: + return SkImageEncoder_JPEG_Factory(); + case kPNG_Type: + return SkImageEncoder_PNG_Factory(); + default: + return NULL; + } +} + +#endif diff --git a/skia/ports/SkImageRef_ashmem.cpp b/skia/ports/SkImageRef_ashmem.cpp new file mode 100644 index 0000000..ef5fc58 --- /dev/null +++ b/skia/ports/SkImageRef_ashmem.cpp @@ -0,0 +1,195 @@ +#include "SkImageRef_ashmem.h" +#include "SkImageDecoder.h" +#include "SkThread.h" + +#include <sys/mman.h> +#include <unistd.h> +#include <cutils/ashmem.h> + +//#define TRACE_ASH_PURGE // just trace purges + +#ifdef DUMP_IMAGEREF_LIFECYCLE + #define DUMP_ASHMEM_LIFECYCLE +#else +// #define DUMP_ASHMEM_LIFECYCLE +#endif + +// ashmem likes lengths on page boundaries +static size_t roundToPageSize(size_t size) { + const size_t mask = getpagesize() - 1; + size_t newsize = (size + mask) & ~mask; +// SkDebugf("---- oldsize %d newsize %d\n", size, newsize); + return newsize; +} + +SkImageRef_ashmem::SkImageRef_ashmem(SkStream* stream, + SkBitmap::Config config, + int sampleSize) + : SkImageRef(stream, config, sampleSize) { + + fRec.fFD = -1; + fRec.fAddr = NULL; + fRec.fSize = 0; + fRec.fPinned = false; + + fCT = NULL; +} + +SkImageRef_ashmem::~SkImageRef_ashmem() { + fCT->safeUnref(); + + if (-1 != fRec.fFD) { +#ifdef DUMP_ASHMEM_LIFECYCLE + SkDebugf("=== ashmem close %d\n", fRec.fFD); +#endif + SkASSERT(fRec.fAddr); + SkASSERT(fRec.fSize); + munmap(fRec.fAddr, fRec.fSize); + close(fRec.fFD); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +class AshmemAllocator : public SkBitmap::Allocator { +public: + AshmemAllocator(SkAshmemRec* rec, const char name[]) + : fRec(rec), fName(name) {} + + virtual bool allocPixelRef(SkBitmap* bm, SkColorTable* ct) { + const size_t size = roundToPageSize(bm->getSize()); + int fd = fRec->fFD; + void* addr = fRec->fAddr; + + SkASSERT(!fRec->fPinned); + + if (-1 == fd) { + SkASSERT(NULL == addr); + SkASSERT(0 == fRec->fSize); + + fd = ashmem_create_region(fName, size); +#ifdef DUMP_ASHMEM_LIFECYCLE + SkDebugf("=== ashmem_create_region %s size=%d fd=%d\n", fName, size, fd); +#endif + if (-1 == fd) { + SkDebugf("------- imageref_ashmem create failed <%s> %d\n", + fName, size); + return false; + } + + int err = ashmem_set_prot_region(fd, PROT_READ | PROT_WRITE); + if (err) { + SkDebugf("------ ashmem_set_prot_region(%d) failed %d %d\n", + fd, err, errno); + return false; + } + + addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); + if (-1 == (long)addr) { + SkDebugf("---------- mmap failed for imageref_ashmem size=%d err=%d\n", + size, errno); + return false; + } + + fRec->fFD = fd; + fRec->fAddr = addr; + fRec->fSize = size; + } else { + SkASSERT(addr); + SkASSERT(size == fRec->fSize); + (void)ashmem_pin_region(fd, 0, 0); + } + + bm->setPixels(addr, ct); + fRec->fPinned = true; + return true; + } + +private: + // we just point to our caller's memory, these are not copies + SkAshmemRec* fRec; + const char* fName; +}; + +bool SkImageRef_ashmem::onDecode(SkImageDecoder* codec, SkStream* stream, + SkBitmap* bitmap, SkBitmap::Config config, + SkImageDecoder::Mode mode) { + + if (SkImageDecoder::kDecodeBounds_Mode == mode) { + return this->INHERITED::onDecode(codec, stream, bitmap, config, mode); + } + + AshmemAllocator alloc(&fRec, this->getName()); + + codec->setAllocator(&alloc); + bool success = this->INHERITED::onDecode(codec, stream, bitmap, config, + mode); + // remove the allocator, since its on the stack + codec->setAllocator(NULL); + + if (success) { + // remember the colortable (if any) + SkRefCnt_SafeAssign(fCT, bitmap->getColorTable()); + return true; + } else { + if (fRec.fPinned) { + ashmem_unpin_region(fRec.fFD, 0, 0); + fRec.fPinned = false; + } + return false; + } +} + +void* SkImageRef_ashmem::onLockPixels(SkColorTable** ct) { + SkASSERT(fBitmap.getPixels() == NULL); + SkASSERT(fBitmap.getColorTable() == NULL); + + // fast case: check if we can just pin and get the cached data + if (-1 != fRec.fFD) { + SkASSERT(fRec.fAddr); + SkASSERT(!fRec.fPinned); + int pin = ashmem_pin_region(fRec.fFD, 0, 0); + + if (ASHMEM_NOT_PURGED == pin) { // yea, fast case! + fBitmap.setPixels(fRec.fAddr, fCT); + fRec.fPinned = true; + } else if (ASHMEM_WAS_PURGED == pin) { + ashmem_unpin_region(fRec.fFD, 0, 0); + // let go of our colortable if we lost the pixels. Well get it back + // again when we re-decode + if (fCT) { + fCT->unref(); + fCT = NULL; + } +#if defined(DUMP_ASHMEM_LIFECYCLE) || defined(TRACE_ASH_PURGE) + SkDebugf("===== ashmem purged %d\n", fBitmap.getSize()); +#endif + } else { + SkDebugf("===== ashmem pin_region(%d) returned %d, treating as error %d\n", + fRec.fFD, pin, errno); + // return null result for failure + if (ct) { + *ct = NULL; + } + return NULL; + } + } else { + // no FD, will create an ashmem region in allocator + } + + return this->INHERITED::onLockPixels(ct); +} + +void SkImageRef_ashmem::onUnlockPixels() { + this->INHERITED::onUnlockPixels(); + + SkASSERT(-1 != fRec.fFD); + SkASSERT(fRec.fAddr); + SkASSERT(fRec.fPinned); + + ashmem_unpin_region(fRec.fFD, 0, 0); + fRec.fPinned = false; + + fBitmap.setPixels(NULL, NULL); +} + diff --git a/skia/ports/SkImageRef_ashmem.h b/skia/ports/SkImageRef_ashmem.h new file mode 100644 index 0000000..909baea --- /dev/null +++ b/skia/ports/SkImageRef_ashmem.h @@ -0,0 +1,33 @@ +#ifndef SkImageRef_ashmem_DEFINED +#define SkImageRef_ashmem_DEFINED + +#include "SkImageRef.h" + +struct SkAshmemRec { + int fFD; + void* fAddr; + size_t fSize; + bool fPinned; +}; + +class SkImageRef_ashmem : public SkImageRef { +public: + SkImageRef_ashmem(SkStream*, SkBitmap::Config, int sampleSize = 1); + virtual ~SkImageRef_ashmem(); + +protected: + virtual bool onDecode(SkImageDecoder* codec, SkStream* stream, + SkBitmap* bitmap, SkBitmap::Config config, + SkImageDecoder::Mode mode); + + virtual void* onLockPixels(SkColorTable**); + virtual void onUnlockPixels(); + +private: + SkColorTable* fCT; + SkAshmemRec fRec; + + typedef SkImageRef INHERITED; +}; + +#endif diff --git a/skia/ports/SkOSEvent_android.cpp b/skia/ports/SkOSEvent_android.cpp new file mode 100644 index 0000000..8e16440 --- /dev/null +++ b/skia/ports/SkOSEvent_android.cpp @@ -0,0 +1,155 @@ +/* libs/graphics/ports/SkOSEvent_android.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkEvent.h" +#include "utils/threads.h" +#include <stdio.h> + +using namespace android; + +Mutex gEventQMutex; +Condition gEventQCondition; + +void SkEvent::SignalNonEmptyQueue() +{ + gEventQCondition.broadcast(); +} + +/////////////////////////////////////////////////////////////////// + +#ifdef FMS_ARCH_ANDROID_ARM + +// don't have pthreads.h, and therefore no timedwait(), so we punt for the demo + +void SkEvent::SignalQueueTimer(SkMSec delay) +{ +} + +void SkEvent_start_timer_thread() +{ +} + +void SkEvent_stop_timer_thread() +{ +} + +#else + +#include <pthread.h> +#include <errno.h> + +static pthread_t gTimerThread; +static pthread_mutex_t gTimerMutex; +static pthread_cond_t gTimerCond; +static timespec gTimeSpec; + +static void* timer_event_thread_proc(void*) +{ + for (;;) + { + int status; + + pthread_mutex_lock(&gTimerMutex); + + timespec spec = gTimeSpec; + // mark our global to be zero + // so we don't call timedwait again on a stale value + gTimeSpec.tv_sec = 0; + gTimeSpec.tv_nsec = 0; + + if (spec.tv_sec == 0 && spec.tv_nsec == 0) + status = pthread_cond_wait(&gTimerCond, &gTimerMutex); + else + status = pthread_cond_timedwait(&gTimerCond, &gTimerMutex, &spec); + + if (status == 0) // someone signaled us with a new time + { + pthread_mutex_unlock(&gTimerMutex); + } + else + { + SkASSERT(status == ETIMEDOUT); // no need to unlock the mutex (its unlocked) + // this is the payoff. Signal the event queue to wake up + // and also check the delay-queue + gEventQCondition.broadcast(); + } + } + return 0; +} + +#define kThousand (1000) +#define kMillion (kThousand * kThousand) +#define kBillion (kThousand * kThousand * kThousand) + +void SkEvent::SignalQueueTimer(SkMSec delay) +{ + pthread_mutex_lock(&gTimerMutex); + + if (delay) + { + struct timeval tv; + gettimeofday(&tv, NULL); + + // normalize tv + if (tv.tv_usec >= kMillion) + { + tv.tv_sec += tv.tv_usec / kMillion; + tv.tv_usec %= kMillion; + } + + // add tv + delay, scale each up to land on nanoseconds + gTimeSpec.tv_nsec = (tv.tv_usec + (delay % kThousand) * kThousand) * kThousand; + gTimeSpec.tv_sec = (tv.tv_sec + (delay / kThousand) * kThousand) * kThousand; + + // check for overflow in nsec + if ((unsigned long)gTimeSpec.tv_nsec >= kBillion) + { + gTimeSpec.tv_nsec -= kBillion; + gTimeSpec.tv_sec += 1; + SkASSERT((unsigned long)gTimeSpec.tv_nsec < kBillion); + } + + // printf("SignalQueueTimer(%d) timespec(%d %d)\n", delay, gTimeSpec.tv_sec, gTimeSpec.tv_nsec); + } + else // cancel the timer + { + gTimeSpec.tv_nsec = 0; + gTimeSpec.tv_sec = 0; + } + + pthread_mutex_unlock(&gTimerMutex); + pthread_cond_signal(&gTimerCond); +} + +void SkEvent_start_timer_thread() +{ + int status; + pthread_attr_t attr; + + status = pthread_attr_init(&attr); + SkASSERT(status == 0); + status = pthread_create(&gTimerThread, &attr, timer_event_thread_proc, 0); + SkASSERT(status == 0); +} + +void SkEvent_stop_timer_thread() +{ + int status = pthread_cancel(gTimerThread); + SkASSERT(status == 0); +} + +#endif diff --git a/skia/ports/SkOSEvent_dummy.cpp b/skia/ports/SkOSEvent_dummy.cpp new file mode 100644 index 0000000..c83995e --- /dev/null +++ b/skia/ports/SkOSEvent_dummy.cpp @@ -0,0 +1,28 @@ +/* libs/graphics/ports/SkOSEvent_dummy.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkEvent.h" + +void SkEvent::SignalNonEmptyQueue() +{ + +} + +void SkEvent::SignalQueueTimer(SkMSec delay) +{ + +} diff --git a/skia/ports/SkOSFile_stdio.cpp b/skia/ports/SkOSFile_stdio.cpp new file mode 100644 index 0000000..15ca451 --- /dev/null +++ b/skia/ports/SkOSFile_stdio.cpp @@ -0,0 +1,106 @@ +/* libs/graphics/ports/SkOSFile_stdio.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkOSFile.h" + +#ifndef SK_BUILD_FOR_BREW + +#include <stdio.h> +#include <errno.h> + +SkFILE* sk_fopen(const char path[], SkFILE_Flags flags) +{ + char perm[4]; + char* p = perm; + + if (flags & kRead_SkFILE_Flag) + *p++ = 'r'; + if (flags & kWrite_SkFILE_Flag) + *p++ = 'w'; + *p++ = 'b'; + *p = 0; + + SkFILE* f = (SkFILE*)::fopen(path, perm); +#if 0 + if (NULL == f) + SkDebugf("sk_fopen failed for %s (%s), errno=%s\n", path, perm, strerror(errno)); +#endif + return f; +} + +size_t sk_fgetsize(SkFILE* f) +{ + SkASSERT(f); + + size_t curr = ::ftell((FILE*)f); // remember where we are + ::fseek((FILE*)f, 0, SEEK_END); // go to the end + size_t size = ::ftell((FILE*)f); // record the size + ::fseek((FILE*)f, (long)curr, SEEK_SET); // go back to our prev loc + return size; +} + +bool sk_frewind(SkFILE* f) +{ + SkASSERT(f); + ::rewind((FILE*)f); +// ::fseek((FILE*)f, 0, SEEK_SET); + return true; +} + +size_t sk_fread(void* buffer, size_t byteCount, SkFILE* f) +{ + SkASSERT(f); + if (buffer == NULL) + { + size_t curr = ::ftell((FILE*)f); + if ((long)curr == -1) { + SkDEBUGF(("sk_fread: ftell(%p) returned -1 feof:%d ferror:%d\n", f, feof((FILE*)f), ferror((FILE*)f))); + return 0; + } + // ::fseek((FILE*)f, (long)(curr + byteCount), SEEK_SET); + int err = ::fseek((FILE*)f, (long)byteCount, SEEK_CUR); + if (err != 0) { + SkDEBUGF(("sk_fread: fseek(%d) tell:%d failed with feof:%d ferror:%d returned:%d\n", + byteCount, curr, feof((FILE*)f), ferror((FILE*)f), err)); + return 0; + } + return byteCount; + } + else + return ::fread(buffer, 1, byteCount, (FILE*)f); +} + +size_t sk_fwrite(const void* buffer, size_t byteCount, SkFILE* f) +{ + SkASSERT(f); + return ::fwrite(buffer, 1, byteCount, (FILE*)f); +} + +void sk_fflush(SkFILE* f) +{ + SkASSERT(f); + ::fflush((FILE*)f); +} + +void sk_fclose(SkFILE* f) +{ + SkASSERT(f); + ::fclose((FILE*)f); +} + +#endif + diff --git a/skia/ports/SkThread_none.cpp b/skia/ports/SkThread_none.cpp new file mode 100644 index 0000000..3e66973 --- /dev/null +++ b/skia/ports/SkThread_none.cpp @@ -0,0 +1,49 @@ +/* libs/graphics/ports/SkThread_none.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkThread.h" + +int32_t sk_atomic_inc(int32_t* addr) +{ + int32_t value = *addr; + *addr = value + 1; + return value; +} + +int32_t sk_atomic_dec(int32_t* addr) +{ + int32_t value = *addr; + *addr = value - 1; + return value; +} + +SkMutex::SkMutex(bool /* isGlobal */) +{ +} + +SkMutex::~SkMutex() +{ +} + +void SkMutex::acquire() +{ +} + +void SkMutex::release() +{ +} + diff --git a/skia/ports/SkThread_pthread.cpp b/skia/ports/SkThread_pthread.cpp new file mode 100644 index 0000000..4ee857d --- /dev/null +++ b/skia/ports/SkThread_pthread.cpp @@ -0,0 +1,90 @@ +#include "SkThread.h" + +#include <pthread.h> +#include <errno.h> + +SkMutex gAtomicMutex; + +int32_t sk_atomic_inc(int32_t* addr) +{ + SkAutoMutexAcquire ac(gAtomicMutex); + + int32_t value = *addr; + *addr = value + 1; + return value; +} + +int32_t sk_atomic_dec(int32_t* addr) +{ + SkAutoMutexAcquire ac(gAtomicMutex); + + int32_t value = *addr; + *addr = value - 1; + return value; +} + +////////////////////////////////////////////////////////////////////////////// + +static void print_pthread_error(int status) +{ + switch (status) { + case 0: // success + break; + case EINVAL: + printf("pthread error [%d] EINVAL\n", status); + break; + case EBUSY: + printf("pthread error [%d] EBUSY\n", status); + break; + default: + printf("pthread error [%d] unknown\n", status); + break; + } +} + +SkMutex::SkMutex(bool isGlobal) : fIsGlobal(isGlobal) +{ + if (sizeof(pthread_mutex_t) > sizeof(fStorage)) + { + SkDEBUGF(("pthread mutex size = %d\n", sizeof(pthread_mutex_t))); + SkASSERT(!"mutex storage is too small"); + } + + int status; + pthread_mutexattr_t attr; + + status = pthread_mutexattr_init(&attr); + print_pthread_error(status); + SkASSERT(0 == status); + + status = pthread_mutex_init((pthread_mutex_t*)fStorage, &attr); + print_pthread_error(status); + SkASSERT(0 == status); +} + +SkMutex::~SkMutex() +{ + int status = pthread_mutex_destroy((pthread_mutex_t*)fStorage); + + // only report errors on non-global mutexes + if (!fIsGlobal) + { + print_pthread_error(status); + SkASSERT(0 == status); + } +} + +void SkMutex::acquire() +{ + int status = pthread_mutex_lock((pthread_mutex_t*)fStorage); + print_pthread_error(status); + SkASSERT(0 == status); +} + +void SkMutex::release() +{ + int status = pthread_mutex_unlock((pthread_mutex_t*)fStorage); + print_pthread_error(status); + SkASSERT(0 == status); +} + diff --git a/skia/ports/SkThread_win.cpp b/skia/ports/SkThread_win.cpp new file mode 100644 index 0000000..791e4f4 --- /dev/null +++ b/skia/ports/SkThread_win.cpp @@ -0,0 +1,64 @@ +/* libs/graphics/ports/SkThread_none.cpp +** +** Copyright 2008, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include <windows.h> +#include "SkThread.h" + +namespace { + +template <bool> +struct CompileAssert { +}; + +} // namespace + +#define COMPILE_ASSERT(expr, msg) \ + typedef CompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1] + +int32_t sk_atomic_inc(int32_t* addr) +{ + // InterlockedIncrement returns the new value, we want to return the old. + return InterlockedIncrement(reinterpret_cast<LONG*>(addr)) - 1; +} + +int32_t sk_atomic_dec(int32_t* addr) +{ + return InterlockedDecrement(reinterpret_cast<LONG*>(addr)) + 1; +} + +SkMutex::SkMutex(bool /* isGlobal */) +{ + COMPILE_ASSERT(sizeof(fStorage) > sizeof(CRITICAL_SECTION), + NotEnoughSizeForCriticalSection); + InitializeCriticalSection(reinterpret_cast<CRITICAL_SECTION*>(&fStorage)); +} + +SkMutex::~SkMutex() +{ + DeleteCriticalSection(reinterpret_cast<CRITICAL_SECTION*>(&fStorage)); +} + +void SkMutex::acquire() +{ + EnterCriticalSection(reinterpret_cast<CRITICAL_SECTION*>(&fStorage)); +} + +void SkMutex::release() +{ + LeaveCriticalSection(reinterpret_cast<CRITICAL_SECTION*>(&fStorage)); +} + diff --git a/skia/ports/SkTime_Unix.cpp b/skia/ports/SkTime_Unix.cpp new file mode 100644 index 0000000..202fcb8 --- /dev/null +++ b/skia/ports/SkTime_Unix.cpp @@ -0,0 +1,50 @@ +/* libs/graphics/ports/SkTime_Unix.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkTime.h" + +#if defined(SK_BUILD_FOR_UNIX) || defined(SK_BUILD_FOR_MAC) +#include <sys/time.h> +#include <time.h> + +void SkTime::GetDateTime(DateTime* dt) +{ + if (dt) + { + time_t m_time; + time(&m_time); + struct tm* tstruct; + tstruct = localtime(&m_time); + + dt->fYear = tstruct->tm_year; + dt->fMonth = SkToU8(tstruct->tm_mon + 1); + dt->fDayOfWeek = SkToU8(tstruct->tm_wday); + dt->fDay = SkToU8(tstruct->tm_mday); + dt->fHour = SkToU8(tstruct->tm_hour); + dt->fMinute = SkToU8(tstruct->tm_min); + dt->fSecond = SkToU8(tstruct->tm_sec); + } +} + +SkMSec SkTime::GetMSecs() +{ + struct timeval tv; + gettimeofday(&tv, NULL); + return (SkMSec) (tv.tv_sec * 1000 + tv.tv_usec / 1000 ); // microseconds to milliseconds +} + +#endif diff --git a/skia/ports/SkXMLParser_empty.cpp b/skia/ports/SkXMLParser_empty.cpp new file mode 100644 index 0000000..a316dba --- /dev/null +++ b/skia/ports/SkXMLParser_empty.cpp @@ -0,0 +1,36 @@ +/* libs/graphics/ports/SkXMLParser_empty.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +// Copyright Skia Inc. 2004 - 2005 +// +#include "SkXMLParser.h" + +bool SkXMLParser::parse(SkStream& docStream) +{ + return false; +} + +bool SkXMLParser::parse(const char doc[], size_t len) +{ + return false; +} + +void SkXMLParser::GetNativeErrorString(int error, SkString* str) +{ + +} + diff --git a/skia/ports/SkXMLParser_expat.cpp b/skia/ports/SkXMLParser_expat.cpp new file mode 100644 index 0000000..82a78db --- /dev/null +++ b/skia/ports/SkXMLParser_expat.cpp @@ -0,0 +1,149 @@ +/* libs/graphics/ports/SkXMLParser_expat.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkXMLParser.h" +#include "SkString.h" +#include "SkStream.h" + +#include "expat.h" + +#ifdef SK_BUILD_FOR_PPI +#define CHAR_16_TO_9 +#endif + +#if defined CHAR_16_TO_9 +inline size_t sk_wcslen(const short* char16) { + const short* start = char16; + while (*char16) + char16++; + return char16 - start; +} + +inline const char* ConvertUnicodeToChar(const short* ch16, size_t len, SkAutoMalloc& ch8Malloc) { + char* ch8 = (char*) ch8Malloc.get(); + int index; + for (index = 0; index < len; index++) + ch8[index] = (char) ch16[index]; + ch8[index] = '\0'; + return ch8; +} +#endif + +static void XMLCALL start_proc(void *data, const char *el, const char **attr) +{ +#if defined CHAR_16_TO_9 + size_t len = sk_wcslen((const short*) el); + SkAutoMalloc el8(len + 1); + el = ConvertUnicodeToChar((const short*) el, len, el8); +#endif + if (((SkXMLParser*)data)->startElement(el)) { + XML_StopParser((XML_Parser) ((SkXMLParser*)data)->fParser, false); + return; + } + while (*attr) + { + const char* attr0 = attr[0]; + const char* attr1 = attr[1]; +#if defined CHAR_16_TO_9 + size_t len0 = sk_wcslen((const short*) attr0); + SkAutoMalloc attr0_8(len0 + 1); + attr0 = ConvertUnicodeToChar((const short*) attr0, len0, attr0_8); + size_t len1 = sk_wcslen((const short*) attr1); + SkAutoMalloc attr1_8(len1 + 1); + attr1 = ConvertUnicodeToChar((const short*) attr1, len1, attr1_8); +#endif + if (((SkXMLParser*)data)->addAttribute(attr0, attr1)) { + XML_StopParser((XML_Parser) ((SkXMLParser*)data)->fParser, false); + return; + } + attr += 2; + } +} + +static void XMLCALL end_proc(void *data, const char *el) +{ +#if defined CHAR_16_TO_9 + size_t len = sk_wcslen((const short*) el); + SkAutoMalloc el8(len + 1); + el = ConvertUnicodeToChar((const short*) el, len, el8); +#endif + if (((SkXMLParser*)data)->endElement(el)) + XML_StopParser((XML_Parser) ((SkXMLParser*)data)->fParser, false); +} + +static void XMLCALL text_proc(void* data, const char* text, int len) +{ +#if defined CHAR_16_TO_9 + SkAutoMalloc text8(len + 1); + text = ConvertUnicodeToChar((const short*) text, len, text8); +#endif + if (((SkXMLParser*)data)->text(text, len)) + XML_StopParser((XML_Parser) ((SkXMLParser*)data)->fParser, false); +} + +bool SkXMLParser::parse(const char doc[], size_t len) +{ + if (len == 0) { + fError->fCode = SkXMLParserError::kEmptyFile; + reportError(NULL); + return false; + } + XML_Parser p = XML_ParserCreate(NULL); + SkASSERT(p); + fParser = p; + XML_SetElementHandler(p, start_proc, end_proc); + XML_SetCharacterDataHandler(p, text_proc); + XML_SetUserData(p, this); + + bool success = true; + int error = XML_Parse(p, doc, len, true); + if (error == XML_STATUS_ERROR) { + reportError(p); + success = false; + } + XML_ParserFree(p); + return success; +} + +bool SkXMLParser::parse(SkStream& input) +{ + size_t len = input.read(NULL, 0); + SkAutoMalloc am(len); + char* doc = (char*)am.get(); + + input.rewind(); + size_t len2 = input.read(doc, len); + SkASSERT(len2 == len); + + return this->parse(doc, len2); +} + +void SkXMLParser::reportError(void* p) +{ + XML_Parser parser = (XML_Parser) p; + if (fError && parser) { + fError->fNativeCode = XML_GetErrorCode(parser); + fError->fLineNumber = XML_GetCurrentLineNumber(parser); + } +} + +void SkXMLParser::GetNativeErrorString(int error, SkString* str) +{ + if (str) + str->set(XML_ErrorString((XML_Error) error)); +} + diff --git a/skia/ports/SkXMLParser_tinyxml.cpp b/skia/ports/SkXMLParser_tinyxml.cpp new file mode 100644 index 0000000..c53587a --- /dev/null +++ b/skia/ports/SkXMLParser_tinyxml.cpp @@ -0,0 +1,96 @@ +/* libs/graphics/ports/SkXMLParser_tinyxml.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkXMLParser.h" +#include "SkStream.h" +#include "SkTemplates.h" +#include "tinyxml.h" + +static void walk_elem(SkXMLParser* parser, const TiXmlElement* elem) +{ + //printf("walk_elem(%s) ", elem->Value()); + + parser->startElement(elem->Value()); + + const TiXmlAttribute* attr = elem->FirstAttribute(); + while (attr) + { + //printf("walk_elem_attr(%s=\"%s\") ", attr->Name(), attr->Value()); + + parser->addAttribute(attr->Name(), attr->Value()); + attr = attr->Next(); + } + //printf("\n"); + + const TiXmlNode* node = elem->FirstChild(); + while (node) + { + if (node->ToElement()) + walk_elem(parser, node->ToElement()); + else if (node->ToText()) + parser->text(node->Value(), strlen(node->Value())); + node = node->NextSibling(); + } + + parser->endElement(elem->Value()); +} + +static bool load_buf(SkXMLParser* parser, const char buf[]) +{ + TiXmlDocument doc; + + (void)doc.Parse(buf); + if (doc.Error()) + { + printf("tinyxml error: <%s> row[%d] col[%d]\n", doc.ErrorDesc(), doc.ErrorRow(), doc.ErrorCol()); + return false; + } + + walk_elem(parser, doc.RootElement()); + return true; +} + +bool SkXMLParser::parse(SkStream& stream) +{ + size_t size = stream.read(NULL, 0); + + SkAutoMalloc buffer(size + 1); + char* buf = (char*)buffer.get(); + + stream.read(buf, size); + buf[size] = 0; + + return load_buf(this, buf); +} + +bool SkXMLParser::parse(const char doc[], size_t len) +{ + SkAutoMalloc buffer(len + 1); + char* buf = (char*)buffer.get(); + + memcpy(buf, doc, len); + buf[len] = 0; + + return load_buf(this, buf); +} + +void SkXMLParser::GetNativeErrorString(int error, SkString* str) +{ + if (str) + str->set("GetNativeErrorString not implemented for TinyXml"); +} + diff --git a/skia/ports/SkXMLPullParser_expat.cpp b/skia/ports/SkXMLPullParser_expat.cpp new file mode 100644 index 0000000..cd29a9e --- /dev/null +++ b/skia/ports/SkXMLPullParser_expat.cpp @@ -0,0 +1,222 @@ +/* libs/graphics/ports/SkXMLParser_expat.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkXMLParser.h" +#include "SkChunkAlloc.h" +#include "SkString.h" +#include "SkStream.h" + +#include "expat.h" + +static inline char* dupstr(SkChunkAlloc& chunk, const char src[], size_t len) +{ + SkASSERT(src); + char* dst = (char*)chunk.alloc(len + 1, SkChunkAlloc::kThrow_AllocFailType); + + memcpy(dst, src, len); + dst[len] = 0; + return dst; +} + +static inline int count_pairs(const char** p) +{ + const char** start = p; + while (*p) + { + SkASSERT(p[1] != NULL); + p += 2; + } + return (p - start) >> 1; +} + +struct Data { + Data() : fAlloc(2048), fState(NORMAL) {} + + XML_Parser fParser; + SkXMLPullParser::Curr* fCurr; + SkChunkAlloc fAlloc; + + enum State { + NORMAL, + MISSED_START_TAG, + RETURN_END_TAG + }; + State fState; + const char* fEndTag; // if state is RETURN_END_TAG +}; + +static void XMLCALL start_proc(void *data, const char *el, const char **attr) +{ + Data* p = (Data*)data; + SkXMLPullParser::Curr* c = p->fCurr; + SkChunkAlloc& alloc = p->fAlloc; + + c->fName = dupstr(alloc, el, strlen(el)); + + int n = count_pairs(attr); + SkXMLPullParser::AttrInfo* info = (SkXMLPullParser::AttrInfo*)alloc.alloc(n * sizeof(SkXMLPullParser::AttrInfo), + SkChunkAlloc::kThrow_AllocFailType); + c->fAttrInfoCount = n; + c->fAttrInfos = info; + + for (int i = 0; i < n; i++) + { + info[i].fName = dupstr(alloc, attr[0], strlen(attr[0])); + info[i].fValue = dupstr(alloc, attr[1], strlen(attr[1])); + attr += 2; + } + + c->fEventType = SkXMLPullParser::START_TAG; + XML_StopParser(p->fParser, true); +} + +static void XMLCALL end_proc(void *data, const char *el) +{ + Data* p = (Data*)data; + SkXMLPullParser::Curr* c = p->fCurr; + + if (c->fEventType == SkXMLPullParser::START_TAG) + { + /* if we get here, we were called with a start_tag immediately + followed by this end_tag. The caller will only see the end_tag, + so we set a flag to notify them of the missed start_tag + */ + p->fState = Data::MISSED_START_TAG; + + SkASSERT(c->fName != NULL); + SkASSERT(strcmp(c->fName, el) == 0); + } + else + c->fName = dupstr(p->fAlloc, el, strlen(el)); + + c->fEventType = SkXMLPullParser::END_TAG; + XML_StopParser(p->fParser, true); +} + +#include <ctype.h> + +static bool isws(const char s[]) +{ + for (; *s; s++) + if (!isspace(*s)) + return false; + return true; +} + +static void XMLCALL text_proc(void* data, const char* text, int len) +{ + Data* p = (Data*)data; + SkXMLPullParser::Curr* c = p->fCurr; + + c->fName = dupstr(p->fAlloc, text, len); + c->fIsWhitespace = isws(c->fName); + + c->fEventType = SkXMLPullParser::TEXT; + XML_StopParser(p->fParser, true); +} + +////////////////////////////////////////////////////////////////////////// + +struct SkXMLPullParser::Impl { + Data fData; + void* fBuffer; + size_t fBufferLen; +}; + +static void reportError(XML_Parser parser) +{ + XML_Error code = XML_GetErrorCode(parser); + int lineNumber = XML_GetCurrentLineNumber(parser); + const char* msg = XML_ErrorString(code); + + printf("-------- XML error [%d] on line %d, %s\n", code, lineNumber, msg); +} + +bool SkXMLPullParser::onInit() +{ + fImpl = new Impl; + + XML_Parser p = XML_ParserCreate(NULL); + SkASSERT(p); + + fImpl->fData.fParser = p; + fImpl->fData.fCurr = &fCurr; + + XML_SetElementHandler(p, start_proc, end_proc); + XML_SetCharacterDataHandler(p, text_proc); + XML_SetUserData(p, &fImpl->fData); + + size_t len = fStream->read(NULL, 0); + fImpl->fBufferLen = len; + fImpl->fBuffer = sk_malloc_throw(len); + fStream->rewind(); + size_t len2 = fStream->read(fImpl->fBuffer, len); + return len2 == len; +} + +void SkXMLPullParser::onExit() +{ + sk_free(fImpl->fBuffer); + XML_ParserFree(fImpl->fData.fParser); + delete fImpl; + fImpl = NULL; +} + +SkXMLPullParser::EventType SkXMLPullParser::onNextToken() +{ + if (Data::RETURN_END_TAG == fImpl->fData.fState) + { + fImpl->fData.fState = Data::NORMAL; + fCurr.fName = fImpl->fData.fEndTag; // restore name from (below) save + return SkXMLPullParser::END_TAG; + } + + fImpl->fData.fAlloc.reuse(); + + XML_Parser p = fImpl->fData.fParser; + XML_Status status; + + status = XML_ResumeParser(p); + +CHECK_STATUS: + switch (status) { + case XML_STATUS_OK: + return SkXMLPullParser::END_DOCUMENT; + + case XML_STATUS_ERROR: + if (XML_GetErrorCode(p) != XML_ERROR_NOT_SUSPENDED) + { + reportError(p); + return SkXMLPullParser::ERROR; + } + status = XML_Parse(p, (const char*)fImpl->fBuffer, fImpl->fBufferLen, true); + goto CHECK_STATUS; + + case XML_STATUS_SUSPENDED: + if (Data::MISSED_START_TAG == fImpl->fData.fState) + { + // return a start_tag, and clear the flag so we return end_tag next + SkASSERT(SkXMLPullParser::END_TAG == fCurr.fEventType); + fImpl->fData.fState = Data::RETURN_END_TAG; + fImpl->fData.fEndTag = fCurr.fName; // save this pointer + return SkXMLPullParser::START_TAG; + } + break; + } + return fCurr.fEventType; +} + diff --git a/skia/precompiled.cc b/skia/precompiled.cc new file mode 100644 index 0000000..e9fbc43 --- /dev/null +++ b/skia/precompiled.cc @@ -0,0 +1,31 @@ +// Copyright 2008, Google 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: +// +// * 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. +// * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Used to generate the precompiled header. +#include "SkTypes.h" diff --git a/skia/precompiled.vsprops b/skia/precompiled.vsprops new file mode 100644 index 0000000..907c969 --- /dev/null +++ b/skia/precompiled.vsprops @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="Windows-1252"?> +<VisualStudioPropertySheet + ProjectType="Visual C++" + Version="8.00" + Name="precompiled" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="2" + PrecompiledHeaderThrough="SkTypes.h" + ForcedIncludeFiles="SkTypes.h" + /> +</VisualStudioPropertySheet> diff --git a/skia/sgl/ARGB32_Clamp_Bilinear_BitmapShader.h b/skia/sgl/ARGB32_Clamp_Bilinear_BitmapShader.h new file mode 100644 index 0000000..c7e23af --- /dev/null +++ b/skia/sgl/ARGB32_Clamp_Bilinear_BitmapShader.h @@ -0,0 +1,171 @@ + +class ARGB32_Clamp_Bilinear_BitmapShader : public SkBitmapShader { +public: + ARGB32_Clamp_Bilinear_BitmapShader(const SkBitmap& src) + : SkBitmapShader(src, true, + SkShader::kClamp_TileMode, SkShader::kClamp_TileMode) + {} + + virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count); +}; + +SkPMColor sample_bilerp(SkFixed fx, SkFixed fy, unsigned srcMaxX, unsigned srcMaxY, + const SkPMColor* srcPixels, int srcRB, const SkFilterPtrProc* proc_table); +SkPMColor sample_bilerp(SkFixed fx, SkFixed fy, unsigned srcMaxX, unsigned srcMaxY, + const SkPMColor* srcPixels, int srcRB, const SkFilterPtrProc* proc_table) +{ + int ix = fx >> 16; + int iy = fy >> 16; + + const SkPMColor *p00, *p01, *p10, *p11; + + p00 = p01 = ((const SkPMColor*)((const char*)srcPixels + + SkClampMax(iy, srcMaxY) * srcRB)) + + SkClampMax(ix, srcMaxX); + + if ((unsigned)ix < srcMaxX) + p01 += 1; + p10 = p00; + p11 = p01; + if ((unsigned)iy < srcMaxY) + { + p10 = (const SkPMColor*)((const char*)p10 + srcRB); + p11 = (const SkPMColor*)((const char*)p11 + srcRB); + } + + SkFilterPtrProc proc = SkGetBilinearFilterPtrProc(proc_table, fx, fy); + return proc(p00, p01, p10, p11); +} + +static inline SkPMColor sample_bilerpx(SkFixed fx, unsigned srcMaxX, const SkPMColor* srcPixels, + int srcRB, const SkFilterPtrProc* proc_table) +{ + int ix = fx >> 16; + + const SkPMColor *p00, *p01, *p10, *p11; + + p00 = p01 = srcPixels + SkClampMax(ix, srcMaxX); + if ((unsigned)ix < srcMaxX) + p01 += 1; + + p10 = (const SkPMColor*)((const char*)p00 + srcRB); + p11 = (const SkPMColor*)((const char*)p01 + srcRB); + + SkFilterPtrProc proc = SkGetBilinearFilterPtrXProc(proc_table, fx); + return proc(p00, p01, p10, p11); +} + +void ARGB32_Clamp_Bilinear_BitmapShader::shadeSpan(int x, int y, SkPMColor dstC[], int count) +{ + SkASSERT(count > 0); + + unsigned srcScale = SkAlpha255To256(this->getPaintAlpha()); + + const SkMatrix& inv = this->getTotalInverse(); + const SkBitmap& srcBitmap = this->getSrcBitmap(); + unsigned srcMaxX = srcBitmap.width() - 1; + unsigned srcMaxY = srcBitmap.height() - 1; + unsigned srcRB = srcBitmap.rowBytes(); + + const SkFilterPtrProc* proc_table = SkGetBilinearFilterPtrProcTable(); + const SkPMColor* srcPixels = (const SkPMColor*)srcBitmap.getPixels(); + + if (this->getInverseClass() == kPerspective_MatrixClass) + { + SkPerspIter iter(inv, SkIntToScalar(x) + SK_ScalarHalf, + SkIntToScalar(y) + SK_ScalarHalf, count); + + if (256 == srcScale) + { + while ((count = iter.next()) != 0) + { + const SkFixed* srcXY = iter.getXY(); + while (--count >= 0) + { + SkFixed fx = *srcXY++ - SK_FixedHalf; + SkFixed fy = *srcXY++ - SK_FixedHalf; + *dstC++ = sample_bilerp(fx, fy, srcMaxX, srcMaxY, srcPixels, srcRB, proc_table); + } + } + } + else // scale by srcScale + { + while ((count = iter.next()) != 0) + { + const SkFixed* srcXY = iter.getXY(); + while (--count >= 0) + { + SkFixed fx = *srcXY++ - SK_FixedHalf; + SkFixed fy = *srcXY++ - SK_FixedHalf; + SkPMColor c = sample_bilerp(fx, fy, srcMaxX, srcMaxY, srcPixels, srcRB, proc_table); + *dstC++ = SkAlphaMulQ(c, srcScale); + } + } + } + } + else // linear case + { + SkFixed fx, fy, dx, dy; + + // now init fx, fy, dx, dy + { + SkPoint srcPt; + this->getInverseMapPtProc()(inv, SkIntToScalar(x) + SK_ScalarHalf, + SkIntToScalar(y) + SK_ScalarHalf, + &srcPt); + + fx = SkScalarToFixed(srcPt.fX) - SK_FixedHalf; + fy = SkScalarToFixed(srcPt.fY) - SK_FixedHalf; + + if (this->getInverseClass() == kFixedStepInX_MatrixClass) + (void)inv.fixedStepInX(SkIntToScalar(y), &dx, &dy); + else + { + dx = SkScalarToFixed(inv.getScaleX()); + dy = SkScalarToFixed(inv.getSkewY()); + } + } + + if (dy == 0 && (unsigned)(fy >> 16) < srcMaxY) + { + srcPixels = (const SkPMColor*)((const char*)srcPixels + (fy >> 16) * srcRB); + proc_table = SkGetBilinearFilterPtrProcYTable(proc_table, fy); + if (256 == srcScale) + { + do { + *dstC++ = sample_bilerpx(fx, srcMaxX, srcPixels, srcRB, proc_table); + fx += dx; + } while (--count != 0); + } + else + { + do { + SkPMColor c = sample_bilerpx(fx, srcMaxX, srcPixels, srcRB, proc_table); + *dstC++ = SkAlphaMulQ(c, srcScale); + fx += dx; + } while (--count != 0); + } + } + else // dy is != 0 + { + if (256 == srcScale) + { + do { + *dstC++ = sample_bilerp(fx, fy, srcMaxX, srcMaxY, srcPixels, srcRB, proc_table); + fx += dx; + fy += dy; + } while (--count != 0); + } + else + { + do { + SkPMColor c = sample_bilerp(fx, fy, srcMaxX, srcMaxY, srcPixels, srcRB, proc_table); + *dstC++ = SkAlphaMulQ(c, srcScale); + fx += dx; + fy += dy; + } while (--count != 0); + } + } + } +} + diff --git a/skia/sgl/SkAlphaRuns.cpp b/skia/sgl/SkAlphaRuns.cpp new file mode 100644 index 0000000..35fcfd6 --- /dev/null +++ b/skia/sgl/SkAlphaRuns.cpp @@ -0,0 +1,185 @@ +/* libs/graphics/sgl/SkAlphaRuns.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkAntiRun.h" + +void SkAlphaRuns::reset(int width) +{ + SkASSERT(width > 0); + + fRuns[0] = SkToS16(width); + fRuns[width] = 0; + fAlpha[0] = 0; + + SkDEBUGCODE(fWidth = width;) + SkDEBUGCODE(this->validate();) +} + +void SkAlphaRuns::Break(int16_t runs[], uint8_t alpha[], int x, int count) +{ + SkASSERT(count > 0 && x >= 0); + +// SkAlphaRuns::BreakAt(runs, alpha, x); +// SkAlphaRuns::BreakAt(&runs[x], &alpha[x], count); + + int16_t* next_runs = runs + x; + uint8_t* next_alpha = alpha + x; + + while (x > 0) + { + int n = runs[0]; + SkASSERT(n > 0); + + if (x < n) + { + alpha[x] = alpha[0]; + runs[0] = SkToS16(x); + runs[x] = SkToS16(n - x); + break; + } + runs += n; + alpha += n; + x -= n; + } + + runs = next_runs; + alpha = next_alpha; + x = count; + + for (;;) + { + int n = runs[0]; + SkASSERT(n > 0); + + if (x < n) + { + alpha[x] = alpha[0]; + runs[0] = SkToS16(x); + runs[x] = SkToS16(n - x); + break; + } + x -= n; + if (x <= 0) + break; + + runs += n; + alpha += n; + } +} + +void SkAlphaRuns::add(int x, U8CPU startAlpha, int middleCount, U8CPU stopAlpha, U8CPU maxValue) +{ + SkASSERT(middleCount >= 0); + SkASSERT(x >= 0 && x + (startAlpha != 0) + middleCount + (stopAlpha != 0) <= fWidth); + + int16_t* runs = fRuns; + uint8_t* alpha = fAlpha; + + if (startAlpha) + { + SkAlphaRuns::Break(runs, alpha, x, 1); + /* I should be able to just add alpha[x] + startAlpha. + However, if the trailing edge of the previous span and the leading + edge of the current span round to the same super-sampled x value, + I might overflow to 256 with this add, hence the funny subtract (crud). + */ + unsigned tmp = alpha[x] + startAlpha; + SkASSERT(tmp <= 256); + alpha[x] = SkToU8(tmp - (tmp >> 8)); // was (tmp >> 7), but that seems wrong if we're trying to catch 256 + + runs += x + 1; + alpha += x + 1; + x = 0; + SkDEBUGCODE(this->validate();) + } + if (middleCount) + { + SkAlphaRuns::Break(runs, alpha, x, middleCount); + alpha += x; + runs += x; + x = 0; + do { + alpha[0] = SkToU8(alpha[0] + maxValue); + int n = runs[0]; + SkASSERT(n <= middleCount); + alpha += n; + runs += n; + middleCount -= n; + } while (middleCount > 0); + SkDEBUGCODE(this->validate();) + } + if (stopAlpha) + { + SkAlphaRuns::Break(runs, alpha, x, 1); + alpha[x] = SkToU8(alpha[x] + stopAlpha); + SkDEBUGCODE(this->validate();) + } +} + +#ifdef SK_DEBUG + void SkAlphaRuns::assertValid(int y, int maxStep) const + { + int max = (y + 1) * maxStep - (y == maxStep - 1); + + const int16_t* runs = fRuns; + const uint8_t* alpha = fAlpha; + + while (*runs) + { + SkASSERT(*alpha <= max); + alpha += *runs; + runs += *runs; + } + } + + void SkAlphaRuns::dump() const + { + const int16_t* runs = fRuns; + const uint8_t* alpha = fAlpha; + + SkDebugf("Runs"); + while (*runs) + { + int n = *runs; + + SkDebugf(" %02x", *alpha); + if (n > 1) + SkDebugf(",%d", n); + alpha += n; + runs += n; + } + SkDebugf("\n"); + } + + void SkAlphaRuns::validate() const + { + SkASSERT(fWidth > 0); + + int count = 0; + const int16_t* runs = fRuns; + + while (*runs) + { + SkASSERT(*runs > 0); + count += *runs; + SkASSERT(count <= fWidth); + runs += *runs; + } + SkASSERT(count == fWidth); + } +#endif + diff --git a/skia/sgl/SkAntiRun.h b/skia/sgl/SkAntiRun.h new file mode 100644 index 0000000..32814f1 --- /dev/null +++ b/skia/sgl/SkAntiRun.h @@ -0,0 +1,185 @@ +/* libs/graphics/sgl/SkAntiRun.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkAntiRun_DEFINED +#define SkAntiRun_DEFINED + +#include "SkBlitter.h" + +inline int sk_make_nonzero_neg_one(int x) +{ + return (x | -x) >> 31; +} + +#if 0 +template <int kShift> class SkAntiRun { + static uint8_t coverage_to_alpha(int aa) + { + aa <<= 8 - 2*kShift; + aa -= aa >> (8 - kShift - 1); + return SkToU8(aa); + } +public: + void set(int start, int stop) + { + SkASSERT(start >= 0 && stop > start); + +#if 1 + int fb = start & kMask; + int fe = stop & kMask; + int n = (stop >> kShift) - (start >> kShift) - 1; + + if (n < 0) + { + fb = fe - fb; + n = 0; + fe = 0; + } + else + { + if (fb == 0) + n += 1; + else + fb = (1 << kShift) - fb; + } + + fStartAlpha = coverage_to_alpha(fb); + fMiddleCount = n; + fStopAlpha = coverage_to_alpha(fe); +#else + int x0 = start >> kShift; + int x1 = (stop - 1) >> kShift; + int middle = x1 - x0; + int aa; + + if (middle == 0) + { + aa = stop - start; + aa <<= 8 - 2*kShift; + aa -= aa >> (8 - kShift - 1); + SkASSERT(aa > 0 && aa < kMax); + fStartAlpha = SkToU8(aa); + fMiddleCount = 0; + fStopAlpha = 0; + } + else + { + int aa = start & kMask; + aa <<= 8 - 2*kShift; + aa -= aa >> (8 - kShift - 1); + SkASSERT(aa >= 0 && aa < kMax); + if (aa) + fStartAlpha = SkToU8(kMax - aa); + else + { + fStartAlpha = 0; + middle += 1; + } + aa = stop & kMask; + aa <<= 8 - 2*kShift; + aa -= aa >> (8 - kShift - 1); + SkASSERT(aa >= 0 && aa < kMax); + middle += sk_make_nonzero_neg_one(aa); + + fStopAlpha = SkToU8(aa); + fMiddleCount = middle; + } + SkASSERT(fStartAlpha < kMax); + SkASSERT(fStopAlpha < kMax); +#endif + } + + void blit(int x, int y, SkBlitter* blitter) + { + int16_t runs[2]; + runs[0] = 1; + runs[1] = 0; + + if (fStartAlpha) + { + blitter->blitAntiH(x, y, &fStartAlpha, runs); + x += 1; + } + if (fMiddleCount) + { + blitter->blitH(x, y, fMiddleCount); + x += fMiddleCount; + } + if (fStopAlpha) + blitter->blitAntiH(x, y, &fStopAlpha, runs); + } + + uint8_t getStartAlpha() const { return fStartAlpha; } + int getMiddleCount() const { return fMiddleCount; } + uint8_t getStopAlpha() const { return fStopAlpha; } + +private: + uint8_t fStartAlpha, fStopAlpha; + int fMiddleCount; + + enum { + kMask = (1 << kShift) - 1, + kMax = (1 << (8 - kShift)) - 1 + }; +}; +#endif + +//////////////////////////////////////////////////////////////////////////////////// + +class SkAlphaRuns { +public: + int16_t* fRuns; + uint8_t* fAlpha; + + bool empty() const + { + SkASSERT(fRuns[0] > 0); + return fAlpha[0] == 0 && fRuns[fRuns[0]] == 0; + } + void reset(int width); + void add(int x, U8CPU startAlpha, int middleCount, U8CPU stopAlpha, U8CPU maxValue); + SkDEBUGCODE(void assertValid(int y, int maxStep) const;) + SkDEBUGCODE(void dump() const;) + + static void Break(int16_t runs[], uint8_t alpha[], int x, int count); + static void BreakAt(int16_t runs[], uint8_t alpha[], int x) + { + while (x > 0) + { + int n = runs[0]; + SkASSERT(n > 0); + + if (x < n) + { + alpha[x] = alpha[0]; + runs[0] = SkToS16(x); + runs[x] = SkToS16(n - x); + break; + } + runs += n; + alpha += n; + x -= n; + } + } + +private: + SkDEBUGCODE(int fWidth;) + SkDEBUGCODE(void validate() const;) +}; + +#endif + diff --git a/skia/sgl/SkAutoKern.h b/skia/sgl/SkAutoKern.h new file mode 100644 index 0000000..644ad85 --- /dev/null +++ b/skia/sgl/SkAutoKern.h @@ -0,0 +1,62 @@ +/* libs/graphics/sgl/SkAutoKern.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkAutoKern_DEFINED +#define SkAutoKern_DEFINED + +#include "SkScalerContext.h" + +#define SkAutoKern_AdjustF(prev, next) (((next) - (prev) + 32) >> 6 << 16) +#define SkAutoKern_AdjustS(prev, next) SkIntToScalar(((next) - (prev) + 32) >> 6) + +/* this is a helper class to perform auto-kerning + * the adjust() method returns a SkFixed corresponding + * to a +1/0/-1 pixel adjustment + */ + +class SkAutoKern { +public: + SkAutoKern() : fPrevRsbDelta(0) {} + + SkFixed adjust(const SkGlyph& glyph) + { +// if (SkAbs32(glyph.fLsbDelta) > 47 || SkAbs32(glyph.fRsbDelta) > 47) +// printf("------- %d> L %d R %d\n", glyph.f_GlyphID, glyph.fLsbDelta, glyph.fRsbDelta); + +#if 0 + int distort = fPrevRsbDelta - glyph.fLsbDelta; + + fPrevRsbDelta = glyph.fRsbDelta; + + if (distort >= 32) + return -SK_Fixed1; + else if (distort < -32) + return +SK_Fixed1; + else + return 0; +#else + SkFixed adjust = SkAutoKern_AdjustF(fPrevRsbDelta, glyph.fLsbDelta); + fPrevRsbDelta = glyph.fRsbDelta; + return adjust; +#endif + } +private: + int fPrevRsbDelta; +}; + +#endif + diff --git a/skia/sgl/SkBitmap.cpp b/skia/sgl/SkBitmap.cpp new file mode 100644 index 0000000..914fc77 --- /dev/null +++ b/skia/sgl/SkBitmap.cpp @@ -0,0 +1,1270 @@ +/* + * Copyright (C) 2006-2008 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "SkBitmap.h" +#include "SkColorPriv.h" +#include "SkDither.h" +#include "SkFlattenable.h" +#include "SkMask.h" +#include "SkPixelRef.h" +#include "SkThread.h" +#include "SkUtils.h" +#include "SkPackBits.h" +#include <new> + +#ifdef SK_SUPPORT_MIPMAP +struct MipLevel { + void* fPixels; + uint32_t fRowBytes; + uint16_t fWidth, fHeight; +}; + +struct SkBitmap::MipMap : SkNoncopyable { + int fRefCnt; + int fLevelCount; +// MipLevel fLevel[fLevelCount]; +// Pixels[] + + static MipMap* Alloc(int levelCount, size_t pixelSize) { + MipMap* mm = (MipMap*)sk_malloc_throw(sizeof(MipMap) + + levelCount * sizeof(MipLevel) + + pixelSize); + mm->fRefCnt = 1; + mm->fLevelCount = levelCount; + return mm; + } + + const MipLevel* levels() const { return (const MipLevel*)(this + 1); } + MipLevel* levels() { return (MipLevel*)(this + 1); } + + const void* pixels() const { return levels() + fLevelCount; } + void* pixels() { return levels() + fLevelCount; } + + void safeRef() { + if (this) { + SkASSERT(fRefCnt > 0); + sk_atomic_inc(&fRefCnt); + } + } + void safeUnref() { + if (this) { + SkASSERT(fRefCnt > 0); + if (sk_atomic_dec(&fRefCnt) == 1) { + sk_free(this); + } + } + } +}; +#endif + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +SkBitmap::SkBitmap() { + bzero(this, sizeof(*this)); +} + +SkBitmap::SkBitmap(const SkBitmap& src) { + SkDEBUGCODE(src.validate();) + bzero(this, sizeof(*this)); + *this = src; + SkDEBUGCODE(this->validate();) +} + +SkBitmap::~SkBitmap() { + SkDEBUGCODE(this->validate();) + this->freePixels(); +} + +SkBitmap& SkBitmap::operator=(const SkBitmap& src) { + if (this != &src) { + this->freePixels(); + memcpy(this, &src, sizeof(src)); + + // inc src reference counts + src.fPixelRef->safeRef(); +#ifdef SK_SUPPORT_MIPMAP + src.fMipMap->safeRef(); +#endif + + // we reset our locks if we get blown away + fPixelLockCount = 0; + + /* The src could be in 3 states + 1. no pixelref, in which case we just copy/ref the pixels/ctable + 2. unlocked pixelref, pixels/ctable should be null + 3. locked pixelref, we should lock the ref again ourselves + */ + if (NULL == fPixelRef) { + // leave fPixels as it is + fColorTable->safeRef(); // ref the user's ctable if present + } else { // we have a pixelref, so pixels/ctable reflect it + // ignore the values from the memcpy + fPixels = NULL; + fColorTable = NULL; + } + } + + SkDEBUGCODE(this->validate();) + return *this; +} + +void SkBitmap::swap(SkBitmap& other) { + SkTSwap<SkColorTable*>(fColorTable, other.fColorTable); + SkTSwap<SkPixelRef*>(fPixelRef, other.fPixelRef); + SkTSwap<size_t>(fPixelRefOffset, other.fPixelRefOffset); + SkTSwap<int>(fPixelLockCount, other.fPixelLockCount); +#ifdef SK_SUPPORT_MIPMAP + SkTSwap<MipMap*>(fMipMap, other.fMipMap); +#endif + SkTSwap<void*>(fPixels, other.fPixels); + SkTSwap<uint16_t>(fWidth, other.fWidth); + SkTSwap<uint16_t>(fHeight, other.fHeight); + SkTSwap<uint32_t>(fRowBytes, other.fRowBytes); + SkTSwap<uint8_t>(fConfig, other.fConfig); + SkTSwap<uint8_t>(fFlags, other.fFlags); + SkTSwap<uint8_t>(fBytesPerPixel, other.fBytesPerPixel); + + SkDEBUGCODE(this->validate();) +} + +void SkBitmap::reset() { + this->freePixels(); + bzero(this, sizeof(*this)); +} + +int SkBitmap::ComputeBytesPerPixel(SkBitmap::Config config) { + int bpp; + switch (config) { + case kNo_Config: + case kA1_Config: + bpp = 0; // not applicable + break; + case kRLE_Index8_Config: + case kA8_Config: + case kIndex8_Config: + bpp = 1; + break; + case kRGB_565_Config: + case kARGB_4444_Config: + bpp = 2; + break; + case kARGB_8888_Config: + bpp = 4; + break; + default: + SkASSERT(!"unknown config"); + bpp = 0; // error + break; + } + return bpp; +} + +int SkBitmap::ComputeRowBytes(Config c, int width) { + int rowBytes = 0; + + switch (c) { + case kNo_Config: + case kRLE_Index8_Config: + // assume that the bitmap has no pixels to draw to + rowBytes = 0; + break; + case kA1_Config: + rowBytes = (width + 7) >> 3; + break; + case kA8_Config: + case kIndex8_Config: + rowBytes = width; + break; + case kRGB_565_Config: + case kARGB_4444_Config: + rowBytes = width << 1; + break; + case kARGB_8888_Config: + rowBytes = width << 2; + break; + default: + SkASSERT(!"unknown config"); + break; + } + return rowBytes; +} + +void SkBitmap::setConfig(Config c, int width, int height, int rowBytes) { + this->freePixels(); + + if (rowBytes == 0) { + rowBytes = SkBitmap::ComputeRowBytes(c, width); + } + fConfig = SkToU8(c); + fWidth = SkToU16(width); + fHeight = SkToU16(height); + fRowBytes = rowBytes; + + fBytesPerPixel = (uint8_t)ComputeBytesPerPixel(c); + + SkDEBUGCODE(this->validate();) +} + +void SkBitmap::updatePixelsFromRef() const { + if (NULL != fPixelRef) { + if (fPixelLockCount > 0) { + SkASSERT(fPixelRef->getLockCount() > 0); + + void* p = fPixelRef->pixels(); + if (NULL != p) { + p = (char*)p + fPixelRefOffset; + } + fPixels = p; + SkRefCnt_SafeAssign(fColorTable, fPixelRef->colorTable()); + } else { + SkASSERT(0 == fPixelLockCount); + fPixels = NULL; + fColorTable->safeUnref(); + fColorTable = NULL; + } + } +} + +SkPixelRef* SkBitmap::setPixelRef(SkPixelRef* pr, size_t offset) { + // do this first, we that we never have a non-zero offset with a null ref + if (NULL == pr) { + offset = 0; + } + + if (fPixelRef != pr || fPixelRefOffset != offset) { + if (fPixelRef != pr) { + this->freePixels(); + SkASSERT(NULL == fPixelRef); + + pr->safeRef(); + fPixelRef = pr; + } + fPixelRefOffset = offset; + this->updatePixelsFromRef(); + } + + SkDEBUGCODE(this->validate();) + return pr; +} + +void SkBitmap::lockPixels() const { + if (NULL != fPixelRef && 1 == ++fPixelLockCount) { + fPixelRef->lockPixels(); + this->updatePixelsFromRef(); + } + SkDEBUGCODE(this->validate();) +} + +void SkBitmap::unlockPixels() const { + SkASSERT(NULL == fPixelRef || fPixelLockCount > 0); + + if (NULL != fPixelRef && 0 == --fPixelLockCount) { + fPixelRef->unlockPixels(); + this->updatePixelsFromRef(); + } + SkDEBUGCODE(this->validate();) +} + +void SkBitmap::setPixels(void* p, SkColorTable* ctable) { + this->freePixels(); + fPixels = p; + SkRefCnt_SafeAssign(fColorTable, ctable); + + SkDEBUGCODE(this->validate();) +} + +bool SkBitmap::allocPixels(Allocator* allocator, SkColorTable* ctable) { + HeapAllocator stdalloc; + + if (NULL == allocator) { + allocator = &stdalloc; + } + return allocator->allocPixelRef(this, ctable); +} + +void SkBitmap::freePixels() { + // if we're gonna free the pixels, we certainly need to free the mipmap + this->freeMipMap(); + + fColorTable->safeUnref(); + fColorTable = NULL; + + if (NULL != fPixelRef) { + if (fPixelLockCount > 0) { + fPixelRef->unlockPixels(); + } + fPixelRef->unref(); + fPixelRef = NULL; + fPixelRefOffset = 0; + } + fPixelLockCount = 0; + fPixels = NULL; +} + +void SkBitmap::freeMipMap() { +#ifdef SK_SUPPORT_MIPMAP + fMipMap->safeUnref(); + fMipMap = NULL; +#endif +} + +uint32_t SkBitmap::getGenerationID() const { + return fPixelRef ? fPixelRef->getGenerationID() : 0; +} + +void SkBitmap::notifyPixelsChanged() const { + if (fPixelRef) { + fPixelRef->notifyPixelsChanged(); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +/** We explicitly use the same allocator for our pixels that SkMask does, + so that we can freely assign memory allocated by one class to the other. + */ +class SkMallocPixelRef : public SkPixelRef { +public: + /** Allocate the specified buffer for pixels. The memory is freed when the + last owner of this pixelref is gone. + */ + SkMallocPixelRef(void* addr, size_t size, SkColorTable* ctable); + virtual ~SkMallocPixelRef(); + + virtual void flatten(SkFlattenableWriteBuffer&) const; + virtual Factory getFactory() const { + return Create; + } + static SkPixelRef* Create(SkFlattenableReadBuffer& buffer) { + return SkNEW_ARGS(SkMallocPixelRef, (buffer)); + } + +protected: + // overrides from SkPixelRef + virtual void* onLockPixels(SkColorTable**); + virtual void onUnlockPixels(); + + SkMallocPixelRef(SkFlattenableReadBuffer& buffer); + +private: + void* fStorage; + size_t fSize; + SkColorTable* fCTable; + + typedef SkPixelRef INHERITED; +}; + +SkMallocPixelRef::SkMallocPixelRef(void* storage, size_t size, + SkColorTable* ctable) { + SkASSERT(storage); + fStorage = storage; + fSize = size; + fCTable = ctable; + ctable->safeRef(); +} + +SkMallocPixelRef::~SkMallocPixelRef() { + fCTable->safeUnref(); + sk_free(fStorage); +} + +void* SkMallocPixelRef::onLockPixels(SkColorTable** ct) { + *ct = fCTable; + return fStorage; +} + +void SkMallocPixelRef::onUnlockPixels() { + // nothing to do +} + +void SkMallocPixelRef::flatten(SkFlattenableWriteBuffer& buffer) const { + this->INHERITED::flatten(buffer); + + buffer.write32(fSize); + buffer.writePad(fStorage, fSize); + if (fCTable) { + buffer.writeBool(true); + fCTable->flatten(buffer); + } else { + buffer.writeBool(false); + } +} + +SkMallocPixelRef::SkMallocPixelRef(SkFlattenableReadBuffer& buffer) : INHERITED(buffer, NULL) { + fSize = buffer.readU32(); + fStorage = sk_malloc_throw(fSize); + buffer.read(fStorage, fSize); + if (buffer.readBool()) { + fCTable = SkNEW_ARGS(SkColorTable, (buffer)); + } else { + fCTable = NULL; + } +} + +static SkPixelRef::Registrar reg("SkMallocPixelRef", + SkMallocPixelRef::Create); + +/** We explicitly use the same allocator for our pixels that SkMask does, + so that we can freely assign memory allocated by one class to the other. + */ +bool SkBitmap::HeapAllocator::allocPixelRef(SkBitmap* dst, + SkColorTable* ctable) { + Sk64 size = dst->getSize64(); + if (size.isNeg() || !size.is32()) { + return false; + } + + void* addr = sk_malloc_flags(size.get32(), 0); // returns NULL on failure + if (NULL == addr) { + return false; + } + + dst->setPixelRef(new SkMallocPixelRef(addr, size.get32(), ctable))->unref(); + // since we're already allocated, we lockPixels right away + dst->lockPixels(); + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool SkBitmap::isOpaque() const { + switch (fConfig) { + case kNo_Config: + return true; + + case kA1_Config: + case kA8_Config: + case kARGB_4444_Config: + case kARGB_8888_Config: + return (fFlags & kImageIsOpaque_Flag) != 0; + + case kIndex8_Config: + case kRLE_Index8_Config: { + uint32_t flags = 0; + + this->lockPixels(); + // if lockPixels failed, we may not have a ctable ptr + if (fColorTable) { + flags = fColorTable->getFlags(); + } + this->unlockPixels(); + + return (flags & SkColorTable::kColorsAreOpaque_Flag) != 0; + } + + case kRGB_565_Config: + return true; + + default: + SkASSERT(!"unknown bitmap config pased to isOpaque"); + return false; + } +} + +void SkBitmap::setIsOpaque(bool isOpaque) { + /* we record this regardless of fConfig, though it is ignored in + isOpaque() for configs that can't support per-pixel alpha. + */ + if (isOpaque) { + fFlags |= kImageIsOpaque_Flag; + } else { + fFlags &= ~kImageIsOpaque_Flag; + } +} + +void* SkBitmap::getAddr(int x, int y) const { + SkASSERT((unsigned)x < (unsigned)this->width()); + SkASSERT((unsigned)y < (unsigned)this->height()); + + char* base = (char*)this->getPixels(); + if (base) { + base += y * this->rowBytes(); + switch (this->config()) { + case SkBitmap::kARGB_8888_Config: + base += x << 2; + break; + case SkBitmap::kARGB_4444_Config: + case SkBitmap::kRGB_565_Config: + base += x << 1; + break; + case SkBitmap::kA8_Config: + case SkBitmap::kIndex8_Config: + base += x; + break; + case SkBitmap::kA1_Config: + base += x >> 3; + break; + case kRLE_Index8_Config: + SkASSERT(!"Can't return addr for kRLE_Index8_Config"); + base = NULL; + break; + default: + SkASSERT(!"Can't return addr for config"); + base = NULL; + break; + } + } + return base; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void SkBitmap::eraseARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) const { + SkDEBUGCODE(this->validate();) + + if (0 == fWidth || 0 == fHeight || + kNo_Config == fConfig || kIndex8_Config == fConfig) { + return; + } + + SkAutoLockPixels alp(*this); + // perform this check after the lock call + if (NULL == fPixels) { + return; + } + + int height = fHeight; + const int width = fWidth; + const int rowBytes = fRowBytes; + + // make rgb premultiplied + if (255 != a) { + r = SkAlphaMul(r, a); + g = SkAlphaMul(g, a); + b = SkAlphaMul(b, a); + } + + switch (fConfig) { + case kA1_Config: { + uint8_t* p = (uint8_t*)fPixels; + const int count = (width + 7) >> 3; + a = (a >> 7) ? 0xFF : 0; + SkASSERT(count <= rowBytes); + while (--height >= 0) { + memset(p, a, count); + p += rowBytes; + } + break; + } + case kA8_Config: { + uint8_t* p = (uint8_t*)fPixels; + while (--height >= 0) { + memset(p, a, width); + p += rowBytes; + } + break; + } + case kARGB_4444_Config: + case kRGB_565_Config: { + uint16_t* p = (uint16_t*)fPixels; + uint16_t v; + + if (kARGB_4444_Config == fConfig) { + v = SkPackARGB4444(a >> 4, r >> 4, g >> 4, b >> 4); + } else { // kRGB_565_Config + v = SkPackRGB16(r >> (8 - SK_R16_BITS), g >> (8 - SK_G16_BITS), + b >> (8 - SK_B16_BITS)); + } + while (--height >= 0) { + sk_memset16(p, v, width); + p = (uint16_t*)((char*)p + rowBytes); + } + break; + } + case kARGB_8888_Config: { + uint32_t* p = (uint32_t*)fPixels; + uint32_t v = SkPackARGB32(a, r, g, b); + + while (--height >= 0) { + sk_memset32(p, v, width); + p = (uint32_t*)((char*)p + rowBytes); + } + break; + } + } + + this->notifyPixelsChanged(); +} + +////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////// + +#define SUB_OFFSET_FAILURE ((size_t)-1) + +static size_t getSubOffset(const SkBitmap& bm, int x, int y) { + SkASSERT((unsigned)x < (unsigned)bm.width()); + SkASSERT((unsigned)y < (unsigned)bm.height()); + + switch (bm.getConfig()) { + case SkBitmap::kA8_Config: + case SkBitmap:: kIndex8_Config: + // x is fine as is for the calculation + break; + + case SkBitmap::kRGB_565_Config: + case SkBitmap::kARGB_4444_Config: + x <<= 1; + break; + + case SkBitmap::kARGB_8888_Config: + x <<= 2; + break; + + case SkBitmap::kNo_Config: + case SkBitmap::kA1_Config: + default: + return SUB_OFFSET_FAILURE; + } + return y * bm.rowBytes() + x; +} + +bool SkBitmap::extractSubset(SkBitmap* result, const SkIRect& subset) const { + SkDEBUGCODE(this->validate();) + + if (NULL == result || (NULL == fPixelRef && NULL == fPixels)) { + return false; // no src pixels + } + + SkIRect srcRect, r; + srcRect.set(0, 0, this->width(), this->height()); + if (!r.intersect(srcRect, subset)) { + return false; // r is empty (i.e. no intersection) + } + + if (kRLE_Index8_Config == fConfig) { + SkAutoLockPixels alp(*this); + if (this->getPixels() == NULL) { + return false; + } + SkBitmap bm; + + bm.setConfig(kIndex8_Config, r.width(), r.height()); + bm.allocPixels(this->getColorTable()); + if (NULL == bm.getPixels()) { + return false; + } + + const RLEPixels* rle = (const RLEPixels*)this->getPixels(); + uint8_t* dst = bm.getAddr8(0, 0); + const int width = bm.width(); + const int rowBytes = bm.rowBytes(); + + for (int y = r.fTop; y < r.fBottom; y++) { + SkPackBits::Unpack8(dst, r.fLeft, width, rle->packedAtY(y)); + dst += rowBytes; + } + result->swap(bm); + return true; + } + + size_t offset = getSubOffset(*this, r.fLeft, r.fTop); + if (SUB_OFFSET_FAILURE == offset) { + return false; // config not supported + } + + SkBitmap dst; + dst.setConfig(this->config(), r.width(), r.height(), this->rowBytes()); + + if (fPixelRef) { + // share the pixelref with a custom offset + dst.setPixelRef(fPixelRef, fPixelRefOffset + offset); + } else { + // share the pixels (owned by the caller) + dst.setPixels((char*)fPixels + offset, this->getColorTable()); + } + SkDEBUGCODE(dst.validate();) + + // we know we're good, so commit to result + result->swap(dst); + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +#include "SkCanvas.h" +#include "SkPaint.h" + +bool SkBitmap::copyTo(SkBitmap* dst, Config dstConfig, Allocator* alloc) const { + if (NULL == dst || this->width() == 0 || this->height() == 0) { + return false; + } + + switch (dstConfig) { + case kA8_Config: + case kARGB_4444_Config: + case kRGB_565_Config: + case kARGB_8888_Config: + break; + default: + return false; + } + + SkBitmap tmp; + + tmp.setConfig(dstConfig, this->width(), this->height()); + // pass null for colortable, since we don't support Index8 config for dst + if (!tmp.allocPixels(alloc, NULL)) { + return false; + } + + SkAutoLockPixels srclock(*this); + SkAutoLockPixels dstlock(tmp); + + if (NULL == this->getPixels() || NULL == tmp.getPixels()) { + // allocator/lock failed + return false; + } + + // if the src has alpha, we have to clear the dst first + if (!this->isOpaque()) { + tmp.eraseColor(0); + } + + SkCanvas canvas(tmp); + SkPaint paint; + + paint.setDither(true); + canvas.drawBitmap(*this, 0, 0, &paint); + + dst->swap(tmp); + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static void downsampleby2_proc32(SkBitmap* dst, int x, int y, + const SkBitmap& src) { + x <<= 1; + y <<= 1; + const SkPMColor* p = src.getAddr32(x, y); + SkPMColor c, ag, rb; + + c = *p; ag = (c >> 8) & 0xFF00FF; rb = c & 0xFF00FF; + if (x < src.width() - 1) { + p += 1; + } + c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF; + + if (y < src.height() - 1) { + p = src.getAddr32(x, y + 1); + } + c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF; + if (x < src.width() - 1) { + p += 1; + } + c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF; + + *dst->getAddr32(x >> 1, y >> 1) = + ((rb >> 2) & 0xFF00FF) | ((ag << 6) & 0xFF00FF00); +} + +static inline uint32_t expand16(U16CPU c) { + return (c & ~SK_G16_MASK_IN_PLACE) | ((c & SK_G16_MASK_IN_PLACE) << 16); +} + +// returns dirt in the top 16bits, but we don't care, since we only +// store the low 16bits. +static inline U16CPU pack16(uint32_t c) { + return (c & ~SK_G16_MASK_IN_PLACE) | ((c >> 16) & SK_G16_MASK_IN_PLACE); +} + +static void downsampleby2_proc16(SkBitmap* dst, int x, int y, + const SkBitmap& src) { + x <<= 1; + y <<= 1; + const uint16_t* p = src.getAddr16(x, y); + SkPMColor c; + + c = expand16(*p); + if (x < (int)src.width() - 1) { + p += 1; + } + c += expand16(*p); + + if (y < (int)src.height() - 1) { + p = src.getAddr16(x, y + 1); + } + c += expand16(*p); + if (x < (int)src.width() - 1) { + p += 1; + } + c += expand16(*p); + + *dst->getAddr16(x >> 1, y >> 1) = (uint16_t)pack16(c >> 2); +} + +static uint32_t expand4444(U16CPU c) { + return (c & 0xF0F) | ((c & ~0xF0F) << 12); +} + +static U16CPU collaps4444(uint32_t c) { + return (c & 0xF0F) | ((c >> 12) & ~0xF0F); +} + +static void downsampleby2_proc4444(SkBitmap* dst, int x, int y, + const SkBitmap& src) { + x <<= 1; + y <<= 1; + const uint16_t* p = src.getAddr16(x, y); + uint32_t c; + + c = expand4444(*p); + if (x < src.width() - 1) { + p += 1; + } + c += expand4444(*p); + + if (y < src.height() - 1) { + p = src.getAddr16(x, y + 1); + } + c += expand4444(*p); + if (x < src.width() - 1) { + p += 1; + } + c += expand4444(*p); + + *dst->getAddr16(x >> 1, y >> 1) = (uint16_t)collaps4444(c >> 2); +} + +void SkBitmap::buildMipMap(bool forceRebuild) { +#ifdef SK_SUPPORT_MIPMAP + if (forceRebuild) + this->freeMipMap(); + else if (fMipMap) + return; // we're already built + + SkASSERT(NULL == fMipMap); + + void (*proc)(SkBitmap* dst, int x, int y, const SkBitmap& src); + + const SkBitmap::Config config = this->getConfig(); + + switch (config) { + case kARGB_8888_Config: + proc = downsampleby2_proc32; + break; + case kRGB_565_Config: + proc = downsampleby2_proc16; + break; + case kARGB_4444_Config: + proc = downsampleby2_proc4444; + break; + case kIndex8_Config: + case kA8_Config: + default: + return; // don't build mipmaps for these configs + } + + // whip through our loop to compute the exact size needed + size_t size = 0; + int maxLevels = 0; + { + unsigned width = this->width(); + unsigned height = this->height(); + for (;;) { + width >>= 1; + height >>= 1; + if (0 == width || 0 == height) { + break; + } + size += ComputeRowBytes(config, width) * height; + maxLevels += 1; + } + } + if (0 == maxLevels) { + return; + } + + MipMap* mm = MipMap::Alloc(maxLevels, size); + MipLevel* level = mm->levels(); + uint8_t* addr = (uint8_t*)mm->pixels(); + + unsigned width = this->width(); + unsigned height = this->height(); + unsigned rowBytes = this->rowBytes(); + SkBitmap srcBM(*this), dstBM; + + srcBM.lockPixels(); + + for (int i = 0; i < maxLevels; i++) { + width >>= 1; + height >>= 1; + rowBytes = ComputeRowBytes(config, width); + + level[i].fPixels = addr; + level[i].fWidth = SkToU16(width); + level[i].fHeight = SkToU16(height); + level[i].fRowBytes = SkToU16(rowBytes); + + dstBM.setConfig(config, width, height, rowBytes); + dstBM.setPixels(addr); + + for (unsigned y = 0; y < height; y++) { + for (unsigned x = 0; x < width; x++) { + proc(&dstBM, x, y, srcBM); + } + } + + srcBM = dstBM; + addr += height * rowBytes; + } + SkASSERT(addr == (uint8_t*)mm->pixels() + size); + fMipMap = mm; +#endif +} + +bool SkBitmap::hasMipMap() const { +#ifdef SK_SUPPORT_MIPMAP + return fMipMap != NULL; +#else + return false; +#endif +} + +int SkBitmap::extractMipLevel(SkBitmap* dst, SkFixed sx, SkFixed sy) { +#ifdef SK_SUPPORT_MIPMAP + if (NULL == fMipMap) + return 0; + + int level = ComputeMipLevel(sx, sy) >> 16; + SkASSERT(level >= 0); + if (level <= 0) { + return 0; + } + + if (level >= fMipMap->fLevelCount) { + level = fMipMap->fLevelCount - 1; + } + if (dst) { + const MipLevel& mip = fMipMap->levels()[level - 1]; + dst->setConfig((SkBitmap::Config)this->config(), + mip.fWidth, mip.fHeight, mip.fRowBytes); + dst->setPixels(mip.fPixels); + } + return level; +#else + return 0; +#endif +} + +SkFixed SkBitmap::ComputeMipLevel(SkFixed sx, SkFixed sy) { +#ifdef SK_SUPPORT_MIPMAP + sx = SkAbs32(sx); + sy = SkAbs32(sy); + if (sx < sy) { + sx = sy; + } + if (sx < SK_Fixed1) { + return 0; + } + int clz = SkCLZ(sx); + SkASSERT(clz >= 1 && clz <= 15); + return SkIntToFixed(15 - clz) + ((unsigned)(sx << (clz + 1)) >> 16); +#else + return 0; +#endif +} + +/////////////////////////////////////////////////////////////////////////////// + +static void GetBitmapAlpha(const SkBitmap& src, uint8_t SK_RESTRICT alpha[], + int alphaRowBytes) { + SkASSERT(alpha != NULL); + SkASSERT(alphaRowBytes >= src.width()); + + SkBitmap::Config config = src.getConfig(); + int w = src.width(); + int h = src.height(); + int rb = src.rowBytes(); + + if (SkBitmap::kA8_Config == config && !src.isOpaque()) { + const uint8_t* s = src.getAddr8(0, 0); + while (--h >= 0) { + memcpy(alpha, s, w); + s += rb; + alpha += alphaRowBytes; + } + } else if (SkBitmap::kARGB_8888_Config == config && !src.isOpaque()) { + const SkPMColor* SK_RESTRICT s = src.getAddr32(0, 0); + while (--h >= 0) { + for (int x = 0; x < w; x++) { + alpha[x] = SkGetPackedA32(s[x]); + } + s = (const SkPMColor*)((const char*)s + rb); + alpha += alphaRowBytes; + } + } else if (SkBitmap::kARGB_4444_Config == config && !src.isOpaque()) { + const SkPMColor16* SK_RESTRICT s = src.getAddr16(0, 0); + while (--h >= 0) { + for (int x = 0; x < w; x++) { + alpha[x] = SkPacked4444ToA32(s[x]); + } + s = (const SkPMColor16*)((const char*)s + rb); + alpha += alphaRowBytes; + } + } else if (SkBitmap::kIndex8_Config == config && !src.isOpaque()) { + SkColorTable* ct = src.getColorTable(); + if (ct) { + const SkPMColor* SK_RESTRICT table = ct->lockColors(); + const uint8_t* SK_RESTRICT s = src.getAddr8(0, 0); + while (--h >= 0) { + for (int x = 0; x < w; x++) { + alpha[x] = SkGetPackedA32(table[s[x]]); + } + s += rb; + alpha += alphaRowBytes; + } + ct->unlockColors(false); + } + } else { // src is opaque, so just fill alpha[] with 0xFF + memset(alpha, 0xFF, h * alphaRowBytes); + } +} + +#include "SkPaint.h" +#include "SkMaskFilter.h" +#include "SkMatrix.h" + +void SkBitmap::extractAlpha(SkBitmap* dst, const SkPaint* paint, + SkIPoint* offset) const { + SkDEBUGCODE(this->validate();) + + SkMatrix identity; + SkMask srcM, dstM; + + srcM.fBounds.set(0, 0, this->width(), this->height()); + srcM.fRowBytes = SkAlign4(this->width()); + srcM.fFormat = SkMask::kA8_Format; + + SkMaskFilter* filter = paint ? paint->getMaskFilter() : NULL; + + // compute our (larger?) dst bounds if we have a filter + if (NULL != filter) { + identity.reset(); + srcM.fImage = NULL; + if (!filter->filterMask(&dstM, srcM, identity, NULL)) { + goto NO_FILTER_CASE; + } + dstM.fRowBytes = SkAlign4(dstM.fBounds.width()); + } else { + NO_FILTER_CASE: + dst->setConfig(SkBitmap::kA8_Config, this->width(), this->height(), + srcM.fRowBytes); + dst->allocPixels(); + GetBitmapAlpha(*this, dst->getAddr8(0, 0), srcM.fRowBytes); + if (offset) { + offset->set(0, 0); + } + return; + } + + SkAutoMaskImage srcCleanup(&srcM, true); + + GetBitmapAlpha(*this, srcM.fImage, srcM.fRowBytes); + if (!filter->filterMask(&dstM, srcM, identity, NULL)) { + goto NO_FILTER_CASE; + } + + SkAutoMaskImage dstCleanup(&dstM, false); + + dst->setConfig(SkBitmap::kA8_Config, dstM.fBounds.width(), + dstM.fBounds.height(), dstM.fRowBytes); + dst->allocPixels(); + memcpy(dst->getPixels(), dstM.fImage, dstM.computeImageSize()); + if (offset) { + offset->set(dstM.fBounds.fLeft, dstM.fBounds.fTop); + } + SkDEBUGCODE(dst->validate();) +} + +/////////////////////////////////////////////////////////////////////////////// + +enum { + SERIALIZE_PIXELTYPE_NONE, + SERIALIZE_PIXELTYPE_RAW_WITH_CTABLE, + SERIALIZE_PIXELTYPE_RAW_NO_CTABLE, + SERIALIZE_PIXELTYPE_REF_DATA, + SERIALIZE_PIXELTYPE_REF_PTR, +}; + +static void writeString(SkFlattenableWriteBuffer& buffer, const char str[]) { + size_t len = strlen(str); + buffer.write32(len); + buffer.writePad(str, len); +} + +static SkPixelRef::Factory deserialize_factory(SkFlattenableReadBuffer& buffer) { + size_t len = buffer.readInt(); + SkAutoSMalloc<256> storage(len + 1); + char* str = (char*)storage.get(); + buffer.read(str, len); + str[len] = 0; + return SkPixelRef::NameToFactory(str); +} + +/* + It is tricky to know how much to flatten. If we don't have a pixelref (i.e. + we just have pixels, then we can only flatten the pixels, or write out an + empty bitmap. + + With a pixelref, we still have the question of recognizing when two sitings + of the same pixelref are the same, and when they are different. Perhaps we + should look at the generationID and keep a record of that in some dictionary + associated with the buffer. SkGLTextureCache does this sort of thing to know + when to create a new texture. +*/ +void SkBitmap::flatten(SkFlattenableWriteBuffer& buffer) const { + buffer.write32(fWidth); + buffer.write32(fHeight); + buffer.write32(fRowBytes); + buffer.write8(fConfig); + buffer.writeBool(this->isOpaque()); + + /* If we are called in this mode, then it is up to the caller to manage + the owner-counts on the pixelref, as we just record the ptr itself. + */ + if (!buffer.persistBitmapPixels()) { + if (fPixelRef) { + buffer.write8(SERIALIZE_PIXELTYPE_REF_PTR); + buffer.write32(fPixelRefOffset); + buffer.writeRefCnt(fPixelRef); + return; + } else { + // we ignore the non-persist request, since we don't have a ref + // ... or we could just write an empty bitmap... + // (true) will write an empty bitmap, (false) will flatten the pix + if (true) { + buffer.write8(SERIALIZE_PIXELTYPE_NONE); + return; + } + } + } + + if (fPixelRef) { + SkPixelRef::Factory fact = fPixelRef->getFactory(); + if (fact) { + const char* name = SkPixelRef::FactoryToName(fact); + if (name && *name) { + buffer.write8(SERIALIZE_PIXELTYPE_REF_DATA); + buffer.write32(fPixelRefOffset); + writeString(buffer, name); + fPixelRef->flatten(buffer); + return; + } + } + // if we get here, we can't record the pixels + buffer.write8(SERIALIZE_PIXELTYPE_NONE); + } else if (fPixels) { + if (fColorTable) { + buffer.write8(SERIALIZE_PIXELTYPE_RAW_WITH_CTABLE); + fColorTable->flatten(buffer); + } else { + buffer.write8(SERIALIZE_PIXELTYPE_RAW_NO_CTABLE); + } + buffer.writePad(fPixels, this->getSize()); + } else { + buffer.write8(SERIALIZE_PIXELTYPE_NONE); + } +} + +void SkBitmap::unflatten(SkFlattenableReadBuffer& buffer) { + this->reset(); + + int width = buffer.readInt(); + int height = buffer.readInt(); + int rowBytes = buffer.readInt(); + int config = buffer.readU8(); + + this->setConfig((Config)config, width, height, rowBytes); + this->setIsOpaque(buffer.readBool()); + + size_t size = this->getSize(); + int reftype = buffer.readU8(); + switch (reftype) { + case SERIALIZE_PIXELTYPE_REF_PTR: { + size_t offset = buffer.readU32(); + SkPixelRef* pr = (SkPixelRef*)buffer.readRefCnt(); + this->setPixelRef(pr, offset); + break; + } + case SERIALIZE_PIXELTYPE_REF_DATA: { + size_t offset = buffer.readU32(); + SkPixelRef::Factory fact = deserialize_factory(buffer); + SkPixelRef* pr = fact(buffer); + this->setPixelRef(pr, offset)->safeUnref(); + break; + } + case SERIALIZE_PIXELTYPE_RAW_WITH_CTABLE: + case SERIALIZE_PIXELTYPE_RAW_NO_CTABLE: { + SkColorTable* ctable = NULL; + if (SERIALIZE_PIXELTYPE_RAW_WITH_CTABLE == reftype) { + ctable = SkNEW_ARGS(SkColorTable, (buffer)); + } + if (this->allocPixels(ctable)) { + this->lockPixels(); + buffer.read(this->getPixels(), size); + this->unlockPixels(); + } else { + buffer.skip(size); + } + ctable->safeUnref(); + break; + } + case SERIALIZE_PIXELTYPE_NONE: + break; + default: + SkASSERT(!"unrecognized pixeltype in serialized data"); + sk_throw(); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +SkBitmap::RLEPixels::RLEPixels(int width, int height) { + fHeight = height; + fYPtrs = (uint8_t**)sk_malloc_throw(height * sizeof(uint8_t*)); + bzero(fYPtrs, height * sizeof(uint8_t*)); +} + +SkBitmap::RLEPixels::~RLEPixels() { + sk_free(fYPtrs); +} + +/////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_DEBUG +void SkBitmap::validate() const { + SkASSERT(fConfig < kConfigCount); + SkASSERT(fRowBytes >= (unsigned)ComputeRowBytes((Config)fConfig, fWidth)); + SkASSERT(fFlags <= kImageIsOpaque_Flag); + SkASSERT(fPixelLockCount >= 0); + SkASSERT(NULL == fColorTable || (unsigned)fColorTable->getRefCnt() < 10000); + SkASSERT((uint8_t)ComputeBytesPerPixel((Config)fConfig) == fBytesPerPixel); + + if (fPixelRef) { + if (fPixelLockCount > 0) { + SkASSERT(fPixelRef->getLockCount() > 0); + } else { + SkASSERT(NULL == fPixels); + SkASSERT(NULL == fColorTable); + } + } +} +#endif + diff --git a/skia/sgl/SkBitmapProcShader.cpp b/skia/sgl/SkBitmapProcShader.cpp new file mode 100644 index 0000000..8685c9f --- /dev/null +++ b/skia/sgl/SkBitmapProcShader.cpp @@ -0,0 +1,194 @@ +#include "SkBitmapProcShader.h" +#include "SkColorPriv.h" +#include "SkPixelRef.h" + +bool SkBitmapProcShader::CanDo(const SkBitmap& bm, TileMode tx, TileMode ty) { + switch (bm.config()) { + case SkBitmap::kA8_Config: + case SkBitmap::kRGB_565_Config: + case SkBitmap::kIndex8_Config: + case SkBitmap::kARGB_8888_Config: + // if (tx == ty && (kClamp_TileMode == tx || kRepeat_TileMode == tx)) + return true; + default: + break; + } + return false; +} + +SkBitmapProcShader::SkBitmapProcShader(const SkBitmap& src, + TileMode tmx, TileMode tmy) { + fRawBitmap = src; + fState.fTileModeX = (uint8_t)tmx; + fState.fTileModeY = (uint8_t)tmy; +} + +SkBitmapProcShader::SkBitmapProcShader(SkFlattenableReadBuffer& buffer) + : INHERITED(buffer) { + fRawBitmap.unflatten(buffer); + fState.fTileModeX = buffer.readU8(); + fState.fTileModeY = buffer.readU8(); +} + +void SkBitmapProcShader::beginSession() { + this->INHERITED::beginSession(); + + fRawBitmap.lockPixels(); +} + +void SkBitmapProcShader::endSession() { + fRawBitmap.unlockPixels(); + + this->INHERITED::endSession(); +} + +bool SkBitmapProcShader::asABitmap(SkBitmap* texture, SkMatrix* texM, + TileMode xy[]) { + if (texture) { + *texture = fRawBitmap; + } + if (texM) { + texM->reset(); + } + if (xy) { + xy[0] = (TileMode)fState.fTileModeX; + xy[1] = (TileMode)fState.fTileModeY; + } + return true; +} + +void SkBitmapProcShader::flatten(SkFlattenableWriteBuffer& buffer) { + this->INHERITED::flatten(buffer); + + fRawBitmap.flatten(buffer); + buffer.write8(fState.fTileModeX); + buffer.write8(fState.fTileModeY); +} + +bool SkBitmapProcShader::setContext(const SkBitmap& device, + const SkPaint& paint, + const SkMatrix& matrix) { + // do this first, so we have a correct inverse matrix + if (!this->INHERITED::setContext(device, paint, matrix)) { + return false; + } + + fState.fOrigBitmap = fRawBitmap; + fState.fOrigBitmap.lockPixels(); + if (fState.fOrigBitmap.getPixels() == NULL) { + fState.fOrigBitmap.unlockPixels(); + return false; + } + + if (!fState.chooseProcs(this->getTotalInverse(), paint)) { + return false; + } + + bool bitmapIsOpaque = fState.fBitmap->isOpaque(); + + // filtering doesn't guarantee that opaque stays opaque (finite precision) + // so pretend we're not opaque if we're being asked to filter. If we had + // more blit-procs, we could specialize on opaque src, and just OR in 0xFF + // after the filter to be sure... + if (paint.isFilterBitmap()) { + bitmapIsOpaque = false; + } + + // update fFlags + fFlags = 0; // this should happen in SkShader.cpp + + if (bitmapIsOpaque && (255 == this->getPaintAlpha())) { + fFlags |= kOpaqueAlpha_Flag; + } + + switch (fState.fBitmap->config()) { + case SkBitmap::kRGB_565_Config: + fFlags |= (kHasSpan16_Flag | kIntrinsicly16_Flag); + break; + case SkBitmap::kIndex8_Config: + case SkBitmap::kARGB_8888_Config: + if (bitmapIsOpaque) { + fFlags |= kHasSpan16_Flag; + } + break; + case SkBitmap::kA8_Config: + break; // never set kHasSpan16_Flag + default: + break; + } + return true; +} + +#define BUF_MAX 128 + +void SkBitmapProcShader::shadeSpan(int x, int y, SkPMColor dstC[], int count) { + uint32_t buffer[BUF_MAX]; + + const SkBitmapProcState& state = fState; + SkBitmapProcState::MatrixProc mproc = state.fMatrixProc; + SkBitmapProcState::SampleProc32 sproc = state.fSampleProc32; + int max = fState.fDoFilter ? (BUF_MAX >> 1) : BUF_MAX; + + SkASSERT(state.fBitmap->getPixels()); + SkASSERT(state.fBitmap->pixelRef() == NULL || + state.fBitmap->pixelRef()->getLockCount()); + + for (;;) { + int n = count; + if (n > max) { + n = max; + } + mproc(state, buffer, n, x, y); + sproc(state, buffer, n, dstC); + + if ((count -= n) == 0) { + break; + } + x += n; + dstC += n; + } +} + +void SkBitmapProcShader::shadeSpan16(int x, int y, uint16_t dstC[], int count) { + uint32_t buffer[BUF_MAX]; + + const SkBitmapProcState& state = fState; + SkBitmapProcState::MatrixProc mproc = state.fMatrixProc; + SkBitmapProcState::SampleProc16 sproc = state.fSampleProc16; + int max = fState.fDoFilter ? (BUF_MAX >> 1) : BUF_MAX; + + SkASSERT(state.fBitmap->getPixels()); + SkASSERT(state.fBitmap->pixelRef() == NULL || + state.fBitmap->pixelRef()->getLockCount()); + + for (;;) { + int n = count; + if (n > max) { + n = max; + } + mproc(state, buffer, n, x, y); + sproc(state, buffer, n, dstC); + + if ((count -= n) == 0) { + break; + } + x += n; + dstC += n; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +#include "SkTemplatesPriv.h" + +SkShader* SkShader::CreateBitmapShader(const SkBitmap& src, + TileMode tmx, TileMode tmy, + void* storage, size_t storageSize) { + SkShader* shader; + SK_PLACEMENT_NEW_ARGS(shader, SkBitmapProcShader, storage, + storageSize, (src, tmx, tmy)); + return shader; +} + +static SkFlattenable::Registrar gBitmapProcShaderReg("SkBitmapProcShader", + SkBitmapProcShader::CreateProc); diff --git a/skia/sgl/SkBitmapProcShader.h b/skia/sgl/SkBitmapProcShader.h new file mode 100644 index 0000000..6d7d0d9 --- /dev/null +++ b/skia/sgl/SkBitmapProcShader.h @@ -0,0 +1,56 @@ +/* libs/graphics/sgl/SkBitmapShader.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkBitmapProcShader_DEFINED +#define SkBitmapProcShader_DEFINED + +#include "SkShader.h" +#include "SkBitmapProcState.h" + +class SkBitmapProcShader : public SkShader { +public: + SkBitmapProcShader(const SkBitmap& src, TileMode tx, TileMode ty); + + // overrides from SkShader + virtual bool setContext(const SkBitmap&, const SkPaint&, const SkMatrix&); + virtual uint32_t getFlags() { return fFlags; } + virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count); + virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count); + virtual void beginSession(); + virtual void endSession(); + virtual bool asABitmap(SkBitmap*, SkMatrix*, TileMode*); + + static bool CanDo(const SkBitmap&, TileMode tx, TileMode ty); + + static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) { + return SkNEW_ARGS(SkBitmapProcShader, (buffer)); + } + +protected: + SkBitmapProcShader(SkFlattenableReadBuffer& ); + virtual void flatten(SkFlattenableWriteBuffer& ); + virtual Factory getFactory() { return CreateProc; } + + SkBitmap fRawBitmap; // experimental for RLE encoding + SkBitmapProcState fState; + uint32_t fFlags; + +private: + typedef SkShader INHERITED; +}; + +#endif diff --git a/skia/sgl/SkBitmapProcState.cpp b/skia/sgl/SkBitmapProcState.cpp new file mode 100644 index 0000000..aff7e9d --- /dev/null +++ b/skia/sgl/SkBitmapProcState.cpp @@ -0,0 +1,487 @@ +#include "SkBitmapProcState.h" +#include "SkColorPriv.h" +#include "SkFilterProc.h" +#include "SkPaint.h" +#include "SkShader.h" // for tilemodes + +#ifdef SK_CPU_BENDIAN + #define UNPACK_PRIMARY_SHORT(packed) ((uint32_t)(packed) >> 16) + #define UNPACK_SECONDARY_SHORT(packed) ((packed) & 0xFFFF) +#else + #define UNPACK_PRIMARY_SHORT(packed) ((packed) & 0xFFFF) + #define UNPACK_SECONDARY_SHORT(packed) ((uint32_t)(packed) >> 16) +#endif + +static inline SkPMColor Filter_32(unsigned x, unsigned y, + SkPMColor a00, SkPMColor a01, + SkPMColor a10, SkPMColor a11) { + SkASSERT((unsigned)x <= 0xF); + SkASSERT((unsigned)y <= 0xF); + + int xy = x * y; + uint32_t mask = gMask_00FF00FF; //0xFF00FF; + + int scale = 256 - 16*y - 16*x + xy; + uint32_t lo = (a00 & mask) * scale; + uint32_t hi = ((a00 >> 8) & mask) * scale; + + scale = 16*x - xy; + lo += (a01 & mask) * scale; + hi += ((a01 >> 8) & mask) * scale; + + scale = 16*y - xy; + lo += (a10 & mask) * scale; + hi += ((a10 >> 8) & mask) * scale; + + lo += (a11 & mask) * xy; + hi += ((a11 >> 8) & mask) * xy; + + return ((lo >> 8) & mask) | (hi & ~mask); +} + +// returns expanded * 5bits +static inline uint32_t Filter_565_Expanded(unsigned x, unsigned y, + uint32_t a00, uint32_t a01, + uint32_t a10, uint32_t a11) { + SkASSERT((unsigned)x <= 0xF); + SkASSERT((unsigned)y <= 0xF); + + a00 = SkExpand_rgb_16(a00); + a01 = SkExpand_rgb_16(a01); + a10 = SkExpand_rgb_16(a10); + a11 = SkExpand_rgb_16(a11); + + int xy = x * y >> 3; + return a00 * (32 - 2*y - 2*x + xy) + + a01 * (2*x - xy) + + a10 * (2*y - xy) + + a11 * xy; +} + +// turn an expanded 565 * 5bits into SkPMColor +// g:11 | r:10 | x:1 | b:10 +static inline SkPMColor SkExpanded_565_To_PMColor(uint32_t c) { + unsigned r = (c >> 13) & 0xFF; + unsigned g = (c >> 24); + unsigned b = (c >> 2) & 0xFF; + return SkPackARGB32(0xFF, r, g, b); +} + +// returns answer in SkPMColor format +static inline SkPMColor Filter_4444_D32(unsigned x, unsigned y, + uint32_t a00, uint32_t a01, + uint32_t a10, uint32_t a11) { + SkASSERT((unsigned)x <= 0xF); + SkASSERT((unsigned)y <= 0xF); + + a00 = SkExpand_4444(a00); + a01 = SkExpand_4444(a01); + a10 = SkExpand_4444(a10); + a11 = SkExpand_4444(a11); + + int xy = x * y >> 4; + uint32_t result = a00 * (16 - y - x + xy) + + a01 * (x - xy) + + a10 * (y - xy) + + a11 * xy; + + return SkCompact_8888(result); +} + +static inline U8CPU Filter_8(unsigned x, unsigned y, + U8CPU a00, U8CPU a01, + U8CPU a10, U8CPU a11) { + SkASSERT((unsigned)x <= 0xF); + SkASSERT((unsigned)y <= 0xF); + + int xy = x * y; + unsigned result = a00 * (256 - 16*y - 16*x + xy) + + a01 * (16*x - xy) + + a10 * (16*y - xy) + + a11 * xy; + + return result >> 8; +} + +/***************************************************************************** + * + * D32 functions + * + */ + +// SRC == 8888 + +#define FILTER_PROC(x, y, a, b, c, d) Filter_32(x, y, a, b, c, d) + +#define MAKENAME(suffix) S32_opaque_D32 ## suffix +#define DSTSIZE 32 +#define SRCTYPE SkPMColor +#define CHECKSTATE(state) SkASSERT(state.fBitmap->config() == SkBitmap::kARGB_8888_Config); \ + SkASSERT(state.fAlphaScale == 256) +#define RETURNDST(src) src +#define SRC_TO_FILTER(src) src +#define FILTER_TO_DST(c) c +#include "SkBitmapProcState_sample.h" + +#define MAKENAME(suffix) S32_alpha_D32 ## suffix +#define DSTSIZE 32 +#define SRCTYPE SkPMColor +#define CHECKSTATE(state) SkASSERT(state.fBitmap->config() == SkBitmap::kARGB_8888_Config); \ + SkASSERT(state.fAlphaScale < 256) +#define PREAMBLE(state) unsigned scale = state.fAlphaScale +#define RETURNDST(src) SkAlphaMulQ(src, scale) +#define SRC_TO_FILTER(src) src +#define FILTER_TO_DST(c) SkAlphaMulQ(c, scale) +#include "SkBitmapProcState_sample.h" + +// SRC == 565 + +#undef FILTER_PROC +#define FILTER_PROC(x, y, a, b, c, d) Filter_565_Expanded(x, y, a, b, c, d) + +#define MAKENAME(suffix) S16_opaque_D32 ## suffix +#define DSTSIZE 32 +#define SRCTYPE uint16_t +#define CHECKSTATE(state) SkASSERT(state.fBitmap->config() == SkBitmap::kRGB_565_Config); \ + SkASSERT(state.fAlphaScale == 256) +#define RETURNDST(src) SkPixel16ToPixel32(src) +#define SRC_TO_FILTER(src) src +#define FILTER_TO_DST(c) SkExpanded_565_To_PMColor(c) +#include "SkBitmapProcState_sample.h" + +#define MAKENAME(suffix) S16_alpha_D32 ## suffix +#define DSTSIZE 32 +#define SRCTYPE uint16_t +#define CHECKSTATE(state) SkASSERT(state.fBitmap->config() == SkBitmap::kRGB_565_Config); \ + SkASSERT(state.fAlphaScale < 256) +#define PREAMBLE(state) unsigned scale = state.fAlphaScale +#define RETURNDST(src) SkAlphaMulQ(SkPixel16ToPixel32(src), scale) +#define SRC_TO_FILTER(src) src +#define FILTER_TO_DST(c) SkAlphaMulQ(SkExpanded_565_To_PMColor(c), scale) +#include "SkBitmapProcState_sample.h" + +// SRC == Index8 + +#undef FILTER_PROC +#define FILTER_PROC(x, y, a, b, c, d) Filter_32(x, y, a, b, c, d) + +#define MAKENAME(suffix) SI8_opaque_D32 ## suffix +#define DSTSIZE 32 +#define SRCTYPE uint8_t +#define CHECKSTATE(state) SkASSERT(state.fBitmap->config() == SkBitmap::kIndex8_Config); \ + SkASSERT(state.fAlphaScale == 256) +#define PREAMBLE(state) const SkPMColor* SK_RESTRICT table = state.fBitmap->getColorTable()->lockColors() +#define RETURNDST(src) table[src] +#define SRC_TO_FILTER(src) table[src] +#define FILTER_TO_DST(c) c +#define POSTAMBLE(state) state.fBitmap->getColorTable()->unlockColors(false) +#include "SkBitmapProcState_sample.h" + +#define MAKENAME(suffix) SI8_alpha_D32 ## suffix +#define DSTSIZE 32 +#define SRCTYPE uint8_t +#define CHECKSTATE(state) SkASSERT(state.fBitmap->config() == SkBitmap::kIndex8_Config); \ + SkASSERT(state.fAlphaScale < 256) +#define PREAMBLE(state) unsigned scale = state.fAlphaScale; \ + const SkPMColor* SK_RESTRICT table = state.fBitmap->getColorTable()->lockColors() +#define RETURNDST(src) SkAlphaMulQ(table[src], scale) +#define SRC_TO_FILTER(src) table[src] +#define FILTER_TO_DST(c) SkAlphaMulQ(c, scale) +#define POSTAMBLE(state) state.fBitmap->getColorTable()->unlockColors(false) +#include "SkBitmapProcState_sample.h" + +// SRC == 4444 + +#undef FILTER_PROC +#define FILTER_PROC(x, y, a, b, c, d) Filter_4444_D32(x, y, a, b, c, d) + +#define MAKENAME(suffix) S4444_opaque_D32 ## suffix +#define DSTSIZE 32 +#define SRCTYPE SkPMColor16 +#define CHECKSTATE(state) SkASSERT(state.fBitmap->config() == SkBitmap::kARGB_4444_Config); \ +SkASSERT(state.fAlphaScale == 256) +#define RETURNDST(src) SkPixel4444ToPixel32(src) +#define SRC_TO_FILTER(src) src +#define FILTER_TO_DST(c) c +#include "SkBitmapProcState_sample.h" + +#define MAKENAME(suffix) S4444_alpha_D32 ## suffix +#define DSTSIZE 32 +#define SRCTYPE SkPMColor16 +#define CHECKSTATE(state) SkASSERT(state.fBitmap->config() == SkBitmap::kARGB_4444_Config); \ +SkASSERT(state.fAlphaScale < 256) +#define PREAMBLE(state) unsigned scale = state.fAlphaScale +#define RETURNDST(src) SkAlphaMulQ(SkPixel4444ToPixel32(src), scale) +#define SRC_TO_FILTER(src) src +#define FILTER_TO_DST(c) SkAlphaMulQ(c, scale) +#include "SkBitmapProcState_sample.h" + +// SRC == A8 + +#undef FILTER_PROC +#define FILTER_PROC(x, y, a, b, c, d) Filter_8(x, y, a, b, c, d) + +#define MAKENAME(suffix) SA8_alpha_D32 ## suffix +#define DSTSIZE 32 +#define SRCTYPE uint8_t +#define CHECKSTATE(state) SkASSERT(state.fBitmap->config() == SkBitmap::kA8_Config); \ + SkASSERT(state.fAlphaScale == 256) +#define PREAMBLE(state) const SkPMColor pmColor = state.fPaintPMColor; +#define RETURNDST(src) SkAlphaMulQ(pmColor, SkAlpha255To256(src)) +#define SRC_TO_FILTER(src) src +#define FILTER_TO_DST(c) SkAlphaMulQ(pmColor, SkAlpha255To256(c)) +#include "SkBitmapProcState_sample.h" + +/***************************************************************************** + * + * D16 functions + * + */ + +// SRC == 8888 + +#undef FILTER_PROC +#define FILTER_PROC(x, y, a, b, c, d) Filter_32(x, y, a, b, c, d) + +#define MAKENAME(suffix) S32_D16 ## suffix +#define DSTSIZE 16 +#define SRCTYPE SkPMColor +#define CHECKSTATE(state) SkASSERT(state.fBitmap->config() == SkBitmap::kARGB_8888_Config); \ + SkASSERT(state.fBitmap->isOpaque()) +#define RETURNDST(src) SkPixel32ToPixel16(src) +#define SRC_TO_FILTER(src) src +#define FILTER_TO_DST(c) SkPixel32ToPixel16(c) +#include "SkBitmapProcState_sample.h" + +// SRC == 565 + +#undef FILTER_PROC +#define FILTER_PROC(x, y, a, b, c, d) Filter_565_Expanded(x, y, a, b, c, d) + +#define MAKENAME(suffix) S16_D16 ## suffix +#define DSTSIZE 16 +#define SRCTYPE uint16_t +#define CHECKSTATE(state) SkASSERT(state.fBitmap->config() == SkBitmap::kRGB_565_Config) +#define RETURNDST(src) src +#define SRC_TO_FILTER(src) src +#define FILTER_TO_DST(c) SkCompact_rgb_16((c) >> 5) +#include "SkBitmapProcState_sample.h" + +// SRC == Index8 + +#undef FILTER_PROC +#define FILTER_PROC(x, y, a, b, c, d) Filter_565_Expanded(x, y, a, b, c, d) + +#define MAKENAME(suffix) SI8_D16 ## suffix +#define DSTSIZE 16 +#define SRCTYPE uint8_t +#define CHECKSTATE(state) SkASSERT(state.fBitmap->config() == SkBitmap::kIndex8_Config); \ + SkASSERT(state.fBitmap->isOpaque()) +#define PREAMBLE(state) const uint16_t* SK_RESTRICT table = state.fBitmap->getColorTable()->lock16BitCache() +#define RETURNDST(src) table[src] +#define SRC_TO_FILTER(src) table[src] +#define FILTER_TO_DST(c) SkCompact_rgb_16(c >> 5) +#define POSTAMBLE(state) state.fBitmap->getColorTable()->unlock16BitCache() +#include "SkBitmapProcState_sample.h" + +static bool valid_for_filtering(unsigned dimension) { + // for filtering, width and height must fit in 14bits, since we use steal + // 2 bits from each to store our 4bit subpixel data + return (dimension & ~0x3FFF) == 0; +} + +bool SkBitmapProcState::chooseProcs(const SkMatrix& inv, const SkPaint& paint) { + if (fOrigBitmap.width() == 0 || fOrigBitmap.height() == 0) { + return false; + } + const SkMatrix* m; + + if (inv.getType() <= SkMatrix::kTranslate_Mask || + (SkShader::kClamp_TileMode == fTileModeX && + SkShader::kClamp_TileMode == fTileModeY)) { + m = &inv; + } else { + fUnitInvMatrix = inv; + fUnitInvMatrix.postIDiv(fOrigBitmap.width(), fOrigBitmap.height()); + m = &fUnitInvMatrix; + } + + fBitmap = &fOrigBitmap; +#ifdef SK_SUPPORT_MIPMAP + if (fOrigBitmap.hasMipMap()) { + int shift = fOrigBitmap.extractMipLevel(&fMipBitmap, + SkScalarToFixed(m->getScaleX()), + SkScalarToFixed(m->getSkewY())); + + if (shift > 0) { + if (m != &fUnitInvMatrix) { + fUnitInvMatrix = *m; + m = &fUnitInvMatrix; + } + + SkScalar scale = SkFixedToScalar(SK_Fixed1 >> shift); + fUnitInvMatrix.postScale(scale, scale); + + // now point here instead of fOrigBitmap + fBitmap = &fMipBitmap; + } + } +#endif + + fInvMatrix = m; + fInvProc = m->getMapXYProc(); + fInvType = m->getType(); + if (fInvType <= SkMatrix::kTranslate_Mask && + inv.getType() > SkMatrix::kTranslate_Mask) { + SkASSERT(inv.getType() <= + (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)); + // It is possible that by the calculation of fUnitInvMatrix, we have + // eliminated the scale transformation of the matrix (e.g., if inv^(-1) + // scales fOrigBitmap into an 1X1 rect). We add the scale flag back so + // that we don't make wrong choice in chooseMatrixProc(). + fInvType |= SkMatrix::kScale_Mask; + } + fInvSx = SkScalarToFixed(m->getScaleX()); + fInvSy = SkScalarToFixed(m->getScaleY()); + fInvKy = SkScalarToFixed(m->getSkewY()); + fInvTxPlusHalf = SkScalarToFixed(m->getTranslateX()) + (fInvSx >> 1); + fInvTyPlusHalf = SkScalarToFixed(m->getTranslateY()) + (fInvSy >> 1); + + /* the -1 keeps us symetric with general policy for rounding, which is + (x + 1/2) >> 16. This sends exact halves to the next large pixel + e.g. x==3.5, round(x) == 4. However, our state is working with the + inverse matrix, and so to match the result of "normal" rounding, we + subtract 1 so that we in effect behave the same at the half-way point. + To compare, try drawing a bitmap with y == exact-half using the sprite + blitters and with us. Without the -1, we will draw the colors a whole + pixel shifted up (yikes). + */ + fInvTxPlusHalf -= 1; + fInvTyPlusHalf -= 1; + + fAlphaScale = SkAlpha255To256(paint.getAlpha()); + + // pick-up filtering from the paint, but only if the matrix is + // more complex than identity/translate (i.e. no need to pay the cost + // of filtering if we're not scaled etc.). + // note: we explicitly check inv, since m might be scaled due to unitinv + // trickery, but we don't want to see that for this test + fDoFilter = paint.isFilterBitmap() && + (inv.getType() > SkMatrix::kTranslate_Mask && + valid_for_filtering(fBitmap->width() | fBitmap->height())); + + fMatrixProc = this->chooseMatrixProc(); + if (NULL == fMatrixProc) { + return false; + } + + /////////////////////////////////////////////////////////////////////// + + int index = 0; + if (fAlphaScale < 256) { // note: this distinction is not used for D16 + index |= 1; + } + if (fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) { + index |= 2; + } + if (fDoFilter) { + index |= 4; + } + // bits 3,4,5 encoding the source bitmap format + switch (fBitmap->config()) { + case SkBitmap::kARGB_8888_Config: + index |= 0; + break; + case SkBitmap::kRGB_565_Config: + index |= 8; + break; + case SkBitmap::kIndex8_Config: + index |= 16; + break; + case SkBitmap::kARGB_4444_Config: + index |= 24; + break; + case SkBitmap::kA8_Config: + index |= 32; + fPaintPMColor = SkPreMultiplyColor(paint.getColor()); + default: + return false; + } + + static const SampleProc32 gSample32[] = { + S32_opaque_D32_nofilter_DXDY, + S32_alpha_D32_nofilter_DXDY, + S32_opaque_D32_nofilter_DX, + S32_alpha_D32_nofilter_DX, + S32_opaque_D32_filter_DXDY, + S32_alpha_D32_filter_DXDY, + S32_opaque_D32_filter_DX, + S32_alpha_D32_filter_DX, + + S16_opaque_D32_nofilter_DXDY, + S16_alpha_D32_nofilter_DXDY, + S16_opaque_D32_nofilter_DX, + S16_alpha_D32_nofilter_DX, + S16_opaque_D32_filter_DXDY, + S16_alpha_D32_filter_DXDY, + S16_opaque_D32_filter_DX, + S16_alpha_D32_filter_DX, + + SI8_opaque_D32_nofilter_DXDY, + SI8_alpha_D32_nofilter_DXDY, + SI8_opaque_D32_nofilter_DX, + SI8_alpha_D32_nofilter_DX, + SI8_opaque_D32_filter_DXDY, + SI8_alpha_D32_filter_DXDY, + SI8_opaque_D32_filter_DX, + SI8_alpha_D32_filter_DX, + + S4444_opaque_D32_nofilter_DXDY, + S4444_alpha_D32_nofilter_DXDY, + S4444_opaque_D32_nofilter_DX, + S4444_alpha_D32_nofilter_DX, + S4444_opaque_D32_filter_DXDY, + S4444_alpha_D32_filter_DXDY, + S4444_opaque_D32_filter_DX, + S4444_alpha_D32_filter_DX, + + // A8 treats alpha/opauqe the same (equally efficient) + SA8_alpha_D32_nofilter_DXDY, + SA8_alpha_D32_nofilter_DXDY, + SA8_alpha_D32_nofilter_DX, + SA8_alpha_D32_nofilter_DX, + SA8_alpha_D32_filter_DXDY, + SA8_alpha_D32_filter_DXDY, + SA8_alpha_D32_filter_DX, + SA8_alpha_D32_filter_DX + }; + + static const SampleProc16 gSample16[] = { + S32_D16_nofilter_DXDY, + S32_D16_nofilter_DX, + S32_D16_filter_DXDY, + S32_D16_filter_DX, + + S16_D16_nofilter_DXDY, + S16_D16_nofilter_DX, + S16_D16_filter_DXDY, + S16_D16_filter_DX, + + SI8_D16_nofilter_DXDY, + SI8_D16_nofilter_DX, + SI8_D16_filter_DXDY, + SI8_D16_filter_DX, + + // Don't support 4444 -> 565 + NULL, NULL, NULL, NULL, + // Don't support A8 -> 565 + NULL, NULL, NULL, NULL + }; + + fSampleProc32 = gSample32[index]; + index >>= 1; // shift away any opaque/alpha distinction + fSampleProc16 = gSample16[index]; + + return true; +} + diff --git a/skia/sgl/SkBitmapProcState.h b/skia/sgl/SkBitmapProcState.h new file mode 100644 index 0000000..e48a8c3 --- /dev/null +++ b/skia/sgl/SkBitmapProcState.h @@ -0,0 +1,81 @@ +/* +** Copyright 2007, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkBitmapProcState_DEFINED +#define SkBitmapProcState_DEFINED + +#include "SkBitmap.h" +#include "SkMatrix.h" + +class SkPaint; + +struct SkBitmapProcState { + + typedef void (*MatrixProc)(const SkBitmapProcState&, + uint32_t bitmapXY[], + int count, + int x, int y); + + typedef void (*SampleProc32)(const SkBitmapProcState&, + const uint32_t[], + int count, + SkPMColor colors[]); + + typedef void (*SampleProc16)(const SkBitmapProcState&, + const uint32_t[], + int count, + uint16_t colors[]); + + typedef SkFixed (*FixedTileProc)(SkFixed, int); + typedef int (*IntTileProc)(int, int); + + MatrixProc fMatrixProc; // chooseProcs + SampleProc32 fSampleProc32; // chooseProcs + SampleProc16 fSampleProc16; // chooseProcs + + SkMatrix fUnitInvMatrix; // chooseProcs + FixedTileProc fTileProcX; // chooseProcs + FixedTileProc fTileProcY; // chooseProcs + IntTileProc iTileProcX; // chooseProcs + IntTileProc iTileProcY; // chooseProcs + SkFixed fFilterOneX; + SkFixed fFilterOneY; + + const SkBitmap* fBitmap; // chooseProcs - orig or mip + SkBitmap fOrigBitmap; // CONSTRUCTOR +#ifdef SK_SUPPORT_MIPMAP + SkBitmap fMipBitmap; +#endif + SkPMColor fPaintPMColor; // chooseProcs - A8 config + const SkMatrix* fInvMatrix; // chooseProcs + SkMatrix::MapXYProc fInvProc; // chooseProcs + SkFixed fInvSx, fInvSy; // chooseProcs + SkFixed fInvKy; // chooseProcs + SkFixed fInvTxPlusHalf; // chooseProcs + SkFixed fInvTyPlusHalf; // chooseProcs + uint16_t fAlphaScale; // chooseProcs + uint8_t fInvType; // chooseProcs + uint8_t fTileModeX; // CONSTRUCTOR + uint8_t fTileModeY; // CONSTRUCTOR + SkBool8 fDoFilter; // chooseProcs + + bool chooseProcs(const SkMatrix& inv, const SkPaint&); + +private: + MatrixProc chooseMatrixProc(); +}; + +#endif diff --git a/skia/sgl/SkBitmapProcState_matrix.h b/skia/sgl/SkBitmapProcState_matrix.h new file mode 100644 index 0000000..fe551c2 --- /dev/null +++ b/skia/sgl/SkBitmapProcState_matrix.h @@ -0,0 +1,313 @@ + +#define TRANSLATE_NOFILTER_NAME MAKENAME(_nofilter_translate) +#define SCALE_NOFILTER_NAME MAKENAME(_nofilter_scale) +#define SCALE_FILTER_NAME MAKENAME(_filter_scale) +#define AFFINE_NOFILTER_NAME MAKENAME(_nofilter_affine) +#define AFFINE_FILTER_NAME MAKENAME(_filter_affine) +#define PERSP_NOFILTER_NAME MAKENAME(_nofilter_persp) +#define PERSP_FILTER_NAME MAKENAME(_filter_persp) + +#define PACK_FILTER_X_NAME MAKENAME(_pack_filter_x) +#define PACK_FILTER_Y_NAME MAKENAME(_pack_filter_y) + +#ifndef PREAMBLE + #define PREAMBLE(state) + #define PREAMBLE_PARAM_X + #define PREAMBLE_PARAM_Y + #define PREAMBLE_ARG_X + #define PREAMBLE_ARG_Y +#endif + +#ifndef PREAMBLE_TRANS + #define PREAMBLE_TRANS(state) +#endif + +static void TRANSLATE_NOFILTER_NAME(const SkBitmapProcState& s, + uint32_t xy[], int count, int x, int y) +{ + SkASSERT((s.fInvType & ~SkMatrix::kTranslate_Mask) == 0); + + PREAMBLE_TRANS(s); + + x += SkScalarFloor(s.fInvMatrix->getTranslateX()); + y += SkScalarFloor(s.fInvMatrix->getTranslateY()); + + *xy++ = (uint32_t)TILEY_TRANS(y, (s.fBitmap->height() - 1)); + + int maxX = s.fBitmap->width() - 1; + int i; + uint16_t* xx = (uint16_t*)xy; + for (i = (count >> 2); i > 0; --i) + { + *xx++ = (uint16_t)TILEX_TRANS(x, maxX); x++; + *xx++ = (uint16_t)TILEX_TRANS(x, maxX); x++; + *xx++ = (uint16_t)TILEX_TRANS(x, maxX); x++; + *xx++ = (uint16_t)TILEX_TRANS(x, maxX); x++; + } + for (i = (count & 3); i > 0; --i) + { + *xx++ = (uint16_t)TILEX_TRANS(x, maxX); x++; + } +} + +static void SCALE_NOFILTER_NAME(const SkBitmapProcState& s, + uint32_t xy[], int count, int x, int y) { + SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask | + SkMatrix::kScale_Mask)) == 0); + + PREAMBLE(s); + // we store y, x, x, x, x, x + + // invert x+half, y+half and convert to fixed + SkFixed fx = s.fInvSy * y + s.fInvTyPlusHalf; + *xy++ = TILEY_PROCF(fx, (s.fBitmap->height() - 1)); + // invert X + SkFixed dx = s.fInvSx; + fx = dx * x + s.fInvTxPlusHalf; + unsigned maxX = s.fBitmap->width() - 1; + +#ifdef CHECK_FOR_DECAL + // test if we don't need to apply the tile proc + if ((unsigned)(fx >> 16) <= maxX && + (unsigned)((fx + dx * (count - 1)) >> 16) <= maxX) { + decal_nofilter_scale(xy, fx, dx, count); + } else +#endif + { + int i; +#if 0 + uint16_t* xx = (uint16_t*)xy; + for (i = (count >> 2); i > 0; --i) { + *xx++ = TILEX_PROCF(fx, maxX); fx += dx; + *xx++ = TILEX_PROCF(fx, maxX); fx += dx; + *xx++ = TILEX_PROCF(fx, maxX); fx += dx; + *xx++ = TILEX_PROCF(fx, maxX); fx += dx; + } + for (i = (count & 3); i > 0; --i) { + *xx++ = TILEX_PROCF(fx, maxX); fx += dx; + } +#else + for (i = (count >> 2); i > 0; --i) { + unsigned a, b; + a = TILEX_PROCF(fx, maxX); fx += dx; + b = TILEX_PROCF(fx, maxX); fx += dx; +#ifdef SK_CPU_BENDIAN + *xy++ = (a << 16) | b; +#else + *xy++ = (b << 16) | a; +#endif + a = TILEX_PROCF(fx, maxX); fx += dx; + b = TILEX_PROCF(fx, maxX); fx += dx; +#ifdef SK_CPU_BENDIAN + *xy++ = (a << 16) | b; +#else + *xy++ = (b << 16) | a; +#endif + } + uint16_t* xx = (uint16_t*)xy; + for (i = (count & 3); i > 0; --i) { + *xx++ = TILEX_PROCF(fx, maxX); fx += dx; + } +#endif + } +} + +// note: we could special-case on a matrix which is skewed in X but not Y. +// this would require a more general setup thatn SCALE does, but could use +// SCALE's inner loop that only looks at dx + +static void AFFINE_NOFILTER_NAME(const SkBitmapProcState& s, + uint32_t xy[], int count, int x, int y) { + SkASSERT(s.fInvType & SkMatrix::kAffine_Mask); + SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask | + SkMatrix::kScale_Mask | + SkMatrix::kAffine_Mask)) == 0); + + PREAMBLE(s); + SkPoint srcPt; + s.fInvProc(*s.fInvMatrix, + SkIntToScalar(x) + SK_ScalarHalf, + SkIntToScalar(y) + SK_ScalarHalf, &srcPt); + + SkFixed fx = SkScalarToFixed(srcPt.fX); + SkFixed fy = SkScalarToFixed(srcPt.fY); + SkFixed dx = s.fInvSx; + SkFixed dy = s.fInvKy; + int maxX = s.fBitmap->width() - 1; + int maxY = s.fBitmap->height() - 1; + + for (int i = count; i > 0; --i) { + *xy++ = (TILEY_PROCF(fy, maxY) << 16) | TILEX_PROCF(fx, maxX); + fx += dx; fy += dy; + } +} + +static void PERSP_NOFILTER_NAME(const SkBitmapProcState& s, + uint32_t* SK_RESTRICT xy, + int count, int x, int y) { + SkASSERT(s.fInvType & SkMatrix::kPerspective_Mask); + + PREAMBLE(s); + int maxX = s.fBitmap->width() - 1; + int maxY = s.fBitmap->height() - 1; + + SkPerspIter iter(*s.fInvMatrix, + SkIntToScalar(x) + SK_ScalarHalf, + SkIntToScalar(y) + SK_ScalarHalf, count); + + while ((count = iter.next()) != 0) { + const SkFixed* SK_RESTRICT srcXY = iter.getXY(); + while (--count >= 0) { + *xy++ = (TILEY_PROCF(srcXY[1], maxY) << 16) | + TILEX_PROCF(srcXY[0], maxX); + srcXY += 2; + } + } +} + +////////////////////////////////////////////////////////////////////////////// + +static inline uint32_t PACK_FILTER_Y_NAME(SkFixed f, unsigned max, + SkFixed one PREAMBLE_PARAM_Y) { + unsigned i = TILEY_PROCF(f, max); + i = (i << 4) | TILEY_LOW_BITS(f, max); + return (i << 14) | (TILEY_PROCF((f + one), max)); +} + +static inline uint32_t PACK_FILTER_X_NAME(SkFixed f, unsigned max, + SkFixed one PREAMBLE_PARAM_X) { + unsigned i = TILEX_PROCF(f, max); + i = (i << 4) | TILEX_LOW_BITS(f, max); + return (i << 14) | (TILEX_PROCF((f + one), max)); +} + +static void SCALE_FILTER_NAME(const SkBitmapProcState& s, + uint32_t xy[], int count, int x, int y) { + SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask | + SkMatrix::kScale_Mask)) == 0); + SkASSERT(s.fInvKy == 0); + + PREAMBLE(s); + // compute our two Y values up front + { + unsigned maxY = s.fBitmap->height() - 1; + SkFixed fy = s.fInvSy * y + s.fInvTyPlusHalf - (s.fFilterOneY >> 1); + *xy++ = PACK_FILTER_Y_NAME(fy, maxY, s.fFilterOneY PREAMBLE_ARG_Y); + } + + unsigned maxX = s.fBitmap->width() - 1; + SkFixed one = s.fFilterOneX; + SkFixed dx = s.fInvSx; + SkFixed fx = dx * x + s.fInvTxPlusHalf - (one >> 1); + +#ifdef CHECK_FOR_DECAL + // test if we don't need to apply the tile proc + if (dx > 0 && + (unsigned)(fx >> 16) <= maxX && + (unsigned)((fx + dx * (count - 1)) >> 16) < maxX) { + decal_filter_scale(xy, fx, dx, count); + } else +#endif + { + do { + *xy++ = PACK_FILTER_X_NAME(fx, maxX, one PREAMBLE_ARG_X); + fx += dx; + } while (--count != 0); + } +} + +static void AFFINE_FILTER_NAME(const SkBitmapProcState& s, + uint32_t xy[], int count, int x, int y) { + SkASSERT(s.fInvType & SkMatrix::kAffine_Mask); + SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask | + SkMatrix::kScale_Mask | + SkMatrix::kAffine_Mask)) == 0); + + PREAMBLE(s); + SkPoint srcPt; + s.fInvProc(*s.fInvMatrix, + SkIntToScalar(x) + SK_ScalarHalf, + SkIntToScalar(y) + SK_ScalarHalf, &srcPt); + + SkFixed oneX = s.fFilterOneX; + SkFixed oneY = s.fFilterOneY; + SkFixed fx = SkScalarToFixed(srcPt.fX) - (oneX >> 1); + SkFixed fy = SkScalarToFixed(srcPt.fY) - (oneY >> 1); + SkFixed dx = s.fInvSx; + SkFixed dy = s.fInvKy; + unsigned maxX = s.fBitmap->width() - 1; + unsigned maxY = s.fBitmap->height() - 1; + + do { + *xy++ = PACK_FILTER_Y_NAME(fy, maxY, oneY PREAMBLE_ARG_Y); + fy += dy; + *xy++ = PACK_FILTER_X_NAME(fx, maxX, oneX PREAMBLE_ARG_X); + fx += dx; + } while (--count != 0); +} + +static void PERSP_FILTER_NAME(const SkBitmapProcState& s, + uint32_t* SK_RESTRICT xy, int count, + int x, int y) { + SkASSERT(s.fInvType & SkMatrix::kPerspective_Mask); + + PREAMBLE(s); + unsigned maxX = s.fBitmap->width() - 1; + unsigned maxY = s.fBitmap->height() - 1; + SkFixed oneX = s.fFilterOneX; + SkFixed oneY = s.fFilterOneY; + + SkPerspIter iter(*s.fInvMatrix, + SkIntToScalar(x) + SK_ScalarHalf, + SkIntToScalar(y) + SK_ScalarHalf, count); + + while ((count = iter.next()) != 0) { + const SkFixed* SK_RESTRICT srcXY = iter.getXY(); + do { + *xy++ = PACK_FILTER_Y_NAME(srcXY[1] - (oneY >> 1), maxY, + oneY PREAMBLE_ARG_Y); + *xy++ = PACK_FILTER_X_NAME(srcXY[0] - (oneX >> 1), maxX, + oneX PREAMBLE_ARG_X); + srcXY += 2; + } while (--count != 0); + } +} + +static SkBitmapProcState::MatrixProc MAKENAME(_Procs)[] = { + TRANSLATE_NOFILTER_NAME, + TRANSLATE_NOFILTER_NAME, // No need to do filtering if the matrix is no + // more complex than identity/translate. + SCALE_NOFILTER_NAME, + SCALE_FILTER_NAME, + AFFINE_NOFILTER_NAME, + AFFINE_FILTER_NAME, + PERSP_NOFILTER_NAME, + PERSP_FILTER_NAME +}; + +#undef MAKENAME +#undef TILEX_PROCF +#undef TILEY_PROCF +#ifdef CHECK_FOR_DECAL + #undef CHECK_FOR_DECAL +#endif +#undef TILEX_TRANS +#undef TILEY_TRANS + +#undef TRANSLATE_NOFILTER_NAME +#undef SCALE_NOFILTER_NAME +#undef SCALE_FILTER_NAME +#undef AFFINE_NOFILTER_NAME +#undef AFFINE_FILTER_NAME +#undef PERSP_NOFILTER_NAME +#undef PERSP_FILTER_NAME + +#undef PREAMBLE +#undef PREAMBLE_PARAM_X +#undef PREAMBLE_PARAM_Y +#undef PREAMBLE_ARG_X +#undef PREAMBLE_ARG_Y +#undef PREAMBLE_TRANS + +#undef TILEX_LOW_BITS +#undef TILEY_LOW_BITS diff --git a/skia/sgl/SkBitmapProcState_matrixProcs.cpp b/skia/sgl/SkBitmapProcState_matrixProcs.cpp new file mode 100644 index 0000000..369f9ff --- /dev/null +++ b/skia/sgl/SkBitmapProcState_matrixProcs.cpp @@ -0,0 +1,248 @@ +#include "SkBitmapProcState.h" +#include "SkPerspIter.h" +#include "SkShader.h" + +void decal_nofilter_scale(uint32_t dst[], SkFixed fx, SkFixed dx, int count); +void decal_filter_scale(uint32_t dst[], SkFixed fx, SkFixed dx, int count); + +#ifdef SK_CPU_BENDIAN + #define PACK_TWO_SHORTS(pri, sec) ((pri) << 16 | (sec)) +#else + #define PACK_TWO_SHORTS(pri, sec) ((pri) | ((sec) << 16)) +#endif + +#ifdef SK_DEBUG + static uint32_t pack_two_shorts(U16CPU pri, U16CPU sec) + { + SkASSERT((uint16_t)pri == pri); + SkASSERT((uint16_t)sec == sec); + return PACK_TWO_SHORTS(pri, sec); + } +#else + #define pack_two_shorts(pri, sec) PACK_TWO_SHORTS(pri, sec) +#endif + +#define MAKENAME(suffix) ClampX_ClampY ## suffix +#define TILEX_PROCF(fx, max) SkClampMax((fx) >> 16, max) +#define TILEY_PROCF(fy, max) SkClampMax((fy) >> 16, max) +#define TILEX_LOW_BITS(fx, max) (((fx) >> 12) & 0xF) +#define TILEY_LOW_BITS(fy, max) (((fy) >> 12) & 0xF) +#define CHECK_FOR_DECAL +#define TILEX_TRANS(x, max) SkClampMax(x, max) +#define TILEY_TRANS(y, max) SkClampMax(y, max) +#include "SkBitmapProcState_matrix.h" + +#define MAKENAME(suffix) RepeatX_RepeatY ## suffix +#define TILEX_PROCF(fx, max) (((fx) & 0xFFFF) * ((max) + 1) >> 16) +#define TILEY_PROCF(fy, max) (((fy) & 0xFFFF) * ((max) + 1) >> 16) +#define TILEX_LOW_BITS(fx, max) ((((fx) & 0xFFFF) * ((max) + 1) >> 12) & 0xF) +#define TILEY_LOW_BITS(fy, max) ((((fy) & 0xFFFF) * ((max) + 1) >> 12) & 0xF) +#define REAL_MOD(val, modulus) (((val)%(modulus)) + (modulus)*( (val)<0 )) +#define TILEX_TRANS(x, max) (REAL_MOD((x), ((max) + 1))) +#define TILEY_TRANS(y, max) (REAL_MOD((y), ((max) + 1))) +#include "SkBitmapProcState_matrix.h" + +#define MAKENAME(suffix) GeneralXY ## suffix +#define PREAMBLE(state) SkBitmapProcState::FixedTileProc tileProcX = (state).fTileProcX; \ + SkBitmapProcState::FixedTileProc tileProcY = (state).fTileProcY +#define PREAMBLE_PARAM_X , SkBitmapProcState::FixedTileProc tileProcX +#define PREAMBLE_PARAM_Y , SkBitmapProcState::FixedTileProc tileProcY +#define PREAMBLE_ARG_X , tileProcX +#define PREAMBLE_ARG_Y , tileProcY +#define TILEX_PROCF(fx, max) (tileProcX(fx, max) >> 16) +#define TILEY_PROCF(fy, max) (tileProcY(fy, max) >> 16) +#define TILEX_LOW_BITS(fx, max) ((tileProcX(fx, max) >> 14) & 0x3) +#define TILEY_LOW_BITS(fy, max) ((tileProcY(fy, max) >> 14) & 0x3) +#define PREAMBLE_TRANS(state) SkBitmapProcState::IntTileProc tileProcX = (state).iTileProcX; \ + SkBitmapProcState::IntTileProc tileProcY = (state).iTileProcY +#define TILEX_TRANS(x, max) tileProcX(x, max) +#define TILEY_TRANS(y, max) tileProcY(y, max) +#include "SkBitmapProcState_matrix.h" + +static inline SkFixed fixed_clamp(SkFixed x, int max) +{ +#ifdef SK_CPU_HAS_CONDITIONAL_INSTR + if (x >> 16) + x = 0xFFFF; + if (x < 0) + x = 0; +#else + if (x >> 16) + { + if (x < 0) + x = 0; + else + x = 0xFFFF; + } +#endif + return x * (max + 1); +} + +static inline SkFixed fixed_repeat(SkFixed x, int max) +{ + return (x & 0xFFFF) * (max + 1); +} + +static inline SkFixed fixed_mirror(SkFixed x, int max) +{ + SkFixed s = x << 15 >> 31; + // s is FFFFFFFF if we're on an odd interval, or 0 if an even interval + x = ((x ^ s) & 0xFFFF) * (max + 1); + return s ? (x ^ 0xFFFF) : x; +} + +static SkBitmapProcState::FixedTileProc choose_tile_proc(unsigned m) +{ + if (SkShader::kClamp_TileMode == m) + return fixed_clamp; + if (SkShader::kRepeat_TileMode == m) + return fixed_repeat; + SkASSERT(SkShader::kMirror_TileMode == m); + return fixed_mirror; +} + +static inline int int_clamp(int x, int max) +{ + SkASSERT(max >= 0); + + return SkClampMax(x, max); +} + +static inline int int_repeat(int x, int max) +{ + SkASSERT(max >= 0); + + return x % (max + 1); +} + +static inline int int_mirror(int x, int max) +{ + SkASSERT(max >= 0); + + int dx = x % (max + 1); + if (dx < 0) + dx = -dx - 1; + + return (x / (max + 1) % 2) ? max - dx : dx; +} + +static SkBitmapProcState::IntTileProc choose_int_tile_proc(unsigned m) +{ + if (SkShader::kClamp_TileMode == m) + return int_clamp; + if (SkShader::kRepeat_TileMode == m) + return int_repeat; + SkASSERT(SkShader::kMirror_TileMode == m); + return int_mirror; +} + +SkBitmapProcState::MatrixProc SkBitmapProcState::chooseMatrixProc() +{ + int index = 0; + if (fDoFilter) + index = 1; + if (fInvType & SkMatrix::kPerspective_Mask) + index |= 6; + else if (fInvType & SkMatrix::kAffine_Mask) + index |= 4; + else if (fInvType & SkMatrix::kScale_Mask) + index |= 2; + + if (SkShader::kClamp_TileMode == fTileModeX && + SkShader::kClamp_TileMode == fTileModeY) + { + // clamp gets special version of filterOne + fFilterOneX = SK_Fixed1; + fFilterOneY = SK_Fixed1; + return ClampX_ClampY_Procs[index]; + } + + // all remaining procs use this form for filterOne + fFilterOneX = SK_Fixed1 / fBitmap->width(); + fFilterOneY = SK_Fixed1 / fBitmap->height(); + + if (SkShader::kRepeat_TileMode == fTileModeX && + SkShader::kRepeat_TileMode == fTileModeY) + { + return RepeatX_RepeatY_Procs[index]; + } + + // only general needs these procs + fTileProcX = choose_tile_proc(fTileModeX); + fTileProcY = choose_tile_proc(fTileModeY); + iTileProcX = choose_int_tile_proc(fTileModeX); + iTileProcY = choose_int_tile_proc(fTileModeY); + return GeneralXY_Procs[index]; +} + +////////////////////////////////////////////////////////////////////////////// + +void decal_nofilter_scale(uint32_t dst[], SkFixed fx, SkFixed dx, int count) +{ + int i; + + for (i = (count >> 2); i > 0; --i) + { + *dst++ = pack_two_shorts(fx >> 16, (fx + dx) >> 16); + fx += dx+dx; + *dst++ = pack_two_shorts(fx >> 16, (fx + dx) >> 16); + fx += dx+dx; + } + uint16_t* xx = (uint16_t*)dst; + + for (i = (count & 3); i > 0; --i) + { + *xx++ = SkToU16(fx >> 16); fx += dx; + } +} + +void decal_filter_scale(uint32_t dst[], SkFixed fx, SkFixed dx, int count) +{ + if (count & 1) + { + SkASSERT((fx >> (16 + 14)) == 0); + *dst++ = (fx >> 12 << 14) | ((fx >> 16) + 1); + fx += dx; + } + while ((count -= 2) >= 0) + { + SkASSERT((fx >> (16 + 14)) == 0); + *dst++ = (fx >> 12 << 14) | ((fx >> 16) + 1); + fx += dx; + + *dst++ = (fx >> 12 << 14) | ((fx >> 16) + 1); + fx += dx; + } +} + +/////////////////////////////////// + +void repeat_nofilter_identity(uint32_t dst[], int x, int width, int count) +{ + if (x >= width) + x %= width; + + int i; + uint16_t* xx = (uint16_t*)dst; + + // do the first partial run + int n = width - x; + if (n > count) + n = count; + + count -= n; + n += x; + for (i = x; i < n; i++) + *xx++ = SkToU16(i); + + // do all the full-width runs + while ((count -= width) >= 0) + for (i = 0; i < width; i++) + *xx++ = SkToU16(i); + + // final cleanup run + count += width; + for (i = 0; i < count; i++) + *xx++ = SkToU16(i); +} + diff --git a/skia/sgl/SkBitmapProcState_sample.h b/skia/sgl/SkBitmapProcState_sample.h new file mode 100644 index 0000000..122ccf8 --- /dev/null +++ b/skia/sgl/SkBitmapProcState_sample.h @@ -0,0 +1,207 @@ + +#if DSTSIZE==32 + #define DSTTYPE SkPMColor +#elif DSTSIZE==16 + #define DSTTYPE uint16_t +#else + #error "need DSTSIZE to be 32 or 16" +#endif + +static void MAKENAME(_nofilter_DXDY)(const SkBitmapProcState& s, + const uint32_t* SK_RESTRICT xy, + int count, DSTTYPE* SK_RESTRICT colors) { + SkASSERT(count > 0 && colors != NULL); + SkASSERT(s.fDoFilter == false); + SkDEBUGCODE(CHECKSTATE(s);) + +#ifdef PREAMBLE + PREAMBLE(s); +#endif + const char* SK_RESTRICT srcAddr = (const char*)s.fBitmap->getPixels(); + int i, rb = s.fBitmap->rowBytes(); + + uint32_t XY; + SRCTYPE src; + + for (i = (count >> 1); i > 0; --i) { + XY = *xy++; + SkASSERT((XY >> 16) < (unsigned)s.fBitmap->height() && + (XY & 0xFFFF) < (unsigned)s.fBitmap->width()); + src = ((const SRCTYPE*)(srcAddr + (XY >> 16) * rb))[XY & 0xFFFF]; + *colors++ = RETURNDST(src); + + XY = *xy++; + SkASSERT((XY >> 16) < (unsigned)s.fBitmap->height() && + (XY & 0xFFFF) < (unsigned)s.fBitmap->width()); + src = ((const SRCTYPE*)(srcAddr + (XY >> 16) * rb))[XY & 0xFFFF]; + *colors++ = RETURNDST(src); + } + if (count & 1) { + XY = *xy++; + SkASSERT((XY >> 16) < (unsigned)s.fBitmap->height() && + (XY & 0xFFFF) < (unsigned)s.fBitmap->width()); + src = ((const SRCTYPE*)(srcAddr + (XY >> 16) * rb))[XY & 0xFFFF]; + *colors++ = RETURNDST(src); + } + +#ifdef POSTAMBLE + POSTAMBLE(s); +#endif +} + +static void MAKENAME(_nofilter_DX)(const SkBitmapProcState& s, + const uint32_t* SK_RESTRICT xy, + int count, DSTTYPE* SK_RESTRICT colors) { + SkASSERT(count > 0 && colors != NULL); + SkASSERT(s.fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)); + SkASSERT(s.fDoFilter == false); + SkDEBUGCODE(CHECKSTATE(s);) + +#ifdef PREAMBLE + PREAMBLE(s); +#endif + const SRCTYPE* SK_RESTRICT srcAddr = (const SRCTYPE*)s.fBitmap->getPixels(); + int i; + + // bump srcAddr to the proper row, since we're told Y never changes + SkASSERT((unsigned)xy[0] < (unsigned)s.fBitmap->height()); + srcAddr = (const SRCTYPE*)((const char*)srcAddr + + xy[0] * s.fBitmap->rowBytes()); + // buffer is y32, x16, x16, x16, x16, x16 + const uint16_t* SK_RESTRICT xx = (const uint16_t*)(xy + 1); + + SRCTYPE src; + + for (i = (count >> 2); i > 0; --i) { + SkASSERT(*xx < (unsigned)s.fBitmap->width()); + src = srcAddr[*xx++]; *colors++ = RETURNDST(src); + + SkASSERT(*xx < (unsigned)s.fBitmap->width()); + src = srcAddr[*xx++]; *colors++ = RETURNDST(src); + + SkASSERT(*xx < (unsigned)s.fBitmap->width()); + src = srcAddr[*xx++]; *colors++ = RETURNDST(src); + + SkASSERT(*xx < (unsigned)s.fBitmap->width()); + src = srcAddr[*xx++]; *colors++ = RETURNDST(src); + } + for (i = (count & 3); i > 0; --i) { + SkASSERT(*xx < (unsigned)s.fBitmap->width()); + src = srcAddr[*xx++]; *colors++ = RETURNDST(src); + } + +#ifdef POSTAMBLE + POSTAMBLE(s); +#endif +} + +/////////////////////////////////////////////////////////////////////////////// + +static void MAKENAME(_filter_DX)(const SkBitmapProcState& s, + const uint32_t* SK_RESTRICT xy, + int count, DSTTYPE* SK_RESTRICT colors) { + SkASSERT(count > 0 && colors != NULL); + SkASSERT(s.fDoFilter); + SkDEBUGCODE(CHECKSTATE(s);) + +#ifdef PREAMBLE + PREAMBLE(s); +#endif + const char* SK_RESTRICT srcAddr = (const char*)s.fBitmap->getPixels(); + unsigned rb = s.fBitmap->rowBytes(); + unsigned subY; + const SRCTYPE* SK_RESTRICT row0; + const SRCTYPE* SK_RESTRICT row1; + + // setup row ptrs and update proc_table + { + uint32_t XY = *xy++; + unsigned y0 = XY >> 14; + row0 = (const SRCTYPE*)(srcAddr + (y0 >> 4) * rb); + row1 = (const SRCTYPE*)(srcAddr + (XY & 0x3FFF) * rb); + subY = y0 & 0xF; + } + + do { + uint32_t XX = *xy++; // x0:14 | 4 | x1:14 + unsigned x0 = XX >> 14; + unsigned x1 = XX & 0x3FFF; + unsigned subX = x0 & 0xF; + x0 >>= 4; + + uint32_t c = FILTER_PROC(subX, subY, + SRC_TO_FILTER(row0[x0]), + SRC_TO_FILTER(row0[x1]), + SRC_TO_FILTER(row1[x0]), + SRC_TO_FILTER(row1[x1])); + *colors++ = FILTER_TO_DST(c); + + } while (--count != 0); + +#ifdef POSTAMBLE + POSTAMBLE(s); +#endif +} +static void MAKENAME(_filter_DXDY)(const SkBitmapProcState& s, + const uint32_t* SK_RESTRICT xy, + int count, DSTTYPE* SK_RESTRICT colors) { + SkASSERT(count > 0 && colors != NULL); + SkASSERT(s.fDoFilter); + SkDEBUGCODE(CHECKSTATE(s);) + +#ifdef PREAMBLE + PREAMBLE(s); +#endif + const char* SK_RESTRICT srcAddr = (const char*)s.fBitmap->getPixels(); + int rb = s.fBitmap->rowBytes(); + + do { + uint32_t data = *xy++; + unsigned y0 = data >> 14; + unsigned y1 = data & 0x3FFF; + unsigned subY = y0 & 0xF; + y0 >>= 4; + + data = *xy++; + unsigned x0 = data >> 14; + unsigned x1 = data & 0x3FFF; + unsigned subX = x0 & 0xF; + x0 >>= 4; + + const SRCTYPE* SK_RESTRICT row0 = (const SRCTYPE*)(srcAddr + y0 * rb); + const SRCTYPE* SK_RESTRICT row1 = (const SRCTYPE*)(srcAddr + y1 * rb); + + uint32_t c = FILTER_PROC(subX, subY, + SRC_TO_FILTER(row0[x0]), + SRC_TO_FILTER(row0[x1]), + SRC_TO_FILTER(row1[x0]), + SRC_TO_FILTER(row1[x1])); + *colors++ = FILTER_TO_DST(c); + } while (--count != 0); + +#ifdef POSTAMBLE + POSTAMBLE(s); +#endif +} + +#undef MAKENAME +#undef DSTSIZE +#undef DSTTYPE +#undef SRCTYPE +#undef CHECKSTATE +#undef RETURNDST +#undef SRC_TO_FILTER +#undef FILTER_TO_DST + +#ifdef PREAMBLE + #undef PREAMBLE +#endif +#ifdef POSTAMBLE + #undef POSTAMBLE +#endif + +#undef FILTER_PROC_TYPE +#undef GET_FILTER_TABLE +#undef GET_FILTER_ROW +#undef GET_FILTER_ROW_PROC +#undef GET_FILTER_PROC diff --git a/skia/sgl/SkBitmapSampler.cpp b/skia/sgl/SkBitmapSampler.cpp new file mode 100644 index 0000000..924aeaa --- /dev/null +++ b/skia/sgl/SkBitmapSampler.cpp @@ -0,0 +1,423 @@ +/* libs/graphics/sgl/SkBitmapSampler.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkBitmapSampler.h" + +static SkTileModeProc get_tilemode_proc(SkShader::TileMode mode) +{ + switch (mode) { + case SkShader::kClamp_TileMode: + return do_clamp; + case SkShader::kRepeat_TileMode: + return do_repeat_mod; + case SkShader::kMirror_TileMode: + return do_mirror_mod; + default: + SkASSERT(!"unknown mode"); + return NULL; + } +} + +SkBitmapSampler::SkBitmapSampler(const SkBitmap& bm, bool filter, + SkShader::TileMode tmx, SkShader::TileMode tmy) + : fBitmap(bm), fFilterBitmap(filter), fTileModeX(tmx), fTileModeY(tmy) +{ + SkASSERT(bm.width() > 0 && bm.height() > 0); + + fMaxX = SkToU16(bm.width() - 1); + fMaxY = SkToU16(bm.height() - 1); + + fTileProcX = get_tilemode_proc(tmx); + fTileProcY = get_tilemode_proc(tmy); +} + +void SkBitmapSampler::setPaint(const SkPaint& paint) +{ +} + +class SkNullBitmapSampler : public SkBitmapSampler { +public: + SkNullBitmapSampler(const SkBitmap& bm, bool filter, + SkShader::TileMode tmx, SkShader::TileMode tmy) + : SkBitmapSampler(bm, filter, tmx, tmy) {} + + virtual SkPMColor sample(SkFixed x, SkFixed y) const { return 0; } +}; + +///////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// + +#define BITMAP_CLASSNAME_PREFIX(name) ARGB32##name +#define BITMAP_PIXEL_TO_PMCOLOR(bitmap, x, y) *bitmap.getAddr32(x, y) +#include "SkBitmapSamplerTemplate.h" + +#include "SkColorPriv.h" + +#define BITMAP_CLASSNAME_PREFIX(name) RGB16##name +#define BITMAP_PIXEL_TO_PMCOLOR(bitmap, x, y) SkPixel16ToPixel32(*bitmap.getAddr16(x, y)) +#include "SkBitmapSamplerTemplate.h" + +#define BITMAP_CLASSNAME_PREFIX(name) Index8##name +#define BITMAP_PIXEL_TO_PMCOLOR(bitmap, x, y) bitmap.getIndex8Color(x, y) +#include "SkBitmapSamplerTemplate.h" + +///////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// +///////////////// The Bilinear versions + +#include "SkFilterProc.h" + +class ARGB32_Bilinear_Sampler : public SkBitmapSampler { +public: + ARGB32_Bilinear_Sampler(const SkBitmap& bm, SkShader::TileMode tmx, SkShader::TileMode tmy) + : SkBitmapSampler(bm, true, tmx, tmy) + { + fPtrProcTable = SkGetBilinearFilterPtrProcTable(); + } + + virtual SkPMColor sample(SkFixed x, SkFixed y) const + { + const uint32_t *p00, *p01, *p10, *p11; + + // turn pixel centers into the top-left of our filter-box + x -= SK_FixedHalf; + y -= SK_FixedHalf; + + // compute our pointers + { + const SkBitmap* bitmap = &fBitmap; + int ix = x >> 16; + int iy = y >> 16; + + int maxX = fMaxX; + SkTileModeProc procX = fTileProcX; + int maxY = fMaxY; + SkTileModeProc procY = fTileProcY; + + int tmpx = procX(ix, maxX); + int tmpy = procY(iy, maxY); + p00 = bitmap->getAddr32(tmpx, tmpy); + + int tmpx1 = procX(ix + 1, maxX); + p01 = bitmap->getAddr32(tmpx1, tmpy); + + int tmpy1 = procY(iy + 1, maxY); + p10 = bitmap->getAddr32(tmpx, tmpy1); + + p11 = bitmap->getAddr32(tmpx1, tmpy1); + } + + SkFilterPtrProc proc = SkGetBilinearFilterPtrProc(fPtrProcTable, x, y); + return proc(p00, p01, p10, p11); + } + +private: + const SkFilterPtrProc* fPtrProcTable; +}; + +class RGB16_Bilinear_Sampler : public SkBitmapSampler { +public: + RGB16_Bilinear_Sampler(const SkBitmap& bm, SkShader::TileMode tmx, SkShader::TileMode tmy) + : SkBitmapSampler(bm, true, tmx, tmy) + { + fProcTable = SkGetBilinearFilterProcTable(); + } + + virtual SkPMColor sample(SkFixed x, SkFixed y) const + { + const uint16_t *p00, *p01, *p10, *p11; + + // turn pixel centers into the top-left of our filter-box + x -= SK_FixedHalf; + y -= SK_FixedHalf; + + // compute our pointers + { + const SkBitmap* bitmap = &fBitmap; + int ix = x >> 16; + int iy = y >> 16; + + int maxX = fMaxX; + SkTileModeProc procX = fTileProcX; + int maxY = fMaxY; + SkTileModeProc procY = fTileProcY; + + int tmpx = procX(ix, maxX); + int tmpy = procY(iy, maxY); + p00 = bitmap->getAddr16(tmpx, tmpy); + + int tmpx1 = procX(ix + 1, maxX); + p01 = bitmap->getAddr16(tmpx1, tmpy); + + int tmpy1 = procY(iy + 1, maxY); + p10 = bitmap->getAddr16(tmpx, tmpy1); + + p11 = bitmap->getAddr16(tmpx1, tmpy1); + } + + SkFilterProc proc = SkGetBilinearFilterProc(fProcTable, x, y); + uint32_t c = proc(SkExpand_rgb_16(*p00), SkExpand_rgb_16(*p01), + SkExpand_rgb_16(*p10), SkExpand_rgb_16(*p11)); + + return SkPixel16ToPixel32((uint16_t)SkCompact_rgb_16(c)); + } + +private: + const SkFilterProc* fProcTable; +}; + +// If we had a init/term method on sampler, we could avoid the per-pixel +// call to lockColors/unlockColors + +class Index8_Bilinear_Sampler : public SkBitmapSampler { +public: + Index8_Bilinear_Sampler(const SkBitmap& bm, SkShader::TileMode tmx, SkShader::TileMode tmy) + : SkBitmapSampler(bm, true, tmx, tmy) + { + fPtrProcTable = SkGetBilinearFilterPtrProcTable(); + } + + virtual SkPMColor sample(SkFixed x, SkFixed y) const + { + const SkBitmap* bitmap = &fBitmap; + + const uint8_t *p00, *p01, *p10, *p11; + + // turn pixel centers into the top-left of our filter-box + x -= SK_FixedHalf; + y -= SK_FixedHalf; + + // compute our pointers + { + int ix = x >> 16; + int iy = y >> 16; + + int maxX = fMaxX; + SkTileModeProc procX = fTileProcX; + int maxY = fMaxY; + SkTileModeProc procY = fTileProcY; + + int tmpx = procX(ix, maxX); + int tmpy = procY(iy, maxY); + p00 = bitmap->getAddr8(tmpx, tmpy); + + int tmpx1 = procX(ix + 1, maxX); + p01 = bitmap->getAddr8(tmpx1, tmpy); + + int tmpy1 = procY(iy + 1, maxY); + p10 = bitmap->getAddr8(tmpx, tmpy1); + + p11 = bitmap->getAddr8(tmpx1, tmpy1); + } + + const SkPMColor* colors = bitmap->getColorTable()->lockColors(); + + SkFilterPtrProc proc = SkGetBilinearFilterPtrProc(fPtrProcTable, x, y); + uint32_t c = proc(&colors[*p00], &colors[*p01], &colors[*p10], &colors[*p11]); + + bitmap->getColorTable()->unlockColors(false); + + return c; + } + +private: + const SkFilterPtrProc* fPtrProcTable; +}; + +class A8_Bilinear_Sampler : public SkBitmapSampler { +public: + A8_Bilinear_Sampler(const SkBitmap& bm, SkShader::TileMode tmx, SkShader::TileMode tmy) + : SkBitmapSampler(bm, true, tmx, tmy) + { + fProcTable = SkGetBilinearFilterProcTable(); + } + + virtual void setPaint(const SkPaint& paint) + { + fColor = SkPreMultiplyColor(paint.getColor()); + } + + virtual SkPMColor sample(SkFixed x, SkFixed y) const + { + const uint8_t *p00, *p01, *p10, *p11; + + // turn pixel centers into the top-left of our filter-box + x -= SK_FixedHalf; + y -= SK_FixedHalf; + + // compute our pointers + { + const SkBitmap* bitmap = &fBitmap; + int ix = x >> 16; + int iy = y >> 16; + + int maxX = fMaxX; + SkTileModeProc procX = fTileProcX; + int maxY = fMaxY; + SkTileModeProc procY = fTileProcY; + + int tmpx = procX(ix, maxX); + int tmpy = procY(iy, maxY); + p00 = bitmap->getAddr8(tmpx, tmpy); + + int tmpx1 = procX(ix + 1, maxX); + p01 = bitmap->getAddr8(tmpx1, tmpy); + + int tmpy1 = procY(iy + 1, maxY); + p10 = bitmap->getAddr8(tmpx, tmpy1); + + p11 = bitmap->getAddr8(tmpx1, tmpy1); + } + + SkFilterProc proc = SkGetBilinearFilterProc(fProcTable, x, y); + int alpha = proc(*p00, *p01, *p10, *p11); + return SkAlphaMulQ(fColor, SkAlpha255To256(alpha)); + } + +private: + const SkFilterProc* fProcTable; + SkPMColor fColor; +}; + +class A8_NoFilter_Sampler : public SkBitmapSampler { +public: + A8_NoFilter_Sampler(const SkBitmap& bm, SkShader::TileMode tmx, SkShader::TileMode tmy) + : SkBitmapSampler(bm, false, tmx, tmy) + { + } + + virtual void setPaint(const SkPaint& paint) + { + fColor = SkPreMultiplyColor(paint.getColor()); + } + + virtual SkPMColor sample(SkFixed x, SkFixed y) const + { + int ix = SkFixedFloor(x); + int iy = SkFixedFloor(y); + + int alpha = *fBitmap.getAddr8(fTileProcX(ix, fMaxX), fTileProcY(iy, fMaxY)); + return SkAlphaMulQ(fColor, SkAlpha255To256(alpha)); + } + +private: + const SkFilterProc* fProcTable; + SkPMColor fColor; +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +SkBitmapSampler* SkBitmapSampler::Create(const SkBitmap& bm, bool doFilter, + SkShader::TileMode tmx, + SkShader::TileMode tmy) +{ + switch (bm.getConfig()) { + case SkBitmap::kARGB_8888_Config: + if (doFilter) + return SkNEW_ARGS(ARGB32_Bilinear_Sampler, (bm, tmx, tmy)); + + if (tmx == tmy) { + switch (tmx) { + case SkShader::kClamp_TileMode: + return SkNEW_ARGS(ARGB32_Point_Clamp_Sampler, (bm)); + case SkShader::kRepeat_TileMode: + if (is_pow2(bm.width()) && is_pow2(bm.height())) + return SkNEW_ARGS(ARGB32_Point_Repeat_Pow2_Sampler, (bm)); + else + return SkNEW_ARGS(ARGB32_Point_Repeat_Mod_Sampler, (bm)); + case SkShader::kMirror_TileMode: + if (is_pow2(bm.width()) && is_pow2(bm.height())) + return SkNEW_ARGS(ARGB32_Point_Mirror_Pow2_Sampler, (bm)); + else + return SkNEW_ARGS(ARGB32_Point_Mirror_Mod_Sampler, (bm)); + default: + SkASSERT(!"unknown mode"); + } + } + else { // tmx != tmy + return SkNEW_ARGS(ARGB32_Point_Sampler, (bm, tmx, tmy)); + } + break; + + case SkBitmap::kRGB_565_Config: + if (doFilter) + return SkNEW_ARGS(RGB16_Bilinear_Sampler, (bm, tmx, tmy)); + + if (tmx == tmy) { + switch (tmx) { + case SkShader::kClamp_TileMode: + return SkNEW_ARGS(RGB16_Point_Clamp_Sampler, (bm)); + case SkShader::kRepeat_TileMode: + if (is_pow2(bm.width()) && is_pow2(bm.height())) + return SkNEW_ARGS(RGB16_Point_Repeat_Pow2_Sampler, (bm)); + else + return SkNEW_ARGS(RGB16_Point_Repeat_Mod_Sampler, (bm)); + case SkShader::kMirror_TileMode: + if (is_pow2(bm.width()) && is_pow2(bm.height())) + return SkNEW_ARGS(RGB16_Point_Mirror_Pow2_Sampler, (bm)); + else + return SkNEW_ARGS(RGB16_Point_Mirror_Mod_Sampler, (bm)); + default: + SkASSERT(!"unknown mode"); + } + } + else { // tmx != tmy + return SkNEW_ARGS(RGB16_Point_Sampler, (bm, tmx, tmy)); + } + break; + + case SkBitmap::kIndex8_Config: + if (doFilter) + return SkNEW_ARGS(Index8_Bilinear_Sampler, (bm, tmx, tmy)); + + if (tmx == tmy) { + switch (tmx) { + case SkShader::kClamp_TileMode: + return SkNEW_ARGS(Index8_Point_Clamp_Sampler, (bm)); + case SkShader::kRepeat_TileMode: + if (is_pow2(bm.width()) && is_pow2(bm.height())) + return SkNEW_ARGS(Index8_Point_Repeat_Pow2_Sampler, (bm)); + else + return SkNEW_ARGS(Index8_Point_Repeat_Mod_Sampler, (bm)); + case SkShader::kMirror_TileMode: + if (is_pow2(bm.width()) && is_pow2(bm.height())) + return SkNEW_ARGS(Index8_Point_Mirror_Pow2_Sampler, (bm)); + else + return SkNEW_ARGS(Index8_Point_Mirror_Mod_Sampler, (bm)); + default: + SkASSERT(!"unknown mode"); + } + } + else { // tmx != tmy + return SkNEW_ARGS(Index8_Point_Sampler, (bm, tmx, tmy)); + } + break; + + case SkBitmap::kA8_Config: + if (doFilter) + return SkNEW_ARGS(A8_Bilinear_Sampler, (bm, tmx, tmy)); + else + return SkNEW_ARGS(A8_NoFilter_Sampler, (bm, tmx, tmy)); + break; + + default: + SkASSERT(!"unknown device"); + } + return SkNEW_ARGS(SkNullBitmapSampler, (bm, doFilter, tmx, tmy)); +} + diff --git a/skia/sgl/SkBitmapSampler.h b/skia/sgl/SkBitmapSampler.h new file mode 100644 index 0000000..b31bb9f --- /dev/null +++ b/skia/sgl/SkBitmapSampler.h @@ -0,0 +1,170 @@ +/* libs/graphics/sgl/SkBitmapSampler.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkBitmapSampler_DEFINED +#define SkBitmapSampler_DEFINED + +#include "SkBitmap.h" +#include "SkPaint.h" +#include "SkShader.h" + +typedef int (*SkTileModeProc)(int value, unsigned max); + +class SkBitmapSampler { +public: + SkBitmapSampler(const SkBitmap&, bool filter, SkShader::TileMode tmx, SkShader::TileMode tmy); + virtual ~SkBitmapSampler() {} + + const SkBitmap& getBitmap() const { return fBitmap; } + bool getFilterBitmap() const { return fFilterBitmap; } + SkShader::TileMode getTileModeX() const { return fTileModeX; } + SkShader::TileMode getTileModeY() const { return fTileModeY; } + + /** Given a pixel center at [x,y], return the color sample + */ + virtual SkPMColor sample(SkFixed x, SkFixed y) const = 0; + + virtual void setPaint(const SkPaint& paint); + + // This is the factory for finding an optimal subclass + static SkBitmapSampler* Create(const SkBitmap&, bool filter, + SkShader::TileMode tmx, SkShader::TileMode tmy); + +protected: + const SkBitmap& fBitmap; + uint16_t fMaxX, fMaxY; + bool fFilterBitmap; + SkShader::TileMode fTileModeX; + SkShader::TileMode fTileModeY; + SkTileModeProc fTileProcX; + SkTileModeProc fTileProcY; + + // illegal + SkBitmapSampler& operator=(const SkBitmapSampler&); +}; + +static inline int fixed_clamp(SkFixed x) +{ +#ifdef SK_CPU_HAS_CONDITIONAL_INSTR + if (x >> 16) + x = 0xFFFF; + if (x < 0) + x = 0; +#else + if (x >> 16) + { + if (x < 0) + x = 0; + else + x = 0xFFFF; + } +#endif + return x; +} + +////////////////////////////////////////////////////////////////////////////////////// + +static inline int fixed_repeat(SkFixed x) +{ + return x & 0xFFFF; +} + +static inline int fixed_mirror(SkFixed x) +{ + SkFixed s = x << 15 >> 31; + // s is FFFFFFFF if we're on an odd interval, or 0 if an even interval + return (x ^ s) & 0xFFFF; +} + +static inline bool is_pow2(int count) +{ + SkASSERT(count > 0); + return (count & (count - 1)) == 0; +} + +static inline int do_clamp(int index, unsigned max) +{ + SkASSERT((int)max >= 0); + +#ifdef SK_CPU_HAS_CONDITIONAL_INSTR + if (index > (int)max) + index = max; + if (index < 0) + index = 0; +#else + if ((unsigned)index > max) + { + if (index < 0) + index = 0; + else + index = max; + } +#endif + return index; +} + +static inline int do_repeat_mod(int index, unsigned max) +{ + SkASSERT((int)max >= 0); + + if ((unsigned)index > max) + { + if (index < 0) + index = max - (~index % (max + 1)); + else + index = index % (max + 1); + } + return index; +} + +static inline int do_repeat_pow2(int index, unsigned max) +{ + SkASSERT((int)max >= 0 && is_pow2(max + 1)); + + return index & max; +} + +static inline int do_mirror_mod(int index, unsigned max) +{ + SkASSERT((int)max >= 0); + + // have to handle negatives so that + // -1 -> 0, -2 -> 1, -3 -> 2, etc. + // so we can't just cal abs + index ^= index >> 31; + + if ((unsigned)index > max) + { + int mod = (max + 1) << 1; + index = index % mod; + if ((unsigned)index > max) + index = mod - index - 1; + } + return index; +} + +static inline int do_mirror_pow2(int index, unsigned max) +{ + SkASSERT((int)max >= 0 && is_pow2(max + 1)); + + int s = (index & (max + 1)) - 1; + s = ~(s >> 31); + // at this stage, s is FFFFFFFF if we're on an odd interval, or 0 if an even interval + return (index ^ s) & max; +} + +#endif diff --git a/skia/sgl/SkBitmapSamplerTemplate.h b/skia/sgl/SkBitmapSamplerTemplate.h new file mode 100644 index 0000000..d9680d6 --- /dev/null +++ b/skia/sgl/SkBitmapSamplerTemplate.h @@ -0,0 +1,116 @@ +/* libs/graphics/sgl/SkBitmapSamplerTemplate.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +/* this guy is pulled in multiple times, with the following symbols defined each time: + + #define BITMAP_CLASSNAME_PREFIX(name) ARGB32##name + #defube BITMAP_PIXEL_TO_PMCOLOR(bitmap, x, y) *bitmap.getAddr32(x, y) +*/ + +class BITMAP_CLASSNAME_PREFIX(_Point_Sampler) : public SkBitmapSampler { +public: + BITMAP_CLASSNAME_PREFIX(_Point_Sampler)(const SkBitmap& bm, SkShader::TileMode tmx, SkShader::TileMode tmy) + : SkBitmapSampler(bm, false, tmx, tmy) + { + } + + virtual SkPMColor sample(SkFixed x, SkFixed y) const + { + x = fTileProcX(SkFixedFloor(x), fMaxX); + y = fTileProcY(SkFixedFloor(y), fMaxY); + return BITMAP_PIXEL_TO_PMCOLOR(fBitmap, x, y); + } +}; + + +class BITMAP_CLASSNAME_PREFIX(_Point_Clamp_Sampler) : public SkBitmapSampler { +public: + BITMAP_CLASSNAME_PREFIX(_Point_Clamp_Sampler)(const SkBitmap& bm) + : SkBitmapSampler(bm, false, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode) + { + } + + virtual SkPMColor sample(SkFixed x, SkFixed y) const + { + x = do_clamp(SkFixedFloor(x), fMaxX); + y = do_clamp(SkFixedFloor(y), fMaxY); + return BITMAP_PIXEL_TO_PMCOLOR(fBitmap, x, y); + } +}; + +class BITMAP_CLASSNAME_PREFIX(_Point_Repeat_Pow2_Sampler) : public SkBitmapSampler { +public: + BITMAP_CLASSNAME_PREFIX(_Point_Repeat_Pow2_Sampler)(const SkBitmap& bm) + : SkBitmapSampler(bm, false, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode) + { + } + + virtual SkPMColor sample(SkFixed x, SkFixed y) const + { + x = do_repeat_pow2(SkFixedFloor(x), fMaxX); + y = do_repeat_pow2(SkFixedFloor(y), fMaxY); + return BITMAP_PIXEL_TO_PMCOLOR(fBitmap, x, y); + } +}; + +class BITMAP_CLASSNAME_PREFIX(_Point_Repeat_Mod_Sampler) : public SkBitmapSampler { +public: + BITMAP_CLASSNAME_PREFIX(_Point_Repeat_Mod_Sampler)(const SkBitmap& bm) + : SkBitmapSampler(bm, false, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode) + { + } + + virtual SkPMColor sample(SkFixed x, SkFixed y) const + { + x = do_repeat_mod(SkFixedFloor(x), fMaxX); + y = do_repeat_mod(SkFixedFloor(y), fMaxY); + return BITMAP_PIXEL_TO_PMCOLOR(fBitmap, x, y); + } +}; + +class BITMAP_CLASSNAME_PREFIX(_Point_Mirror_Pow2_Sampler) : public SkBitmapSampler { +public: + BITMAP_CLASSNAME_PREFIX(_Point_Mirror_Pow2_Sampler)(const SkBitmap& bm) + : SkBitmapSampler(bm, false, SkShader::kMirror_TileMode, SkShader::kMirror_TileMode) + { + } + + virtual SkPMColor sample(SkFixed x, SkFixed y) const + { + x = do_mirror_pow2(SkFixedFloor(x), fMaxX); + y = do_mirror_pow2(SkFixedFloor(y), fMaxY); + return BITMAP_PIXEL_TO_PMCOLOR(fBitmap, x, y); + } +}; + +class BITMAP_CLASSNAME_PREFIX(_Point_Mirror_Mod_Sampler) : public SkBitmapSampler { +public: + BITMAP_CLASSNAME_PREFIX(_Point_Mirror_Mod_Sampler)(const SkBitmap& bm) + : SkBitmapSampler(bm, false, SkShader::kMirror_TileMode, SkShader::kMirror_TileMode) + { + } + + virtual SkPMColor sample(SkFixed x, SkFixed y) const + { + x = do_mirror_mod(SkFixedFloor(x), fMaxX); + y = do_mirror_mod(SkFixedFloor(y), fMaxY); + return BITMAP_PIXEL_TO_PMCOLOR(fBitmap, x, y); + } +}; + +#undef BITMAP_CLASSNAME_PREFIX +#undef BITMAP_PIXEL_TO_PMCOLOR diff --git a/skia/sgl/SkBitmapShader.cpp b/skia/sgl/SkBitmapShader.cpp new file mode 100644 index 0000000..939080d --- /dev/null +++ b/skia/sgl/SkBitmapShader.cpp @@ -0,0 +1,822 @@ +/* Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#if 0 + +#include "SkBitmapShader.h" +#include "SkBitmapSampler.h" + +#ifdef SK_SUPPORT_MIPMAP +static SkFixed find_mip_level(SkFixed dx, SkFixed dy) +{ + dx = SkAbs32(dx); + dy = SkAbs32(dy); + if (dx < dy) + dx = dy; + + if (dx < SK_Fixed1) + return 0; + + int clz = SkCLZ(dx); + SkASSERT(clz >= 1 && clz <= 15); + return SkIntToFixed(15 - clz) + ((unsigned)(dx << (clz + 1)) >> 16); +} +#endif + +SkBitmapShader::SkBitmapShader(const SkBitmap& src, bool doFilter, + TileMode tmx, TileMode tmy) + : +#ifdef SK_SUPPORT_MIPMAP + fMipLevel(0), fMipSrcBitmap(src), +#endif + fOrigSrcBitmap(src) + +{ + fFilterBitmap = doFilter; + fTileModeX = SkToU8(tmx); + fTileModeY = SkToU8(tmy); +} + +SkBitmapShader::SkBitmapShader(SkFlattenableReadBuffer& buffer) : + INHERITED(buffer) +{ + Bitmap src; + buffer.readBitmap(&src); +#ifdef SK_SUPPORT_MIPMAP + fMipLevel = 0; + fMipSrcBitmap = src; +#endif + fOrigSrcBitmap = src; + fFilterBitmap = buffer.readU8(); + fTileModeX = buffer.readU8(); + fTileModeY = buffer.readU8(); +} + +void SkBitmapShader::flatten(SkFlattenableWriteBuffer& buffer) +{ + this->INHERITED::flatten(buffer); + buffer.writeBitmap(&fOrigSrcBitmap); + buffer.write8(fFilterBitmap); + buffer.write8(fTileModeX); + buffer.write8(fTileModeY); +} + +bool SkBitmapShader::setContext(const SkBitmap& device, const SkPaint& paint, const SkMatrix& matrix) +{ + // do this first, so we have a correct inverse matrix + if (!this->INHERITED::setContext(device, paint, matrix)) + return false; + + if (fOrigSrcBitmap.getConfig() == SkBitmap::kNo_Config || + fOrigSrcBitmap.width() == 0 || + fOrigSrcBitmap.height() == 0) + return false; + + SkBitmap& bm = fOrigSrcBitmap; + +#ifdef SK_SUPPORT_MIPMAP + if (fOrigSrcBitmap.countMipLevels()) + { + const SkMatrix& inv = this->getTotalInverse(); + + fMipLevel = SkMin32(find_mip_level( SkScalarToFixed(inv.getScaleX()), + SkScalarToFixed(inv.getSkewY())), + SkIntToFixed(fOrigSrcBitmap.countMipLevels() - 1)); + +// SkDEBUGF(("BitmapShader miplevel=%x\n", fMipLevel)); + + const SkBitmap::MipLevel* mm = fOrigSrcBitmap.getMipLevel(fMipLevel >> 16); + + fMipSrcBitmap.setConfig(fOrigSrcBitmap.getConfig(), + mm->fWidth, + mm->fHeight, + mm->fRowBytes); + fMipSrcBitmap.setPixels(mm->fPixels); + bm = fMipSrcBitmap; + } + else + { + fMipLevel = 0; + fMipSrcBitmap = fOrigSrcBitmap; + } +#endif + + fFlags = 0; + if (paint.getAlpha() == 255 && bm.isOpaque()) + fFlags |= kOpaqueAlpha_Flag; + + return true; +} + +/////////////////////////////////////////////////////////////////////////// + +#include "SkColorPriv.h" +#include "SkBitmapSampler.h" +#include "SkPerspIter.h" + +class Sampler_BitmapShader : public SkBitmapShader { +public: + Sampler_BitmapShader(const SkBitmap& src, bool doFilter, + TileMode tmx, TileMode tmy) + : SkBitmapShader(src, doFilter, tmx, tmy) + { + // make sure to pass our copy of the src bitmap to the sampler, and not the + // original parameter (which might go away). + fSampler = NULL; + } + + virtual ~Sampler_BitmapShader() + { + SkDELETE(fSampler); + } + + virtual bool setContext(const SkBitmap& device, const SkPaint& paint, const SkMatrix& matrix) + { + if (this->INHERITED::setContext(device, paint, matrix)) + { + SkDELETE(fSampler); + fSampler = SkBitmapSampler::Create(this->getSrcBitmap(), this->getFilterBitmap(), + this->getTileModeX(), this->getTileModeY()); + fSampler->setPaint(paint); + return true; + } + return false; + } + + enum { + kMaxPointStorageCount = 32 + }; + + virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count) + { + unsigned scale = SkAlpha255To256(this->getPaintAlpha()); + const SkMatrix& inv = this->getTotalInverse(); + SkMatrix::MapPtProc proc = this->getInverseMapPtProc(); + SkBitmapSampler* sampler = fSampler; + MatrixClass mc = this->getInverseClass(); + + SkPoint srcPt; + + if (mc != kPerspective_MatrixClass) + { + proc(inv, SkIntToScalar(x) + SK_ScalarHalf, + SkIntToScalar(y) + SK_ScalarHalf, &srcPt); + + SkFixed fx = SkScalarToFixed(srcPt.fX); + SkFixed fy = SkScalarToFixed(srcPt.fY); + SkFixed dx, dy; + + if (mc == kLinear_MatrixClass) + { + dx = SkScalarToFixed(inv.getScaleX()); + dy = SkScalarToFixed(inv.getSkewY()); + } + else + (void)inv.fixedStepInX(SkIntToScalar(y), &dx, &dy); + +#if defined(SK_SUPPORT_MIPMAP) + { int level = this->getMipLevel() >> 16; + fx >>= level; + fy >>= level; + dx >>= level; + dy >>= level; + } +#endif + if (scale == 256) + { + for (int i = 0; i < count; i++) + { + dstC[i] = sampler->sample(fx, fy); + fx += dx; + fy += dy; + } + } + else + { + for (int i = 0; i < count; i++) + { + uint32_t c = sampler->sample(fx, fy); + dstC[i] = SkAlphaMulQ(c, scale); + fx += dx; + fy += dy; + } + } + } + else + { + SkPerspIter iter(inv, SkIntToScalar(x) + SK_ScalarHalf, + SkIntToScalar(y) + SK_ScalarHalf, count); + if (scale == 256) + { + while ((count = iter.next()) != 0) + { + const SkFixed* src = iter.getXY(); + for (int i = 0; i < count; i++) + { + *dstC++ = sampler->sample(src[0], src[1]); + src += 2; + } + } + } + else + { + while ((count = iter.next()) != 0) + { + const SkFixed* src = iter.getXY(); + for (int i = 0; i < count; i++) + { + uint32_t c = sampler->sample(src[0] - SK_FixedHalf, src[1] - SK_FixedHalf); + *dstC++ = SkAlphaMulQ(c, scale); + src += 2; + } + } + } + } + } + +protected: + + const SkMatrix& getUnitInverse() const { return fUnitInverse; } + SkMatrix::MapPtProc getUnitInverseProc() const { return fUnitInverseProc; } + + /* takes computed inverse (from setContext) and computes fUnitInverse, + taking srcBitmap width/height into account, so that fUnitInverse + walks 0...1, allowing the tile modes to all operate in a fast 16bit + space (no need for mod). The resulting coords need to be scaled by + width/height to get back into src space (coord * width >> 16). + */ + void computeUnitInverse() + { + const SkBitmap& src = getSrcBitmap(); + fUnitInverse = this->getTotalInverse(); + fUnitInverse.postIDiv(src.width(), src.height()); + fUnitInverseProc = fUnitInverse.getMapPtProc(); + } + +private: + SkBitmapSampler* fSampler; + SkMatrix fUnitInverse; + SkMatrix::MapPtProc fUnitInverseProc; + + typedef SkBitmapShader INHERITED; +}; + +/////////////////////////////////////////////////////////////////////////// + +class HasSpan16_Sampler_BitmapShader : public Sampler_BitmapShader { +public: + HasSpan16_Sampler_BitmapShader(const SkBitmap& src, bool doFilter, + TileMode tmx, TileMode tmy) + : Sampler_BitmapShader(src, doFilter, tmx, tmy) + { + } + + virtual uint32_t getFlags() + { + uint32_t flags = this->INHERITED::getFlags(); + + switch (this->getSrcBitmap().getConfig()) { + case SkBitmap::kRGB_565_Config: + flags |= kHasSpan16_Flag; + break; + case SkBitmap::kIndex8_Config: + case SkBitmap::kARGB_8888_Config: + if (this->getSrcBitmap().isOpaque()) + flags |= kHasSpan16_Flag; + break; + default: + break; + } + return flags; + } + + const SkBitmap& revealSrcBitmap() const { return this->getSrcBitmap(); } + uint8_t revealPaintAlpha() const { return this->getPaintAlpha(); } + const SkMatrix& revealTotalInverse() const { return this->getTotalInverse(); } + +private: + typedef Sampler_BitmapShader INHERITED; +}; + +/////////////////////////////////////////////////////////////////////////// + +static void Index8_RepeatTile_Sprite16(HasSpan16_Sampler_BitmapShader* shader, + int x, int y, uint16_t dstC[], int count) +{ + const SkMatrix& inv = shader->revealTotalInverse(); + const SkBitmap& srcBitmap = shader->revealSrcBitmap(); + int width = srcBitmap.width(); + int height = srcBitmap.height(); + + SkColorTable* ctable = srcBitmap.getColorTable(); + const uint16_t* colors = ctable->lock16BitCache(); + + x += SkScalarRound(inv[SkMatrix::kMTransX]); + y += SkScalarRound(inv[SkMatrix::kMTransY]); + + x = do_repeat_mod(x, width - 1); + y = do_repeat_mod(y, height - 1); + const uint8_t* row = srcBitmap.getAddr8(0, y); + const uint8_t* src = row + x; + + // do the first partial run + int n = width - x; + if (n > count) n = count; + count -= n; + SkASSERT(n > 0); + do { + *dstC++ = colors[*src++]; + } while (--n > 0); + + // do 1 complete run + if (count >= width) + { + uint16_t* baseDstC = dstC; // remember the first complete run start + n = width; + count -= width; + src = row; + do { + *dstC++ = colors[*src++]; + } while (--n > 0); + + // do the rest of the complete runs + while (count >= width) + { + count -= width; + memcpy(dstC, baseDstC, width << 1); + dstC += width; + } + // do final partial run + if (count > 0) + memcpy(dstC, baseDstC, count << 1); + } + else // do final partial + { + if (count > 0) + { + src = row; + do { + *dstC++ = colors[*src++]; + } while (--count > 0); + } + } + + ctable->unlock16BitCache(); +} + +static void Index8_RepeatTile_Sprite32(HasSpan16_Sampler_BitmapShader* shader, + int x, int y, SkPMColor dstC[], int count) +{ + const SkMatrix& inv = shader->revealTotalInverse(); + const SkBitmap& srcBitmap = shader->revealSrcBitmap(); + int width = srcBitmap.width(); + int height = srcBitmap.height(); + + SkColorTable* ctable = srcBitmap.getColorTable(); + const SkPMColor* colors = ctable->lockColors(); + + x += SkScalarRound(inv[SkMatrix::kMTransX]); + y += SkScalarRound(inv[SkMatrix::kMTransY]); + + x = do_repeat_mod(x, width - 1); + y = do_repeat_mod(y, height - 1); + + const uint8_t* row = srcBitmap.getAddr8(0, y); + const uint8_t* src = row + x; + + // do the first partial run + int n = width - x; + if (n > count) n = count; + count -= n; + SkASSERT(n > 0); + do { + *dstC++ = colors[*src++]; + } while (--n > 0); + + // do 1 complete run + if (count >= width) + { + SkPMColor* baseDstC = dstC; // remember the first complete run start + n = width; + count -= width; + src = row; + do { + *dstC++ = colors[*src++]; + } while (--n > 0); + + // do the rest of the complete runs + while (count >= width) + { + count -= width; + memcpy(dstC, baseDstC, width << 2); + dstC += width; + } + // do final partial run + if (count > 0) + memcpy(dstC, baseDstC, count << 2); + } + else // do final partial + { + if (count > 0) + { + src = row; + do { + *dstC++ = colors[*src++]; + } while (--count > 0); + } + } + + ctable->unlockColors(false); +} + +static void RGB16_RepeatTile_Sprite16(HasSpan16_Sampler_BitmapShader* shader, + int x, int y, uint16_t dstC[], int count) +{ + SkASSERT(count > 0); + + const SkMatrix& inv = shader->revealTotalInverse(); + const SkBitmap& srcBitmap = shader->revealSrcBitmap(); + int width = srcBitmap.width(); + int height = srcBitmap.height(); + + SkASSERT(width > 0 && height > 0); + + x += SkScalarRound(inv[SkMatrix::kMTransX]); + y += SkScalarRound(inv[SkMatrix::kMTransY]); + + x = do_repeat_mod(x, width - 1); + y = do_repeat_mod(y, height - 1); + + const uint16_t* row = srcBitmap.getAddr16(0, y); + const uint16_t* src = row + x; + + int n = SkMin32(width - x, count); + + for (;;) + { + SkASSERT(n > 0 && count >= n); + memcpy(dstC, src, n << 1); + count -= n; + if (count == 0) + break; + dstC += n; + src = row; + n = SkMin32(width, count); + } +} + +static void RGB16_RepeatTile_Sprite32(HasSpan16_Sampler_BitmapShader* shader, + int x, int y, SkPMColor dstC[], int count) +{ + SkASSERT(count > 0); + + const SkMatrix& inv = shader->revealTotalInverse(); + const SkBitmap& srcBitmap = shader->revealSrcBitmap(); + int width = srcBitmap.width(); + int height = srcBitmap.height(); + + SkASSERT(width > 0 && height > 0); + + x += SkScalarRound(inv[SkMatrix::kMTransX]); + y += SkScalarRound(inv[SkMatrix::kMTransY]); + + x = do_repeat_mod(x, width - 1); + y = do_repeat_mod(y, height - 1); + + const uint16_t* row = srcBitmap.getAddr16(0, y); + const uint16_t* src = row + x; + + int n = SkMin32(width - x, count); + + // do the first partial run + count -= n; + SkASSERT(n > 0); + do { + *dstC++ = SkPixel16ToPixel32(*src++); + } while (--n > 0); + + // do 1 complete run + if (count >= width) + { + SkPMColor* baseDstC = dstC; // remember the first complete run start + n = width; + count -= width; + src = row; + do { + *dstC++ = SkPixel16ToPixel32(*src++); + } while (--n > 0); + + // do the rest of the complete runs + while (count >= width) + { + count -= width; + memcpy(dstC, baseDstC, width << 2); + dstC += width; + } + // do final partial run + if (count > 0) + memcpy(dstC, baseDstC, count << 2); + } + else // do final partial + { + if (count > 0) + { + src = row; + do { + *dstC++ = SkPixel16ToPixel32(*src++);; + } while (--count > 0); + } + } +} + +static void ARGB32_RepeatTile_Sprite16(HasSpan16_Sampler_BitmapShader* shader, + int x, int y, uint16_t dstC[], int count) +{ + SkASSERT(count > 0); + + const SkMatrix& inv = shader->revealTotalInverse(); + const SkBitmap& srcBitmap = shader->revealSrcBitmap(); + int width = srcBitmap.width(); + int height = srcBitmap.height(); + + SkASSERT(width > 0 && height > 0); + + x += SkScalarRound(inv[SkMatrix::kMTransX]); + y += SkScalarRound(inv[SkMatrix::kMTransY]); + + x = do_repeat_mod(x, width - 1); + y = do_repeat_mod(y, height - 1); + + const SkPMColor* row = srcBitmap.getAddr32(0, y); + const SkPMColor* src = row + x; + + int n = SkMin32(width - x, count); + + // do the first partial run + count -= n; + SkASSERT(n > 0); + do { + *dstC++ = SkPixel32ToPixel16(*src++); + } while (--n > 0); + + // do 1 complete run + if (count >= width) + { + uint16_t* baseDstC = dstC; // remember the first complete run start + n = width; + count -= width; + src = row; + do { + *dstC++ = SkPixel32ToPixel16(*src++); + } while (--n > 0); + + // do the rest of the complete runs + while (count >= width) + { + count -= width; + memcpy(dstC, baseDstC, width << 1); + dstC += width; + } + // do final partial run + if (count > 0) + memcpy(dstC, baseDstC, count << 1); + } + else // do final partial + { + if (count > 0) + { + src = row; + do { + *dstC++ = SkPixel32ToPixel16(*src++);; + } while (--count > 0); + } + } +} + +static void ARGB32_RepeatTile_Sprite32(HasSpan16_Sampler_BitmapShader* shader, + int x, int y, SkPMColor dstC[], int count) +{ + SkASSERT(count > 0); + + const SkMatrix& inv = shader->revealTotalInverse(); + const SkBitmap& srcBitmap = shader->revealSrcBitmap(); + int width = srcBitmap.width(); + int height = srcBitmap.height(); + + SkASSERT(width > 0 && height > 0); + + x += SkScalarRound(inv[SkMatrix::kMTransX]); + y += SkScalarRound(inv[SkMatrix::kMTransY]); + + x = do_repeat_mod(x, width - 1); + y = do_repeat_mod(y, height - 1); + + const SkPMColor* row = srcBitmap.getAddr32(0, y); + const SkPMColor* src = row + x; + + int n = SkMin32(width - x, count); + + for (;;) + { + SkASSERT(n > 0 && count >= n); + memcpy(dstC, src, n << 2); + count -= n; + if (count == 0) + break; + dstC += n; + src = row; + n = SkMin32(width, count); + } +} + +/////////////////////////////////////////////////////////////////////////// + +#define NOFILTER_BITMAP_SHADER_CLASS Index8_NoFilter_RepeatTile_BitmapShader +#define NOFILTER_BITMAP_SHADER_TILEMODE SkShader::kRepeat_TileMode +#define NOFILTER_BITMAP_SHADER_TILEPROC(x, max) (fixed_repeat(x) * (max + 1) >> 16) +#define NOFILTER_BITMAP_SHADER_TYPE uint8_t +#define NOFILTER_BITMAP_SHADER_SAMPLE_X(p, x) colors32[p[x]] +#define NOFILTER_BITMAP_SHADER_SAMPLE_XY(p, x, y, rb) colors32[p[x + y * rb]] +#define NOFILTER_BITMAP_SHADER_PREAMBLE(bitmap, rb) const SkPMColor* colors32 = bitmap.getColorTable()->lockColors() +#define NOFILTER_BITMAP_SHADER_POSTAMBLE(bitmap) bitmap.getColorTable()->unlockColors(false) +#define NOFILTER_BITMAP_SHADER_SAMPLE_X16(p, x) colors16[p[x]] +#define NOFILTER_BITMAP_SHADER_SAMPLE_XY16(p, x, y, rb) colors16[p[x + y * rb]] +#define NOFILTER_BITMAP_SHADER_PREAMBLE16(bitmap, rb) const uint16_t* colors16 = bitmap.getColorTable()->lock16BitCache() +#define NOFILTER_BITMAP_SHADER_POSTAMBLE16(bitmap) bitmap.getColorTable()->unlock16BitCache() +#define NOFILTER_BITMAP_SHADER_USE_UNITINVERSE +#define NOFILTER_BITMAP_SHADER_SPRITEPROC16 Index8_RepeatTile_Sprite16 +#define NOFILTER_BITMAP_SHADER_SPRITEPROC32 Index8_RepeatTile_Sprite32 +#include "SkBitmapShaderTemplate.h" + +#define NOFILTER_BITMAP_SHADER_CLASS U16_NoFilter_RepeatTile_BitmapShader +#define NOFILTER_BITMAP_SHADER_TILEMODE SkShader::kRepeat_TileMode +#define NOFILTER_BITMAP_SHADER_TILEPROC(x, max) (fixed_repeat(x) * (max + 1) >> 16) +#define NOFILTER_BITMAP_SHADER_TYPE uint16_t +#define NOFILTER_BITMAP_SHADER_SAMPLE_X(p, x) SkPixel16ToPixel32(p[x]) +#define NOFILTER_BITMAP_SHADER_SAMPLE_XY(p, x, y, rb) SkPixel16ToPixel32(*(const uint16_t*)((const char*)p + y * rb + (x << 1))) +#define NOFILTER_BITMAP_SHADER_SAMPLE_X16(p, x) p[x] +#define NOFILTER_BITMAP_SHADER_SAMPLE_XY16(p, x, y, rb) *(const uint16_t*)((const char*)p + y * rb + (x << 1)) +#define NOFILTER_BITMAP_SHADER_USE_UNITINVERSE +#define NOFILTER_BITMAP_SHADER_SPRITEPROC16 RGB16_RepeatTile_Sprite16 +#define NOFILTER_BITMAP_SHADER_SPRITEPROC32 RGB16_RepeatTile_Sprite32 +#include "SkBitmapShaderTemplate.h" + +#define NOFILTER_BITMAP_SHADER_CLASS U32_NoFilter_RepeatTile_BitmapShader +#define NOFILTER_BITMAP_SHADER_TILEMODE SkShader::kRepeat_TileMode +#define NOFILTER_BITMAP_SHADER_TILEPROC(x, max) (fixed_repeat(x) * (max + 1) >> 16) +#define NOFILTER_BITMAP_SHADER_TYPE uint32_t +#define NOFILTER_BITMAP_SHADER_SAMPLE_X(p, x) p[x] +#define NOFILTER_BITMAP_SHADER_SAMPLE_XY(p, x, y, rb) *(const uint32_t*)((const char*)p + y * rb + (x << 2)) +#define NOFILTER_BITMAP_SHADER_SAMPLE_X16(p, x) SkPixel32ToPixel16_ToU16(p[x]) +#define NOFILTER_BITMAP_SHADER_SAMPLE_XY16(p, x, y, rb) SkPixel32ToPixel16_ToU16(*(const uint32_t*)((const char*)p + y * rb + (x << 2))) +#define NOFILTER_BITMAP_SHADER_USE_UNITINVERSE +#define NOFILTER_BITMAP_SHADER_SPRITEPROC16 ARGB32_RepeatTile_Sprite16 +#define NOFILTER_BITMAP_SHADER_SPRITEPROC32 ARGB32_RepeatTile_Sprite32 +#include "SkBitmapShaderTemplate.h" + +/////////////////////////////////////////////////////////////////////////////////////////////////////// + +static inline SkPMColor expanded_rgb16_to_8888(uint32_t c, U8CPU alpha) +{ +// GGGG Gggg gggR RRRR rrrr r|BB BBBb bbbb + SkASSERT(alpha <= 255); + +#if 1 + int scale = SkAlpha255To256(alpha); + int r = (c & 0xF800) * scale >> 16; + int g = ((c >> 21) & 0x3F) * scale >> 6; + int b = (c & 0x1F) * scale >> 5; + return SkPackARGB32(alpha, r, g, b); +#else + int scale = SkAlpha255To256(alpha) >> 3; + c &= 0x07E0F81F; + c = c * scale; + int r = (c >> 13) & 0xFF; + int g = (c >> 24) & 0xFF; + int b = (c >> 2) & 0xFF; + return SkPackARGB32(alpha, r, g, b); +#endif +} + +#define BILERP_BITMAP16_SHADER_CLASS U16_Bilerp_BitmapShader +#define BILERP_BITMAP16_SHADER_TYPE uint16_t +#define BILERP_BITMAP16_SHADER_PREAMBLE(bm) +#define BILERP_BITMAP16_SHADER_PIXEL(c) (c) +#define BILERP_BITMAP16_SHADER_POSTAMBLE(bm) +#include "SkBitmapShader16BilerpTemplate.h" + +#define BILERP_BITMAP16_SHADER_CLASS Index8_Bilerp_BitmapShader +#define BILERP_BITMAP16_SHADER_TYPE uint8_t +#define BILERP_BITMAP16_SHADER_PREAMBLE(bm) SkColorTable* ctable = (bm).getColorTable(); const uint16_t* colors16 = ctable->lock16BitCache() +#define BILERP_BITMAP16_SHADER_PIXEL(c) colors16[c] +#define BILERP_BITMAP16_SHADER_POSTAMBLE(bm) ctable->unlock16BitCache() +#include "SkBitmapShader16BilerpTemplate.h" + +#include "ARGB32_Clamp_Bilinear_BitmapShader.h" + +/////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////// + +#include "SkBitmapProcShader.h" + +/////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////// + +#include "SkTemplatesPriv.h" + +SkShader* SkShader::CreateBitmapShader(const SkBitmap& src, + bool doFilter, + TileMode tmx, TileMode tmy, + void* storage, size_t storageSize) +{ +#if 1 + + SkShader* shader; + SK_PLACEMENT_NEW_ARGS(shader, SkBitmapProcShader, storage, + storageSize, (src, doFilter, tmx, tmy)); + return shader; +#else + + if (!doFilter) + { + if (kClamp_TileMode == tmx && kClamp_TileMode == tmy) + { + SK_PLACEMENT_NEW_ARGS(shader, SkBitmapProcShader, storage, + storageSize, (src, doFilter, tmx, tmy)); + } + else if (kRepeat_TileMode == tmx && kRepeat_TileMode == tmy) + { + #if 1 + SK_PLACEMENT_NEW_ARGS(shader, SkBitmapProcShader, storage, + storageSize, (src, doFilter, tmx, tmy)); + #else + switch (src.getConfig()) { + case SkBitmap::kIndex8_Config: + SK_PLACEMENT_NEW_ARGS(shader, Index8_NoFilter_RepeatTile_BitmapShader, storage, storageSize, (src)); + break; + case SkBitmap::kRGB_565_Config: + SK_PLACEMENT_NEW_ARGS(shader, U16_NoFilter_RepeatTile_BitmapShader, storage, storageSize, (src)); + break; + case SkBitmap::kARGB_8888_Config: + SK_PLACEMENT_NEW_ARGS(shader, U32_NoFilter_RepeatTile_BitmapShader, storage, storageSize, (src)); + break; + default: + break; + } + #endif + } + } + else if (kClamp_TileMode == tmx && kClamp_TileMode == tmy) + { +#if 1 + if (SkBitmapProcShader::CanDo(src, tmx, tmy)) + { + SK_PLACEMENT_NEW_ARGS(shader, SkBitmapProcShader, storage, + storageSize, (src, doFilter, tmx, tmy)); + } +#else + switch (src.getConfig()) { + case SkBitmap::kIndex8_Config: + if (src.isOpaque()) + SK_PLACEMENT_NEW_ARGS(shader, Index8_Bilerp_BitmapShader, storage, storageSize, (src)); + break; + case SkBitmap::kRGB_565_Config: + SK_PLACEMENT_NEW_ARGS(shader, U16_Bilerp_BitmapShader, storage, storageSize, (src)); + break; + case SkBitmap::kARGB_8888_Config: + SK_PLACEMENT_NEW_ARGS(shader, ARGB32_Clamp_Bilinear_BitmapShader, storage, storageSize, (src)); + break; + default: + break; + } +#endif + } + + // if shader is null, then none of the special cases could handle the request + // so fall through to our slow-general case + if (shader == NULL) + SK_PLACEMENT_NEW_ARGS(shader, Sampler_BitmapShader, storage, storageSize, + (src, doFilter, tmx, tmy)); + return shader; +#endif +} + +SkShader* SkShader::CreateBitmapShader(const SkBitmap& src, bool doFilter, + TileMode tmx, TileMode tmy) +{ + return SkShader::CreateBitmapShader(src, doFilter, tmx, tmy, NULL, 0); +} + +#endif diff --git a/skia/sgl/SkBitmapShader.h b/skia/sgl/SkBitmapShader.h new file mode 100644 index 0000000..d64274c --- /dev/null +++ b/skia/sgl/SkBitmapShader.h @@ -0,0 +1,73 @@ +/* libs/graphics/sgl/SkBitmapShader.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkBitmapShader_DEFINED +#define SkBitmapShader_DEFINED + +#include "SkShader.h" +#include "SkBitmap.h" +#include "SkPaint.h" + +class SkBitmapShader : public SkShader { +public: + SkBitmapShader( const SkBitmap& src, + bool doFilter, TileMode tx, TileMode ty); + + virtual bool setContext(const SkBitmap&, const SkPaint& paint, const SkMatrix&); + virtual uint32_t getFlags() { return fFlags; } + +protected: + SkBitmapShader(SkFlattenableReadBuffer& ); + virtual void flatten(SkFlattenableWriteBuffer& ); + virtual Factory getFactory() { return CreateProc; } + const SkBitmap& getSrcBitmap() const + { +#ifdef SK_SUPPORT_MIPMAP + return fMipSrcBitmap; +#else + return fOrigSrcBitmap; +#endif + } + bool getFilterBitmap() const { return fFilterBitmap != 0; } + TileMode getTileModeX() const { return (TileMode)fTileModeX; } + TileMode getTileModeY() const { return (TileMode)fTileModeY; } + SkFixed getMipLevel() const + { +#ifdef SK_SUPPORT_MIPMAP + return fMipLevel; +#else + return 0; +#endif + } + +private: + static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) { + return SkNEW_ARGS(SkBitmapShader, (buffer)); } +#ifdef SK_SUPPORT_MIPMAP + SkFixed fMipLevel; + SkBitmap fMipSrcBitmap; // the chosen level (in setContext) +#endif + SkBitmap fOrigSrcBitmap; + uint8_t fFilterBitmap; + uint8_t fTileModeX; + uint8_t fTileModeY; + uint8_t fFlags; + + typedef SkShader INHERITED; +}; + +#endif diff --git a/skia/sgl/SkBitmapShader16BilerpTemplate.h b/skia/sgl/SkBitmapShader16BilerpTemplate.h new file mode 100644 index 0000000..555d587 --- /dev/null +++ b/skia/sgl/SkBitmapShader16BilerpTemplate.h @@ -0,0 +1,253 @@ +/* libs/graphics/sgl/SkBitmapShader16BilerpTemplate.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkFilterProc.h" + +class BILERP_BITMAP16_SHADER_CLASS : public HasSpan16_Sampler_BitmapShader { +public: + BILERP_BITMAP16_SHADER_CLASS(const SkBitmap& src) + : HasSpan16_Sampler_BitmapShader(src, true, + SkShader::kClamp_TileMode, + SkShader::kClamp_TileMode) + { + } + + virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count) + { + SkASSERT(count > 0); + + U8CPU alpha = this->getPaintAlpha(); + + const SkMatrix& inv = this->getTotalInverse(); + const SkBitmap& srcBitmap = this->getSrcBitmap(); + unsigned srcMaxX = srcBitmap.width() - 1; + unsigned srcMaxY = srcBitmap.height() - 1; + unsigned srcRB = srcBitmap.rowBytes(); + + BILERP_BITMAP16_SHADER_PREAMBLE(srcBitmap); + + const SkFilterProc* proc_table = SkGetBilinearFilterProcTable(); + const BILERP_BITMAP16_SHADER_TYPE* srcPixels = (const BILERP_BITMAP16_SHADER_TYPE*)srcBitmap.getPixels(); + + if (this->getInverseClass() == kPerspective_MatrixClass) + { + SkPerspIter iter(inv, SkIntToScalar(x) + SK_ScalarHalf, + SkIntToScalar(y) + SK_ScalarHalf, count); + while ((count = iter.next()) != 0) + { + const SkFixed* srcXY = iter.getXY(); + while (--count >= 0) + { + SkFixed fx = *srcXY++ - SK_FixedHalf; + SkFixed fy = *srcXY++ - SK_FixedHalf; + int ix = fx >> 16; + int iy = fy >> 16; + int x = SkClampMax(ix, srcMaxX); + int y = SkClampMax(iy, srcMaxY); + + const BILERP_BITMAP16_SHADER_TYPE *p00, *p01, *p10, *p11; + + p00 = p01 = ((const BILERP_BITMAP16_SHADER_TYPE*)((const char*)srcPixels + y * srcRB)) + x; + if ((unsigned)ix < srcMaxX) + p01 += 1; + p10 = p00; + p11 = p01; + if ((unsigned)iy < srcMaxY) + { + p10 = (const BILERP_BITMAP16_SHADER_TYPE*)((const char*)p10 + srcRB); + p11 = (const BILERP_BITMAP16_SHADER_TYPE*)((const char*)p11 + srcRB); + } + + SkFilterProc proc = SkGetBilinearFilterProc(proc_table, fx, fy); + uint32_t c = proc(SkExpand_rgb_16(BILERP_BITMAP16_SHADER_PIXEL(*p00)), + SkExpand_rgb_16(BILERP_BITMAP16_SHADER_PIXEL(*p01)), + SkExpand_rgb_16(BILERP_BITMAP16_SHADER_PIXEL(*p10)), + SkExpand_rgb_16(BILERP_BITMAP16_SHADER_PIXEL(*p11))); + + *dstC++ = expanded_rgb16_to_8888(c, alpha); + } + } + } + else // linear case + { + SkFixed fx, fy, dx, dy; + + // now init fx, fy, dx, dy + { + SkPoint srcPt; + this->getInverseMapPtProc()(inv, SkIntToScalar(x) + SK_ScalarHalf, + SkIntToScalar(y) + SK_ScalarHalf, &srcPt); + + fx = SkScalarToFixed(srcPt.fX) - SK_FixedHalf; + fy = SkScalarToFixed(srcPt.fY) - SK_FixedHalf; + + if (this->getInverseClass() == kFixedStepInX_MatrixClass) + (void)inv.fixedStepInX(SkIntToScalar(y), &dx, &dy); + else + { + dx = SkScalarToFixed(inv.getScaleX()); + dy = SkScalarToFixed(inv.getSkewY()); + } + } + + do { + int ix = fx >> 16; + int iy = fy >> 16; + + const BILERP_BITMAP16_SHADER_TYPE *p00, *p01, *p10, *p11; + + p00 = p01 = ((const BILERP_BITMAP16_SHADER_TYPE*)((const char*)srcPixels + + SkClampMax(iy, srcMaxY) * srcRB)) + + SkClampMax(ix, srcMaxX); + if ((unsigned)ix < srcMaxX) + p01 += 1; + p10 = p00; + p11 = p01; + if ((unsigned)iy < srcMaxY) + { + p10 = (const BILERP_BITMAP16_SHADER_TYPE*)((const char*)p10 + srcRB); + p11 = (const BILERP_BITMAP16_SHADER_TYPE*)((const char*)p11 + srcRB); + } + + SkFilterProc proc = SkGetBilinearFilterProc(proc_table, fx, fy); + uint32_t c = proc(SkExpand_rgb_16(BILERP_BITMAP16_SHADER_PIXEL(*p00)), + SkExpand_rgb_16(BILERP_BITMAP16_SHADER_PIXEL(*p01)), + SkExpand_rgb_16(BILERP_BITMAP16_SHADER_PIXEL(*p10)), + SkExpand_rgb_16(BILERP_BITMAP16_SHADER_PIXEL(*p11))); + *dstC++ = expanded_rgb16_to_8888(c, alpha); + + fx += dx; + fy += dy; + } while (--count != 0); + } + BILERP_BITMAP16_SHADER_POSTAMBLE(srcBitmap); + } + + virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count) + { + SkASSERT(count > 0); + + const SkMatrix& inv = this->getTotalInverse(); + const SkBitmap& srcBitmap = this->getSrcBitmap(); + unsigned srcMaxX = srcBitmap.width() - 1; + unsigned srcMaxY = srcBitmap.height() - 1; + unsigned srcRB = srcBitmap.rowBytes(); + + BILERP_BITMAP16_SHADER_PREAMBLE(srcBitmap); + + const SkFilterProc* proc_table = SkGetBilinearFilterProcTable(); + const BILERP_BITMAP16_SHADER_TYPE* srcPixels = (const BILERP_BITMAP16_SHADER_TYPE*)srcBitmap.getPixels(); + + if (this->getInverseClass() == kPerspective_MatrixClass) + { + SkPerspIter iter(inv, SkIntToScalar(x) + SK_ScalarHalf, + SkIntToScalar(y) + SK_ScalarHalf, count); + while ((count = iter.next()) != 0) + { + const SkFixed* srcXY = iter.getXY(); + while (--count >= 0) + { + SkFixed fx = *srcXY++ - SK_FixedHalf; + SkFixed fy = *srcXY++ - SK_FixedHalf; + int ix = fx >> 16; + int iy = fy >> 16; + + const BILERP_BITMAP16_SHADER_TYPE *p00, *p01, *p10, *p11; + + p00 = p01 = ((const BILERP_BITMAP16_SHADER_TYPE*)((const char*)srcPixels + + SkClampMax(iy, srcMaxY) * srcRB)) + + SkClampMax(ix, srcMaxX); + if ((unsigned)ix < srcMaxX) + p01 += 1; + p10 = p00; + p11 = p01; + if ((unsigned)iy < srcMaxY) + { + p10 = (const BILERP_BITMAP16_SHADER_TYPE*)((const char*)p10 + srcRB); + p11 = (const BILERP_BITMAP16_SHADER_TYPE*)((const char*)p11 + srcRB); + } + + SkFilterProc proc = SkGetBilinearFilterProc(proc_table, fx, fy); + uint32_t c = proc(SkExpand_rgb_16(BILERP_BITMAP16_SHADER_PIXEL(*p00)), + SkExpand_rgb_16(BILERP_BITMAP16_SHADER_PIXEL(*p01)), + SkExpand_rgb_16(BILERP_BITMAP16_SHADER_PIXEL(*p10)), + SkExpand_rgb_16(BILERP_BITMAP16_SHADER_PIXEL(*p11))); + *dstC++ = SkCompact_rgb_16(c); + } + } + } + else // linear case + { + SkFixed fx, fy, dx, dy; + + // now init fx, fy, dx, dy + { + SkPoint srcPt; + this->getInverseMapPtProc()(inv, SkIntToScalar(x) + SK_ScalarHalf, + SkIntToScalar(y) + SK_ScalarHalf, &srcPt); + + fx = SkScalarToFixed(srcPt.fX) - SK_FixedHalf; + fy = SkScalarToFixed(srcPt.fY) - SK_FixedHalf; + + if (this->getInverseClass() == kFixedStepInX_MatrixClass) + (void)inv.fixedStepInX(SkIntToScalar(y), &dx, &dy); + else + { + dx = SkScalarToFixed(inv.getScaleX()); + dy = SkScalarToFixed(inv.getSkewY()); + } + } + + do { + int ix = fx >> 16; + int iy = fy >> 16; + + const BILERP_BITMAP16_SHADER_TYPE *p00, *p01, *p10, *p11; + + p00 = p01 = ((const BILERP_BITMAP16_SHADER_TYPE*)((const char*)srcPixels + + SkClampMax(iy, srcMaxY) * srcRB)) + + SkClampMax(ix, srcMaxX); + if ((unsigned)ix < srcMaxX) + p01 += 1; + p10 = p00; + p11 = p01; + if ((unsigned)iy < srcMaxY) + { + p10 = (const BILERP_BITMAP16_SHADER_TYPE*)((const char*)p10 + srcRB); + p11 = (const BILERP_BITMAP16_SHADER_TYPE*)((const char*)p11 + srcRB); + } + + SkFilterProc proc = SkGetBilinearFilterProc(proc_table, fx, fy); + uint32_t c = proc(SkExpand_rgb_16(BILERP_BITMAP16_SHADER_PIXEL(*p00)), + SkExpand_rgb_16(BILERP_BITMAP16_SHADER_PIXEL(*p01)), + SkExpand_rgb_16(BILERP_BITMAP16_SHADER_PIXEL(*p10)), + SkExpand_rgb_16(BILERP_BITMAP16_SHADER_PIXEL(*p11))); + *dstC++ = SkCompact_rgb_16(c); + + fx += dx; + fy += dy; + } while (--count != 0); + } + BILERP_BITMAP16_SHADER_POSTAMBLE(srcBitmap); + } +}; + +#undef BILERP_BITMAP16_SHADER_CLASS +#undef BILERP_BITMAP16_SHADER_TYPE +#undef BILERP_BITMAP16_SHADER_PREAMBLE +#undef BILERP_BITMAP16_SHADER_PIXEL +#undef BILERP_BITMAP16_SHADER_POSTAMBLE diff --git a/skia/sgl/SkBitmapShaderTemplate.h b/skia/sgl/SkBitmapShaderTemplate.h new file mode 100644 index 0000000..b24168c --- /dev/null +++ b/skia/sgl/SkBitmapShaderTemplate.h @@ -0,0 +1,314 @@ +/* libs/graphics/sgl/SkBitmapShaderTemplate.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + + +#ifndef NOFILTER_BITMAP_SHADER_PREAMBLE + #define NOFILTER_BITMAP_SHADER_PREAMBLE(bitmap, rb) +#endif +#ifndef NOFILTER_BITMAP_SHADER_POSTAMBLE + #define NOFILTER_BITMAP_SHADER_POSTAMBLE(bitmap) +#endif +#ifndef NOFILTER_BITMAP_SHADER_PREAMBLE16 + #define NOFILTER_BITMAP_SHADER_PREAMBLE16(bitmap, rb) +#endif +#ifndef NOFILTER_BITMAP_SHADER_POSTAMBLE16 + #define NOFILTER_BITMAP_SHADER_POSTAMBLE16(bitmap) +#endif + +class NOFILTER_BITMAP_SHADER_CLASS : public HasSpan16_Sampler_BitmapShader { +public: + NOFILTER_BITMAP_SHADER_CLASS(const SkBitmap& src) + : HasSpan16_Sampler_BitmapShader(src, false, + NOFILTER_BITMAP_SHADER_TILEMODE, + NOFILTER_BITMAP_SHADER_TILEMODE) + { + } + + virtual bool setContext(const SkBitmap& device, const SkPaint& paint, const SkMatrix& matrix) + { + if (!this->INHERITED::setContext(device, paint, matrix)) + return false; + +#ifdef NOFILTER_BITMAP_SHADER_USE_UNITINVERSE + this->computeUnitInverse(); +#endif + return true; + } + + virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count) + { + SkASSERT(count > 0); + +#ifdef NOFILTER_BITMAP_SHADER_SPRITEPROC32 + if ((this->getTotalInverse().getType() & ~SkMatrix::kTranslate_Mask) == 0) + { + NOFILTER_BITMAP_SHADER_SPRITEPROC32(this, x, y, dstC, count); + return; + } +#endif + + unsigned scale = SkAlpha255To256(this->getPaintAlpha()); +#ifdef NOFILTER_BITMAP_SHADER_USE_UNITINVERSE + const SkMatrix& inv = this->getUnitInverse(); + SkMatrix::MapPtProc invProc = this->getUnitInverseProc(); +#else + const SkMatrix& inv = this->getTotalInverse(); + SkMatrix::MapPtProc invProc = this->getInverseMapPtProc(); +#endif + const SkBitmap& srcBitmap = this->getSrcBitmap(); + unsigned srcMaxX = srcBitmap.width() - 1; + unsigned srcMaxY = srcBitmap.height() - 1; + unsigned srcRB = srcBitmap.rowBytes(); + SkFixed fx, fy, dx, dy; + + const NOFILTER_BITMAP_SHADER_TYPE* srcPixels = (const NOFILTER_BITMAP_SHADER_TYPE*)srcBitmap.getPixels(); + NOFILTER_BITMAP_SHADER_PREAMBLE(srcBitmap, srcRB); + + if (this->getInverseClass() == kPerspective_MatrixClass) + { + SkPerspIter iter(inv, SkIntToScalar(x) + SK_ScalarHalf, + SkIntToScalar(y) + SK_ScalarHalf, count); + while ((count = iter.next()) != 0) + { + const SkFixed* srcXY = iter.getXY(); + +/* Do I need this? +#ifndef NOFILTER_BITMAP_SHADER_USE_UNITINVERSE + fx >>= level; + fy >>= level; +#endif +*/ + if (256 == scale) + { + while (--count >= 0) + { + fx = *srcXY++; + fy = *srcXY++; + unsigned x = NOFILTER_BITMAP_SHADER_TILEPROC(fx, srcMaxX); + unsigned y = NOFILTER_BITMAP_SHADER_TILEPROC(fy, srcMaxY); + *dstC++ = NOFILTER_BITMAP_SHADER_SAMPLE_XY(srcPixels, x, y, srcRB); + } + } + else + { + while (--count >= 0) + { + fx = *srcXY++; + fy = *srcXY++; + unsigned x = NOFILTER_BITMAP_SHADER_TILEPROC(fx, srcMaxX); + unsigned y = NOFILTER_BITMAP_SHADER_TILEPROC(fy, srcMaxY); + uint32_t c = NOFILTER_BITMAP_SHADER_SAMPLE_XY(srcPixels, x, y, srcRB); + *dstC++ = SkAlphaMulQ(c, scale); + } + } + } + return; + } + + // now init fx, fy, dx, dy + { + SkPoint srcPt; + invProc(inv, SkIntToScalar(x) + SK_ScalarHalf, + SkIntToScalar(y) + SK_ScalarHalf, &srcPt); + + fx = SkScalarToFixed(srcPt.fX); + fy = SkScalarToFixed(srcPt.fY); + + if (this->getInverseClass() == kFixedStepInX_MatrixClass) + (void)inv.fixedStepInX(SkIntToScalar(y), &dx, &dy); + else + { + dx = SkScalarToFixed(inv.getScaleX()); + dy = SkScalarToFixed(inv.getSkewY()); + } + } + +#if defined(SK_SUPPORT_MIPMAP) && !defined(NOFILTER_BITMAP_SHADER_USE_UNITINVERSE) + { int level = this->getMipLevel() >> 16; + fx >>= level; + fy >>= level; + dx >>= level; + dy >>= level; + } +#endif + + if (dy == 0) + { + int y_index = NOFILTER_BITMAP_SHADER_TILEPROC(fy, srcMaxY); +// SkDEBUGF(("fy = %g, srcMaxY = %d, y_index = %d\n", SkFixedToFloat(fy), srcMaxY, y_index)); + srcPixels = (const NOFILTER_BITMAP_SHADER_TYPE*)((const char*)srcPixels + y_index * srcRB); + if (scale == 256) + while (--count >= 0) + { + unsigned x = NOFILTER_BITMAP_SHADER_TILEPROC(fx, srcMaxX); + fx += dx; + *dstC++ = NOFILTER_BITMAP_SHADER_SAMPLE_X(srcPixels, x); + } + else + while (--count >= 0) + { + unsigned x = NOFILTER_BITMAP_SHADER_TILEPROC(fx, srcMaxX); + SkPMColor c = NOFILTER_BITMAP_SHADER_SAMPLE_X(srcPixels, x); + fx += dx; + *dstC++ = SkAlphaMulQ(c, scale); + } + } + else // dy != 0 + { + if (scale == 256) + while (--count >= 0) + { + unsigned x = NOFILTER_BITMAP_SHADER_TILEPROC(fx, srcMaxX); + unsigned y = NOFILTER_BITMAP_SHADER_TILEPROC(fy, srcMaxY); + fx += dx; + fy += dy; + *dstC++ = NOFILTER_BITMAP_SHADER_SAMPLE_XY(srcPixels, x, y, srcRB); + } + else + while (--count >= 0) + { + unsigned x = NOFILTER_BITMAP_SHADER_TILEPROC(fx, srcMaxX); + unsigned y = NOFILTER_BITMAP_SHADER_TILEPROC(fy, srcMaxY); + SkPMColor c = NOFILTER_BITMAP_SHADER_SAMPLE_XY(srcPixels, x, y, srcRB); + fx += dx; + fy += dy; + *dstC++ = SkAlphaMulQ(c, scale); + } + } + + NOFILTER_BITMAP_SHADER_POSTAMBLE(srcBitmap); + } + + virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count) + { + SkASSERT(count > 0); + SkASSERT(this->getFlags() & SkShader::kHasSpan16_Flag); + +#ifdef NOFILTER_BITMAP_SHADER_SPRITEPROC16 + if ((this->getTotalInverse().getType() & ~SkMatrix::kTranslate_Mask) == 0) + { + NOFILTER_BITMAP_SHADER_SPRITEPROC16(this, x, y, dstC, count); + return; + } +#endif + +#ifdef NOFILTER_BITMAP_SHADER_USE_UNITINVERSE + const SkMatrix& inv = this->getUnitInverse(); + SkMatrix::MapPtProc invProc = this->getUnitInverseProc(); +#else + const SkMatrix& inv = this->getTotalInverse(); + SkMatrix::MapPtProc invProc = this->getInverseMapPtProc(); +#endif + const SkBitmap& srcBitmap = this->getSrcBitmap(); + unsigned srcMaxX = srcBitmap.width() - 1; + unsigned srcMaxY = srcBitmap.height() - 1; + unsigned srcRB = srcBitmap.rowBytes(); + SkFixed fx, fy, dx, dy; + + const NOFILTER_BITMAP_SHADER_TYPE* srcPixels = (const NOFILTER_BITMAP_SHADER_TYPE*)srcBitmap.getPixels(); + NOFILTER_BITMAP_SHADER_PREAMBLE16(srcBitmap, srcRB); + + if (this->getInverseClass() == kPerspective_MatrixClass) + { + SkPerspIter iter(inv, SkIntToScalar(x) + SK_ScalarHalf, + SkIntToScalar(y) + SK_ScalarHalf, count); + while ((count = iter.next()) != 0) + { + const SkFixed* srcXY = iter.getXY(); + + while (--count >= 0) + { + fx = *srcXY++; + fy = *srcXY++; + unsigned x = NOFILTER_BITMAP_SHADER_TILEPROC(fx, srcMaxX); + unsigned y = NOFILTER_BITMAP_SHADER_TILEPROC(fy, srcMaxY); + *dstC++ = NOFILTER_BITMAP_SHADER_SAMPLE_XY16(srcPixels, x, y, srcRB); + } + } + return; + } + + // now init fx, fy, dx, dy + { + SkPoint srcPt; + invProc(inv, SkIntToScalar(x) + SK_ScalarHalf, + SkIntToScalar(y) + SK_ScalarHalf, &srcPt); + + fx = SkScalarToFixed(srcPt.fX); + fy = SkScalarToFixed(srcPt.fY); + + if (this->getInverseClass() == kFixedStepInX_MatrixClass) + (void)inv.fixedStepInX(SkIntToScalar(y), &dx, &dy); + else + { + dx = SkScalarToFixed(inv.getScaleX()); + dy = SkScalarToFixed(inv.getSkewY()); + } + } + +#if defined(SK_SUPPORT_MIPMAP) && !defined(NOFILTER_BITMAP_SHADER_USE_UNITINVERSE) + { int level = this->getMipLevel() >> 16; + fx >>= level; + fy >>= level; + dx >>= level; + dy >>= level; + } +#endif + + if (dy == 0) + { + srcPixels = (const NOFILTER_BITMAP_SHADER_TYPE*)((const char*)srcPixels + NOFILTER_BITMAP_SHADER_TILEPROC(fy, srcMaxY) * srcRB); + do { + unsigned x = NOFILTER_BITMAP_SHADER_TILEPROC(fx, srcMaxX); + fx += dx; + *dstC++ = NOFILTER_BITMAP_SHADER_SAMPLE_X16(srcPixels, x); + } while (--count != 0); + } + else // dy != 0 + { + do { + unsigned x = NOFILTER_BITMAP_SHADER_TILEPROC(fx, srcMaxX); + unsigned y = NOFILTER_BITMAP_SHADER_TILEPROC(fy, srcMaxY); + fx += dx; + fy += dy; + *dstC++ = NOFILTER_BITMAP_SHADER_SAMPLE_XY16(srcPixels, x, y, srcRB); + } while (--count != 0); + } + + NOFILTER_BITMAP_SHADER_POSTAMBLE16(srcBitmap); + } +private: + typedef HasSpan16_Sampler_BitmapShader INHERITED; +}; + +#undef NOFILTER_BITMAP_SHADER_CLASS +#undef NOFILTER_BITMAP_SHADER_TYPE +#undef NOFILTER_BITMAP_SHADER_PREAMBLE +#undef NOFILTER_BITMAP_SHADER_POSTAMBLE +#undef NOFILTER_BITMAP_SHADER_SAMPLE_X //(x) +#undef NOFILTER_BITMAP_SHADER_SAMPLE_XY //(x, y, rowBytes) +#undef NOFILTER_BITMAP_SHADER_TILEMODE +#undef NOFILTER_BITMAP_SHADER_TILEPROC + +#undef NOFILTER_BITMAP_SHADER_PREAMBLE16 +#undef NOFILTER_BITMAP_SHADER_POSTAMBLE16 +#undef NOFILTER_BITMAP_SHADER_SAMPLE_X16 //(x) +#undef NOFILTER_BITMAP_SHADER_SAMPLE_XY16 //(x, y, rowBytes) + +#undef NOFILTER_BITMAP_SHADER_USE_UNITINVERSE +#undef NOFILTER_BITMAP_SHADER_SPRITEPROC16 +#undef NOFILTER_BITMAP_SHADER_SPRITEPROC32 diff --git a/skia/sgl/SkBitmap_scroll.cpp b/skia/sgl/SkBitmap_scroll.cpp new file mode 100644 index 0000000..eb2abc8 --- /dev/null +++ b/skia/sgl/SkBitmap_scroll.cpp @@ -0,0 +1,106 @@ +#include "SkBitmap.h" +#include "SkRegion.h" + +bool SkBitmap::scrollRect(const SkIRect* subset, int dx, int dy, + SkRegion* inval) const +{ + if (NULL != subset) { + SkBitmap tmp; + + return this->extractSubset(&tmp, *subset) && + // now call again with no rectangle + tmp.scrollRect(NULL, dx, dy, inval); + } + + int shift; + + switch (this->config()) { + case kIndex8_Config: + case kA8_Config: + shift = 0; + break; + case kARGB_4444_Config: + case kRGB_565_Config: + shift = 1; + break; + case kARGB_8888_Config: + shift = 2; + break; + default: + // can't scroll this config + return false; + } + + int width = this->width(); + int height = this->height(); + + // check if there's nothing to do + if ((dx | dy) == 0 || width <= 0 || height <= 0) { + if (NULL != inval) { + inval->setEmpty(); + } + return true; + } + + // compute the inval region now, before we see if there are any pixels + if (NULL != inval) { + SkIRect r; + + r.set(0, 0, width, height); + // initial the region with the entire bounds + inval->setRect(r); + // do the "scroll" + r.offset(dx, dy); + + // check if we scrolled completely away + if (!SkIRect::Intersects(r, inval->getBounds())) { + // inval has already been updated... + return true; + } + + // compute the dirty area + inval->op(r, SkRegion::kDifference_Op); + } + + SkAutoLockPixels alp(*this); + // if we have no pixels, just return (inval is already updated) + if (this->getPixels() == NULL) { + return true; + } + + // if we get this far, then we need to shift the pixels + + char* dst = (char*)this->getPixels(); + const char* src = dst; + int rowBytes = this->rowBytes(); // need rowBytes to be signed + + if (dy <= 0) { + src -= dy * rowBytes; + height += dy; + } else { + dst += dy * rowBytes; + height -= dy; + // now jump src/dst to the last scanline + src += (height - 1) * rowBytes; + dst += (height - 1) * rowBytes; + // now invert rowbytes so we copy backwards in the loop + rowBytes = -rowBytes; + } + + if (dx <= 0) { + src -= dx << shift; + width += dx; + } else { + dst += dx << shift; + width -= dx; + } + + width <<= shift; // now width is the number of bytes to move per line + while (--height >= 0) { + memmove(dst, src, width); + dst += rowBytes; + src += rowBytes; + } + return true; +} + diff --git a/skia/sgl/SkBlitBWMaskTemplate.h b/skia/sgl/SkBlitBWMaskTemplate.h new file mode 100644 index 0000000..f7767fb --- /dev/null +++ b/skia/sgl/SkBlitBWMaskTemplate.h @@ -0,0 +1,137 @@ +/* libs/graphics/sgl/SkBlitBWMaskTemplate.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkBitmap.h" +#include "SkMask.h" + +#ifndef ClearLow3Bits_DEFINED +#define ClearLow3Bits_DEFINED + #define ClearLow3Bits(x) ((unsigned)(x) >> 3 << 3) +#endif + +/* + SK_BLITBWMASK_NAME name of function(const SkBitmap& bitmap, const SkMask& mask, const SkIRect& clip, SK_BLITBWMASK_ARGS) + SK_BLITBWMASK_ARGS list of additional arguments to SK_BLITBWMASK_NAME, beginning with a comma + SK_BLITBWMASK_BLIT8 name of function(U8CPU byteMask, SK_BLITBWMASK_DEVTYPE* dst, int x, int y) + SK_BLITBWMASK_GETADDR either getAddr32 or getAddr16 or getAddr8 + SK_BLITBWMASK_DEVTYPE either U32 or U16 or U8 +*/ + +static void SK_BLITBWMASK_NAME(const SkBitmap& bitmap, const SkMask& srcMask, const SkIRect& clip SK_BLITBWMASK_ARGS) +{ + SkASSERT(clip.fRight <= srcMask.fBounds.fRight); + + int cx = clip.fLeft; + int cy = clip.fTop; + int maskLeft = srcMask.fBounds.fLeft; + unsigned mask_rowBytes = srcMask.fRowBytes; + unsigned bitmap_rowBytes = bitmap.rowBytes(); + unsigned height = clip.height(); + + SkASSERT(mask_rowBytes != 0); + SkASSERT(bitmap_rowBytes != 0); + SkASSERT(height != 0); + + const uint8_t* bits = srcMask.getAddr1(cx, cy); + SK_BLITBWMASK_DEVTYPE* device = bitmap.SK_BLITBWMASK_GETADDR(cx, cy); + + if (cx == maskLeft && clip.fRight == srcMask.fBounds.fRight) + { + do { + SK_BLITBWMASK_DEVTYPE* dst = device; + unsigned rb = mask_rowBytes; + do { + U8CPU mask = *bits++; + SK_BLITBWMASK_BLIT8(mask, dst); + dst += 8; + } while (--rb != 0); + device = (SK_BLITBWMASK_DEVTYPE*)((char*)device + bitmap_rowBytes); + } while (--height != 0); + } + else + { + int left_edge = cx - maskLeft; + SkASSERT(left_edge >= 0); + int rite_edge = clip.fRight - maskLeft; + SkASSERT(rite_edge > left_edge); + + int left_mask = 0xFF >> (left_edge & 7); + int rite_mask = 0xFF << (8 - (rite_edge & 7)); + int full_runs = (rite_edge >> 3) - ((left_edge + 7) >> 3); + + // check for empty right mask, so we don't read off the end (or go slower than we need to) + if (rite_mask == 0) + { + SkASSERT(full_runs >= 0); + full_runs -= 1; + rite_mask = 0xFF; + } + if (left_mask == 0xFF) + full_runs -= 1; + + // back up manually so we can keep in sync with our byte-aligned src + // and not trigger an assert from the getAddr## function + device -= left_edge & 7; + // have cx reflect our actual starting x-coord + cx -= left_edge & 7; + + if (full_runs < 0) + { + left_mask &= rite_mask; + SkASSERT(left_mask != 0); + do { + U8CPU mask = *bits & left_mask; + SK_BLITBWMASK_BLIT8(mask, device); + bits += mask_rowBytes; + device = (SK_BLITBWMASK_DEVTYPE*)((char*)device + bitmap_rowBytes); + } while (--height != 0); + } + else + { + do { + int runs = full_runs; + SK_BLITBWMASK_DEVTYPE* dst = device; + const uint8_t* b = bits; + U8CPU mask; + + mask = *b++ & left_mask; + SK_BLITBWMASK_BLIT8(mask, dst); + dst += 8; + + while (--runs >= 0) + { + mask = *b++; + SK_BLITBWMASK_BLIT8(mask, dst); + dst += 8; + } + + mask = *b & rite_mask; + SK_BLITBWMASK_BLIT8(mask, dst); + + bits += mask_rowBytes; + device = (SK_BLITBWMASK_DEVTYPE*)((char*)device + bitmap_rowBytes); + } while (--height != 0); + } + } +} + +#undef SK_BLITBWMASK_NAME +#undef SK_BLITBWMASK_ARGS +#undef SK_BLITBWMASK_BLIT8 +#undef SK_BLITBWMASK_GETADDR +#undef SK_BLITBWMASK_DEVTYPE +#undef SK_BLITBWMASK_DOROWSETUP diff --git a/skia/sgl/SkBlitRow.h b/skia/sgl/SkBlitRow.h new file mode 100644 index 0000000..bb6a29b --- /dev/null +++ b/skia/sgl/SkBlitRow.h @@ -0,0 +1,22 @@ +#ifndef SkBlitRow_DEFINED +#define SkBlitRow_DEFINED + +#include "SkBitmap.h" +#include "SkColor.h" + +class SkBlitRow { +public: + enum { + kGlobalAlpha_Flag = 0x01, + kSrcPixelAlpha_Flag = 0x02, + kDither_Flag = 0x04 + }; + + typedef void (*Proc)(uint16_t* SK_RESTRICT dst, + const SkPMColor* SK_RESTRICT src, + int count, U8CPU alpha, int x, int y); + + static Proc Factory(unsigned flags, SkBitmap::Config); +}; + +#endif diff --git a/skia/sgl/SkBlitRow_D16.cpp b/skia/sgl/SkBlitRow_D16.cpp new file mode 100644 index 0000000..f40df36 --- /dev/null +++ b/skia/sgl/SkBlitRow_D16.cpp @@ -0,0 +1,258 @@ +#include "SkBlitRow.h" +#include "SkColorPriv.h" +#include "SkDither.h" + +/////////////////////////////////////////////////////////////////////////////// + +static void S32_D565_Opaque(uint16_t* SK_RESTRICT dst, + const SkPMColor* SK_RESTRICT src, int count, + U8CPU alpha, int /*x*/, int /*y*/) { + SkASSERT(255 == alpha); + + if (count > 0) { + do { + SkPMColor c = *src++; + SkPMColorAssert(c); + SkASSERT(SkGetPackedA32(c) == 255); + *dst++ = SkPixel32ToPixel16_ToU16(c); + } while (--count != 0); + } +} + +static void S32_D565_Blend(uint16_t* SK_RESTRICT dst, + const SkPMColor* SK_RESTRICT src, int count, + U8CPU alpha, int /*x*/, int /*y*/) { + SkASSERT(255 > alpha); + + if (count > 0) { + int scale = SkAlpha255To256(alpha); + do { + SkPMColor c = *src++; + SkPMColorAssert(c); + SkASSERT(SkGetPackedA32(c) == 255); + uint16_t d = *dst; + *dst++ = SkPackRGB16( + SkAlphaBlend(SkPacked32ToR16(c), SkGetPackedR16(d), scale), + SkAlphaBlend(SkPacked32ToG16(c), SkGetPackedG16(d), scale), + SkAlphaBlend(SkPacked32ToB16(c), SkGetPackedB16(d), scale)); + } while (--count != 0); + } +} + +static void S32A_D565_Opaque(uint16_t* SK_RESTRICT dst, + const SkPMColor* SK_RESTRICT src, int count, + U8CPU alpha, int /*x*/, int /*y*/) { + SkASSERT(255 == alpha); + + if (count > 0) { + do { + SkPMColor c = *src++; + SkPMColorAssert(c); +// if (__builtin_expect(c!=0, 1)) + if (c) { + *dst = SkSrcOver32To16(c, *dst); + } + dst += 1; + } while (--count != 0); + } +} + +static void S32A_D565_Blend(uint16_t* SK_RESTRICT dst, + const SkPMColor* SK_RESTRICT src, int count, + U8CPU alpha, int /*x*/, int /*y*/) { + SkASSERT(255 > alpha); + + if (count > 0) { + int src_scale = SkAlpha255To256(alpha); + do { + SkPMColor sc = *src++; + SkPMColorAssert(sc); + if (sc) + { + uint16_t dc = *dst; + unsigned sa = SkGetPackedA32(sc); + unsigned dr, dg, db; + + if (sa == 255) { + dr = SkAlphaBlend(SkPacked32ToR16(sc), SkGetPackedR16(dc), src_scale); + dg = SkAlphaBlend(SkPacked32ToG16(sc), SkGetPackedG16(dc), src_scale); + db = SkAlphaBlend(SkPacked32ToB16(sc), SkGetPackedB16(dc), src_scale); + } else { + unsigned dst_scale = 255 - SkAlphaMul(sa, src_scale); + dr = (SkPacked32ToR16(sc) * src_scale + SkGetPackedR16(dc) * dst_scale) >> 8; + dg = (SkPacked32ToG16(sc) * src_scale + SkGetPackedG16(dc) * dst_scale) >> 8; + db = (SkPacked32ToB16(sc) * src_scale + SkGetPackedB16(dc) * dst_scale) >> 8; + } + *dst = SkPackRGB16(dr, dg, db); + } + dst += 1; + } while (--count != 0); + } +} + +///////////////////////////////////////////////////////////////////////////// + +static void S32_D565_Opaque_Dither(uint16_t* SK_RESTRICT dst, + const SkPMColor* SK_RESTRICT src, + int count, U8CPU alpha, int x, int y) { + SkASSERT(255 == alpha); + + if (count > 0) { + DITHER_565_SCAN(y); + do { + SkPMColor c = *src++; + SkPMColorAssert(c); + SkASSERT(SkGetPackedA32(c) == 255); + + unsigned dither = DITHER_VALUE(x); + *dst++ = SkDitherRGB32To565(c, dither); + DITHER_INC_X(x); + } while (--count != 0); + } +} + +static void S32_D565_Blend_Dither(uint16_t* SK_RESTRICT dst, + const SkPMColor* SK_RESTRICT src, + int count, U8CPU alpha, int x, int y) { + SkASSERT(255 > alpha); + + if (count > 0) { + int scale = SkAlpha255To256(alpha); + DITHER_565_SCAN(y); + do { + SkPMColor c = *src++; + SkPMColorAssert(c); + SkASSERT(SkGetPackedA32(c) == 255); + + int dither = DITHER_VALUE(x); + int sr = SkGetPackedR32(c); + int sg = SkGetPackedG32(c); + int sb = SkGetPackedB32(c); + sr = SkDITHER_R32To565(sr, dither); + sg = SkDITHER_G32To565(sg, dither); + sb = SkDITHER_B32To565(sb, dither); + + uint16_t d = *dst; + *dst++ = SkPackRGB16(SkAlphaBlend(sr, SkGetPackedR16(d), scale), + SkAlphaBlend(sg, SkGetPackedG16(d), scale), + SkAlphaBlend(sb, SkGetPackedB16(d), scale)); + DITHER_INC_X(x); + } while (--count != 0); + } +} + +static void S32A_D565_Opaque_Dither(uint16_t* SK_RESTRICT dst, + const SkPMColor* SK_RESTRICT src, + int count, U8CPU alpha, int x, int y) { + SkASSERT(255 == alpha); + + if (count > 0) { + DITHER_565_SCAN(y); + do { + SkPMColor c = *src++; + SkPMColorAssert(c); + if (c) { + unsigned a = SkGetPackedA32(c); + + int d = SkAlphaMul(DITHER_VALUE(x), SkAlpha255To256(a)); + + unsigned sr = SkGetPackedR32(c); + unsigned sg = SkGetPackedG32(c); + unsigned sb = SkGetPackedB32(c); + sr = SkDITHER_R32_FOR_565(sr, d); + sg = SkDITHER_G32_FOR_565(sg, d); + sb = SkDITHER_B32_FOR_565(sb, d); + + uint32_t src_expanded = (sg << 24) | (sr << 13) | (sb << 2); + uint32_t dst_expanded = SkExpand_rgb_16(*dst); + dst_expanded = dst_expanded * (SkAlpha255To256(255 - a) >> 3); + // now src and dst expanded are in g:11 r:10 x:1 b:10 + *dst = SkCompact_rgb_16((src_expanded + dst_expanded) >> 5); + } + dst += 1; + DITHER_INC_X(x); + } while (--count != 0); + } +} + +static void S32A_D565_Blend_Dither(uint16_t* SK_RESTRICT dst, + const SkPMColor* SK_RESTRICT src, + int count, U8CPU alpha, int x, int y) { + SkASSERT(255 > alpha); + + if (count > 0) { + int src_scale = SkAlpha255To256(alpha); + DITHER_565_SCAN(y); + do { + SkPMColor c = *src++; + SkPMColorAssert(c); + if (c) + { + unsigned d = *dst; + int sa = SkGetPackedA32(c); + int dst_scale = SkAlpha255To256(255 - SkAlphaMul(sa, src_scale)); + int dither = DITHER_VALUE(x); + + int sr = SkGetPackedR32(c); + int sg = SkGetPackedG32(c); + int sb = SkGetPackedB32(c); + sr = SkDITHER_R32To565(sr, dither); + sg = SkDITHER_G32To565(sg, dither); + sb = SkDITHER_B32To565(sb, dither); + + int dr = (sr * src_scale + SkGetPackedR16(d) * dst_scale) >> 8; + int dg = (sg * src_scale + SkGetPackedG16(d) * dst_scale) >> 8; + int db = (sb * src_scale + SkGetPackedB16(d) * dst_scale) >> 8; + + *dst = SkPackRGB16(dr, dg, db); + } + dst += 1; + DITHER_INC_X(x); + } while (--count != 0); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#ifdef USE_T32CB16BLEND_ASM + extern "C" void scanline_t32cb16blend_arm(uint16_t*, uint32_t*, size_t); +#endif + +static const SkBlitRow::Proc gProcs16[] = { + // no dither + S32_D565_Opaque, + S32_D565_Blend, + +#ifdef USE_T32CB16BLEND_ASM + (SkBlitRow::Proc)scanline_t32cb16blend_arm, +#else + S32A_D565_Opaque, +#endif + + S32A_D565_Blend, + + // dither + S32_D565_Opaque_Dither, + S32_D565_Blend_Dither, + + S32A_D565_Opaque_Dither, + S32A_D565_Blend_Dither +}; + +extern SkBlitRow::Proc SkBlitRow_Factory_4444(unsigned flags); + +SkBlitRow::Proc SkBlitRow::Factory(unsigned flags, SkBitmap::Config config) { + SkASSERT(flags < SK_ARRAY_COUNT(gProcs16)); + + switch (config) { + case SkBitmap::kRGB_565_Config: + return gProcs16[flags]; + case SkBitmap::kARGB_4444_Config: + return SkBlitRow_Factory_4444(flags); + default: + break; + } + return NULL; +} + diff --git a/skia/sgl/SkBlitRow_D4444.cpp b/skia/sgl/SkBlitRow_D4444.cpp new file mode 100644 index 0000000..e60c721 --- /dev/null +++ b/skia/sgl/SkBlitRow_D4444.cpp @@ -0,0 +1,214 @@ +#include "SkBlitRow.h" +#include "SkColorPriv.h" +#include "SkDither.h" + +/////////////////////////////////////////////////////////////////////////////// + +static void S32_D4444_Opaque(uint16_t* SK_RESTRICT dst, + const SkPMColor* SK_RESTRICT src, int count, + U8CPU alpha, int /*x*/, int /*y*/) { + SkASSERT(255 == alpha); + + if (count > 0) { + do { + SkPMColor c = *src++; + SkPMColorAssert(c); + SkASSERT(SkGetPackedA32(c) == 255); + *dst++ = SkPixel32ToPixel4444(c); + } while (--count != 0); + } +} + +static void S32_D4444_Blend(uint16_t* SK_RESTRICT dst, + const SkPMColor* SK_RESTRICT src, int count, + U8CPU alpha, int /*x*/, int /*y*/) { + SkASSERT(255 > alpha); + + if (count > 0) { + unsigned scale16 = SkAlpha255To256(alpha) >> 4; + do { + SkPMColor c = *src++; + SkPMColorAssert(c); + SkASSERT(SkGetPackedA32(c) == 255); + + uint32_t src_expand = SkExpand32_4444(c); + uint32_t dst_expand = SkExpand_4444(*dst); + dst_expand += (src_expand - dst_expand) * scale16 >> 4; + *dst++ = SkCompact_4444(dst_expand); + } while (--count != 0); + } +} + +static void S32A_D4444_Opaque(uint16_t* SK_RESTRICT dst, + const SkPMColor* SK_RESTRICT src, int count, + U8CPU alpha, int /*x*/, int /*y*/) { + SkASSERT(255 == alpha); + + if (count > 0) { + do { + SkPMColor c = *src++; + SkPMColorAssert(c); +// if (__builtin_expect(c!=0, 1)) + if (c) + { + unsigned scale16 = SkAlpha255To256(255 - SkGetPackedA32(c)) >> 4; + uint32_t src_expand = SkExpand_8888(c); + uint32_t dst_expand = SkExpand_4444(*dst) * scale16; + *dst = SkCompact_4444((src_expand + dst_expand) >> 4); + } + dst += 1; + } while (--count != 0); + } +} + +static void S32A_D4444_Blend(uint16_t* SK_RESTRICT dst, + const SkPMColor* SK_RESTRICT src, int count, + U8CPU alpha, int /*x*/, int /*y*/) { + SkASSERT(255 > alpha); + + if (count > 0) { + int src_scale = SkAlpha255To256(alpha) >> 4; + do { + SkPMColor sc = *src++; + SkPMColorAssert(sc); + + if (sc) { + unsigned dst_scale = 16 - (SkGetPackedA32(sc) * src_scale >> 8); + uint32_t src_expand = SkExpand32_4444(sc) * src_scale; + uint32_t dst_expand = SkExpand_4444(*dst) * dst_scale; + *dst = SkCompact_4444((src_expand + dst_expand) >> 4); + } + dst += 1; + } while (--count != 0); + } +} + +///////////////////////////////////////////////////////////////////////////// + +static void S32_D4444_Opaque_Dither(uint16_t* SK_RESTRICT dst, + const SkPMColor* SK_RESTRICT src, + int count, U8CPU alpha, int x, int y) { + SkASSERT(255 == alpha); + + if (count > 0) { + DITHER_4444_SCAN(y); + do { + SkPMColor c = *src++; + SkPMColorAssert(c); + SkASSERT(SkGetPackedA32(c) == 255); + + unsigned dither = DITHER_VALUE(x); + *dst++ = SkDitherARGB32To4444(c, dither); + DITHER_INC_X(x); + } while (--count != 0); + } +} + +static void S32_D4444_Blend_Dither(uint16_t* SK_RESTRICT dst, + const SkPMColor* SK_RESTRICT src, + int count, U8CPU alpha, int x, int y) { + SkASSERT(255 > alpha); + + if (count > 0) { + int scale16 = SkAlpha255To256(alpha) >> 4; + DITHER_4444_SCAN(y); + do { + SkPMColor c = *src++; + SkPMColorAssert(c); + SkASSERT(SkGetPackedA32(c) == 255); + + uint32_t src_expand = SkExpand32_4444(c) * scale16; + uint32_t dst_expand = SkExpand_4444(*dst) * (16 - scale16); + + c = SkCompact_8888(src_expand + dst_expand); // convert back to SkPMColor + *dst++ = SkDitherARGB32To4444(c, DITHER_VALUE(x)); + DITHER_INC_X(x); + } while (--count != 0); + } +} + +static void S32A_D4444_Opaque_Dither(uint16_t* SK_RESTRICT dst, + const SkPMColor* SK_RESTRICT src, + int count, U8CPU alpha, int x, int y) { + SkASSERT(255 == alpha); + + if (count > 0) { + DITHER_4444_SCAN(y); + do { + SkPMColor c = *src++; + SkPMColorAssert(c); + if (c) { + unsigned a = SkGetPackedA32(c); + int d = SkAlphaMul(DITHER_VALUE(x), SkAlpha255To256(a)); + + unsigned scale16 = SkAlpha255To256(255 - a) >> 4; + uint32_t src_expand = SkExpand_8888(c); + uint32_t dst_expand = SkExpand_4444(*dst) * scale16; + // convert back to SkPMColor + c = SkCompact_8888(src_expand + dst_expand); + *dst = SkDitherARGB32To4444(c, d); + } + dst += 1; + DITHER_INC_X(x); + } while (--count != 0); + } +} + +// need DitherExpand888To4444(expand, dither) + +static void S32A_D4444_Blend_Dither(uint16_t* SK_RESTRICT dst, + const SkPMColor* SK_RESTRICT src, + int count, U8CPU alpha, int x, int y) { + SkASSERT(255 > alpha); + + if (count > 0) { + int src_scale = SkAlpha255To256(alpha) >> 4; + DITHER_4444_SCAN(y); + do { + SkPMColor c = *src++; + SkPMColorAssert(c); + if (c) { + unsigned a = SkAlpha255To256(SkGetPackedA32(c)); + int d = SkAlphaMul(DITHER_VALUE(x), a); + + unsigned dst_scale = 16 - SkAlphaMul(src_scale, a); + uint32_t src_expand = SkExpand32_4444(c) * src_scale; + uint32_t dst_expand = SkExpand_4444(*dst) * dst_scale; + // convert back to SkPMColor + c = SkCompact_8888(src_expand + dst_expand); + *dst = SkDitherARGB32To4444(c, d); + } + dst += 1; + DITHER_INC_X(x); + } while (--count != 0); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const SkBlitRow::Proc gProcs4444[] = { + // no dither + S32_D4444_Opaque, + S32_D4444_Blend, + + S32A_D4444_Opaque, + S32A_D4444_Blend, + + // dither + S32_D4444_Opaque_Dither, + S32_D4444_Blend_Dither, + + S32A_D4444_Opaque_Dither, + S32A_D4444_Blend_Dither +}; + +SkBlitRow::Proc SkBlitRow_Factory_4444(unsigned flags); +SkBlitRow::Proc SkBlitRow_Factory_4444(unsigned flags) +{ + SkASSERT(flags < SK_ARRAY_COUNT(gProcs4444)); + + return gProcs4444[flags]; +} + + diff --git a/skia/sgl/SkBlitter.cpp b/skia/sgl/SkBlitter.cpp new file mode 100644 index 0000000..95a67c7 --- /dev/null +++ b/skia/sgl/SkBlitter.cpp @@ -0,0 +1,923 @@ +/* libs/graphics/sgl/SkBlitter.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkBlitter.h" +#include "SkAntiRun.h" +#include "SkColor.h" +#include "SkColorFilter.h" +#include "SkMask.h" +#include "SkMaskFilter.h" +#include "SkTemplatesPriv.h" +#include "SkUtils.h" +#include "SkXfermode.h" + +SkBlitter::~SkBlitter() +{ +} + +const SkBitmap* SkBlitter::justAnOpaqueColor(uint32_t* value) +{ + return NULL; +} + +void SkBlitter::blitH(int x, int y, int width) +{ + SkASSERT(!"unimplemented"); +} + +void SkBlitter::blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]) +{ + SkASSERT(!"unimplemented"); +} + +void SkBlitter::blitV(int x, int y, int height, SkAlpha alpha) +{ + if (alpha == 255) + this->blitRect(x, y, 1, height); + else + { + int16_t runs[2]; + runs[0] = 1; + runs[1] = 0; + + while (--height >= 0) + this->blitAntiH(x, y++, &alpha, runs); + } +} + +void SkBlitter::blitRect(int x, int y, int width, int height) +{ + while (--height >= 0) + this->blitH(x, y++, width); +} + +////////////////////////////////////////////////////////////////////////////// + +static inline void bits_to_runs(SkBlitter* blitter, int x, int y, const uint8_t bits[], + U8CPU left_mask, int rowBytes, U8CPU right_mask) +{ + int inFill = 0; + int pos = 0; + + while (--rowBytes >= 0) + { + unsigned b = *bits++ & left_mask; + if (rowBytes == 0) + b &= right_mask; + + for (unsigned test = 0x80; test != 0; test >>= 1) + { + if (b & test) + { + if (!inFill) + { + pos = x; + inFill = true; + } + } + else + { + if (inFill) + { + blitter->blitH(pos, y, x - pos); + inFill = false; + } + } + x += 1; + } + left_mask = 0xFF; + } + + // final cleanup + if (inFill) + blitter->blitH(pos, y, x - pos); +} + +void SkBlitter::blitMask(const SkMask& mask, const SkIRect& clip) +{ + SkASSERT(mask.fBounds.contains(clip)); + + if (mask.fFormat == SkMask::kBW_Format) + { + int cx = clip.fLeft; + int cy = clip.fTop; + int maskLeft = mask.fBounds.fLeft; + int mask_rowBytes = mask.fRowBytes; + int height = clip.height(); + + const uint8_t* bits = mask.getAddr1(cx, cy); + + if (cx == maskLeft && clip.fRight == mask.fBounds.fRight) + { + while (--height >= 0) + { + bits_to_runs(this, cx, cy, bits, 0xFF, mask_rowBytes, 0xFF); + bits += mask_rowBytes; + cy += 1; + } + } + else + { + int left_edge = cx - maskLeft; + SkASSERT(left_edge >= 0); + int rite_edge = clip.fRight - maskLeft; + SkASSERT(rite_edge > left_edge); + + int left_mask = 0xFF >> (left_edge & 7); + int rite_mask = 0xFF << (8 - (rite_edge & 7)); + int full_runs = (rite_edge >> 3) - ((left_edge + 7) >> 3); + + // check for empty right mask, so we don't read off the end (or go slower than we need to) + if (rite_mask == 0) + { + SkASSERT(full_runs >= 0); + full_runs -= 1; + rite_mask = 0xFF; + } + if (left_mask == 0xFF) + full_runs -= 1; + + // back up manually so we can keep in sync with our byte-aligned src + // have cx reflect our actual starting x-coord + cx -= left_edge & 7; + + if (full_runs < 0) + { + SkASSERT((left_mask & rite_mask) != 0); + while (--height >= 0) + { + bits_to_runs(this, cx, cy, bits, left_mask, 1, rite_mask); + bits += mask_rowBytes; + cy += 1; + } + } + else + { + while (--height >= 0) + { + bits_to_runs(this, cx, cy, bits, left_mask, full_runs + 2, rite_mask); + bits += mask_rowBytes; + cy += 1; + } + } + } + } + else + { + int width = clip.width(); + SkAutoSTMalloc<64, int16_t> runStorage(width + 1); + int16_t* runs = runStorage.get(); + const uint8_t* aa = mask.getAddr(clip.fLeft, clip.fTop); + + sk_memset16((uint16_t*)runs, 1, width); + runs[width] = 0; + + int height = clip.height(); + int y = clip.fTop; + while (--height >= 0) + { + this->blitAntiH(clip.fLeft, y, aa, runs); + aa += mask.fRowBytes; + y += 1; + } + } +} + +/////////////////////// these guys are not virtual, just a helpers + +void SkBlitter::blitMaskRegion(const SkMask& mask, const SkRegion& clip) { + if (clip.quickReject(mask.fBounds)) { + return; + } + + SkRegion::Cliperator clipper(clip, mask.fBounds); + + while (!clipper.done()) { + const SkIRect& cr = clipper.rect(); + this->blitMask(mask, cr); + clipper.next(); + } +} + +void SkBlitter::blitRectRegion(const SkIRect& rect, const SkRegion& clip) { + SkRegion::Cliperator clipper(clip, rect); + + while (!clipper.done()) { + const SkIRect& cr = clipper.rect(); + this->blitRect(cr.fLeft, cr.fTop, cr.width(), cr.height()); + clipper.next(); + } +} + +void SkBlitter::blitRegion(const SkRegion& clip) { + SkRegion::Iterator iter(clip); + + while (!iter.done()) { + const SkIRect& cr = iter.rect(); + this->blitRect(cr.fLeft, cr.fTop, cr.width(), cr.height()); + iter.next(); + } +} + +/////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////// + +void SkNullBlitter::blitH(int x, int y, int width) +{ +} + +void SkNullBlitter::blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]) +{ +} + +void SkNullBlitter::blitV(int x, int y, int height, SkAlpha alpha) +{ +} + +void SkNullBlitter::blitRect(int x, int y, int width, int height) +{ +} + +void SkNullBlitter::blitMask(const SkMask& mask, const SkIRect& clip) +{ +} + +const SkBitmap* SkNullBlitter::justAnOpaqueColor(uint32_t* value) +{ + return NULL; +} + +/////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////// + +static int compute_anti_width(const int16_t runs[]) +{ + int width = 0; + + for (;;) + { + int count = runs[0]; + + SkASSERT(count >= 0); + if (count == 0) + break; + width += count; + runs += count; + + SkASSERT(width < 20000); + } + return width; +} + +static inline bool y_in_rect(int y, const SkIRect& rect) +{ + return (unsigned)(y - rect.fTop) < (unsigned)rect.height(); +} + +static inline bool x_in_rect(int x, const SkIRect& rect) +{ + return (unsigned)(x - rect.fLeft) < (unsigned)rect.width(); +} + +void SkRectClipBlitter::blitH(int left, int y, int width) +{ + SkASSERT(width > 0); + + if (!y_in_rect(y, fClipRect)) + return; + + int right = left + width; + + if (left < fClipRect.fLeft) + left = fClipRect.fLeft; + if (right > fClipRect.fRight) + right = fClipRect.fRight; + + width = right - left; + if (width > 0) + fBlitter->blitH(left, y, width); +} + +void SkRectClipBlitter::blitAntiH(int left, int y, const SkAlpha aa[], const int16_t runs[]) +{ + if (!y_in_rect(y, fClipRect) || left >= fClipRect.fRight) + return; + + int x0 = left; + int x1 = left + compute_anti_width(runs); + + if (x1 <= fClipRect.fLeft) + return; + + SkASSERT(x0 < x1); + if (x0 < fClipRect.fLeft) + { + int dx = fClipRect.fLeft - x0; + SkAlphaRuns::BreakAt((int16_t*)runs, (uint8_t*)aa, dx); + runs += dx; + aa += dx; + x0 = fClipRect.fLeft; + } + + SkASSERT(x0 < x1 && runs[x1 - x0] == 0); + if (x1 > fClipRect.fRight) + { + x1 = fClipRect.fRight; + SkAlphaRuns::BreakAt((int16_t*)runs, (uint8_t*)aa, x1 - x0); + ((int16_t*)runs)[x1 - x0] = 0; + } + + SkASSERT(x0 < x1 && runs[x1 - x0] == 0); + SkASSERT(compute_anti_width(runs) == x1 - x0); + + fBlitter->blitAntiH(x0, y, aa, runs); +} + +void SkRectClipBlitter::blitV(int x, int y, int height, SkAlpha alpha) +{ + SkASSERT(height > 0); + + if (!x_in_rect(x, fClipRect)) + return; + + int y0 = y; + int y1 = y + height; + + if (y0 < fClipRect.fTop) + y0 = fClipRect.fTop; + if (y1 > fClipRect.fBottom) + y1 = fClipRect.fBottom; + + if (y0 < y1) + fBlitter->blitV(x, y0, y1 - y0, alpha); +} + +void SkRectClipBlitter::blitRect(int left, int y, int width, int height) +{ + SkIRect r; + + r.set(left, y, left + width, y + height); + if (r.intersect(fClipRect)) + fBlitter->blitRect(r.fLeft, r.fTop, r.width(), r.height()); +} + +void SkRectClipBlitter::blitMask(const SkMask& mask, const SkIRect& clip) +{ + SkASSERT(mask.fBounds.contains(clip)); + + SkIRect r = clip; + + if (r.intersect(fClipRect)) + fBlitter->blitMask(mask, r); +} + +const SkBitmap* SkRectClipBlitter::justAnOpaqueColor(uint32_t* value) +{ + return fBlitter->justAnOpaqueColor(value); +} + +/////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////// + +void SkRgnClipBlitter::blitH(int x, int y, int width) +{ + SkRegion::Spanerator span(*fRgn, y, x, x + width); + int left, right; + + while (span.next(&left, &right)) + { + SkASSERT(left < right); + fBlitter->blitH(left, y, right - left); + } +} + +void SkRgnClipBlitter::blitAntiH(int x, int y, const SkAlpha aa[], const int16_t runs[]) +{ + int width = compute_anti_width(runs); + SkRegion::Spanerator span(*fRgn, y, x, x + width); + int left, right; + SkDEBUGCODE(const SkIRect& bounds = fRgn->getBounds();) + + int prevRite = x; + while (span.next(&left, &right)) + { + SkASSERT(x <= left); + SkASSERT(left < right); + SkASSERT(left >= bounds.fLeft && right <= bounds.fRight); + + SkAlphaRuns::Break((int16_t*)runs, (uint8_t*)aa, left - x, right - left); + + // now zero before left + if (left > prevRite) + { + int index = prevRite - x; + ((uint8_t*)aa)[index] = 0; // skip runs after right + ((int16_t*)runs)[index] = SkToS16(left - prevRite); + } + + prevRite = right; + } + + if (prevRite > x) + { + ((int16_t*)runs)[prevRite - x] = 0; + + if (x < 0) { + int skip = runs[0]; + SkASSERT(skip >= -x); + aa += skip; + runs += skip; + x += skip; + } + fBlitter->blitAntiH(x, y, aa, runs); + } +} + +void SkRgnClipBlitter::blitV(int x, int y, int height, SkAlpha alpha) +{ + SkIRect bounds; + bounds.set(x, y, x + 1, y + height); + + SkRegion::Cliperator iter(*fRgn, bounds); + + while (!iter.done()) + { + const SkIRect& r = iter.rect(); + SkASSERT(bounds.contains(r)); + + fBlitter->blitV(x, r.fTop, r.height(), alpha); + iter.next(); + } +} + +void SkRgnClipBlitter::blitRect(int x, int y, int width, int height) +{ + SkIRect bounds; + bounds.set(x, y, x + width, y + height); + + SkRegion::Cliperator iter(*fRgn, bounds); + + while (!iter.done()) + { + const SkIRect& r = iter.rect(); + SkASSERT(bounds.contains(r)); + + fBlitter->blitRect(r.fLeft, r.fTop, r.width(), r.height()); + iter.next(); + } +} + +void SkRgnClipBlitter::blitMask(const SkMask& mask, const SkIRect& clip) +{ + SkASSERT(mask.fBounds.contains(clip)); + + SkRegion::Cliperator iter(*fRgn, clip); + const SkIRect& r = iter.rect(); + SkBlitter* blitter = fBlitter; + + while (!iter.done()) + { + blitter->blitMask(mask, r); + iter.next(); + } +} + +const SkBitmap* SkRgnClipBlitter::justAnOpaqueColor(uint32_t* value) +{ + return fBlitter->justAnOpaqueColor(value); +} + +/////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////// + +SkBlitter* SkBlitterClipper::apply(SkBlitter* blitter, const SkRegion* clip, const SkIRect* ir) +{ + if (clip) + { + const SkIRect& clipR = clip->getBounds(); + + if (clip->isEmpty() || (ir && !SkIRect::Intersects(clipR, *ir))) + blitter = &fNullBlitter; + else if (clip->isRect()) + { + if (ir == NULL || !clipR.contains(*ir)) + { + fRectBlitter.init(blitter, clipR); + blitter = &fRectBlitter; + } + } + else + { + fRgnBlitter.init(blitter, clip); + blitter = &fRgnBlitter; + } + } + return blitter; +} + +/////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////// + +#include "SkColorShader.h" +#include "SkColorPriv.h" + +class Sk3DShader : public SkShader { +public: + Sk3DShader(SkShader* proxy) : fProxy(proxy) + { + proxy->safeRef(); + fMask = NULL; + } + virtual ~Sk3DShader() + { + fProxy->safeUnref(); + } + void setMask(const SkMask* mask) { fMask = mask; } + + virtual bool setContext(const SkBitmap& device, const SkPaint& paint, const SkMatrix& matrix) + { + if (fProxy) + return fProxy->setContext(device, paint, matrix); + else + { + fPMColor = SkPreMultiplyColor(paint.getColor()); + return this->INHERITED::setContext(device, paint, matrix); + } + } + virtual void shadeSpan(int x, int y, SkPMColor span[], int count) + { + if (fProxy) + fProxy->shadeSpan(x, y, span, count); + + if (fMask == NULL) + { + if (fProxy == NULL) + sk_memset32(span, fPMColor, count); + return; + } + + SkASSERT(fMask->fBounds.contains(x, y)); + SkASSERT(fMask->fBounds.contains(x + count - 1, y)); + + size_t size = fMask->computeImageSize(); + const uint8_t* alpha = fMask->getAddr(x, y); + const uint8_t* mulp = alpha + size; + const uint8_t* addp = mulp + size; + + if (fProxy) + { + for (int i = 0; i < count; i++) + { + if (alpha[i]) + { + SkPMColor c = span[i]; + if (c) + { + unsigned a = SkGetPackedA32(c); + unsigned r = SkGetPackedR32(c); + unsigned g = SkGetPackedG32(c); + unsigned b = SkGetPackedB32(c); + + unsigned mul = SkAlpha255To256(mulp[i]); + unsigned add = addp[i]; + + r = SkFastMin32(SkAlphaMul(r, mul) + add, a); + g = SkFastMin32(SkAlphaMul(g, mul) + add, a); + b = SkFastMin32(SkAlphaMul(b, mul) + add, a); + + span[i] = SkPackARGB32(a, r, g, b); + } + } + else + span[i] = 0; + } + } + else // color + { + unsigned a = SkGetPackedA32(fPMColor); + unsigned r = SkGetPackedR32(fPMColor); + unsigned g = SkGetPackedG32(fPMColor); + unsigned b = SkGetPackedB32(fPMColor); + for (int i = 0; i < count; i++) + { + if (alpha[i]) + { + unsigned mul = SkAlpha255To256(mulp[i]); + unsigned add = addp[i]; + + span[i] = SkPackARGB32( a, + SkFastMin32(SkAlphaMul(r, mul) + add, a), + SkFastMin32(SkAlphaMul(g, mul) + add, a), + SkFastMin32(SkAlphaMul(b, mul) + add, a)); + } + else + span[i] = 0; + } + } + } + + virtual void beginSession() + { + this->INHERITED::beginSession(); + if (fProxy) + fProxy->beginSession(); + } + + virtual void endSession() + { + if (fProxy) + fProxy->endSession(); + this->INHERITED::endSession(); + } + +protected: + Sk3DShader(SkFlattenableReadBuffer& buffer) : + INHERITED(buffer) + { + fProxy = static_cast<SkShader*>(buffer.readFlattenable()); + fPMColor = buffer.readU32(); + fMask = NULL; + } + + virtual void flatten(SkFlattenableWriteBuffer& buffer) + { + this->INHERITED::flatten(buffer); + buffer.writeFlattenable(fProxy); + buffer.write32(fPMColor); + } + + virtual Factory getFactory() + { + return CreateProc; + } + +private: + static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) + { + return SkNEW_ARGS(Sk3DShader, (buffer)); + } + + SkShader* fProxy; + SkPMColor fPMColor; + const SkMask* fMask; + + typedef SkShader INHERITED; +}; + +class Sk3DBlitter : public SkBlitter { +public: + Sk3DBlitter(SkBlitter* proxy, Sk3DShader* shader, void (*killProc)(void*)) + : fProxy(proxy), f3DShader(shader), fKillProc(killProc) + { + shader->ref(); + } + virtual ~Sk3DBlitter() + { + f3DShader->unref(); + fKillProc(fProxy); + } + + virtual void blitH(int x, int y, int width) + { + fProxy->blitH(x, y, width); + } + virtual void blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]) + { + fProxy->blitAntiH(x, y, antialias, runs); + } + virtual void blitV(int x, int y, int height, SkAlpha alpha) + { + fProxy->blitV(x, y, height, alpha); + } + virtual void blitRect(int x, int y, int width, int height) + { + fProxy->blitRect(x, y, width, height); + } + virtual void blitMask(const SkMask& mask, const SkIRect& clip) + { + if (mask.fFormat == SkMask::k3D_Format) + { + f3DShader->setMask(&mask); + + ((SkMask*)&mask)->fFormat = SkMask::kA8_Format; + fProxy->blitMask(mask, clip); + ((SkMask*)&mask)->fFormat = SkMask::k3D_Format; + + f3DShader->setMask(NULL); + } + else + fProxy->blitMask(mask, clip); + } +private: + SkBlitter* fProxy; + Sk3DShader* f3DShader; + void (*fKillProc)(void*); +}; + +/////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////// + +#include "SkCoreBlitters.h" + +class SkAutoRestoreShader { +public: + SkAutoRestoreShader(const SkPaint& p) : fPaint((SkPaint*)&p) + { + fShader = fPaint->getShader(); + fShader->safeRef(); + } + ~SkAutoRestoreShader() + { + fPaint->setShader(fShader); + fShader->safeUnref(); + } +private: + SkPaint* fPaint; + SkShader* fShader; +}; + +class SkAutoCallProc { +public: + typedef void (*Proc)(void*); + SkAutoCallProc(void* obj, Proc proc) + : fObj(obj), fProc(proc) + { + } + ~SkAutoCallProc() + { + if (fObj && fProc) + fProc(fObj); + } + void* get() const { return fObj; } + void* detach() + { + void* obj = fObj; + fObj = NULL; + return obj; + } +private: + void* fObj; + Proc fProc; +}; + +static void destroy_blitter(void* blitter) +{ + ((SkBlitter*)blitter)->~SkBlitter(); +} + +static void delete_blitter(void* blitter) +{ + SkDELETE((SkBlitter*)blitter); +} + +SkBlitter* SkBlitter::Choose(const SkBitmap& device, + const SkMatrix& matrix, + const SkPaint& paint, + void* storage, size_t storageSize) +{ + SkASSERT(storageSize == 0 || storage != NULL); + + SkBlitter* blitter = NULL; + + // which check, in case we're being called by a client with a dummy device + // (e.g. they have a bounder that always aborts the draw) + if (SkBitmap::kNo_Config == device.getConfig()) + { + SK_PLACEMENT_NEW(blitter, SkNullBlitter, storage, storageSize); + return blitter; + } + + SkAutoRestoreShader restore(paint); + SkShader* shader = paint.getShader(); + + Sk3DShader* shader3D = NULL; + if (paint.getMaskFilter() != NULL && paint.getMaskFilter()->getFormat() == SkMask::k3D_Format) + { + shader3D = SkNEW_ARGS(Sk3DShader, (shader)); + ((SkPaint*)&paint)->setShader(shader3D)->unref(); + shader = shader3D; + } + + SkXfermode* mode = paint.getXfermode(); + if (NULL == shader && (NULL != mode || paint.getColorFilter() != NULL)) + { + // xfermodes require shaders for our current set of blitters + shader = SkNEW(SkColorShader); + ((SkPaint*)&paint)->setShader(shader)->unref(); + } + + if (paint.getColorFilter() != NULL) + { + SkASSERT(shader); + shader = SkNEW_ARGS(SkFilterShader, (shader, paint.getColorFilter())); + ((SkPaint*)&paint)->setShader(shader)->unref(); + } + + bool doDither = paint.isDither(); + + if (shader) + { + if (!shader->setContext(device, paint, matrix)) + return SkNEW(SkNullBlitter); + + // disable dither if our shader is natively 16bit (no need to upsample) + if (shader->getFlags() & SkShader::kIntrinsicly16_Flag) + doDither = false; + } + + switch (device.getConfig()) { + case SkBitmap::kA1_Config: + SK_PLACEMENT_NEW_ARGS(blitter, SkA1_Blitter, storage, storageSize, (device, paint)); + break; + + case SkBitmap::kA8_Config: + if (shader) + SK_PLACEMENT_NEW_ARGS(blitter, SkA8_Shader_Blitter, storage, storageSize, (device, paint)); + else + SK_PLACEMENT_NEW_ARGS(blitter, SkA8_Blitter, storage, storageSize, (device, paint)); + break; + + case SkBitmap::kARGB_4444_Config: + blitter = SkBlitter_ChooseD4444(device, paint, storage, storageSize); + break; + + case SkBitmap::kRGB_565_Config: + if (shader) + { + if (mode) + SK_PLACEMENT_NEW_ARGS(blitter, SkRGB16_Shader_Xfermode_Blitter, storage, storageSize, (device, paint)); + else if (SkShader::CanCallShadeSpan16(shader->getFlags()) && !doDither) + SK_PLACEMENT_NEW_ARGS(blitter, SkRGB16_Shader16_Blitter, storage, storageSize, (device, paint)); + else + SK_PLACEMENT_NEW_ARGS(blitter, SkRGB16_Shader_Blitter, storage, storageSize, (device, paint)); + } + else if (paint.getColor() == SK_ColorBLACK) + SK_PLACEMENT_NEW_ARGS(blitter, SkRGB16_Black_Blitter, storage, storageSize, (device, paint)); + else + SK_PLACEMENT_NEW_ARGS(blitter, SkRGB16_Blitter, storage, storageSize, (device, paint)); + break; + + case SkBitmap::kARGB_8888_Config: + if (shader) + SK_PLACEMENT_NEW_ARGS(blitter, SkARGB32_Shader_Blitter, storage, storageSize, (device, paint)); + else if (paint.getColor() == SK_ColorBLACK) + SK_PLACEMENT_NEW_ARGS(blitter, SkARGB32_Black_Blitter, storage, storageSize, (device, paint)); + else if (paint.getAlpha() == 0xFF) + SK_PLACEMENT_NEW_ARGS(blitter, SkARGB32_Opaque_Blitter, storage, storageSize, (device, paint)); + else + SK_PLACEMENT_NEW_ARGS(blitter, SkARGB32_Blitter, storage, storageSize, (device, paint)); + break; + + default: + SkASSERT(!"unsupported device config"); + SK_PLACEMENT_NEW(blitter, SkNullBlitter, storage, storageSize); + } + + if (shader3D) + { + void (*proc)(void*) = ((void*)storage == (void*)blitter) ? destroy_blitter : delete_blitter; + SkAutoCallProc tmp(blitter, proc); + + blitter = SkNEW_ARGS(Sk3DBlitter, (blitter, shader3D, proc)); + (void)tmp.detach(); + } + return blitter; +} + +////////////////////////////////////////////////////////////////////////////////////////////////////// + +const uint16_t gMask_0F0F = 0xF0F; +const uint32_t gMask_00FF00FF = 0xFF00FF; + +////////////////////////////////////////////////////////////////////////////////////////////////////// + +SkShaderBlitter::SkShaderBlitter(const SkBitmap& device, const SkPaint& paint) + : INHERITED(device) +{ + fShader = paint.getShader(); + SkASSERT(fShader); + + fShader->ref(); + fShader->beginSession(); +} + +SkShaderBlitter::~SkShaderBlitter() +{ + fShader->endSession(); + fShader->unref(); +} + diff --git a/skia/sgl/SkBlitter.h b/skia/sgl/SkBlitter.h new file mode 100644 index 0000000..56d69c9 --- /dev/null +++ b/skia/sgl/SkBlitter.h @@ -0,0 +1,143 @@ +/* libs/graphics/sgl/SkBlitter.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkBlitter_DEFINED +#define SkBlitter_DEFINED + +#include "SkBitmap.h" +#include "SkMatrix.h" +#include "SkPaint.h" +#include "SkRefCnt.h" +#include "SkRegion.h" +#include "SkMask.h" + +class SkBlitter { +public: + virtual ~SkBlitter(); + + virtual void blitH(int x, int y, int width); + virtual void blitAntiH(int x, int y, const SkAlpha[], const int16_t runs[]); + virtual void blitV(int x, int y, int height, SkAlpha alpha); + virtual void blitRect(int x, int y, int width, int height); + virtual void blitMask(const SkMask&, const SkIRect& clip); + + /* If the blitter just sets a single value for each pixel, return the + bitmap it draws into, and assign value. If not, return NULL and ignore + the value parameter. + */ + virtual const SkBitmap* justAnOpaqueColor(uint32_t* value); + + // not virtual, just helpers + void blitMaskRegion(const SkMask& mask, const SkRegion& clip); + void blitRectRegion(const SkIRect& rect, const SkRegion& clip); + void blitRegion(const SkRegion& clip); + + // factories + static SkBlitter* Choose(const SkBitmap& device, + const SkMatrix& matrix, + const SkPaint& paint) { + return Choose(device, matrix, paint, NULL, 0); + } + + static SkBlitter* Choose(const SkBitmap& device, + const SkMatrix& matrix, + const SkPaint& paint, + void* storage, size_t storageSize); + + static SkBlitter* ChooseSprite(const SkBitmap& device, + const SkPaint&, + const SkBitmap& src, + int left, int top, + void* storage, size_t storageSize); + +private: +}; + +/** This blitter silently never draws anything. +*/ +class SkNullBlitter : public SkBlitter { +public: + virtual void blitH(int x, int y, int width); + virtual void blitAntiH(int x, int y, const SkAlpha[], const int16_t runs[]); + virtual void blitV(int x, int y, int height, SkAlpha alpha); + virtual void blitRect(int x, int y, int width, int height); + virtual void blitMask(const SkMask&, const SkIRect& clip); + virtual const SkBitmap* justAnOpaqueColor(uint32_t* value); +}; + +/** Wraps another (real) blitter, and ensures that the real blitter is only + called with coordinates that have been clipped by the specified clipRect. + This means the caller need not perform the clipping ahead of time. +*/ +class SkRectClipBlitter : public SkBlitter { +public: + void init(SkBlitter* blitter, const SkIRect& clipRect) { + SkASSERT(!clipRect.isEmpty()); + fBlitter = blitter; + fClipRect = clipRect; + } + + // overrides + virtual void blitH(int x, int y, int width); + virtual void blitAntiH(int x, int y, const SkAlpha[], const int16_t runs[]); + virtual void blitV(int x, int y, int height, SkAlpha alpha); + virtual void blitRect(int x, int y, int width, int height); + virtual void blitMask(const SkMask&, const SkIRect& clip); + virtual const SkBitmap* justAnOpaqueColor(uint32_t* value); + +private: + SkBlitter* fBlitter; + SkIRect fClipRect; +}; + +/** Wraps another (real) blitter, and ensures that the real blitter is only +called with coordinates that have been clipped by the specified clipRgn. +This means the caller need not perform the clipping ahead of time. +*/ +class SkRgnClipBlitter : public SkBlitter { +public: + void init(SkBlitter* blitter, const SkRegion* clipRgn) { + SkASSERT(clipRgn && !clipRgn->isEmpty()); + fBlitter = blitter; + fRgn = clipRgn; + } + + // overrides + virtual void blitH(int x, int y, int width); + virtual void blitAntiH(int x, int y, const SkAlpha[], const int16_t runs[]); + virtual void blitV(int x, int y, int height, SkAlpha alpha); + virtual void blitRect(int x, int y, int width, int height); + virtual void blitMask(const SkMask&, const SkIRect& clip); + virtual const SkBitmap* justAnOpaqueColor(uint32_t* value); + +private: + SkBlitter* fBlitter; + const SkRegion* fRgn; +}; + +class SkBlitterClipper { +public: + SkBlitter* apply(SkBlitter* blitter, const SkRegion* clip, + const SkIRect* bounds = NULL); + +private: + SkNullBlitter fNullBlitter; + SkRectClipBlitter fRectBlitter; + SkRgnClipBlitter fRgnBlitter; +}; + +#endif diff --git a/skia/sgl/SkBlitter_4444.cpp b/skia/sgl/SkBlitter_4444.cpp new file mode 100644 index 0000000..de42312 --- /dev/null +++ b/skia/sgl/SkBlitter_4444.cpp @@ -0,0 +1,495 @@ +/* libs/graphics/sgl/SkBlitter_ARGB32.cpp + ** + ** Copyright 2006, Google Inc. + ** + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** + ** http://www.apache.org/licenses/LICENSE-2.0 + ** + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + */ + +#include "SkCoreBlitters.h" +#include "SkColorPriv.h" +#include "SkDither.h" +#include "SkShader.h" +#include "SkTemplatesPriv.h" +#include "SkUtils.h" +#include "SkXfermode.h" + +inline SkPMColor SkBlendARGB4444(SkPMColor16 src, SkPMColor16 dst, U8CPU aa) +{ + SkASSERT((unsigned)aa <= 255); + + unsigned src_scale = SkAlpha255To256(aa) >> 4; + unsigned dst_scale = SkAlpha15To16(15 - SkAlphaMul4(SkGetPackedA4444(src), src_scale)); + + uint32_t src32 = SkExpand_4444(src) * src_scale; + uint32_t dst32 = SkExpand_4444(dst) * dst_scale; + return SkCompact_4444((src32 + dst32) >> 4); +} + +/////////////////////////////////////////////////////////////////////////////// + +class SkARGB4444_Blitter : public SkRasterBlitter { +public: + SkARGB4444_Blitter(const SkBitmap& device, const SkPaint& paint); + virtual void blitH(int x, int y, int width); + virtual void blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]); + virtual void blitV(int x, int y, int height, SkAlpha alpha); + virtual void blitRect(int x, int y, int width, int height); + virtual void blitMask(const SkMask&, const SkIRect&); + virtual const SkBitmap* justAnOpaqueColor(uint32_t*); + +protected: + SkPMColor16 fPMColor16, fPMColor16Other; + SkPMColor16 fRawColor16, fRawColor16Other; + uint8_t fScale16; + +private: + // illegal + SkARGB4444_Blitter& operator=(const SkARGB4444_Blitter&); + + typedef SkRasterBlitter INHERITED; +}; + + +SkARGB4444_Blitter::SkARGB4444_Blitter(const SkBitmap& device, const SkPaint& paint) + : INHERITED(device) +{ + // cache premultiplied versions in 4444 + SkPMColor c = SkPreMultiplyColor(paint.getColor()); + fPMColor16 = SkPixel32ToPixel4444(c); + if (paint.isDither()) { + fPMColor16Other = SkDitherPixel32To4444(c); + } else { + fPMColor16Other = fPMColor16; + } + + // cache raw versions in 4444 + fRawColor16 = SkPackARGB4444(0xFF >> 4, SkColorGetR(c) >> 4, + SkColorGetG(c) >> 4, SkColorGetB(c) >> 4); + if (paint.isDither()) { + fRawColor16Other = SkDitherARGB32To4444(0xFF, SkColorGetR(c), + SkColorGetG(c), SkColorGetB(c)); + } else { + fRawColor16Other = fRawColor16; + } + + // our dithered color will be the same or more opaque than the original + // so use dithered to compute our scale + SkASSERT(SkGetPackedA4444(fPMColor16Other) >= SkGetPackedA4444(fPMColor16)); + + fScale16 = SkAlpha15To16(SkGetPackedA4444(fPMColor16Other)); + if (16 == fScale16) { + // force the original to also be opaque + fPMColor16 |= (0xF << SK_A4444_SHIFT); + } +} + +const SkBitmap* SkARGB4444_Blitter::justAnOpaqueColor(uint32_t* value) +{ + if (16 == fScale16) { + *value = fPMColor16; + return &fDevice; + } + return NULL; +} + +static void src_over_4444(SkPMColor16 dst[], SkPMColor16 color, + SkPMColor16 other, unsigned invScale, int count) +{ + int twice = count >> 1; + while (--twice >= 0) { + *dst = color + SkAlphaMulQ4(*dst, invScale); + dst++; + *dst = other + SkAlphaMulQ4(*dst, invScale); + dst++; + } + if (color & 1) { + *dst = color + SkAlphaMulQ4(*dst, invScale); + } +} + +static inline uint32_t SkExpand_4444_Replicate(SkPMColor16 c) +{ + uint32_t c32 = SkExpand_4444(c); + return c32 | (c32 << 4); +} + +static void src_over_4444x(SkPMColor16 dst[], uint32_t color, + uint32_t other, unsigned invScale, int count) +{ + int twice = count >> 1; + uint32_t tmp; + while (--twice >= 0) { + tmp = SkExpand_4444(*dst) * invScale; + *dst++ = SkCompact_4444((color + tmp) >> 4); + tmp = SkExpand_4444(*dst) * invScale; + *dst++ = SkCompact_4444((other + tmp) >> 4); + } + if (color & 1) { + tmp = SkExpand_4444(*dst) * invScale; + *dst = SkCompact_4444((color + tmp) >> 4); + } +} + +void SkARGB4444_Blitter::blitH(int x, int y, int width) +{ + SkASSERT(x >= 0 && y >= 0 && x + width <= fDevice.width()); + + if (0 == fScale16) { + return; + } + + SkPMColor16* device = fDevice.getAddr16(x, y); + SkPMColor16 color = fPMColor16; + SkPMColor16 other = fPMColor16Other; + + if ((x ^ y) & 1) { + SkTSwap<SkPMColor16>(color, other); + } + + if (16 == fScale16) { + sk_dither_memset16(device, color, other, width); + } + else { + src_over_4444x(device, SkExpand_4444_Replicate(color), + SkExpand_4444_Replicate(other), + 16 - fScale16, width); + } +} + +void SkARGB4444_Blitter::blitV(int x, int y, int height, SkAlpha alpha) +{ + if (0 == alpha || 0 == fScale16) { + return; + } + + SkPMColor16* device = fDevice.getAddr16(x, y); + SkPMColor16 color = fPMColor16; + SkPMColor16 other = fPMColor16Other; + unsigned rb = fDevice.rowBytes(); + + if ((x ^ y) & 1) { + SkTSwap<SkPMColor16>(color, other); + } + + if (16 == fScale16 && 255 == alpha) { + while (--height >= 0) { + *device = color; + device = (SkPMColor16*)((char*)device + rb); + SkTSwap<SkPMColor16>(color, other); + } + } else { + unsigned alphaScale = SkAlpha255To256(alpha); + uint32_t c32 = SkExpand_4444(color) * (alphaScale >> 4); + // need to normalize the low nibble of each expanded component + // so we don't overflow the add with d32 + c32 = SkCompact_4444(c32 >> 4); + unsigned invScale = 16 - SkAlpha15To16(SkGetPackedA4444(c32)); + // now re-expand and replicate + c32 = SkExpand_4444_Replicate(c32); + + while (--height >= 0) { + uint32_t d32 = SkExpand_4444(*device) * invScale; + *device = SkCompact_4444((c32 + d32) >> 4); + device = (SkPMColor16*)((char*)device + rb); + } + } +} + +void SkARGB4444_Blitter::blitRect(int x, int y, int width, int height) +{ + SkASSERT(x >= 0 && y >= 0 && x + width <= fDevice.width() && y + height <= fDevice.height()); + + if (0 == fScale16) { + return; + } + + SkPMColor16* device = fDevice.getAddr16(x, y); + SkPMColor16 color = fPMColor16; + SkPMColor16 other = fPMColor16Other; + + if ((x ^ y) & 1) { + SkTSwap<SkPMColor16>(color, other); + } + + if (16 == fScale16) { + while (--height >= 0) { + sk_dither_memset16(device, color, other, width); + device = (SkPMColor16*)((char*)device + fDevice.rowBytes()); + SkTSwap<SkPMColor16>(color, other); + } + } else { + unsigned invScale = 16 - fScale16; + + uint32_t c32 = SkExpand_4444_Replicate(color); + uint32_t o32 = SkExpand_4444_Replicate(other); + while (--height >= 0) { + src_over_4444x(device, c32, o32, invScale, width); + device = (SkPMColor16*)((char*)device + fDevice.rowBytes()); + SkTSwap<uint32_t>(c32, o32); + } + } +} + +void SkARGB4444_Blitter::blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]) +{ + if (0 == fScale16) { + return; + } + + SkPMColor16* device = fDevice.getAddr16(x, y); + SkPMColor16 color = fPMColor16; + SkPMColor16 other = fPMColor16Other; + + if ((x ^ y) & 1) { + SkTSwap<SkPMColor16>(color, other); + } + + for (;;) { + int count = runs[0]; + SkASSERT(count >= 0); + if (count <= 0) { + return; + } + + unsigned aa = antialias[0]; + if (aa) { + if (0xFF == aa) { + if (16 == fScale16) { + sk_dither_memset16(device, color, other, count); + } else { + src_over_4444(device, color, other, 16 - fScale16, count); + } + } else { + // todo: respect dithering + aa = SkAlpha255To256(aa); // FIX + SkPMColor16 src = SkAlphaMulQ4(color, aa >> 4); + unsigned dst_scale = SkAlpha15To16(15 - SkGetPackedA4444(src)); // FIX + int n = count; + do { + --n; + device[n] = src + SkAlphaMulQ4(device[n], dst_scale); + } while (n > 0); + } + } + + runs += count; + antialias += count; + device += count; + + if (count & 1) { + SkTSwap<SkPMColor16>(color, other); + } + } +} + +////////////////////////////////////////////////////////////////////////////////////// + +#define solid_8_pixels(mask, dst, color) \ +do { \ +if (mask & 0x80) dst[0] = color; \ +if (mask & 0x40) dst[1] = color; \ +if (mask & 0x20) dst[2] = color; \ +if (mask & 0x10) dst[3] = color; \ +if (mask & 0x08) dst[4] = color; \ +if (mask & 0x04) dst[5] = color; \ +if (mask & 0x02) dst[6] = color; \ +if (mask & 0x01) dst[7] = color; \ +} while (0) + +#define SK_BLITBWMASK_NAME SkARGB4444_BlitBW +#define SK_BLITBWMASK_ARGS , SkPMColor16 color +#define SK_BLITBWMASK_BLIT8(mask, dst) solid_8_pixels(mask, dst, color) +#define SK_BLITBWMASK_GETADDR getAddr16 +#define SK_BLITBWMASK_DEVTYPE uint16_t +#include "SkBlitBWMaskTemplate.h" + +#define blend_8_pixels(mask, dst, sc, dst_scale) \ +do { \ +if (mask & 0x80) { dst[0] = sc + SkAlphaMulQ4(dst[0], dst_scale); } \ +if (mask & 0x40) { dst[1] = sc + SkAlphaMulQ4(dst[1], dst_scale); } \ +if (mask & 0x20) { dst[2] = sc + SkAlphaMulQ4(dst[2], dst_scale); } \ +if (mask & 0x10) { dst[3] = sc + SkAlphaMulQ4(dst[3], dst_scale); } \ +if (mask & 0x08) { dst[4] = sc + SkAlphaMulQ4(dst[4], dst_scale); } \ +if (mask & 0x04) { dst[5] = sc + SkAlphaMulQ4(dst[5], dst_scale); } \ +if (mask & 0x02) { dst[6] = sc + SkAlphaMulQ4(dst[6], dst_scale); } \ +if (mask & 0x01) { dst[7] = sc + SkAlphaMulQ4(dst[7], dst_scale); } \ +} while (0) + +#define SK_BLITBWMASK_NAME SkARGB4444_BlendBW +#define SK_BLITBWMASK_ARGS , uint16_t sc, unsigned dst_scale +#define SK_BLITBWMASK_BLIT8(mask, dst) blend_8_pixels(mask, dst, sc, dst_scale) +#define SK_BLITBWMASK_GETADDR getAddr16 +#define SK_BLITBWMASK_DEVTYPE uint16_t +#include "SkBlitBWMaskTemplate.h" + +void SkARGB4444_Blitter::blitMask(const SkMask& mask, const SkIRect& clip) +{ + SkASSERT(mask.fBounds.contains(clip)); + + if (0 == fScale16) { + return; + } + + if (mask.fFormat == SkMask::kBW_Format) { + if (16 == fScale16) { + SkARGB4444_BlitBW(fDevice, mask, clip, fPMColor16); + } else { + SkARGB4444_BlendBW(fDevice, mask, clip, fPMColor16, 16 - fScale16); + } + return; + } + + int x = clip.fLeft; + int y = clip.fTop; + int width = clip.width(); + int height = clip.height(); + + SkPMColor16* device = fDevice.getAddr16(x, y); + const uint8_t* alpha = mask.getAddr(x, y); + SkPMColor16 srcColor = fPMColor16; + unsigned devRB = fDevice.rowBytes() - (width << 1); + unsigned maskRB = mask.fRowBytes - width; + + do { + int w = width; + do { + unsigned aa = *alpha++; + *device = SkBlendARGB4444(srcColor, *device, aa); + device += 1; + } while (--w != 0); + device = (SkPMColor16*)((char*)device + devRB); + alpha += maskRB; + } while (--height != 0); +} + +////////////////////////////////////////////////////////////////////////////////////////// + +class SkARGB4444_Shader_Blitter : public SkShaderBlitter { + SkXfermode* fXfermode; + SkBlitRow::Proc fOpaqueProc; + SkBlitRow::Proc fAlphaProc; + SkPMColor* fBuffer; +public: +SkARGB4444_Shader_Blitter(const SkBitmap& device, const SkPaint& paint) + : INHERITED(device, paint) +{ + fBuffer = (SkPMColor*)sk_malloc_throw(device.width() * (sizeof(SkPMColor))); + + (fXfermode = paint.getXfermode())->safeRef(); + + unsigned flags = 0; + if (!(fShader->getFlags() & SkShader::kOpaqueAlpha_Flag)) { + flags |= SkBlitRow::kSrcPixelAlpha_Flag; + } + if (paint.isDither()) { + flags |= SkBlitRow::kDither_Flag; + } + fOpaqueProc = SkBlitRow::Factory(flags, SkBitmap::kARGB_4444_Config); + fAlphaProc = SkBlitRow::Factory(flags | SkBlitRow::kGlobalAlpha_Flag, + SkBitmap::kARGB_4444_Config); +} + +virtual ~SkARGB4444_Shader_Blitter() +{ + fXfermode->safeUnref(); + sk_free(fBuffer); +} + +virtual void blitH(int x, int y, int width) +{ + SkASSERT(x >= 0 && y >= 0 && x + width <= fDevice.width()); + + SkPMColor16* device = fDevice.getAddr16(x, y); + SkPMColor* span = fBuffer; + + fShader->shadeSpan(x, y, span, width); + if (fXfermode) { + fXfermode->xfer4444(device, span, width, NULL); + } + else { + fOpaqueProc(device, span, width, 0xFF, x, y); + } +} + +virtual void blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]) +{ + SkPMColor* span = fBuffer; + SkPMColor16* device = fDevice.getAddr16(x, y); + SkShader* shader = fShader; + SkXfermode* xfer = fXfermode; + + if (NULL != xfer) { + for (;;) { + int count = *runs; + if (count <= 0) + break; + int aa = *antialias; + if (aa) { + shader->shadeSpan(x, y, span, count); + if (255 == aa) { + xfer->xfer4444(device, span, count, NULL); + } else { + // count is almost always 1 + for (int i = count - 1; i >= 0; --i) { + xfer->xfer4444(&device[i], &span[i], count, antialias); + } + } + } + device += count; + runs += count; + antialias += count; + x += count; + } + } else { // no xfermode + for (;;) { + int count = *runs; + if (count <= 0) + break; + int aa = *antialias; + if (aa) { + fShader->shadeSpan(x, y, span, count); + if (255 == aa) { + fOpaqueProc(device, span, count, aa, x, y); + } else { + fAlphaProc(device, span, count, aa, x, y); + } + } + device += count; + runs += count; + antialias += count; + x += count; + } + } +} + +private: + typedef SkShaderBlitter INHERITED; +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +SkBlitter* SkBlitter_ChooseD4444(const SkBitmap& device, + const SkPaint& paint, + void* storage, size_t storageSize) +{ + SkBlitter* blitter; + + if (paint.getShader()) { + SK_PLACEMENT_NEW_ARGS(blitter, SkARGB4444_Shader_Blitter, storage, storageSize, (device, paint)); + } else { + SK_PLACEMENT_NEW_ARGS(blitter, SkARGB4444_Blitter, storage, storageSize, (device, paint)); + } + return blitter; +} + diff --git a/skia/sgl/SkBlitter_A1.cpp b/skia/sgl/SkBlitter_A1.cpp new file mode 100644 index 0000000..61d9cf6 --- /dev/null +++ b/skia/sgl/SkBlitter_A1.cpp @@ -0,0 +1,63 @@ +/* libs/graphics/sgl/SkBlitter_A1.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkCoreBlitters.h" + +SkA1_Blitter::SkA1_Blitter(const SkBitmap& device, const SkPaint& paint) + : INHERITED(device) +{ + fSrcA = SkToU8(SkColorGetA(paint.getColor())); +} + +void SkA1_Blitter::blitH(int x, int y, int width) +{ + SkASSERT(x >= 0 && y >= 0 && (unsigned)(x + width) <= (unsigned)fDevice.width()); + + if (fSrcA <= 0x7F) + return; + + uint8_t* dst = fDevice.getAddr1(x, y); + int right = x + width; + + int left_mask = 0xFF >> (x & 7); + int rite_mask = 0xFF << (8 - (right & 7)); + int full_runs = (right >> 3) - ((x + 7) >> 3); + + // check for empty right mask, so we don't read off the end (or go slower than we need to) + if (rite_mask == 0) + { + SkASSERT(full_runs >= 0); + full_runs -= 1; + rite_mask = 0xFF; + } + if (left_mask == 0xFF) + full_runs -= 1; + + if (full_runs < 0) + { + SkASSERT((left_mask & rite_mask) != 0); + *dst |= (left_mask & rite_mask); + } + else + { + *dst++ |= left_mask; + memset(dst, 0xFF, full_runs); + dst += full_runs; + *dst |= rite_mask; + } +} + diff --git a/skia/sgl/SkBlitter_A8.cpp b/skia/sgl/SkBlitter_A8.cpp new file mode 100644 index 0000000..23f7f01 --- /dev/null +++ b/skia/sgl/SkBlitter_A8.cpp @@ -0,0 +1,387 @@ +/* libs/graphics/sgl/SkBlitter_A8.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkCoreBlitters.h" +#include "SkColorPriv.h" +#include "SkShader.h" +#include "SkXfermode.h" + +SkA8_Blitter::SkA8_Blitter(const SkBitmap& device, const SkPaint& paint) + : INHERITED(device) +{ + fSrcA = SkColorGetA(paint.getColor()); +} + +const SkBitmap* SkA8_Blitter::justAnOpaqueColor(uint32_t* value) +{ + if (255 == fSrcA) + { + *value = 255; + return &fDevice; + } + return NULL; +} + +void SkA8_Blitter::blitH(int x, int y, int width) +{ + SkASSERT(x >= 0 && y >= 0 && (unsigned)(x + width) <= (unsigned)fDevice.width()); + + if (fSrcA == 0) + return; + + uint8_t* device = fDevice.getAddr8(x, y); + + if (fSrcA == 255) + { + memset(device, 0xFF, width); + } + else + { + unsigned scale = 256 - SkAlpha255To256(fSrcA); + unsigned srcA = fSrcA; + + for (int i = 0; i < width; i++) + { + device[i] = SkToU8(srcA + SkAlphaMul(device[i], scale)); + } + } +} + +void SkA8_Blitter::blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]) +{ + if (fSrcA == 0) + return; + + uint8_t* device = fDevice.getAddr8(x, y); + unsigned srcA = fSrcA; + + for (;;) + { + int count = runs[0]; + SkASSERT(count >= 0); + if (count == 0) + return; + unsigned aa = antialias[0]; + + if (aa == 255 && srcA == 255) + memset(device, 0xFF, count); + else + { + unsigned sa = SkAlphaMul(srcA, SkAlpha255To256(aa)); + unsigned scale = 256 - sa; + + for (int i = 0; i < count; i++) + { + device[i] = SkToU8(sa + SkAlphaMul(device[i], scale)); + } + } + runs += count; + antialias += count; + device += count; + } +} + +///////////////////////////////////////////////////////////////////////////////////// + +#define solid_8_pixels(mask, dst) \ + do { \ + if (mask & 0x80) dst[0] = 0xFF; \ + if (mask & 0x40) dst[1] = 0xFF; \ + if (mask & 0x20) dst[2] = 0xFF; \ + if (mask & 0x10) dst[3] = 0xFF; \ + if (mask & 0x08) dst[4] = 0xFF; \ + if (mask & 0x04) dst[5] = 0xFF; \ + if (mask & 0x02) dst[6] = 0xFF; \ + if (mask & 0x01) dst[7] = 0xFF; \ + } while (0) + +#define SK_BLITBWMASK_NAME SkA8_BlitBW +#define SK_BLITBWMASK_ARGS +#define SK_BLITBWMASK_BLIT8(mask, dst) solid_8_pixels(mask, dst) +#define SK_BLITBWMASK_GETADDR getAddr8 +#define SK_BLITBWMASK_DEVTYPE uint8_t +#include "SkBlitBWMaskTemplate.h" + +static inline void blend_8_pixels(U8CPU bw, uint8_t dst[], U8CPU sa, unsigned dst_scale) +{ + if (bw & 0x80) dst[0] = SkToU8(sa + SkAlphaMul(dst[0], dst_scale)); + if (bw & 0x40) dst[1] = SkToU8(sa + SkAlphaMul(dst[1], dst_scale)); + if (bw & 0x20) dst[2] = SkToU8(sa + SkAlphaMul(dst[2], dst_scale)); + if (bw & 0x10) dst[3] = SkToU8(sa + SkAlphaMul(dst[3], dst_scale)); + if (bw & 0x08) dst[4] = SkToU8(sa + SkAlphaMul(dst[4], dst_scale)); + if (bw & 0x04) dst[5] = SkToU8(sa + SkAlphaMul(dst[5], dst_scale)); + if (bw & 0x02) dst[6] = SkToU8(sa + SkAlphaMul(dst[6], dst_scale)); + if (bw & 0x01) dst[7] = SkToU8(sa + SkAlphaMul(dst[7], dst_scale)); +} + +#define SK_BLITBWMASK_NAME SkA8_BlendBW +#define SK_BLITBWMASK_ARGS , U8CPU sa, unsigned dst_scale +#define SK_BLITBWMASK_BLIT8(mask, dst) blend_8_pixels(mask, dst, sa, dst_scale) +#define SK_BLITBWMASK_GETADDR getAddr8 +#define SK_BLITBWMASK_DEVTYPE uint8_t +#include "SkBlitBWMaskTemplate.h" + +void SkA8_Blitter::blitMask(const SkMask& mask, const SkIRect& clip) +{ + if (fSrcA == 0) + return; + + if (mask.fFormat == SkMask::kBW_Format) + { + if (fSrcA == 0xFF) + SkA8_BlitBW(fDevice, mask, clip); + else + SkA8_BlendBW(fDevice, mask, clip, fSrcA, SkAlpha255To256(255 - fSrcA)); + return; + } + + int x = clip.fLeft; + int y = clip.fTop; + int width = clip.width(); + int height = clip.height(); + uint8_t* device = fDevice.getAddr8(x, y); + const uint8_t* alpha = mask.getAddr(x, y); + unsigned srcA = fSrcA; + + while (--height >= 0) + { + for (int i = width - 1; i >= 0; --i) + { + unsigned sa; + // scale our src by the alpha value + { + int aa = alpha[i]; + if (aa == 0) + continue; + + if (aa == 255) + { + if (srcA == 255) + { + device[i] = 0xFF; + continue; + } + sa = srcA; + } + else + sa = SkAlphaMul(srcA, SkAlpha255To256(aa)); + } + + int scale = 256 - SkAlpha255To256(sa); + device[i] = SkToU8(sa + SkAlphaMul(device[i], scale)); + } + device += fDevice.rowBytes(); + alpha += mask.fRowBytes; + } +} + +/////////////////////////////////////////////////////////////////////////////////////// + +void SkA8_Blitter::blitV(int x, int y, int height, SkAlpha alpha) +{ + if (fSrcA == 0) + return; + + unsigned sa = SkAlphaMul(fSrcA, SkAlpha255To256(alpha)); + uint8_t* device = fDevice.getAddr8(x, y); + int rowBytes = fDevice.rowBytes(); + + if (sa == 0xFF) + { + for (int i = 0; i < height; i++) + { + *device = SkToU8(sa); + device += rowBytes; + } + } + else + { + unsigned scale = 256 - SkAlpha255To256(sa); + + for (int i = 0; i < height; i++) + { + *device = SkToU8(sa + SkAlphaMul(*device, scale)); + device += rowBytes; + } + } +} + +void SkA8_Blitter::blitRect(int x, int y, int width, int height) +{ + SkASSERT(x >= 0 && y >= 0 && (unsigned)(x + width) <= (unsigned)fDevice.width() && (unsigned)(y + height) <= (unsigned)fDevice.height()); + + if (fSrcA == 0) + return; + + uint8_t* device = fDevice.getAddr8(x, y); + unsigned srcA = fSrcA; + + if (srcA == 255) + { + while (--height >= 0) + { + memset(device, 0xFF, width); + device += fDevice.rowBytes(); + } + } + else + { + unsigned scale = 256 - SkAlpha255To256(srcA); + + while (--height >= 0) + { + for (int i = 0; i < width; i++) + { + device[i] = SkToU8(srcA + SkAlphaMul(device[i], scale)); + } + device += fDevice.rowBytes(); + } + } +} + +/////////////////////////////////////////////////////////////////////// + +SkA8_Shader_Blitter::SkA8_Shader_Blitter(const SkBitmap& device, const SkPaint& paint) + : INHERITED(device, paint) +{ + if ((fXfermode = paint.getXfermode()) != NULL) + { + fXfermode->ref(); + SkASSERT(fShader); + } + + int width = device.width(); + fBuffer = (SkPMColor*)sk_malloc_throw(sizeof(SkPMColor) * (width + (SkAlign4(width) >> 2))); + fAAExpand = (uint8_t*)(fBuffer + width); +} + +SkA8_Shader_Blitter::~SkA8_Shader_Blitter() +{ + fXfermode->safeUnref(); + sk_free(fBuffer); +} + +void SkA8_Shader_Blitter::blitH(int x, int y, int width) +{ + SkASSERT(x >= 0 && y >= 0 && (unsigned)(x + width) <= (unsigned)fDevice.width()); + + uint8_t* device = fDevice.getAddr8(x, y); + + if ((fShader->getFlags() & SkShader::kOpaqueAlpha_Flag) && fXfermode == NULL) + { + memset(device, 0xFF, width); + } + else + { + SkPMColor* span = fBuffer; + + fShader->shadeSpan(x, y, span, width); + if (fXfermode) + fXfermode->xferA8(device, span, width, NULL); + else + { + for (int i = width - 1; i >= 0; --i) + { + unsigned srcA = SkGetPackedA32(span[i]); + unsigned scale = 256 - SkAlpha255To256(srcA); + + device[i] = SkToU8(srcA + SkAlphaMul(device[i], scale)); + } + } + } +} + +static inline uint8_t aa_blend8(SkPMColor src, U8CPU da, int aa) +{ + SkASSERT((unsigned)aa <= 255); + + int src_scale = SkAlpha255To256(aa); + int sa = SkGetPackedA32(src); + int dst_scale = 256 - SkAlphaMul(sa, src_scale); + + return SkToU8((sa * src_scale + da * dst_scale) >> 8); +} + +void SkA8_Shader_Blitter::blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]) +{ + SkShader* shader = fShader; + SkXfermode* mode = fXfermode; + uint8_t* aaExpand = fAAExpand; + SkPMColor* span = fBuffer; + uint8_t* device = fDevice.getAddr8(x, y); + int opaque = fShader->getFlags() & SkShader::kOpaqueAlpha_Flag; + + for (;;) + { + int count = *runs; + if (count == 0) + break; + int aa = *antialias; + if (aa) + { + if (opaque && aa == 255 && mode == NULL) + memset(device, 0xFF, count); + else + { + shader->shadeSpan(x, y, span, count); + if (mode) + { + memset(aaExpand, aa, count); + mode->xferA8(device, span, count, aaExpand); + } + else + { + for (int i = count - 1; i >= 0; --i) + device[i] = aa_blend8(span[i], device[i], aa); + } + } + } + device += count; + runs += count; + antialias += count; + x += count; + } +} + +void SkA8_Shader_Blitter::blitMask(const SkMask& mask, const SkIRect& clip) +{ + if (mask.fFormat == SkMask::kBW_Format) + { + this->INHERITED::blitMask(mask, clip); + return; + } + + int x = clip.fLeft; + int y = clip.fTop; + int width = clip.width(); + int height = clip.height(); + uint8_t* device = fDevice.getAddr8(x, y); + const uint8_t* alpha = mask.getAddr(x, y); + + SkPMColor* span = fBuffer; + + while (--height >= 0) + { + fShader->shadeSpan(x, y, span, width); + fXfermode->xferA8(device, span, width, alpha); + + y += 1; + device += fDevice.rowBytes(); + alpha += mask.fRowBytes; + } +} + diff --git a/skia/sgl/SkBlitter_ARGB32.cpp b/skia/sgl/SkBlitter_ARGB32.cpp new file mode 100644 index 0000000..0fa0e0b --- /dev/null +++ b/skia/sgl/SkBlitter_ARGB32.cpp @@ -0,0 +1,485 @@ +/* libs/graphics/sgl/SkBlitter_ARGB32.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkCoreBlitters.h" +#include "SkColorPriv.h" +#include "SkShader.h" +#include "SkUtils.h" +#include "SkXfermode.h" + +SkARGB32_Blitter::SkARGB32_Blitter(const SkBitmap& device, const SkPaint& paint) + : INHERITED(device) { + uint32_t color = paint.getColor(); + + fSrcA = SkColorGetA(color); + unsigned scale = SkAlpha255To256(fSrcA); + fSrcR = SkAlphaMul(SkColorGetR(color), scale); + fSrcG = SkAlphaMul(SkColorGetG(color), scale); + fSrcB = SkAlphaMul(SkColorGetB(color), scale); + + fPMColor = SkPackARGB32(fSrcA, fSrcR, fSrcG, fSrcB); +} + +const SkBitmap* SkARGB32_Blitter::justAnOpaqueColor(uint32_t* value) { + if (255 == fSrcA) { + *value = fPMColor; + return &fDevice; + } + return NULL; +} + +#if defined _WIN32 && _MSC_VER >= 1300 // disable warning : local variable used without having been initialized +#pragma warning ( push ) +#pragma warning ( disable : 4701 ) +#endif + +void SkARGB32_Blitter::blitH(int x, int y, int width) { + SkASSERT(x >= 0 && y >= 0 && x + width <= fDevice.width()); + + if (fSrcA == 0) { + return; + } + + uint32_t* device = fDevice.getAddr32(x, y); + + if (fSrcA == 255) { + sk_memset32(device, fPMColor, width); + } else { + uint32_t color = fPMColor; + unsigned dst_scale = SkAlpha255To256(255 - fSrcA); + uint32_t prevDst = ~device[0]; // so we always fail the test the first time + uint32_t result SK_INIT_TO_AVOID_WARNING; + + for (int i = 0; i < width; i++) { + uint32_t currDst = device[i]; + if (currDst != prevDst) { + result = color + SkAlphaMulQ(currDst, dst_scale); + prevDst = currDst; + } + device[i] = result; + } + } +} + +void SkARGB32_Blitter::blitAntiH(int x, int y, const SkAlpha antialias[], + const int16_t runs[]) { + if (fSrcA == 0) { + return; + } + + uint32_t color = fPMColor; + uint32_t* device = fDevice.getAddr32(x, y); + unsigned opaqueMask = fSrcA; // if fSrcA is 0xFF, then we will catch the fast opaque case + + for (;;) { + int count = runs[0]; + SkASSERT(count >= 0); + if (count <= 0) { + return; + } + unsigned aa = antialias[0]; + if (aa) { + if ((opaqueMask & aa) == 255) { + sk_memset32(device, color, count); + } else { + uint32_t sc = SkAlphaMulQ(color, aa); + unsigned dst_scale = 255 - SkGetPackedA32(sc); + int n = count; + do { + --n; + device[n] = sc + SkAlphaMulQ(device[n], dst_scale); + } while (n > 0); + } + } + runs += count; + antialias += count; + device += count; + } +} + +////////////////////////////////////////////////////////////////////////////////////// + +#define solid_8_pixels(mask, dst, color) \ + do { \ + if (mask & 0x80) dst[0] = color; \ + if (mask & 0x40) dst[1] = color; \ + if (mask & 0x20) dst[2] = color; \ + if (mask & 0x10) dst[3] = color; \ + if (mask & 0x08) dst[4] = color; \ + if (mask & 0x04) dst[5] = color; \ + if (mask & 0x02) dst[6] = color; \ + if (mask & 0x01) dst[7] = color; \ + } while (0) + +#define SK_BLITBWMASK_NAME SkARGB32_BlitBW +#define SK_BLITBWMASK_ARGS , SkPMColor color +#define SK_BLITBWMASK_BLIT8(mask, dst) solid_8_pixels(mask, dst, color) +#define SK_BLITBWMASK_GETADDR getAddr32 +#define SK_BLITBWMASK_DEVTYPE uint32_t +#include "SkBlitBWMaskTemplate.h" + +#define blend_8_pixels(mask, dst, sc, dst_scale) \ + do { \ + if (mask & 0x80) { dst[0] = sc + SkAlphaMulQ(dst[0], dst_scale); } \ + if (mask & 0x40) { dst[1] = sc + SkAlphaMulQ(dst[1], dst_scale); } \ + if (mask & 0x20) { dst[2] = sc + SkAlphaMulQ(dst[2], dst_scale); } \ + if (mask & 0x10) { dst[3] = sc + SkAlphaMulQ(dst[3], dst_scale); } \ + if (mask & 0x08) { dst[4] = sc + SkAlphaMulQ(dst[4], dst_scale); } \ + if (mask & 0x04) { dst[5] = sc + SkAlphaMulQ(dst[5], dst_scale); } \ + if (mask & 0x02) { dst[6] = sc + SkAlphaMulQ(dst[6], dst_scale); } \ + if (mask & 0x01) { dst[7] = sc + SkAlphaMulQ(dst[7], dst_scale); } \ + } while (0) + +#define SK_BLITBWMASK_NAME SkARGB32_BlendBW +#define SK_BLITBWMASK_ARGS , uint32_t sc, unsigned dst_scale +#define SK_BLITBWMASK_BLIT8(mask, dst) blend_8_pixels(mask, dst, sc, dst_scale) +#define SK_BLITBWMASK_GETADDR getAddr32 +#define SK_BLITBWMASK_DEVTYPE uint32_t +#include "SkBlitBWMaskTemplate.h" + +void SkARGB32_Blitter::blitMask(const SkMask& mask, const SkIRect& clip) { + SkASSERT(mask.fBounds.contains(clip)); + SkASSERT(fSrcA != 0xFF); + + if (fSrcA == 0) { + return; + } + + if (mask.fFormat == SkMask::kBW_Format) { + SkARGB32_BlendBW(fDevice, mask, clip, fPMColor, SkAlpha255To256(255 - fSrcA)); + return; + } + + int x = clip.fLeft; + int y = clip.fTop; + int width = clip.width(); + int height = clip.height(); + + uint32_t* device = fDevice.getAddr32(x, y); + const uint8_t* alpha = mask.getAddr(x, y); + uint32_t srcColor = fPMColor; + unsigned devRB = fDevice.rowBytes() - (width << 2); + unsigned maskRB = mask.fRowBytes - width; + + do { + int w = width; + do { + unsigned aa = *alpha++; + *device = SkBlendARGB32(srcColor, *device, aa); + device += 1; + } while (--w != 0); + device = (uint32_t*)((char*)device + devRB); + alpha += maskRB; + } while (--height != 0); +} + +void SkARGB32_Opaque_Blitter::blitMask(const SkMask& mask, + const SkIRect& clip) { + SkASSERT(mask.fBounds.contains(clip)); + + if (mask.fFormat == SkMask::kBW_Format) { + SkARGB32_BlitBW(fDevice, mask, clip, fPMColor); + return; + } + + int x = clip.fLeft; + int y = clip.fTop; + int width = clip.width(); + int height = clip.height(); + + uint32_t* device = fDevice.getAddr32(x, y); + const uint8_t* alpha = mask.getAddr(x, y); + uint32_t srcColor = fPMColor; + unsigned devRB = fDevice.rowBytes() - (width << 2); + unsigned maskRB = mask.fRowBytes - width; + + do { + int w = width; + do { + unsigned aa = *alpha++; + *device = SkAlphaMulQ(srcColor, SkAlpha255To256(aa)) + SkAlphaMulQ(*device, SkAlpha255To256(255 - aa)); + device += 1; + } while (--w != 0); + device = (uint32_t*)((char*)device + devRB); + alpha += maskRB; + } while (--height != 0); +} + +////////////////////////////////////////////////////////////////////////////////////// + +void SkARGB32_Blitter::blitV(int x, int y, int height, SkAlpha alpha) { + if (alpha == 0 || fSrcA == 0) { + return; + } + + uint32_t* device = fDevice.getAddr32(x, y); + uint32_t color = fPMColor; + + if (alpha != 255) { + color = SkAlphaMulQ(color, SkAlpha255To256(alpha)); + } + + unsigned dst_scale = 255 - SkGetPackedA32(color); + uint32_t prevDst = ~device[0]; + uint32_t result SK_INIT_TO_AVOID_WARNING; + uint32_t rowBytes = fDevice.rowBytes(); + + while (--height >= 0) { + uint32_t dst = device[0]; + if (dst != prevDst) { + result = color + SkAlphaMulQ(dst, dst_scale); + prevDst = dst; + } + device[0] = result; + device = (uint32_t*)((char*)device + rowBytes); + } +} + +void SkARGB32_Blitter::blitRect(int x, int y, int width, int height) { + SkASSERT(x >= 0 && y >= 0 && x + width <= fDevice.width() && y + height <= fDevice.height()); + + if (fSrcA == 0) { + return; + } + + uint32_t* device = fDevice.getAddr32(x, y); + uint32_t color = fPMColor; + + if (fSrcA == 255) { + while (--height >= 0) { + sk_memset32(device, color, width); + device = (uint32_t*)((char*)device + fDevice.rowBytes()); + } + } else { + unsigned dst_scale = SkAlpha255To256(255 - fSrcA); + + while (--height >= 0) { + uint32_t prevDst = ~device[0]; + uint32_t result SK_INIT_TO_AVOID_WARNING; + + for (int i = 0; i < width; i++) { + uint32_t dst = device[i]; + if (dst != prevDst) { + result = color + SkAlphaMulQ(dst, dst_scale); + prevDst = dst; + } + device[i] = result; + } + device = (uint32_t*)((char*)device + fDevice.rowBytes()); + } + } +} + +#if defined _WIN32 && _MSC_VER >= 1300 +#pragma warning ( pop ) +#endif + +/////////////////////////////////////////////////////////////////////// + +void SkARGB32_Black_Blitter::blitMask(const SkMask& mask, const SkIRect& clip) { + SkASSERT(mask.fBounds.contains(clip)); + + if (mask.fFormat == SkMask::kBW_Format) { + SkPMColor black = (SkPMColor)(SK_A32_MASK << SK_A32_SHIFT); + + SkARGB32_BlitBW(fDevice, mask, clip, black); + } else { + uint32_t* device = fDevice.getAddr32(clip.fLeft, clip.fTop); + const uint8_t* alpha = mask.getAddr(clip.fLeft, clip.fTop); + unsigned width = clip.width(); + unsigned height = clip.height(); + unsigned deviceRB = fDevice.rowBytes() - (width << 2); + unsigned maskRB = mask.fRowBytes - width; + + SkASSERT((int)height > 0); + SkASSERT((int)width > 0); + SkASSERT((int)deviceRB >= 0); + SkASSERT((int)maskRB >= 0); + + do { + unsigned w = width; + do { + unsigned aa = *alpha++; + *device = (aa << SK_A32_SHIFT) + SkAlphaMulQ(*device, SkAlpha255To256(255 - aa)); + device += 1; + } while (--w != 0); + device = (uint32_t*)((char*)device + deviceRB); + alpha += maskRB; + } while (--height != 0); + } +} + +void SkARGB32_Black_Blitter::blitAntiH(int x, int y, const SkAlpha antialias[], + const int16_t runs[]) { + uint32_t* device = fDevice.getAddr32(x, y); + SkPMColor black = (SkPMColor)(SK_A32_MASK << SK_A32_SHIFT); + + for (;;) { + int count = runs[0]; + SkASSERT(count >= 0); + if (count <= 0) { + return; + } + unsigned aa = antialias[0]; + if (aa) { + if (aa == 255) { + sk_memset32(device, black, count); + } else { + SkPMColor src = aa << SK_A32_SHIFT; + unsigned dst_scale = 256 - aa; + int n = count; + do { + --n; + device[n] = src + SkAlphaMulQ(device[n], dst_scale); + } while (n > 0); + } + } + runs += count; + antialias += count; + device += count; + } +} + +////////////////////////////////////////////////////////////////////////////////////////// + +SkARGB32_Shader_Blitter::SkARGB32_Shader_Blitter(const SkBitmap& device, + const SkPaint& paint) + : INHERITED(device, paint) { + fBuffer = (SkPMColor*)sk_malloc_throw(device.width() * (sizeof(SkPMColor))); + + (fXfermode = paint.getXfermode())->safeRef(); +} + +SkARGB32_Shader_Blitter::~SkARGB32_Shader_Blitter() { + fXfermode->safeUnref(); + sk_free(fBuffer); +} + +void SkARGB32_Shader_Blitter::blitH(int x, int y, int width) { + SkASSERT(x >= 0 && y >= 0 && x + width <= fDevice.width()); + + uint32_t* device = fDevice.getAddr32(x, y); + + if (fXfermode == NULL && (fShader->getFlags() & SkShader::kOpaqueAlpha_Flag)) { + fShader->shadeSpan(x, y, device, width); + } else { + SkPMColor* span = fBuffer; + fShader->shadeSpan(x, y, span, width); + if (fXfermode) { + fXfermode->xfer32(device, span, width, NULL); + } else { + for (int i = 0; i < width; i++) { + uint32_t src = span[i]; + if (src) { + unsigned srcA = SkGetPackedA32(src); + if (srcA != 0xFF) { + src += SkAlphaMulQ(device[i], SkAlpha255To256(255 - srcA)); + } + device[i] = src; + } + } + } + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +void SkARGB32_Shader_Blitter::blitAntiH(int x, int y, const SkAlpha antialias[], + const int16_t runs[]) { + SkPMColor* span = fBuffer; + uint32_t* device = fDevice.getAddr32(x, y); + SkShader* shader = fShader; + + if (fXfermode) { + for (;;) { + SkXfermode* xfer = fXfermode; + + int count = *runs; + if (count <= 0) + break; + int aa = *antialias; + if (aa) { + shader->shadeSpan(x, y, span, count); + if (aa == 255) { + xfer->xfer32(device, span, count, NULL); + } else { + // count is almost always 1 + for (int i = count - 1; i >= 0; --i) { + xfer->xfer32(&device[i], &span[i], 1, antialias); + } + } + } + device += count; + runs += count; + antialias += count; + x += count; + } + } else if (fShader->getFlags() & SkShader::kOpaqueAlpha_Flag) { + for (;;) { + int count = *runs; + if (count <= 0) { + break; + } + int aa = *antialias; + if (aa) { + if (aa == 255) { // cool, have the shader draw right into the device + shader->shadeSpan(x, y, device, count); + } else { + shader->shadeSpan(x, y, span, count); + for (int i = count - 1; i >= 0; --i) { + if (span[i]) { + device[i] = SkBlendARGB32(span[i], device[i], aa); + } + } + } + } + device += count; + runs += count; + antialias += count; + x += count; + } + } else { // no xfermode but we are not opaque + for (;;) { + int count = *runs; + if (count <= 0) { + break; + } + int aa = *antialias; + if (aa) { + fShader->shadeSpan(x, y, span, count); + if (aa == 255) { + for (int i = count - 1; i >= 0; --i) { + if (span[i]) { + device[i] = SkPMSrcOver(span[i], device[i]); + } + } + } else { + for (int i = count - 1; i >= 0; --i) { + if (span[i]) { + device[i] = SkBlendARGB32(span[i], device[i], aa); + } + } + } + } + device += count; + runs += count; + antialias += count; + x += count; + } + } +} + diff --git a/skia/sgl/SkBlitter_RGB16.cpp b/skia/sgl/SkBlitter_RGB16.cpp new file mode 100644 index 0000000..aca515d --- /dev/null +++ b/skia/sgl/SkBlitter_RGB16.cpp @@ -0,0 +1,782 @@ +/* libs/graphics/sgl/SkBlitter_RGB16.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkBlitRow.h" +#include "SkCoreBlitters.h" +#include "SkColorPriv.h" +#include "SkDither.h" +#include "SkShader.h" +#include "SkUtils.h" +#include "SkXfermode.h" + +void sk_dither_memset16(uint16_t dst[], uint16_t value, uint16_t other, + int count) { + if (count > 0) { + // see if we need to write one short before we can cast to an 4byte ptr + // (we do this subtract rather than (unsigned)dst so we don't get warnings + // on 64bit machines) + if (((char*)dst - (char*)0) & 2) { + *dst++ = value; + count -= 1; + SkTSwap(value, other); + } + + // fast way to set [value,other] pairs +#ifdef SK_CPU_BENDIAN + sk_memset32((uint32_t*)dst, (value << 16) | other, count >> 1); +#else + sk_memset32((uint32_t*)dst, (other << 16) | value, count >> 1); +#endif + + if (count & 1) { + dst[count - 1] = value; + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +SkRGB16_Black_Blitter::SkRGB16_Black_Blitter(const SkBitmap& device, const SkPaint& paint) + : SkRGB16_Blitter(device, paint) { + SkASSERT(paint.getShader() == NULL); + SkASSERT(paint.getColorFilter() == NULL); + SkASSERT(paint.getXfermode() == NULL); + SkASSERT(paint.getColor() == SK_ColorBLACK); +} + +#if 1 +#define black_8_pixels(mask, dst) \ + do { \ + if (mask & 0x80) dst[0] = 0; \ + if (mask & 0x40) dst[1] = 0; \ + if (mask & 0x20) dst[2] = 0; \ + if (mask & 0x10) dst[3] = 0; \ + if (mask & 0x08) dst[4] = 0; \ + if (mask & 0x04) dst[5] = 0; \ + if (mask & 0x02) dst[6] = 0; \ + if (mask & 0x01) dst[7] = 0; \ + } while (0) +#else +static inline black_8_pixels(U8CPU mask, uint16_t dst[]) +{ + if (mask & 0x80) dst[0] = 0; + if (mask & 0x40) dst[1] = 0; + if (mask & 0x20) dst[2] = 0; + if (mask & 0x10) dst[3] = 0; + if (mask & 0x08) dst[4] = 0; + if (mask & 0x04) dst[5] = 0; + if (mask & 0x02) dst[6] = 0; + if (mask & 0x01) dst[7] = 0; +} +#endif + +#define SK_BLITBWMASK_NAME SkRGB16_Black_BlitBW +#define SK_BLITBWMASK_ARGS +#define SK_BLITBWMASK_BLIT8(mask, dst) black_8_pixels(mask, dst) +#define SK_BLITBWMASK_GETADDR getAddr16 +#define SK_BLITBWMASK_DEVTYPE uint16_t +#include "SkBlitBWMaskTemplate.h" + +void SkRGB16_Black_Blitter::blitMask(const SkMask& SK_RESTRICT mask, + const SkIRect& SK_RESTRICT clip) + SK_RESTRICT { + if (mask.fFormat == SkMask::kBW_Format) { + SkRGB16_Black_BlitBW(fDevice, mask, clip); + } else { + uint16_t* SK_RESTRICT device = fDevice.getAddr16(clip.fLeft, clip.fTop); + const uint8_t* SK_RESTRICT alpha = mask.getAddr(clip.fLeft, clip.fTop); + unsigned width = clip.width(); + unsigned height = clip.height(); + unsigned deviceRB = fDevice.rowBytes() - (width << 1); + unsigned maskRB = mask.fRowBytes - width; + + SkASSERT((int)height > 0); + SkASSERT((int)width > 0); + SkASSERT((int)deviceRB >= 0); + SkASSERT((int)maskRB >= 0); + + do { + unsigned w = width; + do { + unsigned aa = *alpha++; + *device = SkAlphaMulRGB16(*device, SkAlpha255To256(255 - aa)); + device += 1; + } while (--w != 0); + device = (uint16_t*)((char*)device + deviceRB); + alpha += maskRB; + } while (--height != 0); + } +} + +void SkRGB16_Black_Blitter::blitAntiH(int x, int y, + const SkAlpha* SK_RESTRICT antialias, + const int16_t* SK_RESTRICT runs) + SK_RESTRICT { + uint16_t* SK_RESTRICT device = fDevice.getAddr16(x, y); + + for (;;) { + int count = runs[0]; + SkASSERT(count >= 0); + if (count <= 0) { + return; + } + runs += count; + + unsigned aa = antialias[0]; + antialias += count; + if (aa) { + if (aa == 255) { + memset(device, 0, count << 1); + } else { + aa = SkAlpha255To256(255 - aa); + do { + *device = SkAlphaMulRGB16(*device, aa); + device += 1; + } while (--count != 0); + continue; + } + } + device += count; + } +} + +////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////// + +SkRGB16_Blitter::SkRGB16_Blitter(const SkBitmap& device, const SkPaint& paint) + : INHERITED(device) { + SkColor color = paint.getColor(); + + fScale = SkAlpha255To256(SkColorGetA(color)); + + int r = SkColorGetR(color); + int g = SkColorGetG(color); + int b = SkColorGetB(color); + + fRawColor16 = fRawDither16 = SkPack888ToRGB16(r, g, b); + // if we're dithered, use fRawDither16 to hold that. + if ((fDoDither = paint.isDither()) != false) { + fRawDither16 = SkDitherPack888ToRGB16(r, g, b); + } + + fColor16 = SkPackRGB16( SkAlphaMul(r, fScale) >> (8 - SK_R16_BITS), + SkAlphaMul(g, fScale) >> (8 - SK_G16_BITS), + SkAlphaMul(b, fScale) >> (8 - SK_B16_BITS)); +} + +const SkBitmap* SkRGB16_Blitter::justAnOpaqueColor(uint32_t* value) { + if (!fDoDither && 256 == fScale) { + *value = fRawColor16; + return &fDevice; + } + return NULL; +} + +void SkRGB16_Blitter::blitH(int x, int y, int width) SK_RESTRICT { + SkASSERT(width > 0); + SkASSERT(x + width <= fDevice.width()); + + if (fScale == 0) { + return; + } + + uint16_t* SK_RESTRICT device = fDevice.getAddr16(x, y); + uint16_t srcColor = fColor16; + + if (256 == fScale) { + SkASSERT(fRawColor16 == srcColor); + if (fDoDither) { + uint16_t ditherColor = fRawDither16; + if ((x ^ y) & 1) { + SkTSwap(ditherColor, srcColor); + } + sk_dither_memset16(device, srcColor, ditherColor, width); + } else { + sk_memset16(device, srcColor, width); + } + } else { + // TODO: respect fDoDither + unsigned scale = 256 - fScale; + do { + *device = srcColor + SkAlphaMulRGB16(*device, scale); + device += 1; + } while (--width != 0); + } +} + +// return 1 or 0 from a bool +static int Bool2Int(bool value) { + return !!value; +} + +void SkRGB16_Blitter::blitAntiH(int x, int y, + const SkAlpha* SK_RESTRICT antialias, + const int16_t* SK_RESTRICT runs) SK_RESTRICT { + if (fScale == 0) { + return; + } + + uint16_t* SK_RESTRICT device = fDevice.getAddr16(x, y); + uint16_t srcColor = fRawColor16; + unsigned scale = fScale; + int ditherInt = Bool2Int(fDoDither); + + if (256 == scale) { + uint16_t ditherColor = fRawDither16; + // if we have no dithering, this will always fail + if ((x ^ y) & ditherInt) { + SkTSwap(ditherColor, srcColor); + } + for (;;) { + int count = runs[0]; + SkASSERT(count >= 0); + if (count <= 0) { + return; + } + runs += count; + + unsigned aa = antialias[0]; + antialias += count; + if (aa) { + if (aa == 255) { + if (ditherInt) { + sk_dither_memset16(device, srcColor, + ditherColor, count); + } else { + sk_memset16(device, srcColor, count); + } + } else { + // TODO: respect fDoDither + unsigned scale5 = SkAlpha255To256(aa) >> 3; + uint32_t src32 = SkExpand_rgb_16(srcColor) * scale5; + scale5 = 32 - scale5; // now we can use it on the device + int n = count; + do { + uint32_t dst32 = SkExpand_rgb_16(*device) * scale5; + *device++ = SkCompact_rgb_16((src32 + dst32) >> 5); + } while (--n != 0); + goto DONE; + } + } + device += count; + + DONE: + // if we have no dithering, this will always fail + if (count & ditherInt) { + SkTSwap(ditherColor, srcColor); + } + } + } else { + // TODO: respect fDoDither + for (;;) { + int count = runs[0]; + SkASSERT(count >= 0); + if (count <= 0) { + return; + } + runs += count; + + unsigned aa = antialias[0]; + antialias += count; + if (aa) { + unsigned scale5 = SkAlpha255To256(aa) * scale >> (8 + 3); + uint32_t src32 = SkExpand_rgb_16(srcColor) * scale5; + scale5 = 32 - scale5; + do { + uint32_t dst32 = SkExpand_rgb_16(*device) * scale5; + *device++ = SkCompact_rgb_16((src32 + dst32) >> 5); + } while (--count != 0); + continue; + } + device += count; + } + } +} + +////////////////////////////////////////////////////////////////////////////////////// + +#define solid_8_pixels(mask, dst, color) \ + do { \ + if (mask & 0x80) dst[0] = color; \ + if (mask & 0x40) dst[1] = color; \ + if (mask & 0x20) dst[2] = color; \ + if (mask & 0x10) dst[3] = color; \ + if (mask & 0x08) dst[4] = color; \ + if (mask & 0x04) dst[5] = color; \ + if (mask & 0x02) dst[6] = color; \ + if (mask & 0x01) dst[7] = color; \ + } while (0) + +#define SK_BLITBWMASK_NAME SkRGB16_BlitBW +#define SK_BLITBWMASK_ARGS , uint16_t color +#define SK_BLITBWMASK_BLIT8(mask, dst) solid_8_pixels(mask, dst, color) +#define SK_BLITBWMASK_GETADDR getAddr16 +#define SK_BLITBWMASK_DEVTYPE uint16_t +#include "SkBlitBWMaskTemplate.h" + +static inline void blend_8_pixels(U8CPU bw, uint16_t dst[], unsigned dst_scale, + U16CPU srcColor) { + if (bw & 0x80) dst[0] = srcColor + SkAlphaMulRGB16(dst[0], dst_scale); + if (bw & 0x40) dst[1] = srcColor + SkAlphaMulRGB16(dst[1], dst_scale); + if (bw & 0x20) dst[2] = srcColor + SkAlphaMulRGB16(dst[2], dst_scale); + if (bw & 0x10) dst[3] = srcColor + SkAlphaMulRGB16(dst[3], dst_scale); + if (bw & 0x08) dst[4] = srcColor + SkAlphaMulRGB16(dst[4], dst_scale); + if (bw & 0x04) dst[5] = srcColor + SkAlphaMulRGB16(dst[5], dst_scale); + if (bw & 0x02) dst[6] = srcColor + SkAlphaMulRGB16(dst[6], dst_scale); + if (bw & 0x01) dst[7] = srcColor + SkAlphaMulRGB16(dst[7], dst_scale); +} + +#define SK_BLITBWMASK_NAME SkRGB16_BlendBW +#define SK_BLITBWMASK_ARGS , unsigned dst_scale, U16CPU src_color +#define SK_BLITBWMASK_BLIT8(mask, dst) blend_8_pixels(mask, dst, dst_scale, src_color) +#define SK_BLITBWMASK_GETADDR getAddr16 +#define SK_BLITBWMASK_DEVTYPE uint16_t +#include "SkBlitBWMaskTemplate.h" + +static U16CPU blend_compact(uint32_t src32, uint32_t dst32, unsigned scale5) { + return SkCompact_rgb_16(dst32 + ((src32 - dst32) * scale5 >> 5)); +} + +void SkRGB16_Blitter::blitMask(const SkMask& SK_RESTRICT mask, + const SkIRect& SK_RESTRICT clip) SK_RESTRICT { + if (fScale == 0) { + return; + } + if (mask.fFormat == SkMask::kBW_Format) { + if (fScale == 256) { + SkRGB16_BlitBW(fDevice, mask, clip, fColor16); + } else { + SkRGB16_BlendBW(fDevice, mask, clip, 256 - fScale, fColor16); + } + return; + } + + uint16_t* SK_RESTRICT device = fDevice.getAddr16(clip.fLeft, clip.fTop); + const uint8_t* SK_RESTRICT alpha = mask.getAddr(clip.fLeft, clip.fTop); + int width = clip.width(); + int height = clip.height(); + unsigned deviceRB = fDevice.rowBytes() - (width << 1); + unsigned maskRB = mask.fRowBytes - width; + uint32_t color32 = SkExpand_rgb_16(fRawColor16); + + if (256 == fScale) { + do { + int w = width; + do { + *device = blend_compact(color32, SkExpand_rgb_16(*device), + SkAlpha255To256(*alpha++) >> 3); + device += 1; + } while (--w != 0); + device = (uint16_t*)((char*)device + deviceRB); + alpha += maskRB; + } while (--height != 0); + } else { // scale < 256 + unsigned scale256 = fScale; + do { + int w = width; + do { + unsigned aa = *alpha++; + unsigned scale = SkAlpha255To256(aa) * scale256 >> (8 + 3); + uint32_t src32 = color32 * scale; + uint32_t dst32 = SkExpand_rgb_16(*device) * (32 - scale); + *device++ = SkCompact_rgb_16((src32 + dst32) >> 5); + } while (--w != 0); + device = (uint16_t*)((char*)device + deviceRB); + alpha += maskRB; + } while (--height != 0); + } +} + +void SkRGB16_Blitter::blitV(int x, int y, int height, SkAlpha alpha) { + if (fScale == 0) { + return; + } + uint16_t* SK_RESTRICT device = fDevice.getAddr16(x, y); + uint16_t color16 = fRawColor16; + unsigned deviceRB = fDevice.rowBytes(); + + if (alpha + fScale == (255 + 256)) { + if (fDoDither) { + uint16_t ditherColor = fRawDither16; + if ((x ^ y) & 1) { + SkTSwap(ditherColor, color16); + } + do { + device[0] = color16; + device = (uint16_t*)((char*)device + deviceRB); + SkTSwap(ditherColor, color16); + } while (--height != 0); + } else { + do { + device[0] = color16; + device = (uint16_t*)((char*)device + deviceRB); + } while (--height != 0); + } + } else { + // TODO: respect fDoDither + unsigned scale5 = SkAlpha255To256(alpha) * fScale >> (8 + 3); + uint32_t src32 = SkExpand_rgb_16(color16) * scale5; + scale5 = 32 - scale5; + do { + uint32_t dst32 = SkExpand_rgb_16(*device) * scale5; + *device = SkCompact_rgb_16((src32 + dst32) >> 5); + device = (uint16_t*)((char*)device + deviceRB); + } while (--height != 0); + } +} + +void SkRGB16_Blitter::blitRect(int x, int y, int width, int height) { + SkASSERT(x + width <= fDevice.width() && y + height <= fDevice.height()); + + if (fScale == 0) { + return; + } + uint16_t* SK_RESTRICT device = fDevice.getAddr16(x, y); + unsigned deviceRB = fDevice.rowBytes(); + uint16_t color16 = fColor16; + + if (256 == fScale) { + if (fDoDither) { + uint16_t ditherColor = fRawDither16; + if ((x ^ y) & 1) { + SkTSwap(ditherColor, color16); + } + while (--height >= 0) { + sk_dither_memset16(device, color16, ditherColor, width); + SkTSwap(ditherColor, color16); + device = (uint16_t*)((char*)device + deviceRB); + } + } else { // no dither + while (--height >= 0) { + sk_memset16(device, color16, width); + device = (uint16_t*)((char*)device + deviceRB); + } + } + } else { + unsigned dst_scale = 256 - fScale; // apply it to the dst + + while (--height >= 0) { + for (int i = width - 1; i >= 0; --i) { + device[i] = color16 + SkAlphaMulRGB16(device[i], dst_scale); + } + device = (uint16_t*)((char*)device + deviceRB); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +SkRGB16_Shader16_Blitter::SkRGB16_Shader16_Blitter(const SkBitmap& device, + const SkPaint& paint) + : SkRGB16_Shader_Blitter(device, paint) { + SkASSERT(SkShader::CanCallShadeSpan16(fShader->getFlags())); +} + +void SkRGB16_Shader16_Blitter::blitH(int x, int y, int width) SK_RESTRICT { + SkASSERT(x + width <= fDevice.width()); + + uint16_t* SK_RESTRICT device = fDevice.getAddr16(x, y); + SkShader* shader = fShader; + + int alpha = shader->getSpan16Alpha(); + if (0xFF == alpha) { + shader->shadeSpan16(x, y, device, width); + } else { + uint16_t* span16 = (uint16_t*)fBuffer; + shader->shadeSpan16(x, y, span16, width); + SkBlendRGB16(span16, device, SkAlpha255To256(alpha), width); + } +} + +void SkRGB16_Shader16_Blitter::blitAntiH(int x, int y, + const SkAlpha* SK_RESTRICT antialias, + const int16_t* SK_RESTRICT runs) + SK_RESTRICT { + SkShader* shader = fShader; + SkPMColor* SK_RESTRICT span = fBuffer; + uint16_t* SK_RESTRICT device = fDevice.getAddr16(x, y); + + int alpha = shader->getSpan16Alpha(); + uint16_t* span16 = (uint16_t*)span; + + if (0xFF == alpha) { + for (;;) { + int count = *runs; + if (count <= 0) { + break; + } + SkASSERT(count <= fDevice.width()); // don't overrun fBuffer + + int aa = *antialias; + if (aa == 255) { + // go direct to the device! + shader->shadeSpan16(x, y, device, count); + } else if (aa) { + shader->shadeSpan16(x, y, span16, count); + SkBlendRGB16(span16, device, SkAlpha255To256(aa), count); + } + device += count; + runs += count; + antialias += count; + x += count; + } + } else { // span alpha is < 255 + alpha = SkAlpha255To256(alpha); + for (;;) { + int count = *runs; + if (count <= 0) { + break; + } + SkASSERT(count <= fDevice.width()); // don't overrun fBuffer + + int aa = SkAlphaMul(*antialias, alpha); + if (aa) { + shader->shadeSpan16(x, y, span16, count); + SkBlendRGB16(span16, device, SkAlpha255To256(aa), count); + } + + device += count; + runs += count; + antialias += count; + x += count; + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +SkRGB16_Shader_Blitter::SkRGB16_Shader_Blitter(const SkBitmap& device, + const SkPaint& paint) +: INHERITED(device, paint) { + SkASSERT(paint.getXfermode() == NULL); + + fBuffer = (SkPMColor*)sk_malloc_throw(device.width() * sizeof(SkPMColor)); + + // compute SkBlitRow::Procs + unsigned flags = 0; + + uint32_t shaderFlags = fShader->getFlags(); + // shaders take care of global alpha, so we never set it in SkBlitRow + if (!(shaderFlags & SkShader::kOpaqueAlpha_Flag)) { + flags |= SkBlitRow::kSrcPixelAlpha_Flag; + } + // don't dither if the shader is really 16bit + if (paint.isDither() && !(shaderFlags & SkShader::kIntrinsicly16_Flag)) { + flags |= SkBlitRow::kDither_Flag; + } + // used when we know our global alpha is 0xFF + fOpaqueProc = SkBlitRow::Factory(flags, SkBitmap::kRGB_565_Config); + // used when we know our global alpha is < 0xFF + fAlphaProc = SkBlitRow::Factory(flags | SkBlitRow::kGlobalAlpha_Flag, + SkBitmap::kRGB_565_Config); +} + +SkRGB16_Shader_Blitter::~SkRGB16_Shader_Blitter() { + sk_free(fBuffer); +} + +void SkRGB16_Shader_Blitter::blitH(int x, int y, int width) { + SkASSERT(x + width <= fDevice.width()); + + fShader->shadeSpan(x, y, fBuffer, width); + // shaders take care of global alpha, so we pass 0xFF (should be ignored) + fOpaqueProc(fDevice.getAddr16(x, y), fBuffer, width, 0xFF, x, y); +} + +static inline int count_nonzero_span(const int16_t runs[], const SkAlpha aa[]) { + int count = 0; + for (;;) { + int n = *runs; + if (n == 0 || *aa == 0) { + break; + } + runs += n; + aa += n; + count += n; + } + return count; +} + +void SkRGB16_Shader_Blitter::blitAntiH(int x, int y, + const SkAlpha* SK_RESTRICT antialias, + const int16_t* SK_RESTRICT runs) + SK_RESTRICT { + SkShader* shader = fShader; + SkPMColor* SK_RESTRICT span = fBuffer; + uint16_t* SK_RESTRICT device = fDevice.getAddr16(x, y); + + for (;;) { + int count = *runs; + if (count <= 0) { + break; + } + int aa = *antialias; + if (0 == aa) { + device += count; + runs += count; + antialias += count; + x += count; + continue; + } + + int nonZeroCount = count + count_nonzero_span(runs + count, antialias + count); + + SkASSERT(nonZeroCount <= fDevice.width()); // don't overrun fBuffer + shader->shadeSpan(x, y, span, nonZeroCount); + + SkPMColor* localSpan = span; + for (;;) { + SkBlitRow::Proc proc = (aa == 0xFF) ? fOpaqueProc : fAlphaProc; + proc(device, localSpan, count, aa, x, y); + + x += count; + device += count; + runs += count; + antialias += count; + nonZeroCount -= count; + if (nonZeroCount == 0) { + break; + } + localSpan += count; + SkASSERT(nonZeroCount > 0); + count = *runs; + SkASSERT(count > 0); + aa = *antialias; + } + } +} + +/////////////////////////////////////////////////////////////////////// + +SkRGB16_Shader_Xfermode_Blitter::SkRGB16_Shader_Xfermode_Blitter( + const SkBitmap& device, const SkPaint& paint) +: INHERITED(device, paint) { + fXfermode = paint.getXfermode(); + SkASSERT(fXfermode); + fXfermode->ref(); + + int width = device.width(); + fBuffer = (SkPMColor*)sk_malloc_throw((width + (SkAlign4(width) >> 2)) * sizeof(SkPMColor)); + fAAExpand = (uint8_t*)(fBuffer + width); +} + +SkRGB16_Shader_Xfermode_Blitter::~SkRGB16_Shader_Xfermode_Blitter() { + fXfermode->unref(); + sk_free(fBuffer); +} + +void SkRGB16_Shader_Xfermode_Blitter::blitH(int x, int y, int width) { + SkASSERT(x + width <= fDevice.width()); + + uint16_t* device = fDevice.getAddr16(x, y); + SkPMColor* span = fBuffer; + + fShader->shadeSpan(x, y, span, width); + fXfermode->xfer16(device, span, width, NULL); +} + +void SkRGB16_Shader_Xfermode_Blitter::blitAntiH(int x, int y, + const SkAlpha* SK_RESTRICT antialias, + const int16_t* SK_RESTRICT runs) SK_RESTRICT { + SkShader* shader = fShader; + SkXfermode* mode = fXfermode; + SkPMColor* SK_RESTRICT span = fBuffer; + uint8_t* SK_RESTRICT aaExpand = fAAExpand; + uint16_t* SK_RESTRICT device = fDevice.getAddr16(x, y); + + for (;;) { + int count = *runs; + if (count <= 0) { + break; + } + int aa = *antialias; + if (0 == aa) { + device += count; + runs += count; + antialias += count; + x += count; + continue; + } + + int nonZeroCount = count + count_nonzero_span(runs + count, + antialias + count); + + SkASSERT(nonZeroCount <= fDevice.width()); // don't overrun fBuffer + shader->shadeSpan(x, y, span, nonZeroCount); + + x += nonZeroCount; + SkPMColor* localSpan = span; + for (;;) { + if (aa == 0xFF) { + mode->xfer16(device, localSpan, count, NULL); + } else { + SkASSERT(aa); + memset(aaExpand, aa, count); + mode->xfer16(device, localSpan, count, aaExpand); + } + device += count; + runs += count; + antialias += count; + nonZeroCount -= count; + if (nonZeroCount == 0) { + break; + } + localSpan += count; + SkASSERT(nonZeroCount > 0); + count = *runs; + SkASSERT(count > 0); + aa = *antialias; + } + } +} + +//////////////////////// + +#if 0 +static inline uint16_t aa_blendS32D16(SkPMColor src, U16CPU dst, int aa +#ifdef DITHER_SHADER + , int dither +#endif + ) +{ + SkASSERT((unsigned)aa <= 255); + + int src_scale = SkAlpha255To256(aa); + int sa = SkGetPackedA32(src); + int dst_scale = SkAlpha255To256(255 - SkAlphaMul(sa, src_scale)); + +#ifdef DITHER_SHADER + int sr = SkGetPackedR32(src); + int sg = SkGetPackedG32(src); + int sb = SkGetPackedB32(src); + sr = SkDITHER_R32To16(sr, dither); + sg = SkDITHER_G32To16(sg, dither); + sb = SkDITHER_B32To16(sb, dither); +#else + int sr = SkPacked32ToR16(src); + int sg = SkPacked32ToG16(src); + int sb = SkPacked32ToB16(src); +#endif + + int dr = (sr * src_scale + SkGetPackedR16(dst) * dst_scale) >> 8; + int dg = (sg * src_scale + SkGetPackedG16(dst) * dst_scale) >> 8; + int db = (sb * src_scale + SkGetPackedB16(dst) * dst_scale) >> 8; + + return SkPackRGB16(dr, dg, db); +} +#endif + diff --git a/skia/sgl/SkBlitter_Sprite.cpp b/skia/sgl/SkBlitter_Sprite.cpp new file mode 100644 index 0000000..6ce8f13 --- /dev/null +++ b/skia/sgl/SkBlitter_Sprite.cpp @@ -0,0 +1,101 @@ +/* libs/graphics/sgl/SkBlitter_Sprite.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkSpriteBlitter.h" + +SkSpriteBlitter::SkSpriteBlitter(const SkBitmap& source) + : fSource(&source) +{ + fSource->lockPixels(); +} + +SkSpriteBlitter::~SkSpriteBlitter() +{ + fSource->unlockPixels(); +} + +void SkSpriteBlitter::setup(const SkBitmap& device, int left, int top, + const SkPaint& paint) +{ + fDevice = &device; + fLeft = left; + fTop = top; + fPaint = &paint; +} + +#ifdef SK_DEBUG +void SkSpriteBlitter::blitH(int x, int y, int width) +{ + SkASSERT(!"how did we get here?"); +} + +void SkSpriteBlitter::blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]) +{ + SkASSERT(!"how did we get here?"); +} + +void SkSpriteBlitter::blitV(int x, int y, int height, SkAlpha alpha) +{ + SkASSERT(!"how did we get here?"); +} + +void SkSpriteBlitter::blitMask(const SkMask&, const SkIRect& clip) +{ + SkASSERT(!"how did we get here?"); +} +#endif + +////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////// + +// returning null means the caller will call SkBlitter::Choose() and +// have wrapped the source bitmap inside a shader +SkBlitter* SkBlitter::ChooseSprite( const SkBitmap& device, + const SkPaint& paint, + const SkBitmap& source, + int left, int top, + void* storage, size_t storageSize) +{ + /* We currently ignore antialiasing and filtertype, meaning we will take our + special blitters regardless of these settings. Ignoring filtertype seems fine + since by definition there is no scale in the matrix. Ignoring antialiasing is + a bit of a hack, since we "could" pass in the fractional left/top for the bitmap, + and respect that by blending the edges of the bitmap against the device. To support + this we could either add more special blitters here, or detect antialiasing in the + paint and return null if it is set, forcing the client to take the slow shader case + (which does respect soft edges). + */ + + SkSpriteBlitter* blitter; + + switch (device.getConfig()) { + case SkBitmap::kRGB_565_Config: + blitter = SkSpriteBlitter::ChooseD16(source, paint, storage, storageSize); + break; + case SkBitmap::kARGB_8888_Config: + blitter = SkSpriteBlitter::ChooseD32(source, paint, storage, storageSize); + break; + default: + blitter = NULL; + break; + } + + if (blitter) + blitter->setup(device, left, top, paint); + return blitter; +} + diff --git a/skia/sgl/SkCanvas.cpp b/skia/sgl/SkCanvas.cpp new file mode 100644 index 0000000..a657023 --- /dev/null +++ b/skia/sgl/SkCanvas.cpp @@ -0,0 +1,1323 @@ +/* + * Copyright (C) 2006-2008 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "SkCanvas.h" +#include "SkBounder.h" +#include "SkDevice.h" +#include "SkDraw.h" +#include "SkDrawFilter.h" +#include "SkDrawLooper.h" +#include "SkPicture.h" +#include "SkTemplates.h" +#include "SkUtils.h" +#include <new> + +//#define SK_TRACE_SAVERESTORE + +#ifdef SK_TRACE_SAVERESTORE + static int gLayerCounter; + static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); } + static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); } + + static int gRecCounter; + static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); } + static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); } + + static int gCanvasCounter; + static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); } + static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); } +#else + #define inc_layer() + #define dec_layer() + #define inc_rec() + #define dec_rec() + #define inc_canvas() + #define dec_canvas() +#endif + +/////////////////////////////////////////////////////////////////////////////// + +/* This is the record we keep for each SkDevice that the user installs. + The clip/matrix/proc are fields that reflect the top of the save/restore + stack. Whenever the canvas changes, it marks a dirty flag, and then before + these are used (assuming we're not on a layer) we rebuild these cache + values: they reflect the top of the save stack, but translated and clipped + by the device's XY offset and bitmap-bounds. +*/ +struct DeviceCM { + DeviceCM* fNext; + SkDevice* fDevice; + SkRegion fClip; + const SkMatrix* fMatrix; + SkPaint* fPaint; // may be null (in the future) + int16_t fX, fY; // relative to base matrix/clip + + DeviceCM(SkDevice* device, int x, int y, const SkPaint* paint) + : fNext(NULL) { + if (NULL != device) { + device->ref(); + device->lockPixels(); + } + fDevice = device; + fX = SkToS16(x); + fY = SkToS16(y); + fPaint = paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL; + } + + ~DeviceCM() { + if (NULL != fDevice) { + fDevice->unlockPixels(); + fDevice->unref(); + } + SkDELETE(fPaint); + } + + void updateMC(const SkMatrix& totalMatrix, const SkRegion& totalClip, + SkRegion* updateClip) { + int x = fX; + int y = fY; + int width = fDevice->width(); + int height = fDevice->height(); + + if ((x | y) == 0) { + fMatrix = &totalMatrix; + fClip = totalClip; + } else { + fMatrixStorage = totalMatrix; + fMatrixStorage.postTranslate(SkIntToScalar(-x), + SkIntToScalar(-y)); + fMatrix = &fMatrixStorage; + + totalClip.translate(-x, -y, &fClip); + } + + fClip.op(0, 0, width, height, SkRegion::kIntersect_Op); + + // intersect clip, but don't translate it (yet) + + if (updateClip) { + updateClip->op(x, y, x + width, y + height, + SkRegion::kDifference_Op); + } + + fDevice->setMatrixClip(*fMatrix, fClip); + +#ifdef SK_DEBUG + if (!fClip.isEmpty()) { + SkIRect deviceR; + deviceR.set(0, 0, width, height); + SkASSERT(deviceR.contains(fClip.getBounds())); + } +#endif + } + + void translateClip() { + if (fX | fY) { + fClip.translate(fX, fY); + } + } + +private: + SkMatrix fMatrixStorage; +}; + +/* This is the record we keep for each save/restore level in the stack. + Since a level optionally copies the matrix and/or stack, we have pointers + for these fields. If the value is copied for this level, the copy is + stored in the ...Storage field, and the pointer points to that. If the + value is not copied for this level, we ignore ...Storage, and just point + at the corresponding value in the previous level in the stack. +*/ +class SkCanvas::MCRec { +public: + MCRec* fNext; + SkMatrix* fMatrix; // points to either fMatrixStorage or prev MCRec + SkRegion* fRegion; // points to either fRegionStorage or prev MCRec + SkDrawFilter* fFilter; // the current filter (or null) + + DeviceCM* fLayer; + /* If there are any layers in the stack, this points to the top-most + one that is at or below this level in the stack (so we know what + bitmap/device to draw into from this level. This value is NOT + reference counted, since the real owner is either our fLayer field, + or a previous one in a lower level.) + */ + DeviceCM* fTopLayer; + + MCRec(const MCRec* prev, int flags) { + if (NULL != prev) { + if (flags & SkCanvas::kMatrix_SaveFlag) { + fMatrixStorage = *prev->fMatrix; + fMatrix = &fMatrixStorage; + } else { + fMatrix = prev->fMatrix; + } + + if (flags & SkCanvas::kClip_SaveFlag) { + fRegionStorage = *prev->fRegion; + fRegion = &fRegionStorage; + } else { + fRegion = prev->fRegion; + } + + fFilter = prev->fFilter; + fFilter->safeRef(); + + fTopLayer = prev->fTopLayer; + } else { // no prev + fMatrixStorage.reset(); + + fMatrix = &fMatrixStorage; + fRegion = &fRegionStorage; + fFilter = NULL; + fTopLayer = NULL; + } + fLayer = NULL; + + // don't bother initializing fNext + inc_rec(); + } + ~MCRec() { + fFilter->safeUnref(); + SkDELETE(fLayer); + dec_rec(); + } + +private: + SkMatrix fMatrixStorage; + SkRegion fRegionStorage; +}; + +class SkDrawIter : public SkDraw { +public: + SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) { + fCanvas = canvas; + canvas->updateDeviceCMCache(); + + fBounder = canvas->getBounder(); + fCurrLayer = canvas->fMCRec->fTopLayer; + fSkipEmptyClips = skipEmptyClips; + } + + bool next() { + // skip over recs with empty clips + if (fSkipEmptyClips) { + while (fCurrLayer && fCurrLayer->fClip.isEmpty()) { + fCurrLayer = fCurrLayer->fNext; + } + } + + if (NULL != fCurrLayer) { + const DeviceCM* rec = fCurrLayer; + + fMatrix = rec->fMatrix; + fClip = &rec->fClip; + fDevice = rec->fDevice; + fBitmap = &fDevice->accessBitmap(true); + fLayerX = rec->fX; + fLayerY = rec->fY; + SkDEBUGCODE(this->validate();) + + fCurrLayer = rec->fNext; + if (fBounder) { + fBounder->setClip(fClip); + } + + // fCurrLayer may be NULL now + + fCanvas->prepareForDeviceDraw(fDevice); + return true; + } + return false; + } + + int getX() const { return fLayerX; } + int getY() const { return fLayerY; } + SkDevice* getDevice() const { return fDevice; } + const SkMatrix& getMatrix() const { return *fMatrix; } + const SkRegion& getClip() const { return *fClip; } + +private: + SkCanvas* fCanvas; + const DeviceCM* fCurrLayer; + int fLayerX; + int fLayerY; + SkBool8 fSkipEmptyClips; + + typedef SkDraw INHERITED; +}; + +///////////////////////////////////////////////////////////////////////////// + +class AutoDrawLooper { +public: + AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint, SkDrawFilter::Type t) + : fCanvas(canvas), fPaint((SkPaint*)&paint), fType(t) { + if ((fLooper = paint.getLooper()) != NULL) { + fLooper->init(canvas, (SkPaint*)&paint); + } else { + fOnce = true; + } + fFilter = canvas->getDrawFilter(); + fNeedFilterRestore = false; + } + + ~AutoDrawLooper() { + if (fNeedFilterRestore) { + SkASSERT(fFilter); + fFilter->restore(fCanvas, fPaint, fType); + } + if (NULL != fLooper) { + fLooper->restore(); + } + } + + bool next() { + SkDrawFilter* filter = fFilter; + + // if we drew earlier with a filter, then we need to restore first + if (fNeedFilterRestore) { + SkASSERT(filter); + filter->restore(fCanvas, fPaint, fType); + fNeedFilterRestore = false; + } + + bool result; + + if (NULL != fLooper) { + result = fLooper->next(); + } else { + result = fOnce; + fOnce = false; + } + + // if we're gonna draw, give the filter a chance to do its work + if (result && NULL != filter) { + fNeedFilterRestore = result = filter->filter(fCanvas, fPaint, + fType); + } + return result; + } + +private: + SkDrawLooper* fLooper; + SkDrawFilter* fFilter; + SkCanvas* fCanvas; + SkPaint* fPaint; + SkDrawFilter::Type fType; + bool fOnce; + bool fNeedFilterRestore; + +}; + +/* Stack helper for managing a SkBounder. In the destructor, if we were + given a bounder, we call its commit() method, signifying that we are + done accumulating bounds for that draw. +*/ +class SkAutoBounderCommit { +public: + SkAutoBounderCommit(SkBounder* bounder) : fBounder(bounder) {} + ~SkAutoBounderCommit() { + if (NULL != fBounder) { + fBounder->commit(); + } + } +private: + SkBounder* fBounder; +}; + +#include "SkColorPriv.h" + +class AutoValidator { +public: + AutoValidator(SkDevice* device) : fDevice(device) {} + ~AutoValidator() { +#ifdef SK_DEBUG + const SkBitmap& bm = fDevice->accessBitmap(false); + if (bm.config() == SkBitmap::kARGB_4444_Config) { + for (int y = 0; y < bm.height(); y++) { + const SkPMColor16* p = bm.getAddr16(0, y); + for (int x = 0; x < bm.width(); x++) { + SkPMColor16 c = p[x]; + SkPMColor16Assert(c); + } + } + } +#endif + } +private: + SkDevice* fDevice; +}; + +////////// macros to place around the internal draw calls ////////////////// + +#define ITER_BEGIN(paint, type) \ +/* AutoValidator validator(fMCRec->fTopLayer->fDevice); */ \ + AutoDrawLooper looper(this, paint, type); \ + while (looper.next()) { \ + SkAutoBounderCommit ac(fBounder); \ + SkDrawIter iter(this); + +#define ITER_END } + +//////////////////////////////////////////////////////////////////////////// + +SkDevice* SkCanvas::init(SkDevice* device) { + fBounder = NULL; + + fMCRec = (MCRec*)fMCStack.push_back(); + new (fMCRec) MCRec(NULL, 0); + + fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL)); + fMCRec->fTopLayer = fMCRec->fLayer; + fMCRec->fNext = NULL; + + return this->setDevice(device); +} + +SkCanvas::SkCanvas(SkDevice* device) + : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) { + inc_canvas(); + + this->init(device); +} + +SkCanvas::SkCanvas(const SkBitmap& bitmap) + : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) { + inc_canvas(); + + this->init(SkNEW_ARGS(SkDevice, (bitmap)))->unref(); +} + +SkCanvas::~SkCanvas() { + // free up the contents of our deque + this->restoreToCount(1); // restore everything but the last + this->internalRestore(); // restore the last, since we're going away + + fBounder->safeUnref(); + + dec_canvas(); +} + +SkBounder* SkCanvas::setBounder(SkBounder* bounder) { + SkRefCnt_SafeAssign(fBounder, bounder); + return bounder; +} + +SkDrawFilter* SkCanvas::getDrawFilter() const { + return fMCRec->fFilter; +} + +SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) { + SkRefCnt_SafeAssign(fMCRec->fFilter, filter); + return filter; +} + +/////////////////////////////////////////////////////////////////////////////// + +SkDevice* SkCanvas::getDevice() const { + // return root device + SkDeque::Iter iter(fMCStack); + MCRec* rec = (MCRec*)iter.next(); + SkASSERT(rec && rec->fLayer); + return rec->fLayer->fDevice; +} + +SkDevice* SkCanvas::setDevice(SkDevice* device) { + // return root device + SkDeque::Iter iter(fMCStack); + MCRec* rec = (MCRec*)iter.next(); + SkASSERT(rec && rec->fLayer); + SkDevice* rootDevice = rec->fLayer->fDevice; + + if (rootDevice == device) { + return device; + } + + /* Notify the devices that they are going in/out of scope, so they can do + things like lock/unlock their pixels, etc. + */ + if (device) { + device->lockPixels(); + } + if (rootDevice) { + rootDevice->unlockPixels(); + } + + SkRefCnt_SafeAssign(rec->fLayer->fDevice, device); + rootDevice = device; + + fDeviceCMDirty = true; + + /* Now we update our initial region to have the bounds of the new device, + and then intersect all of the clips in our stack with these bounds, + to ensure that we can't draw outside of the device's bounds (and trash + memory). + + NOTE: this is only a partial-fix, since if the new device is larger than + the previous one, we don't know how to "enlarge" the clips in our stack, + so drawing may be artificially restricted. Without keeping a history of + all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly + reconstruct the correct clips, so this approximation will have to do. + The caller really needs to restore() back to the base if they want to + accurately take advantage of the new device bounds. + */ + + if (NULL == device) { + rec->fRegion->setEmpty(); + while ((rec = (MCRec*)iter.next()) != NULL) { + (void)rec->fRegion->setEmpty(); + } + } else { + // compute our total bounds for all devices + SkIRect bounds; + + bounds.set(0, 0, device->width(), device->height()); + + // now jam our 1st clip to be bounds, and intersect the rest with that + rec->fRegion->setRect(bounds); + while ((rec = (MCRec*)iter.next()) != NULL) { + (void)rec->fRegion->op(bounds, SkRegion::kIntersect_Op); + } + } + return device; +} + +SkDevice* SkCanvas::setBitmapDevice(const SkBitmap& bitmap) { + SkDevice* device = this->setDevice(SkNEW_ARGS(SkDevice, (bitmap))); + device->unref(); + return device; +} + +////////////////////////////////////////////////////////////////////////////// + +bool SkCanvas::getViewport(SkIPoint* size) const { + return false; +} + +bool SkCanvas::setViewport(int width, int height) { + return false; +} + +void SkCanvas::updateDeviceCMCache() { + if (fDeviceCMDirty) { + const SkMatrix& totalMatrix = this->getTotalMatrix(); + const SkRegion& totalClip = this->getTotalClip(); + DeviceCM* layer = fMCRec->fTopLayer; + + if (NULL == layer->fNext) { // only one layer + layer->updateMC(totalMatrix, totalClip, NULL); + } else { + SkRegion clip; + clip = totalClip; // make a copy + do { + layer->updateMC(totalMatrix, clip, &clip); + } while ((layer = layer->fNext) != NULL); + } + fDeviceCMDirty = false; + } +} + +void SkCanvas::prepareForDeviceDraw(SkDevice* device) { + SkASSERT(device); + device->gainFocus(this); +} + +/////////////////////////////////////////////////////////////////////////////// + +int SkCanvas::internalSave(SaveFlags flags) { + int saveCount = this->getSaveCount(); // record this before the actual save + + MCRec* newTop = (MCRec*)fMCStack.push_back(); + new (newTop) MCRec(fMCRec, flags); // balanced in restore() + + newTop->fNext = fMCRec; + fMCRec = newTop; + + return saveCount; +} + +int SkCanvas::save(SaveFlags flags) { + // call shared impl + return this->internalSave(flags); +} + +#define C32MASK (1 << SkBitmap::kARGB_8888_Config) +#define C16MASK (1 << SkBitmap::kRGB_565_Config) +#define C8MASK (1 << SkBitmap::kA8_Config) + +static SkBitmap::Config resolve_config(SkCanvas* canvas, + const SkIRect& bounds, + SkCanvas::SaveFlags flags, + bool* isOpaque) { + *isOpaque = (flags & SkCanvas::kHasAlphaLayer_SaveFlag) == 0; + +#if 0 + // loop through and union all the configs we may draw into + uint32_t configMask = 0; + for (int i = canvas->countLayerDevices() - 1; i >= 0; --i) + { + SkDevice* device = canvas->getLayerDevice(i); + if (device->intersects(bounds)) + configMask |= 1 << device->config(); + } + + // if the caller wants alpha or fullcolor, we can't return 565 + if (flags & (SkCanvas::kFullColorLayer_SaveFlag | + SkCanvas::kHasAlphaLayer_SaveFlag)) + configMask &= ~C16MASK; + + switch (configMask) { + case C8MASK: // if we only have A8, return that + return SkBitmap::kA8_Config; + + case C16MASK: // if we only have 565, return that + return SkBitmap::kRGB_565_Config; + + default: + return SkBitmap::kARGB_8888_Config; // default answer + } +#else + return SkBitmap::kARGB_8888_Config; // default answer +#endif +} + +static bool bounds_affects_clip(SkCanvas::SaveFlags flags) { + return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0; +} + +int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint, + SaveFlags flags) { + // do this before we create the layer. We don't call the public save() since + // that would invoke a possibly overridden virtual + int count = this->internalSave(flags); + + fDeviceCMDirty = true; + + SkIRect ir; + const SkIRect& clipBounds = this->getTotalClip().getBounds(); + + if (NULL != bounds) { + SkRect r; + + this->getTotalMatrix().mapRect(&r, *bounds); + r.roundOut(&ir); + // early exit if the layer's bounds are clipped out + if (!ir.intersect(clipBounds)) { + if (bounds_affects_clip(flags)) + fMCRec->fRegion->setEmpty(); + return count; + } + } else { // no user bounds, so just use the clip + ir = clipBounds; + } + + // early exit if the clip is now empty + if (bounds_affects_clip(flags) && + !fMCRec->fRegion->op(ir, SkRegion::kIntersect_Op)) { + return count; + } + + bool isOpaque; + SkBitmap::Config config = resolve_config(this, ir, flags, &isOpaque); + + SkDevice* device = this->createDevice(config, ir.width(), ir.height(), + isOpaque, true); + DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint)); + device->unref(); + + layer->fNext = fMCRec->fTopLayer; + fMCRec->fLayer = layer; + fMCRec->fTopLayer = layer; // this field is NOT an owner of layer + + return count; +} + +int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha, + SaveFlags flags) { + if (0xFF == alpha) { + return this->saveLayer(bounds, NULL, flags); + } else { + SkPaint tmpPaint; + tmpPaint.setAlpha(alpha); + return this->saveLayer(bounds, &tmpPaint, flags); + } +} + +void SkCanvas::restore() { + // check for underflow + if (fMCStack.count() > 1) { + this->internalRestore(); + } +} + +void SkCanvas::internalRestore() { + SkASSERT(fMCStack.count() != 0); + + fDeviceCMDirty = true; + + // reserve our layer (if any) + DeviceCM* layer = fMCRec->fLayer; // may be null + // now detach it from fMCRec so we can pop(). Gets freed after its drawn + fMCRec->fLayer = NULL; + + // now do the normal restore() + fMCRec->~MCRec(); // balanced in save() + fMCStack.pop_back(); + fMCRec = (MCRec*)fMCStack.back(); + + /* Time to draw the layer's offscreen. We can't call the public drawSprite, + since if we're being recorded, we don't want to record this (the + recorder will have already recorded the restore). + */ + if (NULL != layer) { + if (layer->fNext) { + this->drawDevice(layer->fDevice, layer->fX, layer->fY, + layer->fPaint); + // reset this, since drawDevice will have set it to true + fDeviceCMDirty = true; + } + SkDELETE(layer); + } +} + +int SkCanvas::getSaveCount() const { + return fMCStack.count(); +} + +void SkCanvas::restoreToCount(int count) { + // sanity check + if (count < 1) { + count = 1; + } + while (fMCStack.count() > count) { + this->restore(); + } +} + +///////////////////////////////////////////////////////////////////////////// + +// can't draw it if its empty, or its too big for a fixed-point width or height +static bool reject_bitmap(const SkBitmap& bitmap) { + return bitmap.width() <= 0 || bitmap.height() <= 0 || + bitmap.width() > 32767 || bitmap.height() > 32767; +} + +void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap, + const SkMatrix& matrix, const SkPaint* paint) { + if (reject_bitmap(bitmap)) { + return; + } + + if (NULL == paint) { + SkPaint tmpPaint; + this->commonDrawBitmap(bitmap, matrix, tmpPaint); + } else { + this->commonDrawBitmap(bitmap, matrix, *paint); + } +} + +void SkCanvas::drawDevice(SkDevice* device, int x, int y, + const SkPaint* paint) { + SkPaint tmp; + if (NULL == paint) { + tmp.setDither(true); + paint = &tmp; + } + + ITER_BEGIN(*paint, SkDrawFilter::kBitmap_Type) + while (iter.next()) { + iter.fDevice->drawDevice(iter, device, x - iter.getX(), y - iter.getY(), + *paint); + } + ITER_END +} + +///////////////////////////////////////////////////////////////////////////// + +bool SkCanvas::translate(SkScalar dx, SkScalar dy) { + fDeviceCMDirty = true; + return fMCRec->fMatrix->preTranslate(dx, dy); +} + +bool SkCanvas::scale(SkScalar sx, SkScalar sy) { + fDeviceCMDirty = true; + return fMCRec->fMatrix->preScale(sx, sy); +} + +bool SkCanvas::rotate(SkScalar degrees) { + fDeviceCMDirty = true; + return fMCRec->fMatrix->preRotate(degrees); +} + +bool SkCanvas::skew(SkScalar sx, SkScalar sy) { + fDeviceCMDirty = true; + return fMCRec->fMatrix->preSkew(sx, sy); +} + +bool SkCanvas::concat(const SkMatrix& matrix) { + fDeviceCMDirty = true; + return fMCRec->fMatrix->preConcat(matrix); +} + +void SkCanvas::setMatrix(const SkMatrix& matrix) { + fDeviceCMDirty = true; + *fMCRec->fMatrix = matrix; +} + +// this is not virtual, so it must call a virtual method so that subclasses +// will see its action +void SkCanvas::resetMatrix() { + SkMatrix matrix; + + matrix.reset(); + this->setMatrix(matrix); +} + +////////////////////////////////////////////////////////////////////////////// + +bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op) { + fDeviceCMDirty = true; + if (fMCRec->fMatrix->rectStaysRect()) { + SkRect r; + SkIRect ir; + + fMCRec->fMatrix->mapRect(&r, rect); + r.round(&ir); + return fMCRec->fRegion->op(ir, op); + } else { + SkPath path; + + path.addRect(rect); + return this->clipPath(path, op); + } +} + +bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op) { + fDeviceCMDirty = true; + + SkPath devPath; + path.transform(*fMCRec->fMatrix, &devPath); + + if (SkRegion::kIntersect_Op == op) { + return fMCRec->fRegion->setPath(devPath, *fMCRec->fRegion); + } else { + SkRegion base; + const SkBitmap& bm = this->getDevice()->accessBitmap(false); + base.setRect(0, 0, bm.width(), bm.height()); + + if (SkRegion::kReplace_Op == op) { + return fMCRec->fRegion->setPath(devPath, base); + } else { + SkRegion rgn; + rgn.setPath(devPath, base); + return fMCRec->fRegion->op(rgn, op); + } + } +} + +bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) { + fDeviceCMDirty = true; + return fMCRec->fRegion->op(rgn, op); +} + +bool SkCanvas::quickReject(const SkRect& rect, EdgeType et) const { + if (fMCRec->fRegion->isEmpty() || rect.isEmpty()) { + return true; + } + + SkRect r; + SkIRect ir; + + fMCRec->fMatrix->mapRect(&r, rect); + if (kAA_EdgeType == et) { + r.roundOut(&ir); + } else { + r.round(&ir); + } + return fMCRec->fRegion->quickReject(ir); +} + +bool SkCanvas::quickReject(const SkPath& path, EdgeType et) const { + if (fMCRec->fRegion->isEmpty() || path.isEmpty()) { + return true; + } + + if (fMCRec->fMatrix->rectStaysRect()) { + SkRect r; + path.computeBounds(&r, SkPath::kExact_BoundsType); + return this->quickReject(r, et); + } + + SkPath dstPath; + SkRect r; + SkIRect ir; + + path.transform(*fMCRec->fMatrix, &dstPath); + dstPath.computeBounds(&r, SkPath::kExact_BoundsType); + if (kAA_EdgeType == et) { + r.roundOut(&ir); + } else { + r.round(&ir); + } + return fMCRec->fRegion->quickReject(ir); +} + +bool SkCanvas::quickRejectY(SkScalar top, SkScalar bottom, EdgeType et) const { + if (fMCRec->fRegion->isEmpty() || top >= bottom) { + return true; + } + + const SkMatrix& matrix = *fMCRec->fMatrix; + + // if we're rotated/skewed/perspective, give up (for now) + // TODO: cache this attribute of the matrix? or specialized query method? + // TODO: if rotate=90 or 270 is common, we can handle those too... + if (matrix.getType() & ~(SkMatrix::kTranslate_Mask | + SkMatrix::kScale_Mask)) { + return false; + } + // transform top/botttom into device coordinates + const SkScalar sy = matrix[SkMatrix::kMScaleY]; + const SkScalar ty = matrix[SkMatrix::kMTransY]; + top = SkScalarMulAdd(top, sy, ty); + bottom = SkScalarMulAdd(bottom, sy, ty); + + // if the scale flipped us, flip back + if (top > bottom) { + SkTSwap<SkScalar>(top, bottom); + } + // now round based on the edge type + int ymin, ymax; + if (kAA_EdgeType == et) { + ymin = SkScalarFloor(top); + ymax = SkScalarCeil(bottom); + } else { + ymin = SkScalarRound(top); + ymax = SkScalarRound(bottom); + } + + // now compare against the bounds of the clip + const SkIRect& bounds = fMCRec->fRegion->getBounds(); + return ymin >= bounds.fBottom || ymax <= bounds.fTop; +} + +bool SkCanvas::getClipBounds(SkRect* bounds) const { + const SkRegion& clip = *fMCRec->fRegion; + if (clip.isEmpty()) { + if (bounds) { + bounds->setEmpty(); + } + return false; + } + + if (NULL != bounds) { + SkMatrix inverse; + SkRect r; + + // TODO: should we cache the inverse (with a dirty bit)? + fMCRec->fMatrix->invert(&inverse); + r.set(clip.getBounds()); + inverse.mapRect(bounds, r); + } + return true; +} + +const SkMatrix& SkCanvas::getTotalMatrix() const { + return *fMCRec->fMatrix; +} + +const SkRegion& SkCanvas::getTotalClip() const { + return *fMCRec->fRegion; +} + +/////////////////////////////////////////////////////////////////////////////// + +SkDevice* SkCanvas::createDevice(SkBitmap::Config config, int width, + int height, bool isOpaque, bool isForLayer) { + SkBitmap bitmap; + + bitmap.setConfig(config, width, height); + bitmap.setIsOpaque(isOpaque); + + // should this happen in the device subclass? + bitmap.allocPixels(); + if (!bitmap.isOpaque()) { + bitmap.eraseARGB(0, 0, 0, 0); + } + + return SkNEW_ARGS(SkDevice, (bitmap)); +} + +////////////////////////////////////////////////////////////////////////////// +// These are the virtual drawing methods +////////////////////////////////////////////////////////////////////////////// + +void SkCanvas::drawPaint(const SkPaint& paint) { + ITER_BEGIN(paint, SkDrawFilter::kPaint_Type) + + while (iter.next()) { + iter.fDevice->drawPaint(iter, paint); + } + + ITER_END +} + +void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], + const SkPaint& paint) { + if ((long)count <= 0) { + return; + } + + SkASSERT(pts != NULL); + + ITER_BEGIN(paint, SkDrawFilter::kPoint_Type) + + while (iter.next()) { + iter.fDevice->drawPoints(iter, mode, count, pts, paint); + } + + ITER_END +} + +void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) { + ITER_BEGIN(paint, SkDrawFilter::kRect_Type) + + while (iter.next()) { + iter.fDevice->drawRect(iter, r, paint); + } + + ITER_END +} + +void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) { + ITER_BEGIN(paint, SkDrawFilter::kPath_Type) + + while (iter.next()) { + iter.fDevice->drawPath(iter, path, paint); + } + + ITER_END +} + +void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, + const SkPaint* paint) { + SkDEBUGCODE(bitmap.validate();) + + SkMatrix matrix; + matrix.setTranslate(x, y); + this->internalDrawBitmap(bitmap, matrix, paint); +} + +void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src, + const SkRect& dst, const SkPaint* paint) { + if (bitmap.width() == 0 || bitmap.height() == 0 || dst.isEmpty()) { + return; + } + + // do this now, to avoid the cost of calling extract for RLE bitmaps + if (this->quickReject(dst, paint != NULL && paint->isAntiAlias() ? + kAA_EdgeType : kBW_EdgeType)) { + return; + } + + SkBitmap tmp; // storage if we need a subset of bitmap + const SkBitmap* bitmapPtr = &bitmap; + + if (NULL != src) { + if (!bitmap.extractSubset(&tmp, *src)) { + return; // extraction failed + } + bitmapPtr = &tmp; + } + + SkScalar width = SkIntToScalar(bitmapPtr->width()); + SkScalar height = SkIntToScalar(bitmapPtr->height()); + SkMatrix matrix; + + if (dst.width() == width && dst.height() == height) { + matrix.setTranslate(dst.fLeft, dst.fTop); + } else { + SkRect tmpSrc; + tmpSrc.set(0, 0, width, height); + matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit); + } + this->internalDrawBitmap(*bitmapPtr, matrix, paint); +} + +void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix, + const SkPaint* paint) { + SkDEBUGCODE(bitmap.validate();) + this->internalDrawBitmap(bitmap, matrix, paint); +} + +void SkCanvas::commonDrawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix, + const SkPaint& paint) { + SkDEBUGCODE(bitmap.validate();) + + ITER_BEGIN(paint, SkDrawFilter::kBitmap_Type) + + while (iter.next()) { + iter.fDevice->drawBitmap(iter, bitmap, matrix, paint); + } + + ITER_END +} + +void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y, + const SkPaint* paint) { + SkDEBUGCODE(bitmap.validate();) + + if (reject_bitmap(bitmap)) { + return; + } + + SkPaint tmp; + if (NULL == paint) { + paint = &tmp; + } + + ITER_BEGIN(*paint, SkDrawFilter::kBitmap_Type) + + while (iter.next()) { + iter.fDevice->drawSprite(iter, bitmap, x - iter.getX(), y - iter.getY(), + *paint); + } + ITER_END +} + +void SkCanvas::drawText(const void* text, size_t byteLength, + SkScalar x, SkScalar y, const SkPaint& paint) { + ITER_BEGIN(paint, SkDrawFilter::kText_Type) + + while (iter.next()) { + iter.fDevice->drawText(iter, text, byteLength, x, y, paint); + } + + ITER_END +} + +void SkCanvas::drawPosText(const void* text, size_t byteLength, + const SkPoint pos[], const SkPaint& paint) { + ITER_BEGIN(paint, SkDrawFilter::kText_Type) + + while (iter.next()) { + iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2, + paint); + } + + ITER_END +} + +void SkCanvas::drawPosTextH(const void* text, size_t byteLength, + const SkScalar xpos[], SkScalar constY, + const SkPaint& paint) { + ITER_BEGIN(paint, SkDrawFilter::kText_Type) + + while (iter.next()) { + iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1, + paint); + } + + ITER_END +} + +void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, + const SkPath& path, const SkMatrix* matrix, + const SkPaint& paint) { + ITER_BEGIN(paint, SkDrawFilter::kText_Type) + + while (iter.next()) { + iter.fDevice->drawTextOnPath(iter, text, byteLength, path, + matrix, paint); + } + + ITER_END +} + +void SkCanvas::drawVertices(VertexMode vmode, int vertexCount, + const SkPoint verts[], const SkPoint texs[], + const SkColor colors[], SkXfermode* xmode, + const uint16_t indices[], int indexCount, + const SkPaint& paint) { + ITER_BEGIN(paint, SkDrawFilter::kPath_Type) + + while (iter.next()) { + iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs, + colors, xmode, indices, indexCount, paint); + } + + ITER_END +} + +////////////////////////////////////////////////////////////////////////////// +// These methods are NOT virtual, and therefore must call back into virtual +// methods, rather than actually drawing themselves. +////////////////////////////////////////////////////////////////////////////// + +void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b, + SkPorterDuff::Mode mode) { + SkPaint paint; + + paint.setARGB(a, r, g, b); + if (SkPorterDuff::kSrcOver_Mode != mode) { + paint.setPorterDuffXfermode(mode); + } + this->drawPaint(paint); +} + +void SkCanvas::drawColor(SkColor c, SkPorterDuff::Mode mode) { + SkPaint paint; + + paint.setColor(c); + if (SkPorterDuff::kSrcOver_Mode != mode) { + paint.setPorterDuffXfermode(mode); + } + this->drawPaint(paint); +} + +void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) { + SkPoint pt; + + pt.set(x, y); + this->drawPoints(kPoints_PointMode, 1, &pt, paint); +} + +void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) { + SkPoint pt; + SkPaint paint; + + pt.set(x, y); + paint.setColor(color); + this->drawPoints(kPoints_PointMode, 1, &pt, paint); +} + +void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1, + const SkPaint& paint) { + SkPoint pts[2]; + + pts[0].set(x0, y0); + pts[1].set(x1, y1); + this->drawPoints(kLines_PointMode, 2, pts, paint); +} + +void SkCanvas::drawRectCoords(SkScalar left, SkScalar top, + SkScalar right, SkScalar bottom, + const SkPaint& paint) { + SkRect r; + + r.set(left, top, right, bottom); + this->drawRect(r, paint); +} + +void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius, + const SkPaint& paint) { + if (radius < 0) { + radius = 0; + } + + SkPath path; + SkRect r; + + r.set(cx - radius, cy - radius, cx + radius, cy + radius); + path.addOval(r); + this->drawPath(path, paint); +} + +void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry, + const SkPaint& paint) { + if (rx > 0 && ry > 0) { + SkPath path; + path.addRoundRect(r, rx, ry, SkPath::kCW_Direction); + this->drawPath(path, paint); + } else { + this->drawRect(r, paint); + } +} + +void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) { + SkPath path; + path.addOval(oval); + this->drawPath(path, paint); +} + +void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle, + SkScalar sweepAngle, bool useCenter, + const SkPaint& paint) { + if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) { + this->drawOval(oval, paint); + } else { + SkPath path; + if (useCenter) { + path.moveTo(oval.centerX(), oval.centerY()); + } + path.arcTo(oval, startAngle, sweepAngle, !useCenter); + if (useCenter) { + path.close(); + } + this->drawPath(path, paint); + } +} + +void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength, + const SkPath& path, SkScalar hOffset, + SkScalar vOffset, const SkPaint& paint) { + SkMatrix matrix; + + matrix.setTranslate(hOffset, vOffset); + this->drawTextOnPath(text, byteLength, path, &matrix, paint); +} + +void SkCanvas::drawPicture(SkPicture& picture) { + int saveCount = save(); + picture.draw(this); + restoreToCount(saveCount); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) { + // need COMPILE_TIME_ASSERT + SkASSERT(sizeof(fStorage) >= sizeof(SkDrawIter)); + + SkASSERT(canvas); + + fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips); + fDone = !fImpl->next(); +} + +SkCanvas::LayerIter::~LayerIter() { + fImpl->~SkDrawIter(); +} + +void SkCanvas::LayerIter::next() { + fDone = !fImpl->next(); +} + +SkDevice* SkCanvas::LayerIter::device() const { + return fImpl->getDevice(); +} + +const SkMatrix& SkCanvas::LayerIter::matrix() const { + return fImpl->getMatrix(); +} + +const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); } +int SkCanvas::LayerIter::x() const { return fImpl->getX(); } +int SkCanvas::LayerIter::y() const { return fImpl->getY(); } + + diff --git a/skia/sgl/SkColor.cpp b/skia/sgl/SkColor.cpp new file mode 100644 index 0000000..1e7aa70 --- /dev/null +++ b/skia/sgl/SkColor.cpp @@ -0,0 +1,136 @@ +/* libs/graphics/sgl/SkColor.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkColor.h" +#include "SkColorPriv.h" + +SkPMColor SkPreMultiplyARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) { + if (a != 255) { +#if 0 + unsigned scale = SkAlpha255To256(a); + r = SkAlphaMul(r, scale); + g = SkAlphaMul(g, scale); + b = SkAlphaMul(b, scale); +#else + r = SkMulDiv255Round(r, a); + g = SkMulDiv255Round(g, a); + b = SkMulDiv255Round(b, a); +#endif + } + return SkPackARGB32(a, r, g, b); +} + +SkPMColor SkPreMultiplyColor(SkColor c) { + unsigned a = SkColorGetA(c); + unsigned r = SkColorGetR(c); + unsigned g = SkColorGetG(c); + unsigned b = SkColorGetB(c); + + return SkPreMultiplyARGB(a, r, g, b); +} + +////////////////////////////////////////////////////////////////////////////////////////////// + +static inline SkScalar ByteToScalar(U8CPU x) { + SkASSERT(x <= 255); + return SkIntToScalar(x) / 255; +} + +static inline SkScalar ByteDivToScalar(int numer, U8CPU denom) { + // cast to keep the answer signed + return SkIntToScalar(numer) / (int)denom; +} + +void SkRGBToHSV(U8CPU r, U8CPU g, U8CPU b, SkScalar hsv[3]) { + SkASSERT(hsv); + + unsigned min = SkMin32(r, SkMin32(g, b)); + unsigned max = SkMax32(r, SkMax32(g, b)); + unsigned delta = max - min; + + SkScalar v = ByteToScalar(max); + SkASSERT(v >= 0 && v <= SK_Scalar1); + + if (0 == delta) { // we're a shade of gray + hsv[0] = hsv[1] = hsv[2] = v; + return; + } + + SkScalar s = ByteDivToScalar(delta, max); + SkASSERT(s >= 0 && s <= SK_Scalar1); + + SkScalar h; + if (r == max) { + h = ByteDivToScalar(g - b, delta); + } else if (g == max) { + h = SkIntToScalar(2) + ByteDivToScalar(b - r, delta); + } else { // b == max + h = SkIntToScalar(4) + ByteDivToScalar(r - g, delta); + } + + h *= 60; + if (h < 0) { + h += SkIntToScalar(360); + } + SkASSERT(h >= 0 && h < SkIntToScalar(360)); + + hsv[0] = h; + hsv[1] = s; + hsv[2] = v; +} + +static inline U8CPU UnitScalarToByte(SkScalar x) { + if (x < 0) { + return 0; + } + if (x >= SK_Scalar1) { + return 255; + } + return SkScalarToFixed(x) >> 8; +} + +SkColor SkHSVToColor(U8CPU a, const SkScalar hsv[3]) { + SkASSERT(hsv); + + U8CPU s = UnitScalarToByte(hsv[1]); + U8CPU v = UnitScalarToByte(hsv[2]); + + if (0 == s) { // shade of gray + return SkColorSetARGB(a, v, v, v); + } + SkFixed hx = (hsv[0] < 0 || hsv[0] >= SkIntToScalar(360)) ? 0 : SkScalarToFixed(hsv[0]/60); + SkFixed f = hx & 0xFFFF; + + unsigned v_scale = SkAlpha255To256(v); + unsigned p = SkAlphaMul(255 - s, v_scale); + unsigned q = SkAlphaMul(255 - (s * f >> 16), v_scale); + unsigned t = SkAlphaMul(255 - (s * (SK_Fixed1 - f) >> 16), v_scale); + + unsigned r, g, b; + + SkASSERT((unsigned)(hx >> 16) < 6); + switch (hx >> 16) { + case 0: r = v; g = t; b = p; break; + case 1: r = q; g = v; b = p; break; + case 2: r = p; g = v; b = t; break; + case 3: r = p; g = q; b = v; break; + case 4: r = t; g = p; b = v; break; + default: r = v; g = p; b = q; break; + } + return SkColorSetARGB(a, r, g, b); +} + diff --git a/skia/sgl/SkColorFilter.cpp b/skia/sgl/SkColorFilter.cpp new file mode 100644 index 0000000..d9034c6 --- /dev/null +++ b/skia/sgl/SkColorFilter.cpp @@ -0,0 +1,108 @@ +/* libs/graphics/sgl/SkColorFilter.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkColorFilter.h" +#include "SkShader.h" + +void SkColorFilter::filterSpan16(const uint16_t s[], int count, uint16_t d[]) +{ + SkASSERT(this->getFlags() & SkColorFilter::kHasFilter16_Flag); + SkASSERT(!"missing implementation of SkColorFilter::filterSpan16"); + + if (d != s) + memcpy(d, s, count * sizeof(uint16_t)); +} + +////////////////////////////////////////////////////////////////////////////// + +SkFilterShader::SkFilterShader(SkShader* shader, SkColorFilter* filter) +{ + fShader = shader; shader->ref(); + fFilter = filter; filter->ref(); +} + +SkFilterShader::SkFilterShader(SkFlattenableReadBuffer& buffer) : + INHERITED(buffer) +{ + fShader = static_cast<SkShader*>(buffer.readFlattenable()); + fFilter = static_cast<SkColorFilter*>(buffer.readFlattenable()); +} + +SkFilterShader::~SkFilterShader() +{ + fFilter->unref(); + fShader->unref(); +} + +void SkFilterShader::beginSession() +{ + this->INHERITED::beginSession(); + fShader->beginSession(); +} + +void SkFilterShader::endSession() +{ + fShader->endSession(); + this->INHERITED::endSession(); +} + +void SkFilterShader::flatten(SkFlattenableWriteBuffer& buffer) +{ + this->INHERITED::flatten(buffer); + buffer.writeFlattenable(fShader); + buffer.writeFlattenable(fFilter); +} + +uint32_t SkFilterShader::getFlags() +{ + uint32_t shaderF = fShader->getFlags(); + uint32_t filterF = fFilter->getFlags(); + + // if the filter doesn't support 16bit, clear the matching bit in the shader + if (!(filterF & SkColorFilter::kHasFilter16_Flag)) + shaderF &= ~SkShader::kHasSpan16_Flag; + + // if the filter might change alpha, clear the opaque flag in the shader + if (!(filterF & SkColorFilter::kAlphaUnchanged_Flag)) + shaderF &= ~(SkShader::kOpaqueAlpha_Flag | SkShader::kHasSpan16_Flag); + + return shaderF; +} + +bool SkFilterShader::setContext(const SkBitmap& device, + const SkPaint& paint, + const SkMatrix& matrix) +{ + return this->INHERITED::setContext(device, paint, matrix) && + fShader->setContext(device, paint, matrix); +} + +void SkFilterShader::shadeSpan(int x, int y, SkPMColor result[], int count) +{ + fShader->shadeSpan(x, y, result, count); + fFilter->filterSpan(result, count, result); +} + +void SkFilterShader::shadeSpan16(int x, int y, uint16_t result[], int count) +{ + SkASSERT(fShader->getFlags() & SkShader::kHasSpan16_Flag); + SkASSERT(fFilter->getFlags() & SkColorFilter::kHasFilter16_Flag); + + fShader->shadeSpan16(x, y, result, count); + fFilter->filterSpan16(result, count, result); +} + diff --git a/skia/sgl/SkColorTable.cpp b/skia/sgl/SkColorTable.cpp new file mode 100644 index 0000000..b8edf18 --- /dev/null +++ b/skia/sgl/SkColorTable.cpp @@ -0,0 +1,143 @@ +/* libs/graphics/sgl/SkColorTable.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkBitmap.h" +#include "SkFlattenable.h" +#include "SkStream.h" +#include "SkTemplates.h" + +SkColorTable::SkColorTable(int count) + : f16BitCache(NULL), fFlags(0) +{ + if (count < 0) + count = 0; + else if (count > 256) + count = 256; + + fCount = SkToU16(count); + fColors = (SkPMColor*)sk_malloc_throw(count * sizeof(SkPMColor)); + memset(fColors, 0, count * sizeof(SkPMColor)); + + SkDEBUGCODE(fColorLockCount = 0;) + SkDEBUGCODE(f16BitCacheLockCount = 0;) +} + +SkColorTable::SkColorTable(const SkPMColor colors[], int count) + : f16BitCache(NULL), fFlags(0) +{ + if (count < 0) + count = 0; + else if (count > 256) + count = 256; + + fCount = SkToU16(count); + fColors = (SkPMColor*)sk_malloc_throw(count * sizeof(SkPMColor)); + + if (colors) + memcpy(fColors, colors, count * sizeof(SkPMColor)); + + SkDEBUGCODE(fColorLockCount = 0;) + SkDEBUGCODE(f16BitCacheLockCount = 0;) +} + +SkColorTable::~SkColorTable() +{ + SkASSERT(fColorLockCount == 0); + SkASSERT(f16BitCacheLockCount == 0); + + sk_free(fColors); + sk_free(f16BitCache); +} + +void SkColorTable::setFlags(unsigned flags) +{ + fFlags = SkToU8(flags); +} + +void SkColorTable::unlockColors(bool changed) +{ + SkASSERT(fColorLockCount != 0); + SkDEBUGCODE(fColorLockCount -= 1;) + if (changed) + this->inval16BitCache(); +} + +void SkColorTable::inval16BitCache() +{ + SkASSERT(f16BitCacheLockCount == 0); + if (f16BitCache) + { + sk_free(f16BitCache); + f16BitCache = NULL; + } +} + +#include "SkColorPriv.h" + +static inline void build_16bitcache(uint16_t dst[], const SkPMColor src[], int count) +{ + while (--count >= 0) + *dst++ = SkPixel32ToPixel16_ToU16(*src++); +} + +const uint16_t* SkColorTable::lock16BitCache() +{ + if (fFlags & kColorsAreOpaque_Flag) + { + if (f16BitCache == NULL) // build the cache + { + f16BitCache = (uint16_t*)sk_malloc_throw(fCount * sizeof(uint16_t)); + build_16bitcache(f16BitCache, fColors, fCount); + } + } + else // our colors have alpha, so no cache + { + this->inval16BitCache(); + if (f16BitCache) + { + sk_free(f16BitCache); + f16BitCache = NULL; + } + } + + SkDEBUGCODE(f16BitCacheLockCount += 1); + return f16BitCache; +} + +/////////////////////////////////////////////////////////////////////////////// + +SkColorTable::SkColorTable(SkFlattenableReadBuffer& buffer) { + f16BitCache = NULL; + SkDEBUGCODE(fColorLockCount = 0;) + SkDEBUGCODE(f16BitCacheLockCount = 0;) + + fCount = buffer.readU16(); + SkASSERT((unsigned)fCount <= 256); + + fFlags = buffer.readU8(); + + fColors = (SkPMColor*)sk_malloc_throw(fCount * sizeof(SkPMColor)); + buffer.read(fColors, fCount * sizeof(SkPMColor)); +} + +void SkColorTable::flatten(SkFlattenableWriteBuffer& buffer) const { + int count = this->count(); + buffer.write16(count); + buffer.write8(this->getFlags()); + buffer.writeMul4(fColors, count * sizeof(SkPMColor)); +} + diff --git a/skia/sgl/SkCoreBlitters.h b/skia/sgl/SkCoreBlitters.h new file mode 100644 index 0000000..e1692f0 --- /dev/null +++ b/skia/sgl/SkCoreBlitters.h @@ -0,0 +1,258 @@ +/* libs/graphics/sgl/SkCoreBlitters.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkCoreBlitters_DEFINED +#define SkCoreBlitters_DEFINED + +#include "SkBlitter.h" +#include "SkBlitRow.h" + +class SkRasterBlitter : public SkBlitter { +public: + SkRasterBlitter(const SkBitmap& device) : fDevice(device) {} + +protected: + const SkBitmap& fDevice; + +private: + typedef SkBlitter INHERITED; +}; + +class SkShaderBlitter : public SkRasterBlitter { +public: + SkShaderBlitter(const SkBitmap& device, const SkPaint& paint); + virtual ~SkShaderBlitter(); + +protected: + SkShader* fShader; + +private: + // illegal + SkShaderBlitter& operator=(const SkShaderBlitter&); + + typedef SkRasterBlitter INHERITED; +}; + +/////////////////////////////////////////////////////////////////////////////// + +class SkA8_Blitter : public SkRasterBlitter { +public: + SkA8_Blitter(const SkBitmap& device, const SkPaint& paint); + virtual void blitH(int x, int y, int width); + virtual void blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]); + virtual void blitV(int x, int y, int height, SkAlpha alpha); + virtual void blitRect(int x, int y, int width, int height); + virtual void blitMask(const SkMask&, const SkIRect&); + virtual const SkBitmap* justAnOpaqueColor(uint32_t*); + +private: + unsigned fSrcA; + + // illegal + SkA8_Blitter& operator=(const SkA8_Blitter&); + + typedef SkRasterBlitter INHERITED; +}; + +class SkA8_Shader_Blitter : public SkShaderBlitter { +public: + SkA8_Shader_Blitter(const SkBitmap& device, const SkPaint& paint); + virtual ~SkA8_Shader_Blitter(); + virtual void blitH(int x, int y, int width); + virtual void blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]); + virtual void blitMask(const SkMask&, const SkIRect&); + +private: + SkXfermode* fXfermode; + SkPMColor* fBuffer; + uint8_t* fAAExpand; + + // illegal + SkA8_Shader_Blitter& operator=(const SkA8_Shader_Blitter&); + + typedef SkShaderBlitter INHERITED; +}; + +//////////////////////////////////////////////////////////////// + +class SkARGB32_Blitter : public SkRasterBlitter { +public: + SkARGB32_Blitter(const SkBitmap& device, const SkPaint& paint); + virtual void blitH(int x, int y, int width); + virtual void blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]); + virtual void blitV(int x, int y, int height, SkAlpha alpha); + virtual void blitRect(int x, int y, int width, int height); + virtual void blitMask(const SkMask&, const SkIRect&); + virtual const SkBitmap* justAnOpaqueColor(uint32_t*); + +protected: + SkColor fPMColor; + +private: + unsigned fSrcA, fSrcR, fSrcG, fSrcB; + + // illegal + SkARGB32_Blitter& operator=(const SkARGB32_Blitter&); + + typedef SkRasterBlitter INHERITED; +}; + +class SkARGB32_Black_Blitter : public SkARGB32_Blitter { +public: + SkARGB32_Black_Blitter(const SkBitmap& device, const SkPaint& paint) + : SkARGB32_Blitter(device, paint) {} + virtual void blitMask(const SkMask&, const SkIRect&); + virtual void blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]); + +private: + typedef SkARGB32_Blitter INHERITED; +}; + +class SkARGB32_Opaque_Blitter : public SkARGB32_Blitter { +public: + SkARGB32_Opaque_Blitter(const SkBitmap& device, const SkPaint& paint) + : SkARGB32_Blitter(device, paint) { SkASSERT(paint.getAlpha() == 0xFF); } + virtual void blitMask(const SkMask&, const SkIRect&); + +private: + typedef SkARGB32_Blitter INHERITED; +}; + +class SkARGB32_Shader_Blitter : public SkShaderBlitter { +public: + SkARGB32_Shader_Blitter(const SkBitmap& device, const SkPaint& paint); + virtual ~SkARGB32_Shader_Blitter(); + virtual void blitH(int x, int y, int width); + virtual void blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]); + +private: + SkXfermode* fXfermode; + SkPMColor* fBuffer; + + // illegal + SkARGB32_Shader_Blitter& operator=(const SkARGB32_Shader_Blitter&); + + typedef SkShaderBlitter INHERITED; +}; + +//////////////////////////////////////////////////////////////// + +class SkRGB16_Blitter : public SkRasterBlitter { +public: + SkRGB16_Blitter(const SkBitmap& device, const SkPaint& paint); + virtual void blitH(int x, int y, int width); + virtual void blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]); + virtual void blitV(int x, int y, int height, SkAlpha alpha); + virtual void blitRect(int x, int y, int width, int height); + virtual void blitMask(const SkMask&, const SkIRect&); + virtual const SkBitmap* justAnOpaqueColor(uint32_t*); + +private: + unsigned fScale; + uint16_t fColor16; // already scaled by fScale + uint16_t fRawColor16; // unscaled + uint16_t fRawDither16; // unscaled + SkBool8 fDoDither; + + // illegal + SkRGB16_Blitter& operator=(const SkRGB16_Blitter&); + + typedef SkRasterBlitter INHERITED; +}; + +class SkRGB16_Black_Blitter : public SkRGB16_Blitter { +public: + SkRGB16_Black_Blitter(const SkBitmap& device, const SkPaint& paint); + + virtual void blitMask(const SkMask&, const SkIRect&); + virtual void blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]); + +private: + typedef SkRGB16_Blitter INHERITED; +}; + +class SkRGB16_Shader_Blitter : public SkShaderBlitter { +public: + SkRGB16_Shader_Blitter(const SkBitmap& device, const SkPaint& paint); + virtual ~SkRGB16_Shader_Blitter(); + virtual void blitH(int x, int y, int width); + virtual void blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]); + +protected: + SkPMColor* fBuffer; + SkBlitRow::Proc fOpaqueProc; + SkBlitRow::Proc fAlphaProc; + +private: + // illegal + SkRGB16_Shader_Blitter& operator=(const SkRGB16_Shader_Blitter&); + + typedef SkShaderBlitter INHERITED; +}; + +// used only if the shader can perform shadSpan16 +class SkRGB16_Shader16_Blitter : public SkRGB16_Shader_Blitter { +public: + SkRGB16_Shader16_Blitter(const SkBitmap& device, const SkPaint& paint); + virtual void blitH(int x, int y, int width); + virtual void blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]); + +private: + typedef SkRGB16_Shader_Blitter INHERITED; +}; + +class SkRGB16_Shader_Xfermode_Blitter : public SkShaderBlitter { +public: + SkRGB16_Shader_Xfermode_Blitter(const SkBitmap& device, const SkPaint& paint); + virtual ~SkRGB16_Shader_Xfermode_Blitter(); + virtual void blitH(int x, int y, int width); + virtual void blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]); + +private: + SkXfermode* fXfermode; + SkPMColor* fBuffer; + uint8_t* fAAExpand; + + // illegal + SkRGB16_Shader_Xfermode_Blitter& operator=(const SkRGB16_Shader_Xfermode_Blitter&); + + typedef SkShaderBlitter INHERITED; +}; + +///////////////////////////////////////////////////////////////////////////// + +class SkA1_Blitter : public SkRasterBlitter { +public: + SkA1_Blitter(const SkBitmap& device, const SkPaint& paint); + virtual void blitH(int x, int y, int width); + +private: + uint8_t fSrcA; + + // illegal + SkA1_Blitter& operator=(const SkA1_Blitter&); + + typedef SkRasterBlitter INHERITED; +}; + + +extern SkBlitter* SkBlitter_ChooseD4444(const SkBitmap& device, + const SkPaint& paint, + void* storage, size_t storageSize); + +#endif + diff --git a/skia/sgl/SkDeque.cpp b/skia/sgl/SkDeque.cpp new file mode 100644 index 0000000..e424817 --- /dev/null +++ b/skia/sgl/SkDeque.cpp @@ -0,0 +1,252 @@ +/* libs/graphics/sgl/SkDeque.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkDeque.h" + +#define INIT_ELEM_COUNT 1 // should we let the caller set this? + +struct SkDeque::Head { + Head* fNext; + Head* fPrev; + char* fBegin; // start of used section in this chunk + char* fEnd; // end of used section in this chunk + char* fStop; // end of the allocated chunk + + char* start() { return (char*)(this + 1); } + const char* start() const { return (const char*)(this + 1); } + + void init(size_t size) { + fNext = fPrev = NULL; + fBegin = fEnd = NULL; + fStop = (char*)this + size; + } +}; + +SkDeque::SkDeque(size_t elemSize) + : fElemSize(elemSize), fInitialStorage(NULL), fCount(0) { + fFront = fBack = NULL; +} + +SkDeque::SkDeque(size_t elemSize, void* storage, size_t storageSize) + : fElemSize(elemSize), fInitialStorage(storage), fCount(0) { + SkASSERT(storageSize == 0 || storage != NULL); + + if (storageSize >= sizeof(Head) + elemSize) { + fFront = (Head*)storage; + fFront->init(storageSize); + } else { + fFront = NULL; + } + fBack = fFront; +} + +SkDeque::~SkDeque() { + Head* head = fFront; + Head* initialHead = (Head*)fInitialStorage; + + while (head) { + Head* next = head->fNext; + if (head != initialHead) { + sk_free(head); + } + head = next; + } +} + +const void* SkDeque::front() const { + Head* front = fFront; + + if (NULL == front) { + return NULL; + } + if (NULL == front->fBegin) { + front = front->fNext; + if (NULL == front) { + return NULL; + } + } + SkASSERT(front->fBegin); + return front->fBegin; +} + +const void* SkDeque::back() const { + Head* back = fBack; + + if (NULL == back) { + return NULL; + } + if (NULL == back->fEnd) { // marked as deleted + back = back->fPrev; + if (NULL == back) { + return NULL; + } + } + SkASSERT(back->fEnd); + return back->fEnd - fElemSize; +} + +void* SkDeque::push_front() { + fCount += 1; + + if (NULL == fFront) { + fFront = (Head*)sk_malloc_throw(sizeof(Head) + + INIT_ELEM_COUNT * fElemSize); + fFront->init(sizeof(Head) + INIT_ELEM_COUNT * fElemSize); + fBack = fFront; // update our linklist + } + + Head* first = fFront; + char* begin; + + if (NULL == first->fBegin) { + INIT_CHUNK: + first->fEnd = first->fStop; + begin = first->fStop - fElemSize; + } else { + begin = first->fBegin - fElemSize; + if (begin < first->start()) { // no more room in this chunk + // should we alloc more as we accumulate more elements? + size_t size = sizeof(Head) + INIT_ELEM_COUNT * fElemSize; + + first = (Head*)sk_malloc_throw(size); + first->init(size); + first->fNext = fFront; + fFront->fPrev = first; + fFront = first; + goto INIT_CHUNK; + } + } + + first->fBegin = begin; + return begin; +} + +void* SkDeque::push_back() { + fCount += 1; + + if (NULL == fBack) { + fBack = (Head*)sk_malloc_throw(sizeof(Head) + + INIT_ELEM_COUNT * fElemSize); + fBack->init(sizeof(Head) + INIT_ELEM_COUNT * fElemSize); + fFront = fBack; // update our linklist + } + + Head* last = fBack; + char* end; + + if (NULL == last->fBegin) { + INIT_CHUNK: + last->fBegin = last->start(); + end = last->fBegin + fElemSize; + } else { + end = last->fEnd + fElemSize; + if (end > last->fStop) { // no more room in this chunk + // should we alloc more as we accumulate more elements? + size_t size = sizeof(Head) + INIT_ELEM_COUNT * fElemSize; + + last = (Head*)sk_malloc_throw(size); + last->init(size); + last->fPrev = fBack; + fBack->fNext = last; + fBack = last; + goto INIT_CHUNK; + } + } + + last->fEnd = end; + return end - fElemSize; +} + +void SkDeque::pop_front() { + SkASSERT(fCount > 0); + fCount -= 1; + + Head* first = fFront; + + SkASSERT(first != NULL); + + if (first->fBegin == NULL) { // we were marked empty from before + first = first->fNext; + first->fPrev = NULL; + sk_free(fFront); + fFront = first; + SkASSERT(first != NULL); // else we popped too far + } + + char* begin = first->fBegin + fElemSize; + SkASSERT(begin <= first->fEnd); + + if (begin < fFront->fEnd) { + first->fBegin = begin; + } else { + first->fBegin = first->fEnd = NULL; // mark as empty + } +} + +void SkDeque::pop_back() { + SkASSERT(fCount > 0); + fCount -= 1; + + Head* last = fBack; + + SkASSERT(last != NULL); + + if (last->fEnd == NULL) { // we were marked empty from before + last = last->fPrev; + last->fNext = NULL; + sk_free(fBack); + fBack = last; + SkASSERT(last != NULL); // else we popped too far + } + + char* end = last->fEnd - fElemSize; + SkASSERT(end >= last->fBegin); + + if (end > last->fBegin) { + last->fEnd = end; + } else { + last->fBegin = last->fEnd = NULL; // mark as empty + } +} + +/////////////////////////////////////////////////////////////////////////////// + +SkDeque::Iter::Iter(const SkDeque& d) : fElemSize(d.fElemSize) { + fHead = d.fFront; + while (fHead != NULL && fHead->fBegin == NULL) { + fHead = fHead->fNext; + } + fPos = fHead ? fHead->fBegin : NULL; +} + +void* SkDeque::Iter::next() { + char* pos = fPos; + + if (pos) { // if we were valid, try to move to the next setting + char* next = pos + fElemSize; + SkASSERT(next <= fHead->fEnd); + if (next == fHead->fEnd) { // exhausted this chunk, move to next + do { + fHead = fHead->fNext; + } while (fHead != NULL && fHead->fBegin == NULL); + next = fHead ? fHead->fBegin : NULL; + } + fPos = next; + } + return pos; +} + diff --git a/skia/sgl/SkDevice.cpp b/skia/sgl/SkDevice.cpp new file mode 100644 index 0000000..139174d --- /dev/null +++ b/skia/sgl/SkDevice.cpp @@ -0,0 +1,110 @@ +#include "SkDevice.h" +#include "SkDraw.h" +#include "SkRect.h" + +SkDevice::SkDevice() {} + +SkDevice::SkDevice(const SkBitmap& bitmap) : fBitmap(bitmap) {} + +void SkDevice::lockPixels() { + fBitmap.lockPixels(); +} + +void SkDevice::unlockPixels() { + fBitmap.unlockPixels(); +} + +const SkBitmap& SkDevice::accessBitmap(bool changePixels) { + this->onAccessBitmap(&fBitmap); + if (changePixels) { + fBitmap.notifyPixelsChanged(); + } + return fBitmap; +} + +void SkDevice::getBounds(SkIRect* bounds) const { + if (bounds) { + bounds->set(0, 0, fBitmap.width(), fBitmap.height()); + } +} + +bool SkDevice::intersects(const SkIRect& r, SkIRect* sect) const { + SkIRect bounds; + + this->getBounds(&bounds); + return sect ? sect->intersect(r, bounds) : SkIRect::Intersects(r, bounds); +} + +void SkDevice::eraseColor(SkColor eraseColor) { + fBitmap.eraseColor(eraseColor); +} + +void SkDevice::onAccessBitmap(SkBitmap* bitmap) {} + +void SkDevice::setMatrixClip(const SkMatrix&, const SkRegion&) {} + +/////////////////////////////////////////////////////////////////////////////// + +void SkDevice::drawPaint(const SkDraw& draw, const SkPaint& paint) { + draw.drawPaint(paint); +} + +void SkDevice::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode, size_t count, + const SkPoint pts[], const SkPaint& paint) { + draw.drawPoints(mode, count, pts, paint); +} + +void SkDevice::drawRect(const SkDraw& draw, const SkRect& r, + const SkPaint& paint) { + draw.drawRect(r, paint); +} + +void SkDevice::drawPath(const SkDraw& draw, const SkPath& path, + const SkPaint& paint) { + draw.drawPath(path, paint); +} + +void SkDevice::drawBitmap(const SkDraw& draw, const SkBitmap& bitmap, + const SkMatrix& matrix, const SkPaint& paint) { + draw.drawBitmap(bitmap, matrix, paint); +} + +void SkDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap, + int x, int y, const SkPaint& paint) { + draw.drawSprite(bitmap, x, y, paint); +} + +void SkDevice::drawText(const SkDraw& draw, const void* text, size_t len, + SkScalar x, SkScalar y, const SkPaint& paint) { + draw.drawText((const char*)text, len, x, y, paint); +} + +void SkDevice::drawPosText(const SkDraw& draw, const void* text, size_t len, + const SkScalar xpos[], SkScalar y, + int scalarsPerPos, const SkPaint& paint) { + draw.drawPosText((const char*)text, len, xpos, y, scalarsPerPos, paint); +} + +void SkDevice::drawTextOnPath(const SkDraw& draw, const void* text, + size_t len, const SkPath& path, + const SkMatrix* matrix, + const SkPaint& paint) { + draw.drawTextOnPath((const char*)text, len, path, matrix, paint); +} + +void SkDevice::drawVertices(const SkDraw& draw, SkCanvas::VertexMode vmode, + int vertexCount, + const SkPoint verts[], const SkPoint textures[], + const SkColor colors[], SkXfermode* xmode, + const uint16_t indices[], int indexCount, + const SkPaint& paint) { + draw.drawVertices(vmode, vertexCount, verts, textures, colors, xmode, + indices, indexCount, paint); +} + +void SkDevice::drawDevice(const SkDraw& draw, SkDevice* device, + int x, int y, const SkPaint& paint) { + draw.drawSprite(device->accessBitmap(false), x, y, paint); +} + + diff --git a/skia/sgl/SkDither.cpp b/skia/sgl/SkDither.cpp new file mode 100644 index 0000000..53a8573 --- /dev/null +++ b/skia/sgl/SkDither.cpp @@ -0,0 +1,49 @@ +#include "SkDither.h" + +/* The base dither matrix we use to derive optimized ones for 565 and 4444 + + { 0, 32, 8, 40, 2, 34, 10, 42 }, + { 48, 16, 56, 24, 50, 18, 58, 26 }, + { 12, 44, 4, 36, 14, 46, 6, 38 }, + { 60, 28, 52, 20, 62, 30, 54, 22 }, + { 3, 35, 11, 43, 1, 33, 9, 41 }, + { 51, 19, 59, 27, 49, 17, 57, 25 }, + { 15, 47, 7, 39, 13, 45, 5, 37 }, + { 63, 31, 55, 23, 61, 29, 53, 21 } + + The 4444 version only needs 4 bits, and given that we can reduce its size + since the other 4x4 sub pieces all look the same once we truncate the bits. + + The 565 version only needs 3 bits for red/blue, and only 2 bits for green. + For simplicity, we store 3 bits, and have the dither macros for green know + this, and they shift the dither value down by 1 to make it 2 bits. + */ + +#ifdef ENABLE_DITHER_MATRIX_4X4 + +const uint8_t gDitherMatrix_4Bit_4X4[4][4] = { + { 0, 8, 2, 10 }, + { 12, 4, 14, 6 }, + { 3, 11, 1, 9 }, + { 15, 7, 13, 5 } +}; + +const uint8_t gDitherMatrix_3Bit_4X4[4][4] = { + { 0, 4, 1, 5 }, + { 6, 2, 7, 3 }, + { 1, 5, 0, 4 }, + { 7, 3, 6, 2 } +}; + +#else // used packed shorts for a scanlines worth of dither values + +const uint16_t gDitherMatrix_4Bit_16[4] = { + 0xA280, 0x6E4C, 0x91B3, 0x5D7F +}; + +const uint16_t gDitherMatrix_3Bit_16[4] = { + 0x5140, 0x3726, 0x4051, 0x2637 +}; + +#endif + diff --git a/skia/sgl/SkDraw.cpp b/skia/sgl/SkDraw.cpp new file mode 100644 index 0000000..106543a --- /dev/null +++ b/skia/sgl/SkDraw.cpp @@ -0,0 +1,2301 @@ +/* libs/graphics/sgl/SkDraw.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkDraw.h" +#include "SkBlitter.h" +#include "SkBounder.h" +#include "SkCanvas.h" +#include "SkColorPriv.h" +#include "SkDevice.h" +#include "SkMaskFilter.h" +#include "SkPaint.h" +#include "SkPathEffect.h" +#include "SkRasterizer.h" +#include "SkScan.h" +#include "SkShader.h" +#include "SkStroke.h" +#include "SkTemplatesPriv.h" +#include "SkUtils.h" + +#include "SkAutoKern.h" +#include "SkBitmapProcShader.h" +#include "SkDrawProcs.h" + +//#define TRACE_BITMAP_DRAWS + +static SkPoint* rect_points(SkRect& r, int index) { + SkASSERT((unsigned)index < 2); + return &((SkPoint*)(void*)&r)[index]; +} + +/** Helper for allocating small blitters on the stack. +*/ + +#define kBlitterStorageLongCount (sizeof(SkBitmapProcShader) >> 2) + +class SkAutoBlitterChoose { +public: + SkAutoBlitterChoose(const SkBitmap& device, const SkMatrix& matrix, + const SkPaint& paint) { + fBlitter = SkBlitter::Choose(device, matrix, paint, + fStorage, sizeof(fStorage)); + } + ~SkAutoBlitterChoose(); + + SkBlitter* operator->() { return fBlitter; } + SkBlitter* get() const { return fBlitter; } + +private: + SkBlitter* fBlitter; + uint32_t fStorage[kBlitterStorageLongCount]; +}; + +SkAutoBlitterChoose::~SkAutoBlitterChoose() { + if ((void*)fBlitter == (void*)fStorage) { + fBlitter->~SkBlitter(); + } else { + SkDELETE(fBlitter); + } +} + +class SkAutoBitmapShaderInstall { +public: + SkAutoBitmapShaderInstall(const SkBitmap& src, const SkPaint* paint) + : fPaint((SkPaint*)paint) { + fPrevShader = paint->getShader(); + fPrevShader->safeRef(); + fPaint->setShader(SkShader::CreateBitmapShader( src, + SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, + fStorage, sizeof(fStorage))); + } + ~SkAutoBitmapShaderInstall() { + SkShader* shader = fPaint->getShader(); + + fPaint->setShader(fPrevShader); + fPrevShader->safeUnref(); + + if ((void*)shader == (void*)fStorage) { + shader->~SkShader(); + } else { + SkDELETE(shader); + } + } +private: + SkPaint* fPaint; + SkShader* fPrevShader; + uint32_t fStorage[kBlitterStorageLongCount]; +}; + +class SkAutoPaintStyleRestore { +public: + SkAutoPaintStyleRestore(const SkPaint& paint, SkPaint::Style style) + : fPaint((SkPaint&)paint) { + fStyle = paint.getStyle(); // record the old + fPaint.setStyle(style); // change it to the specified style + } + ~SkAutoPaintStyleRestore() { + fPaint.setStyle(fStyle); // restore the old + } +private: + SkPaint& fPaint; + SkPaint::Style fStyle; + + // illegal + SkAutoPaintStyleRestore(const SkAutoPaintStyleRestore&); + SkAutoPaintStyleRestore& operator=(const SkAutoPaintStyleRestore&); +}; + +/////////////////////////////////////////////////////////////////////////////// + +SkDraw::SkDraw(const SkDraw& src) { + memcpy(this, &src, sizeof(*this)); +} + +/////////////////////////////////////////////////////////////////////////////// + +typedef void (*BitmapXferProc)(void* pixels, size_t bytes, uint32_t data); + +static void D_Clear_BitmapXferProc(void* pixels, size_t bytes, uint32_t) { + bzero(pixels, bytes); +} + +static void D_Dst_BitmapXferProc(void*, size_t, uint32_t data) {} + +static void D32_Src_BitmapXferProc(void* pixels, size_t bytes, uint32_t data) { + sk_memset32((uint32_t*)pixels, data, bytes >> 2); +} + +static void D16_Src_BitmapXferProc(void* pixels, size_t bytes, uint32_t data) { + sk_memset16((uint16_t*)pixels, data, bytes >> 1); +} + +static void DA8_Src_BitmapXferProc(void* pixels, size_t bytes, uint32_t data) { + memset(pixels, data, bytes); +} + +static BitmapXferProc ChooseBitmapXferProc(const SkBitmap& bitmap, + const SkPaint& paint, + uint32_t* data) { + // todo: we can apply colorfilter up front if no shader, so we wouldn't + // need to abort this fastpath + if (paint.getShader() || paint.getColorFilter()) { + return NULL; + } + + SkPorterDuff::Mode mode; + if (!SkPorterDuff::IsMode(paint.getXfermode(), &mode)) { + return NULL; + } + + SkColor color = paint.getColor(); + + // collaps modes based on color... + if (SkPorterDuff::kSrcOver_Mode == mode) { + unsigned alpha = SkColorGetA(color); + if (0 == alpha) { + mode = SkPorterDuff::kDst_Mode; + } else if (0xFF == alpha) { + mode = SkPorterDuff::kSrc_Mode; + } + } + + switch (mode) { + case SkPorterDuff::kClear_Mode: +// SkDebugf("--- D_Clear_BitmapXferProc\n"); + return D_Clear_BitmapXferProc; // ignore data + case SkPorterDuff::kDst_Mode: +// SkDebugf("--- D_Dst_BitmapXferProc\n"); + return D_Dst_BitmapXferProc; // ignore data + case SkPorterDuff::kSrc_Mode: { + /* + should I worry about dithering for the lower depths? <reed> + */ + SkPMColor pmc = SkPreMultiplyColor(color); + switch (bitmap.config()) { + case SkBitmap::kARGB_8888_Config: + if (data) { + *data = pmc; + } +// SkDebugf("--- D32_Src_BitmapXferProc\n"); + return D32_Src_BitmapXferProc; + case SkBitmap::kARGB_4444_Config: + if (data) { + *data = SkPixel32ToPixel4444(pmc); + } +// SkDebugf("--- D16_Src_BitmapXferProc\n"); + return D16_Src_BitmapXferProc; + case SkBitmap::kRGB_565_Config: + if (data) { + *data = SkPixel32ToPixel16(pmc); + } +// SkDebugf("--- D16_Src_BitmapXferProc\n"); + return D16_Src_BitmapXferProc; + case SkBitmap::kA8_Config: + if (data) { + *data = SkGetPackedA32(pmc); + } +// SkDebugf("--- DA8_Src_BitmapXferProc\n"); + return DA8_Src_BitmapXferProc; + default: + break; + } + break; + } + default: + break; + } + return NULL; +} + +static void CallBitmapXferProc(const SkBitmap& bitmap, const SkIRect& rect, + BitmapXferProc proc, uint32_t procData) { + int shiftPerPixel; + switch (bitmap.config()) { + case SkBitmap::kARGB_8888_Config: + shiftPerPixel = 2; + break; + case SkBitmap::kARGB_4444_Config: + case SkBitmap::kRGB_565_Config: + shiftPerPixel = 1; + break; + case SkBitmap::kA8_Config: + shiftPerPixel = 0; + break; + default: + SkASSERT(!"Can't use xferproc on this config"); + return; + } + + uint8_t* pixels = (uint8_t*)bitmap.getPixels(); + SkASSERT(pixels); + const size_t rowBytes = bitmap.rowBytes(); + const int widthBytes = rect.width() << shiftPerPixel; + + // skip down to the first scanline and X position + pixels += rect.fTop * rowBytes + (rect.fLeft << shiftPerPixel); + for (int scans = rect.height() - 1; scans >= 0; --scans) { + proc(pixels, widthBytes, procData); + pixels += rowBytes; + } +} + +void SkDraw::drawPaint(const SkPaint& paint) const { + SkDEBUGCODE(this->validate();) + + if (fClip->isEmpty()) { + return; + } + + SkIRect devRect; + devRect.set(0, 0, fBitmap->width(), fBitmap->height()); + if (fBounder && !fBounder->doIRect(devRect)) { + return; + } + + /* If we don't have a shader (i.e. we're just a solid color) we may + be faster to operate directly on the device bitmap, rather than invoking + a blitter. Esp. true for xfermodes, which require a colorshader to be + present, which is just redundant work. Since we're drawing everywhere + in the clip, we don't have to worry about antialiasing. + */ + uint32_t procData = 0; // to avoid the warning + BitmapXferProc proc = ChooseBitmapXferProc(*fBitmap, paint, &procData); + if (proc) { + if (D_Dst_BitmapXferProc == proc) { // nothing to do + return; + } + + SkRegion::Iterator iter(*fClip); + while (!iter.done()) { + CallBitmapXferProc(*fBitmap, iter.rect(), proc, procData); + iter.next(); + } + } else { + // normal case: use a blitter + SkAutoBlitterChoose blitter(*fBitmap, *fMatrix, paint); + SkScan::FillIRect(devRect, fClip, blitter.get()); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +struct PtProcRec { + SkCanvas::PointMode fMode; + const SkPaint* fPaint; + const SkRegion* fClip; + + // computed values + SkFixed fRadius; + + typedef void (*Proc)(const PtProcRec&, const SkPoint devPts[], int count, + SkBlitter*); + + bool init(SkCanvas::PointMode, const SkPaint&, const SkMatrix* matrix, + const SkRegion* clip); + Proc chooseProc(SkBlitter* blitter); +}; + +static void bw_pt_rect_hair_proc(const PtProcRec& rec, const SkPoint devPts[], + int count, SkBlitter* blitter) { + SkASSERT(rec.fClip->isRect()); + const SkIRect& r = rec.fClip->getBounds(); + + for (int i = 0; i < count; i++) { + int x = SkScalarFloor(devPts[i].fX); + int y = SkScalarFloor(devPts[i].fY); + if (r.contains(x, y)) { + blitter->blitH(x, y, 1); + } + } +} + +static void bw_pt_rect_16_hair_proc(const PtProcRec& rec, + const SkPoint devPts[], int count, + SkBlitter* blitter) { + SkASSERT(rec.fClip->isRect()); + const SkIRect& r = rec.fClip->getBounds(); + uint32_t value; + const SkBitmap* bitmap = blitter->justAnOpaqueColor(&value); + SkASSERT(bitmap); + + uint16_t* addr = bitmap->getAddr16(0, 0); + int rb = bitmap->rowBytes(); + + for (int i = 0; i < count; i++) { + int x = SkScalarFloor(devPts[i].fX); + int y = SkScalarFloor(devPts[i].fY); + if (r.contains(x, y)) { +// *bitmap->getAddr16(x, y) = SkToU16(value); + ((uint16_t*)((char*)addr + y * rb))[x] = SkToU16(value); + } + } +} + +static void bw_pt_hair_proc(const PtProcRec& rec, const SkPoint devPts[], + int count, SkBlitter* blitter) { + for (int i = 0; i < count; i++) { + int x = SkScalarFloor(devPts[i].fX); + int y = SkScalarFloor(devPts[i].fY); + if (rec.fClip->contains(x, y)) { + blitter->blitH(x, y, 1); + } + } +} + +static void bw_line_hair_proc(const PtProcRec& rec, const SkPoint devPts[], + int count, SkBlitter* blitter) { + for (int i = 0; i < count; i += 2) { + SkScan::HairLine(devPts[i], devPts[i+1], rec.fClip, blitter); + } +} + +static void bw_poly_hair_proc(const PtProcRec& rec, const SkPoint devPts[], + int count, SkBlitter* blitter) { + for (int i = 0; i < count - 1; i++) { + SkScan::HairLine(devPts[i], devPts[i+1], rec.fClip, blitter); + } +} + +// aa versions + +static void aa_line_hair_proc(const PtProcRec& rec, const SkPoint devPts[], + int count, SkBlitter* blitter) { + for (int i = 0; i < count; i += 2) { + SkScan::AntiHairLine(devPts[i], devPts[i+1], rec.fClip, blitter); + } +} + +static void aa_poly_hair_proc(const PtProcRec& rec, const SkPoint devPts[], + int count, SkBlitter* blitter) { + for (int i = 0; i < count - 1; i++) { + SkScan::AntiHairLine(devPts[i], devPts[i+1], rec.fClip, blitter); + } +} + +// square procs (strokeWidth > 0 but matrix is square-scale (sx == sy) + +static void bw_square_proc(const PtProcRec& rec, const SkPoint devPts[], + int count, SkBlitter* blitter) { + const SkFixed radius = rec.fRadius; + for (int i = 0; i < count; i++) { + SkFixed x = SkScalarToFixed(devPts[i].fX); + SkFixed y = SkScalarToFixed(devPts[i].fY); + + SkXRect r; + r.fLeft = x - radius; + r.fTop = y - radius; + r.fRight = x + radius; + r.fBottom = y + radius; + + SkScan::FillXRect(r, rec.fClip, blitter); + } +} + +static void aa_square_proc(const PtProcRec& rec, const SkPoint devPts[], + int count, SkBlitter* blitter) { + const SkFixed radius = rec.fRadius; + for (int i = 0; i < count; i++) { + SkFixed x = SkScalarToFixed(devPts[i].fX); + SkFixed y = SkScalarToFixed(devPts[i].fY); + + SkXRect r; + r.fLeft = x - radius; + r.fTop = y - radius; + r.fRight = x + radius; + r.fBottom = y + radius; + + SkScan::AntiFillXRect(r, rec.fClip, blitter); + } +} + +bool PtProcRec::init(SkCanvas::PointMode mode, const SkPaint& paint, + const SkMatrix* matrix, const SkRegion* clip) { + if (paint.getPathEffect()) { + return false; + } + SkScalar width = paint.getStrokeWidth(); + if (0 == width) { + fMode = mode; + fPaint = &paint; + fClip = clip; + fRadius = SK_Fixed1 >> 1; + return true; + } + if (matrix->rectStaysRect() && SkCanvas::kPoints_PointMode == mode) { + SkScalar sx = matrix->get(SkMatrix::kMScaleX); + SkScalar sy = matrix->get(SkMatrix::kMScaleY); + if (SkScalarNearlyZero(sx - sy)) { + if (sx < 0) { + sx = -sx; + } + + fMode = mode; + fPaint = &paint; + fClip = clip; + fRadius = SkScalarToFixed(SkScalarMul(width, sx)) >> 1; + return true; + } + } + return false; +} + +PtProcRec::Proc PtProcRec::chooseProc(SkBlitter* blitter) { + Proc proc; + + // for our arrays + SkASSERT(0 == SkCanvas::kPoints_PointMode); + SkASSERT(1 == SkCanvas::kLines_PointMode); + SkASSERT(2 == SkCanvas::kPolygon_PointMode); + SkASSERT((unsigned)fMode <= (unsigned)SkCanvas::kPolygon_PointMode); + + // first check for hairlines + if (0 == fPaint->getStrokeWidth()) { + if (fPaint->isAntiAlias()) { + static const Proc gAAProcs[] = { + aa_square_proc, aa_line_hair_proc, aa_poly_hair_proc + }; + proc = gAAProcs[fMode]; + } else { + if (SkCanvas::kPoints_PointMode == fMode && fClip->isRect()) { + uint32_t value; + const SkBitmap* bm = blitter->justAnOpaqueColor(&value); + if (bm && bm->config() == SkBitmap::kRGB_565_Config) { + proc = bw_pt_rect_16_hair_proc; + } else { + proc = bw_pt_rect_hair_proc; + } + } else { + static Proc gBWProcs[] = { + bw_pt_hair_proc, bw_line_hair_proc, bw_poly_hair_proc + }; + proc = gBWProcs[fMode]; + } + } + } else { + SkASSERT(SkCanvas::kPoints_PointMode == fMode); + if (fPaint->isAntiAlias()) { + proc = aa_square_proc; + } else { + proc = bw_square_proc; + } + } + return proc; +} + +// each of these costs 8-bytes of stack space, so don't make it too large +// must be even for lines/polygon to work +#define MAX_DEV_PTS 32 + +void SkDraw::drawPoints(SkCanvas::PointMode mode, size_t count, + const SkPoint pts[], const SkPaint& paint) const { + // if we're in lines mode, force count to be even + if (SkCanvas::kLines_PointMode == mode) { + count &= ~(size_t)1; + } + + if ((long)count <= 0) { + return; + } + + SkASSERT(pts != NULL); + SkDEBUGCODE(this->validate();) + + // nothing to draw + if (fClip->isEmpty() || + (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) { + return; + } + + PtProcRec rec; + if (rec.init(mode, paint, fMatrix, fClip)) { + SkAutoBlitterChoose blitter(*fBitmap, *fMatrix, paint); + + SkPoint devPts[MAX_DEV_PTS]; + const SkMatrix* matrix = fMatrix; + SkBlitter* bltr = blitter.get(); + PtProcRec::Proc proc = rec.chooseProc(bltr); + // we have to back up subsequent passes if we're in polygon mode + const size_t backup = (SkCanvas::kPolygon_PointMode == mode); + + do { + size_t n = count; + if (n > MAX_DEV_PTS) { + n = MAX_DEV_PTS; + } + matrix->mapPoints(devPts, pts, n); + proc(rec, devPts, n, bltr); + pts += n - backup; + SkASSERT(count >= n); + count -= n; + if (count > 0) { + count += backup; + } + } while (count != 0); + } else { + switch (mode) { + case SkCanvas::kPoints_PointMode: { + // temporarily mark the paint as filling. + SkAutoPaintStyleRestore restore(paint, SkPaint::kFill_Style); + + SkScalar width = paint.getStrokeWidth(); + SkScalar radius = SkScalarHalf(width); + + if (paint.getStrokeCap() == SkPaint::kRound_Cap) { + SkPath path; + SkMatrix preMatrix; + + path.addCircle(0, 0, radius); + for (size_t i = 0; i < count; i++) { + preMatrix.setTranslate(pts[i].fX, pts[i].fY); + // pass true for the last point, since we can modify + // then path then + this->drawPath(path, paint, &preMatrix, (count-1) == i); + } + } else { + SkRect r; + + for (size_t i = 0; i < count; i++) { + r.fLeft = pts[i].fX - radius; + r.fTop = pts[i].fY - radius; + r.fRight = r.fLeft + width; + r.fBottom = r.fTop + width; + this->drawRect(r, paint); + } + } + break; + } + case SkCanvas::kLines_PointMode: + case SkCanvas::kPolygon_PointMode: { + count -= 1; + SkPath path; + SkPaint p(paint); + p.setStyle(SkPaint::kStroke_Style); + size_t inc = (SkCanvas::kLines_PointMode == mode) ? 2 : 1; + for (size_t i = 0; i < count; i += inc) { + path.moveTo(pts[i]); + path.lineTo(pts[i+1]); + this->drawPath(path, p, NULL, true); + path.rewind(); + } + break; + } + } + } +} + +static inline SkPoint* as_lefttop(SkRect* r) { + return (SkPoint*)(void*)r; +} + +static inline SkPoint* as_rightbottom(SkRect* r) { + return ((SkPoint*)(void*)r) + 1; +} + +void SkDraw::drawRect(const SkRect& rect, const SkPaint& paint) const { + SkDEBUGCODE(this->validate();) + + // nothing to draw + if (fClip->isEmpty() || + (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) { + return; + } + + // complex enough to draw as a path + if (paint.getPathEffect() || paint.getMaskFilter() || + paint.getRasterizer() || !fMatrix->rectStaysRect() || + (paint.getStyle() != SkPaint::kFill_Style && + SkScalarHalf(paint.getStrokeWidth()) > 0)) { + SkPath tmp; + tmp.addRect(rect); + tmp.setFillType(SkPath::kWinding_FillType); + this->drawPath(tmp, paint); + return; + } + + const SkMatrix& matrix = *fMatrix; + SkRect devRect; + + // transform rect into devRect + { + matrix.mapXY(rect.fLeft, rect.fTop, rect_points(devRect, 0)); + matrix.mapXY(rect.fRight, rect.fBottom, rect_points(devRect, 1)); + devRect.sort(); + } + + if (fBounder && !fBounder->doRect(devRect, paint)) { + return; + } + + // look for the quick exit, before we build a blitter + { + SkIRect ir; + devRect.roundOut(&ir); + if (fClip->quickReject(ir)) + return; + } + + SkAutoBlitterChoose blitterStorage(*fBitmap, matrix, paint); + SkBlitter* blitter = blitterStorage.get(); + const SkRegion* clip = fClip; + + if (paint.getStyle() == SkPaint::kFill_Style) { + if (paint.isAntiAlias()) { + SkScan::AntiFillRect(devRect, clip, blitter); + } else { + SkScan::FillRect(devRect, clip, blitter); + } + } else { + if (paint.isAntiAlias()) { + SkScan::AntiHairRect(devRect, clip, blitter); + } else { + SkScan::HairRect(devRect, clip, blitter); + } + } +} + +void SkDraw::drawDevMask(const SkMask& srcM, const SkPaint& paint) const { + if (srcM.fBounds.isEmpty()) { + return; + } + + SkMask dstM; + const SkMask* mask = &srcM; + + dstM.fImage = NULL; + SkAutoMaskImage ami(&dstM, false); + + if (paint.getMaskFilter() && + paint.getMaskFilter()->filterMask(&dstM, srcM, *fMatrix, NULL)) { + mask = &dstM; + } + + if (fBounder && !fBounder->doIRect(mask->fBounds)) { + return; + } + + SkAutoBlitterChoose blitter(*fBitmap, *fMatrix, paint); + + blitter->blitMaskRegion(*mask, *fClip); +} + +class SkAutoPaintRestoreColorStrokeWidth { +public: + SkAutoPaintRestoreColorStrokeWidth(const SkPaint& paint) { + fPaint = (SkPaint*)&paint; + fColor = paint.getColor(); + fWidth = paint.getStrokeWidth(); + } + ~SkAutoPaintRestoreColorStrokeWidth() { + fPaint->setColor(fColor); + fPaint->setStrokeWidth(fWidth); + } + +private: + SkPaint* fPaint; + SkColor fColor; + SkScalar fWidth; +}; + +void SkDraw::drawPath(const SkPath& origSrcPath, const SkPaint& paint, + const SkMatrix* prePathMatrix, bool pathIsMutable) const { + SkDEBUGCODE(this->validate();) + + // nothing to draw + if (fClip->isEmpty() || + (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) { + return; + } + + SkPath* pathPtr = (SkPath*)&origSrcPath; + bool doFill = true; + SkPath tmpPath; + SkMatrix tmpMatrix; + const SkMatrix* matrix = fMatrix; + + if (prePathMatrix) { + if (paint.getPathEffect() || paint.getStyle() != SkPaint::kFill_Style || + paint.getRasterizer()) { + SkPath* result = pathPtr; + + if (!pathIsMutable) { + result = &tmpPath; + pathIsMutable = true; + } + pathPtr->transform(*prePathMatrix, result); + pathPtr = result; + } else { + if (!tmpMatrix.setConcat(*matrix, *prePathMatrix)) { + // overflow + return; + } + matrix = &tmpMatrix; + } + } + // at this point we're done with prePathMatrix + SkDEBUGCODE(prePathMatrix = (const SkMatrix*)0x50FF8001;) + + /* + If the device thickness <= 1.0, then make it a hairline, and + modulate alpha if the thickness is even smaller (e.g. thickness == 0.5 + should modulate the alpha by 1/2) + */ + + SkAutoPaintRestoreColorStrokeWidth aprc(paint); + + if (paint.getStyle() == SkPaint::kStroke_Style && + paint.getXfermode() == NULL && + (matrix->getType() & SkMatrix::kPerspective_Mask) == 0) { + SkScalar width = paint.getStrokeWidth(); + if (width > 0) { + width = matrix->mapRadius(paint.getStrokeWidth()); + if (width <= SK_Scalar1) { + int scale = (int)SkScalarMul(width, 256); + int alpha = paint.getAlpha() * scale >> 8; + + // pretend to be a hairline, with a modulated alpha + ((SkPaint*)&paint)->setAlpha(alpha); + ((SkPaint*)&paint)->setStrokeWidth(0); + +// SkDebugf("------ convert to hairline %d\n", scale); + } + } + } + + if (paint.getPathEffect() || paint.getStyle() != SkPaint::kFill_Style) { + doFill = paint.getFillPath(*pathPtr, &tmpPath); + pathPtr = &tmpPath; + } + + if (paint.getRasterizer()) { + SkMask mask; + if (paint.getRasterizer()->rasterize(*pathPtr, *matrix, + &fClip->getBounds(), paint.getMaskFilter(), &mask, + SkMask::kComputeBoundsAndRenderImage_CreateMode)) { + this->drawDevMask(mask, paint); + SkMask::FreeImage(mask.fImage); + } + return; + } + + // avoid possibly allocating a new path in transform if we can + SkPath* devPathPtr = pathIsMutable ? pathPtr : &tmpPath; + + // transform the path into device space + pathPtr->transform(*matrix, devPathPtr); + + SkAutoBlitterChoose blitter(*fBitmap, *fMatrix, paint); + + // how does filterPath() know to fill or hairline the path??? <mrr> + if (paint.getMaskFilter() && + paint.getMaskFilter()->filterPath(*devPathPtr, *fMatrix, *fClip, + fBounder, blitter.get())) { + return; // filterPath() called the blitter, so we're done + } + + if (fBounder && !fBounder->doPath(*devPathPtr, paint, doFill)) { + return; + } + + if (doFill) { + if (paint.isAntiAlias()) { + SkScan::AntiFillPath(*devPathPtr, *fClip, blitter.get()); + } else { + SkScan::FillPath(*devPathPtr, *fClip, blitter.get()); + } + } else { // hairline + if (paint.isAntiAlias()) { + SkScan::AntiHairPath(*devPathPtr, fClip, blitter.get()); + } else { + SkScan::HairPath(*devPathPtr, fClip, blitter.get()); + } + } +} + +static inline bool just_translate(const SkMatrix& m) { + return (m.getType() & ~SkMatrix::kTranslate_Mask) == 0; +} + +void SkDraw::drawBitmapAsMask(const SkBitmap& bitmap, + const SkPaint& paint) const { + SkASSERT(bitmap.getConfig() == SkBitmap::kA8_Config); + + if (just_translate(*fMatrix)) { + int ix = SkScalarRound(fMatrix->getTranslateX()); + int iy = SkScalarRound(fMatrix->getTranslateY()); + + SkMask mask; + mask.fBounds.set(ix, iy, ix + bitmap.width(), iy + bitmap.height()); + mask.fFormat = SkMask::kA8_Format; + mask.fRowBytes = bitmap.rowBytes(); + mask.fImage = bitmap.getAddr8(0, 0); + + this->drawDevMask(mask, paint); + } else { // need to xform the bitmap first + SkRect r; + SkMask mask; + + r.set(0, 0, + SkIntToScalar(bitmap.width()), SkIntToScalar(bitmap.height())); + fMatrix->mapRect(&r); + r.round(&mask.fBounds); + + // set the mask's bounds to the transformed bitmap-bounds, + // clipped to the actual device + { + SkIRect devBounds; + devBounds.set(0, 0, fBitmap->width(), fBitmap->height()); + // need intersect(l, t, r, b) on irect + if (!mask.fBounds.intersect(devBounds)) { + return; + } + } + mask.fFormat = SkMask::kA8_Format; + mask.fRowBytes = SkAlign4(mask.fBounds.width()); + + // allocate (and clear) our temp buffer to hold the transformed bitmap + size_t size = mask.computeImageSize(); + SkAutoMalloc storage(size); + mask.fImage = (uint8_t*)storage.get(); + memset(mask.fImage, 0, size); + + // now draw our bitmap(src) into mask(dst), transformed by the matrix + { + SkBitmap device; + device.setConfig(SkBitmap::kA8_Config, mask.fBounds.width(), + mask.fBounds.height(), mask.fRowBytes); + device.setPixels(mask.fImage); + + SkCanvas c(device); + // need the unclipped top/left for the translate + c.translate(-SkIntToScalar(mask.fBounds.fLeft), + -SkIntToScalar(mask.fBounds.fTop)); + c.concat(*fMatrix); + c.drawBitmap(bitmap, 0, 0, NULL); + } + this->drawDevMask(mask, paint); + } +} + +static bool clipped_out(const SkMatrix& m, const SkRegion& c, + const SkRect& srcR) { + SkRect dstR; + SkIRect devIR; + + m.mapRect(&dstR, srcR); + dstR.roundOut(&devIR); + return c.quickReject(devIR); +} + +static bool clipped_out(const SkMatrix& matrix, const SkRegion& clip, + int width, int height) { + SkRect r; + r.set(0, 0, SkIntToScalar(width), SkIntToScalar(height)); + return clipped_out(matrix, clip, r); +} + +void SkDraw::drawBitmap(const SkBitmap& bitmap, const SkMatrix& prematrix, + const SkPaint& paint) const { + SkDEBUGCODE(this->validate();) + + // nothing to draw + if (fClip->isEmpty() || + bitmap.width() == 0 || bitmap.height() == 0 || + bitmap.getConfig() == SkBitmap::kNo_Config || + (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) { + return; + } + + // run away on too-big bitmaps for now (exceed 16.16) + if (bitmap.width() > 32767 || bitmap.height() > 32767) { + return; + } + + SkAutoPaintStyleRestore restore(paint, SkPaint::kFill_Style); + + SkMatrix matrix; + if (!matrix.setConcat(*fMatrix, prematrix)) { + return; + } + + // do I need to call the bounder first??? <reed> + if (clipped_out(matrix, *fClip, bitmap.width(), bitmap.height())) { + return; + } + + // only lock the pixels if we passed the clip test + SkAutoLockPixels alp(bitmap); + // after the lock, check if we have valid pixels + if (bitmap.getPixels() == NULL) { + return; + } + + if (bitmap.getConfig() != SkBitmap::kA8_Config && just_translate(matrix)) { + int ix = SkScalarRound(matrix.getTranslateX()); + int iy = SkScalarRound(matrix.getTranslateY()); + uint32_t storage[kBlitterStorageLongCount]; + SkBlitter* blitter = SkBlitter::ChooseSprite(*fBitmap, paint, bitmap, + ix, iy, storage, sizeof(storage)); + if (blitter) { + SkAutoTPlacementDelete<SkBlitter> ad(blitter, storage); + + SkIRect ir; + ir.set(ix, iy, ix + bitmap.width(), iy + bitmap.height()); + + if (fBounder && !fBounder->doIRect(ir)) { + return; + } + + SkRegion::Cliperator iter(*fClip, ir); + const SkIRect& cr = iter.rect(); + + for (; !iter.done(); iter.next()) { + SkASSERT(!cr.isEmpty()); + blitter->blitRect(cr.fLeft, cr.fTop, cr.width(), cr.height()); + } + return; + } +#if 0 + SkDebugf("---- MISSING sprite case: config=%d [%d %d], device=%d, xfer=%p, alpha=0x%X colorFilter=%p\n", + bitmap.config(), bitmap.width(), bitmap.height(), fBitmap->config(), + paint.getXfermode(), paint.getAlpha(), paint.getColorFilter()); +#endif + } + + // now make a temp draw on the stack, and use it + // + SkDraw draw(*this); + draw.fMatrix = &matrix; + + if (bitmap.getConfig() == SkBitmap::kA8_Config) { + draw.drawBitmapAsMask(bitmap, paint); + } else { + SkAutoBitmapShaderInstall install(bitmap, &paint); + + SkRect r; + r.set(0, 0, SkIntToScalar(bitmap.width()), + SkIntToScalar(bitmap.height())); + // is this ok if paint has a rasterizer? <reed> + draw.drawRect(r, paint); + } +} + +void SkDraw::drawSprite(const SkBitmap& bitmap, int x, int y, + const SkPaint& paint) const { + SkDEBUGCODE(this->validate();) + + // nothing to draw + if (fClip->isEmpty() || + bitmap.width() == 0 || bitmap.height() == 0 || + bitmap.getConfig() == SkBitmap::kNo_Config || + (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) { + return; + } + + SkIRect bounds; + bounds.set(x, y, x + bitmap.width(), y + bitmap.height()); + + if (fClip->quickReject(bounds)) { + return; // nothing to draw + } + + SkAutoPaintStyleRestore restore(paint, SkPaint::kFill_Style); + + if (NULL == paint.getColorFilter()) { + uint32_t storage[kBlitterStorageLongCount]; + SkBlitter* blitter = SkBlitter::ChooseSprite(*fBitmap, paint, bitmap, + x, y, storage, sizeof(storage)); + + if (blitter) { + SkAutoTPlacementDelete<SkBlitter> ad(blitter, storage); + + if (fBounder && !fBounder->doIRect(bounds)) { + return; + } + + SkRegion::Cliperator iter(*fClip, bounds); + const SkIRect& cr = iter.rect(); + + for (; !iter.done(); iter.next()) { + SkASSERT(!cr.isEmpty()); + blitter->blitRect(cr.fLeft, cr.fTop, cr.width(), cr.height()); + } + return; + } + } + + SkAutoBitmapShaderInstall install(bitmap, &paint); + + SkMatrix matrix; + SkRect r; + + // get a scalar version of our rect + r.set(bounds); + + // tell the shader our offset + matrix.setTranslate(r.fLeft, r.fTop); + paint.getShader()->setLocalMatrix(matrix); + + SkDraw draw(*this); + matrix.reset(); + draw.fMatrix = &matrix; + // call ourself with a rect + // is this OK if paint has a rasterizer? <reed> + draw.drawRect(r, paint); +} + +/////////////////////////////////////////////////////////////////////////////// + +#include "SkScalerContext.h" +#include "SkGlyphCache.h" +#include "SkUtils.h" + +static void measure_text(SkGlyphCache* cache, SkDrawCacheProc glyphCacheProc, + const char text[], size_t byteLength, SkVector* stopVector) { + SkFixed x = 0, y = 0; + const char* stop = text + byteLength; + + SkAutoKern autokern; + + while (text < stop) { + // don't need x, y here, since all subpixel variants will have the + // same advance + const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0); + + x += autokern.adjust(glyph) + glyph.fAdvanceX; + y += glyph.fAdvanceY; + } + stopVector->set(SkFixedToScalar(x), SkFixedToScalar(y)); + + SkASSERT(text == stop); +} + +void SkDraw::drawText_asPaths(const char text[], size_t byteLength, + SkScalar x, SkScalar y, + const SkPaint& paint) const { + SkDEBUGCODE(this->validate();) + + SkTextToPathIter iter(text, byteLength, paint, true, true); + + SkMatrix matrix; + matrix.setScale(iter.getPathScale(), iter.getPathScale()); + matrix.postTranslate(x, y); + + const SkPath* iterPath; + SkScalar xpos, prevXPos = 0; + + while ((iterPath = iter.next(&xpos)) != NULL) { + matrix.postTranslate(xpos - prevXPos, 0); + this->drawPath(*iterPath, iter.getPaint(), &matrix, false); + prevXPos = xpos; + } +} + +#define kStdStrikeThru_Offset (-SK_Scalar1 * 6 / 21) +#define kStdUnderline_Offset (SK_Scalar1 / 9) +#define kStdUnderline_Thickness (SK_Scalar1 / 18) + +static void draw_paint_rect(const SkDraw* draw, const SkPaint& paint, + const SkRect& r, SkScalar textSize) { + if (paint.getStyle() == SkPaint::kFill_Style) { + draw->drawRect(r, paint); + } else { + SkPaint p(paint); + p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth())); + draw->drawRect(r, p); + } +} + +static void handle_aftertext(const SkDraw* draw, const SkPaint& paint, + SkScalar width, const SkPoint& start) { + uint32_t flags = paint.getFlags(); + + if (flags & (SkPaint::kUnderlineText_Flag | + SkPaint::kStrikeThruText_Flag)) { + SkScalar textSize = paint.getTextSize(); + SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness); + SkRect r; + + r.fLeft = start.fX; + r.fRight = start.fX + width; + + if (flags & SkPaint::kUnderlineText_Flag) { + SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset, + start.fY); + r.fTop = offset; + r.fBottom = offset + height; + draw_paint_rect(draw, paint, r, textSize); + } + if (flags & SkPaint::kStrikeThruText_Flag) { + SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset, + start.fY); + r.fTop = offset; + r.fBottom = offset + height; + draw_paint_rect(draw, paint, r, textSize); + } + } +} + +// disable warning : local variable used without having been initialized +#if defined _WIN32 && _MSC_VER >= 1300 +#pragma warning ( push ) +#pragma warning ( disable : 4701 ) +#endif + +////////////////////////////////////////////////////////////////////////////// + +static void D1G_NoBounder_RectClip(const SkDraw1Glyph& state, + const SkGlyph& glyph, int left, int top) { + SkASSERT(glyph.fWidth > 0 && glyph.fHeight > 0); + SkASSERT(state.fClip->isRect()); + SkASSERT(NULL == state.fBounder); + SkASSERT(state.fClipBounds == state.fClip->getBounds()); + + left += glyph.fLeft; + top += glyph.fTop; + + int right = left + glyph.fWidth; + int bottom = top + glyph.fHeight; + + SkMask mask; + SkIRect storage; + SkIRect* bounds = &mask.fBounds; + + mask.fBounds.set(left, top, right, bottom); + + // this extra test is worth it, assuming that most of the time it succeeds + // since we can avoid writing to storage + if (!state.fClipBounds.containsNoEmptyCheck(left, top, right, bottom)) { + if (!storage.intersectNoEmptyCheck(mask.fBounds, state.fClipBounds)) + return; + bounds = &storage; + } + + uint8_t* aa = (uint8_t*)glyph.fImage; + if (NULL == aa) { + aa = (uint8_t*)state.fCache->findImage(glyph); + if (NULL == aa) { + return; // can't rasterize glyph + } + } + + mask.fRowBytes = glyph.rowBytes(); + mask.fFormat = glyph.fMaskFormat; + mask.fImage = aa; + state.fBlitter->blitMask(mask, *bounds); +} + +static void D1G_NoBounder_RgnClip(const SkDraw1Glyph& state, + const SkGlyph& glyph, int left, int top) { + SkASSERT(glyph.fWidth > 0 && glyph.fHeight > 0); + SkASSERT(!state.fClip->isRect()); + SkASSERT(NULL == state.fBounder); + + SkMask mask; + + left += glyph.fLeft; + top += glyph.fTop; + + mask.fBounds.set(left, top, left + glyph.fWidth, top + glyph.fHeight); + SkRegion::Cliperator clipper(*state.fClip, mask.fBounds); + + if (!clipper.done()) { + const SkIRect& cr = clipper.rect(); + const uint8_t* aa = (const uint8_t*)glyph.fImage; + if (NULL == aa) { + aa = (uint8_t*)state.fCache->findImage(glyph); + if (NULL == aa) { + return; + } + } + + mask.fRowBytes = glyph.rowBytes(); + mask.fFormat = glyph.fMaskFormat; + mask.fImage = (uint8_t*)aa; + do { + state.fBlitter->blitMask(mask, cr); + clipper.next(); + } while (!clipper.done()); + } +} + +static void D1G_Bounder(const SkDraw1Glyph& state, + const SkGlyph& glyph, int left, int top) { + SkASSERT(glyph.fWidth > 0 && glyph.fHeight > 0); + + SkMask mask; + + left += glyph.fLeft; + top += glyph.fTop; + + mask.fBounds.set(left, top, left + glyph.fWidth, top + glyph.fHeight); + SkRegion::Cliperator clipper(*state.fClip, mask.fBounds); + + if (!clipper.done()) { + const SkIRect& cr = clipper.rect(); + const uint8_t* aa = (const uint8_t*)glyph.fImage; + if (NULL == aa) { + aa = (uint8_t*)state.fCache->findImage(glyph); + if (NULL == aa) { + return; + } + } + + if (state.fBounder->doIRect(cr)) { + mask.fRowBytes = glyph.rowBytes(); + mask.fFormat = glyph.fMaskFormat; + mask.fImage = (uint8_t*)aa; + do { + state.fBlitter->blitMask(mask, cr); + clipper.next(); + } while (!clipper.done()); + } + } +} + +SkDraw1Glyph::Proc SkDraw1Glyph::init(const SkDraw* draw, SkBlitter* blitter, + SkGlyphCache* cache) { + fDraw = draw; + fBounder = draw->fBounder; + fClip = draw->fClip; + fClipBounds = fClip->getBounds(); + fBlitter = blitter; + fCache = cache; + + if (draw->fProcs && draw->fProcs->fD1GProc) { + return draw->fProcs->fD1GProc; + } + + if (NULL == fBounder) { + if (fClip->isRect()) { + return D1G_NoBounder_RectClip; + } else { + return D1G_NoBounder_RgnClip; + } + } else { + return D1G_Bounder; + } +} + +enum RoundBaseline { + kDont_Round_Baseline, + kRound_X_Baseline, + kRound_Y_Baseline +}; + +static RoundBaseline computeRoundBaseline(const SkMatrix& mat) { + if (mat[1] == 0 && mat[3] == 0) { + // we're 0 or 180 degrees, round the y coordinate of the baseline + return kRound_Y_Baseline; + } else if (mat[0] == 0 && mat[4] == 0) { + // we're 90 or 270 degrees, round the x coordinate of the baseline + return kRound_X_Baseline; + } else { + return kDont_Round_Baseline; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void SkDraw::drawText(const char text[], size_t byteLength, + SkScalar x, SkScalar y, const SkPaint& paint) const { + SkASSERT(byteLength == 0 || text != NULL); + + SkDEBUGCODE(this->validate();) + + // nothing to draw + if (text == NULL || byteLength == 0 || + fClip->isEmpty() || + (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) { + return; + } + + SkScalar underlineWidth = 0; + SkPoint underlineStart; + + underlineStart.set(0, 0); // to avoid warning + if (paint.getFlags() & (SkPaint::kUnderlineText_Flag | + SkPaint::kStrikeThruText_Flag)) { + underlineWidth = paint.measureText(text, byteLength); + + SkScalar offsetX = 0; + if (paint.getTextAlign() == SkPaint::kCenter_Align) { + offsetX = SkScalarHalf(underlineWidth); + } else if (paint.getTextAlign() == SkPaint::kRight_Align) { + offsetX = underlineWidth; + } + underlineStart.set(x - offsetX, y); + } + + if (/*paint.isLinearText() ||*/ + (fMatrix->getType() & SkMatrix::kPerspective_Mask)) { + this->drawText_asPaths(text, byteLength, x, y, paint); + handle_aftertext(this, paint, underlineWidth, underlineStart); + return; + } + + SkDrawCacheProc glyphCacheProc = paint.getDrawCacheProc(); + + SkAutoGlyphCache autoCache(paint, fMatrix); + SkGlyphCache* cache = autoCache.getCache(); + SkAutoBlitterChoose blitter(*fBitmap, *fMatrix, paint); + + // transform our starting point + { + SkPoint loc; + fMatrix->mapXY(x, y, &loc); + x = loc.fX; + y = loc.fY; + } + + // need to measure first + if (paint.getTextAlign() != SkPaint::kLeft_Align) { + SkVector stop; + + measure_text(cache, glyphCacheProc, text, byteLength, &stop); + + SkScalar stopX = stop.fX; + SkScalar stopY = stop.fY; + + if (paint.getTextAlign() == SkPaint::kCenter_Align) { + stopX = SkScalarHalf(stopX); + stopY = SkScalarHalf(stopY); + } + x -= stopX; + y -= stopY; + } + + SkFixed fx = SkScalarToFixed(x); + SkFixed fy = SkScalarToFixed(y); + const char* stop = text + byteLength; + + if (paint.isSubpixelText()) { + RoundBaseline roundBaseline = computeRoundBaseline(*fMatrix); + if (kRound_Y_Baseline == roundBaseline) { + fy = (fy + 0x8000) & ~0xFFFF; + } else if (kRound_X_Baseline == roundBaseline) { + fx = (fx + 0x8000) & ~0xFFFF; + } + } else { + // apply the bias here, so we don't have to add 1/2 in the loop + fx += SK_Fixed1/2; + fy += SK_Fixed1/2; + } + + SkAutoKern autokern; + SkDraw1Glyph d1g; + SkDraw1Glyph::Proc proc = d1g.init(this, blitter.get(), cache); + + while (text < stop) { + const SkGlyph& glyph = glyphCacheProc(cache, &text, fx, fy); + + fx += autokern.adjust(glyph); + + if (glyph.fWidth) { + proc(d1g, glyph, SkFixedFloor(fx), SkFixedFloor(fy)); + } + fx += glyph.fAdvanceX; + fy += glyph.fAdvanceY; + } + + if (underlineWidth) { + autoCache.release(); // release this now to free up the RAM + handle_aftertext(this, paint, underlineWidth, underlineStart); + } +} + +// last parameter is interpreted as SkFixed [x, y] +// return the fixed position, which may be rounded or not by the caller +// e.g. subpixel doesn't round +typedef void (*AlignProc)(const SkPoint&, const SkGlyph&, SkIPoint*); + +static void leftAlignProc(const SkPoint& loc, const SkGlyph& glyph, + SkIPoint* dst) { + dst->set(SkScalarToFixed(loc.fX), SkScalarToFixed(loc.fY)); +} + +static void centerAlignProc(const SkPoint& loc, const SkGlyph& glyph, + SkIPoint* dst) { + dst->set(SkScalarToFixed(loc.fX) - (glyph.fAdvanceX >> 1), + SkScalarToFixed(loc.fY) - (glyph.fAdvanceY >> 1)); +} + +static void rightAlignProc(const SkPoint& loc, const SkGlyph& glyph, + SkIPoint* dst) { + dst->set(SkScalarToFixed(loc.fX) - glyph.fAdvanceX, + SkScalarToFixed(loc.fY) - glyph.fAdvanceY); +} + +static AlignProc pick_align_proc(SkPaint::Align align) { + static const AlignProc gProcs[] = { + leftAlignProc, centerAlignProc, rightAlignProc + }; + + SkASSERT((unsigned)align < SK_ARRAY_COUNT(gProcs)); + + return gProcs[align]; +} + +class TextMapState { +public: + mutable SkPoint fLoc; + + TextMapState(const SkMatrix& matrix, SkScalar y) + : fMatrix(matrix), fProc(matrix.getMapXYProc()), fY(y) {} + + typedef void (*Proc)(const TextMapState&, const SkScalar pos[]); + + Proc pickProc(int scalarsPerPosition); + +private: + const SkMatrix& fMatrix; + SkMatrix::MapXYProc fProc; + SkScalar fY; // ignored by MapXYProc + // these are only used by Only... procs + SkScalar fScaleX, fTransX, fTransformedY; + + static void MapXProc(const TextMapState& state, const SkScalar pos[]) { + state.fProc(state.fMatrix, *pos, state.fY, &state.fLoc); + } + + static void MapXYProc(const TextMapState& state, const SkScalar pos[]) { + state.fProc(state.fMatrix, pos[0], pos[1], &state.fLoc); + } + + static void MapOnlyScaleXProc(const TextMapState& state, + const SkScalar pos[]) { + state.fLoc.set(SkScalarMul(state.fScaleX, *pos) + state.fTransX, + state.fTransformedY); + } + + static void MapOnlyTransXProc(const TextMapState& state, + const SkScalar pos[]) { + state.fLoc.set(*pos + state.fTransX, state.fTransformedY); + } +}; + +TextMapState::Proc TextMapState::pickProc(int scalarsPerPosition) { + SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition); + + if (1 == scalarsPerPosition) { + unsigned mtype = fMatrix.getType(); + if (mtype & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask)) { + return MapXProc; + } else { + fScaleX = fMatrix.getScaleX(); + fTransX = fMatrix.getTranslateX(); + fTransformedY = SkScalarMul(fY, fMatrix.getScaleY()) + + fMatrix.getTranslateY(); + return (mtype & SkMatrix::kScale_Mask) ? + MapOnlyScaleXProc : MapOnlyTransXProc; + } + } else { + return MapXYProc; + } +} + +////////////////////////////////////////////////////////////////////////////// + +void SkDraw::drawPosText(const char text[], size_t byteLength, + const SkScalar pos[], SkScalar constY, + int scalarsPerPosition, const SkPaint& paint) const { + SkASSERT(byteLength == 0 || text != NULL); + SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition); + + SkDEBUGCODE(this->validate();) + + // nothing to draw + if (text == NULL || byteLength == 0 || + fClip->isEmpty() || + (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) { + return; + } + + if (/*paint.isLinearText() ||*/ + (fMatrix->getType() & SkMatrix::kPerspective_Mask)) { + // TODO !!!! +// this->drawText_asPaths(text, byteLength, x, y, paint); + return; + } + + SkDrawCacheProc glyphCacheProc = paint.getDrawCacheProc(); + SkAutoGlyphCache autoCache(paint, fMatrix); + SkGlyphCache* cache = autoCache.getCache(); + SkAutoBlitterChoose blitter(*fBitmap, *fMatrix, paint); + + const char* stop = text + byteLength; + AlignProc alignProc = pick_align_proc(paint.getTextAlign()); + SkDraw1Glyph d1g; + SkDraw1Glyph::Proc proc = d1g.init(this, blitter.get(), cache); + TextMapState tms(*fMatrix, constY); + TextMapState::Proc tmsProc = tms.pickProc(scalarsPerPosition); + + if (paint.isSubpixelText()) { + // maybe we should skip the rounding if linearText is set + RoundBaseline roundBaseline = computeRoundBaseline(*fMatrix); + + if (SkPaint::kLeft_Align == paint.getTextAlign()) { + while (text < stop) { + tmsProc(tms, pos); + + SkFixed fx = SkScalarToFixed(tms.fLoc.fX); + SkFixed fy = SkScalarToFixed(tms.fLoc.fY); + + if (kRound_Y_Baseline == roundBaseline) { + fy = (fy + 0x8000) & ~0xFFFF; + } else if (kRound_X_Baseline == roundBaseline) { + fx = (fx + 0x8000) & ~0xFFFF; + } + + const SkGlyph& glyph = glyphCacheProc(cache, &text, fx, fy); + + if (glyph.fWidth) { + proc(d1g, glyph, SkFixedFloor(fx), SkFixedFloor(fy)); + } + pos += scalarsPerPosition; + } + } else { + while (text < stop) { + const SkGlyph* glyph = &glyphCacheProc(cache, &text, 0, 0); + + if (glyph->fWidth) { + SkDEBUGCODE(SkFixed prevAdvX = glyph->fAdvanceX;) + SkDEBUGCODE(SkFixed prevAdvY = glyph->fAdvanceY;) + + SkFixed fx, fy; + tmsProc(tms, pos); + + { + SkIPoint fixedLoc; + alignProc(tms.fLoc, *glyph, &fixedLoc); + fx = fixedLoc.fX; + fy = fixedLoc.fY; + + if (kRound_Y_Baseline == roundBaseline) { + fy = (fy + 0x8000) & ~0xFFFF; + } else if (kRound_X_Baseline == roundBaseline) { + fx = (fx + 0x8000) & ~0xFFFF; + } + } + + // have to call again, now that we've been "aligned" + glyph = &glyphCacheProc(cache, &text, fx, fy); + // the assumption is that the advance hasn't changed + SkASSERT(prevAdvX == glyph->fAdvanceX); + SkASSERT(prevAdvY == glyph->fAdvanceY); + + proc(d1g, *glyph, SkFixedFloor(fx), SkFixedFloor(fy)); + } + pos += scalarsPerPosition; + } + } + } else { // not subpixel + while (text < stop) { + // the last 2 parameters are ignored + const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0); + + if (glyph.fWidth) { + tmsProc(tms, pos); + + SkIPoint fixedLoc; + alignProc(tms.fLoc, glyph, &fixedLoc); + + proc(d1g, glyph, + SkFixedRound(fixedLoc.fX), SkFixedRound(fixedLoc.fY)); + } + pos += scalarsPerPosition; + } + } +} + +#if defined _WIN32 && _MSC_VER >= 1300 +#pragma warning ( pop ) +#endif + +/////////////////////////////////////////////////////////////////////////////// + +#include "SkPathMeasure.h" + +static void morphpoints(SkPoint dst[], const SkPoint src[], int count, + SkPathMeasure& meas, const SkMatrix& matrix) { + SkMatrix::MapXYProc proc = matrix.getMapXYProc(); + + for (int i = 0; i < count; i++) { + SkPoint pos; + SkVector tangent; + + proc(matrix, src[i].fX, src[i].fY, &pos); + SkScalar sx = pos.fX; + SkScalar sy = pos.fY; + + meas.getPosTan(sx, &pos, &tangent); + + /* This is the old way (that explains our approach but is way too slow + SkMatrix matrix; + SkPoint pt; + + pt.set(sx, sy); + matrix.setSinCos(tangent.fY, tangent.fX); + matrix.preTranslate(-sx, 0); + matrix.postTranslate(pos.fX, pos.fY); + matrix.mapPoints(&dst[i], &pt, 1); + */ + dst[i].set(pos.fX - SkScalarMul(tangent.fY, sy), + pos.fY + SkScalarMul(tangent.fX, sy)); + } +} + +/* TODO + + Need differentially more subdivisions when the follow-path is curvy. Not sure how to + determine that, but we need it. I guess a cheap answer is let the caller tell us, + but that seems like a cop-out. Another answer is to get Rob Johnson to figure it out. +*/ +static void morphpath(SkPath* dst, const SkPath& src, SkPathMeasure& meas, + const SkMatrix& matrix) { + SkPath::Iter iter(src, false); + SkPoint srcP[4], dstP[3]; + SkPath::Verb verb; + + while ((verb = iter.next(srcP)) != SkPath::kDone_Verb) { + switch (verb) { + case SkPath::kMove_Verb: + morphpoints(dstP, srcP, 1, meas, matrix); + dst->moveTo(dstP[0]); + break; + case SkPath::kLine_Verb: + // turn lines into quads to look bendy + srcP[0].fX = SkScalarAve(srcP[0].fX, srcP[1].fX); + srcP[0].fY = SkScalarAve(srcP[0].fY, srcP[1].fY); + morphpoints(dstP, srcP, 2, meas, matrix); + dst->quadTo(dstP[0], dstP[1]); + break; + case SkPath::kQuad_Verb: + morphpoints(dstP, &srcP[1], 2, meas, matrix); + dst->quadTo(dstP[0], dstP[1]); + break; + case SkPath::kCubic_Verb: + morphpoints(dstP, &srcP[1], 3, meas, matrix); + dst->cubicTo(dstP[0], dstP[1], dstP[2]); + break; + case SkPath::kClose_Verb: + dst->close(); + break; + default: + SkASSERT(!"unknown verb"); + break; + } + } +} + +void SkDraw::drawTextOnPath(const char text[], size_t byteLength, + const SkPath& follow, const SkMatrix* matrix, + const SkPaint& paint) const { + SkASSERT(byteLength == 0 || text != NULL); + + // nothing to draw + if (text == NULL || byteLength == 0 || + fClip->isEmpty() || + (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) { + return; + } + + SkTextToPathIter iter(text, byteLength, paint, true, true); + SkPathMeasure meas(follow, false); + SkScalar hOffset = 0; + + // need to measure first + if (paint.getTextAlign() != SkPaint::kLeft_Align) { + SkScalar pathLen = meas.getLength(); + if (paint.getTextAlign() == SkPaint::kCenter_Align) { + pathLen = SkScalarHalf(pathLen); + } + hOffset += pathLen; + } + + const SkPath* iterPath; + SkScalar xpos; + SkMatrix scaledMatrix; + SkScalar scale = iter.getPathScale(); + + scaledMatrix.setScale(scale, scale); + + while ((iterPath = iter.next(&xpos)) != NULL) { + SkPath tmp; + SkMatrix m(scaledMatrix); + + m.postTranslate(xpos + hOffset, 0); + if (matrix) { + m.postConcat(*matrix); + } + morphpath(&tmp, *iterPath, meas, m); + this->drawPath(tmp, iter.getPaint()); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +struct VertState { + int f0, f1, f2; + + VertState(int vCount, const uint16_t indices[], int indexCount) + : fIndices(indices) { + fCurrIndex = 0; + if (indices) { + fCount = indexCount; + } else { + fCount = vCount; + } + } + + typedef bool (*Proc)(VertState*); + Proc chooseProc(SkCanvas::VertexMode mode); + +private: + int fCount; + int fCurrIndex; + const uint16_t* fIndices; + + static bool Triangles(VertState*); + static bool TrianglesX(VertState*); + static bool TriangleStrip(VertState*); + static bool TriangleStripX(VertState*); + static bool TriangleFan(VertState*); + static bool TriangleFanX(VertState*); +}; + +bool VertState::Triangles(VertState* state) { + int index = state->fCurrIndex; + if (index + 3 > state->fCount) { + return false; + } + state->f0 = index + 0; + state->f1 = index + 1; + state->f2 = index + 2; + state->fCurrIndex = index + 3; + return true; +} + +bool VertState::TrianglesX(VertState* state) { + const uint16_t* indices = state->fIndices; + int index = state->fCurrIndex; + if (index + 3 > state->fCount) { + return false; + } + state->f0 = indices[index + 0]; + state->f1 = indices[index + 1]; + state->f2 = indices[index + 2]; + state->fCurrIndex = index + 3; + return true; +} + +bool VertState::TriangleStrip(VertState* state) { + int index = state->fCurrIndex; + if (index + 3 > state->fCount) { + return false; + } + state->f2 = index + 2; + if (index & 1) { + state->f0 = index + 1; + state->f1 = index + 0; + } else { + state->f0 = index + 0; + state->f1 = index + 1; + } + state->fCurrIndex = index + 1; + return true; +} + +bool VertState::TriangleStripX(VertState* state) { + const uint16_t* indices = state->fIndices; + int index = state->fCurrIndex; + if (index + 3 > state->fCount) { + return false; + } + state->f2 = indices[index + 2]; + if (index & 1) { + state->f0 = indices[index + 1]; + state->f1 = indices[index + 0]; + } else { + state->f0 = indices[index + 0]; + state->f1 = indices[index + 1]; + } + state->fCurrIndex = index + 1; + return true; +} + +bool VertState::TriangleFan(VertState* state) { + int index = state->fCurrIndex; + if (index + 3 > state->fCount) { + return false; + } + state->f0 = 0; + state->f1 = index + 1; + state->f2 = index + 2; + state->fCurrIndex = index + 1; + return true; +} + +bool VertState::TriangleFanX(VertState* state) { + const uint16_t* indices = state->fIndices; + int index = state->fCurrIndex; + if (index + 3 > state->fCount) { + return false; + } + state->f0 = indices[0]; + state->f1 = indices[index + 1]; + state->f2 = indices[index + 2]; + state->fCurrIndex = index + 1; + return true; +} + +VertState::Proc VertState::chooseProc(SkCanvas::VertexMode mode) { + switch (mode) { + case SkCanvas::kTriangles_VertexMode: + return fIndices ? TrianglesX : Triangles; + case SkCanvas::kTriangleStrip_VertexMode: + return fIndices ? TriangleStripX : TriangleStrip; + case SkCanvas::kTriangleFan_VertexMode: + return fIndices ? TriangleFanX : TriangleFan; + default: + return NULL; + } +} + +typedef void (*HairProc)(const SkPoint&, const SkPoint&, const SkRegion*, + SkBlitter*); + +static HairProc ChooseHairProc(bool doAntiAlias) { + return doAntiAlias ? SkScan::AntiHairLine : SkScan::HairLine; +} + +static bool texture_to_matrix(const VertState& state, const SkPoint verts[], + const SkPoint texs[], SkMatrix* matrix) { + SkPoint src[3], dst[3]; + + src[0] = texs[state.f0]; + src[1] = texs[state.f1]; + src[2] = texs[state.f2]; + dst[0] = verts[state.f0]; + dst[1] = verts[state.f1]; + dst[2] = verts[state.f2]; + return matrix->setPolyToPoly(src, dst, 3); +} + +class SkTriColorShader : public SkShader { +public: + SkTriColorShader() {} + + bool setup(const SkPoint pts[], const SkColor colors[], int, int, int); + + virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count); + +protected: + SkTriColorShader(SkFlattenableReadBuffer& buffer) : SkShader(buffer) {} + + virtual Factory getFactory() { return CreateProc; } + +private: + SkMatrix fDstToUnit; + SkPMColor fColors[3]; + + static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) { + return SkNEW_ARGS(SkTriColorShader, (buffer)); + } + typedef SkShader INHERITED; +}; + +bool SkTriColorShader::setup(const SkPoint pts[], const SkColor colors[], + int index0, int index1, int index2) { + + fColors[0] = SkPreMultiplyColor(colors[index0]); + fColors[1] = SkPreMultiplyColor(colors[index1]); + fColors[2] = SkPreMultiplyColor(colors[index2]); + + SkMatrix m, im; + m.reset(); + m.set(0, pts[index1].fX - pts[index0].fX); + m.set(1, pts[index2].fX - pts[index0].fX); + m.set(2, pts[index0].fX); + m.set(3, pts[index1].fY - pts[index0].fY); + m.set(4, pts[index2].fY - pts[index0].fY); + m.set(5, pts[index0].fY); + if (!m.invert(&im)) { + return false; + } + return fDstToUnit.setConcat(im, this->getTotalInverse()); +} + +#include "SkColorPriv.h" +#include "SkPorterDuff.h" +#include "SkShaderExtras.h" +#include "SkXfermode.h" + +static int ScalarTo256(SkScalar v) { + int scale = SkScalarToFixed(v) >> 8; + if (scale < 0) { + scale = 0; + } + if (scale > 255) { + scale = 255; + } + return SkAlpha255To256(scale); +} + +void SkTriColorShader::shadeSpan(int x, int y, SkPMColor dstC[], int count) { + SkPoint src; + + for (int i = 0; i < count; i++) { + fDstToUnit.mapXY(SkIntToScalar(x), SkIntToScalar(y), &src); + x += 1; + + int scale1 = ScalarTo256(src.fX); + int scale2 = ScalarTo256(src.fY); + int scale0 = 256 - scale1 - scale2; + if (scale0 < 0) { + if (scale1 > scale2) { + scale2 = 256 - scale1; + } else { + scale1 = 256 - scale2; + } + scale0 = 0; + } + + dstC[i] = SkAlphaMulQ(fColors[0], scale0) + + SkAlphaMulQ(fColors[1], scale1) + + SkAlphaMulQ(fColors[2], scale2); + } +} + +void SkDraw::drawVertices(SkCanvas::VertexMode vmode, int count, + const SkPoint vertices[], const SkPoint textures[], + const SkColor colors[], SkXfermode* xmode, + const uint16_t indices[], int indexCount, + const SkPaint& paint) const { + SkASSERT(0 == count || NULL != vertices); + + // abort early if there is nothing to draw + if (count < 3 || (indices && indexCount < 3) || fClip->isEmpty() || + (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) { + return; + } + + // transform out vertices into device coordinates + SkAutoSTMalloc<16, SkPoint> storage(count); + SkPoint* devVerts = storage.get(); + fMatrix->mapPoints(devVerts, vertices, count); + + if (fBounder) { + SkRect bounds; + bounds.set(devVerts, count); + if (!fBounder->doRect(bounds, paint)) { + return; + } + } + + /* + We can draw the vertices in 1 of 4 ways: + + - solid color (no shader/texture[], no colors[]) + - just colors (no shader/texture[], has colors[]) + - just texture (has shader/texture[], no colors[]) + - colors * texture (has shader/texture[], has colors[]) + + Thus for texture drawing, we need both texture[] and a shader. + */ + + SkTriColorShader triShader; // must be above declaration of p + SkPaint p(paint); + + SkShader* shader = p.getShader(); + if (NULL == shader) { + // if we have no shader, we ignore the texture coordinates + textures = NULL; + } else if (NULL == textures) { + // if we don't have texture coordinates, ignore the shader + p.setShader(NULL); + shader = NULL; + } + + // setup the custom shader (if needed) + if (NULL != colors) { + if (NULL == textures) { + // just colors (no texture) + p.setShader(&triShader); + } else { + // colors * texture + SkASSERT(shader); + bool releaseMode = false; + if (NULL == xmode) { + xmode = SkPorterDuff::CreateXfermode( + SkPorterDuff::kMultiply_Mode); + releaseMode = true; + } + SkShader* compose = SkNEW_ARGS(SkComposeShader, + (&triShader, shader, xmode)); + p.setShader(compose)->unref(); + if (releaseMode) { + xmode->unref(); + } + } + } + + SkAutoBlitterChoose blitter(*fBitmap, *fMatrix, p); + // setup our state and function pointer for iterating triangles + VertState state(count, indices, indexCount); + VertState::Proc vertProc = state.chooseProc(vmode); + + if (NULL != textures || NULL != colors) { + SkMatrix localM, tempM; + bool hasLocalM = shader && shader->getLocalMatrix(&localM); + + if (NULL != colors) { + if (!triShader.setContext(*fBitmap, p, *fMatrix)) { + colors = NULL; + } + } + + while (vertProc(&state)) { + if (NULL != textures) { + if (texture_to_matrix(state, vertices, textures, &tempM)) { + if (hasLocalM) { + tempM.postConcat(localM); + } + shader->setLocalMatrix(tempM); + // need to recal setContext since we changed the local matrix + if (!shader->setContext(*fBitmap, p, *fMatrix)) { + continue; + } + } + } + if (NULL != colors) { + if (!triShader.setup(vertices, colors, + state.f0, state.f1, state.f2)) { + continue; + } + } + SkScan::FillTriangle(devVerts[state.f0], devVerts[state.f1], + devVerts[state.f2], fClip, blitter.get()); + } + // now restore the shader's original local matrix + if (NULL != shader) { + if (hasLocalM) { + shader->setLocalMatrix(localM); + } else { + shader->resetLocalMatrix(); + } + } + } else { + // no colors[] and no texture + HairProc hairProc = ChooseHairProc(paint.isAntiAlias()); + while (vertProc(&state)) { + hairProc(devVerts[state.f0], devVerts[state.f1], fClip, blitter.get()); + hairProc(devVerts[state.f1], devVerts[state.f2], fClip, blitter.get()); + hairProc(devVerts[state.f2], devVerts[state.f0], fClip, blitter.get()); + } + } +} + +//////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_DEBUG + +void SkDraw::validate() const { + SkASSERT(fBitmap != NULL); + SkASSERT(fMatrix != NULL); + SkASSERT(fClip != NULL); + + const SkIRect& cr = fClip->getBounds(); + SkIRect br; + + br.set(0, 0, fBitmap->width(), fBitmap->height()); + SkASSERT(cr.isEmpty() || br.contains(cr)); +} + +#endif + +////////////////////////////////////////////////////////////////////////////////////////// + +bool SkBounder::doIRect(const SkIRect& r) { + SkIRect rr; + return rr.intersect(fClip->getBounds(), r) && this->onIRect(rr); +} + +bool SkBounder::doHairline(const SkPoint& pt0, const SkPoint& pt1, + const SkPaint& paint) { + SkIRect r; + SkScalar v0, v1; + + v0 = pt0.fX; + v1 = pt1.fX; + if (v0 > v1) { + SkTSwap<SkScalar>(v0, v1); + } + r.fLeft = SkScalarFloor(v0); + r.fRight = SkScalarCeil(v1); + + v0 = pt0.fY; + v1 = pt1.fY; + if (v0 > v1) { + SkTSwap<SkScalar>(v0, v1); + } + r.fTop = SkScalarFloor(v0); + r.fBottom = SkScalarCeil(v1); + + if (paint.isAntiAlias()) { + r.inset(-1, -1); + } + return this->doIRect(r); +} + +bool SkBounder::doRect(const SkRect& rect, const SkPaint& paint) { + SkIRect r; + + if (paint.getStyle() == SkPaint::kFill_Style) { + rect.round(&r); + } else { + int rad = -1; + rect.roundOut(&r); + if (paint.isAntiAlias()) { + rad = -2; + } + r.inset(rad, rad); + } + return this->doIRect(r); +} + +bool SkBounder::doPath(const SkPath& path, const SkPaint& paint, bool doFill) { + SkRect bounds; + SkIRect r; + + path.computeBounds(&bounds, SkPath::kFast_BoundsType); + + if (doFill) { + bounds.round(&r); + } else { // hairline + bounds.roundOut(&r); + } + + if (paint.isAntiAlias()) { + r.inset(-1, -1); + } + return this->doIRect(r); +} + +void SkBounder::commit() { + // override in subclass +} + +//////////////////////////////////////////////////////////////////////////////////////////////// + +#include "SkPath.h" +#include "SkDraw.h" +#include "SkRegion.h" +#include "SkBlitter.h" + +static bool compute_bounds(const SkPath& devPath, const SkIRect* clipBounds, + SkMaskFilter* filter, const SkMatrix* filterMatrix, + SkIRect* bounds) { + if (devPath.isEmpty()) { + return false; + } + + SkIPoint margin; + margin.set(0, 0); + + // init our bounds from the path + { + SkRect pathBounds; + devPath.computeBounds(&pathBounds, SkPath::kExact_BoundsType); + pathBounds.inset(-SK_ScalarHalf, -SK_ScalarHalf); + pathBounds.roundOut(bounds); + } + + if (filter) { + SkASSERT(filterMatrix); + + SkMask srcM, dstM; + + srcM.fBounds = *bounds; + srcM.fFormat = SkMask::kA8_Format; + srcM.fImage = NULL; + if (!filter->filterMask(&dstM, srcM, *filterMatrix, &margin)) { + return false; + } + *bounds = dstM.fBounds; + } + + if (clipBounds && !SkIRect::Intersects(*clipBounds, *bounds)) { + return false; + } + + // (possibly) trim the srcM bounds to reflect the clip + // (plus whatever slop the filter needs) + if (clipBounds && !clipBounds->contains(*bounds)) { + SkIRect tmp = *bounds; + (void)tmp.intersect(*clipBounds); + tmp.inset(-margin.fX, -margin.fY); + (void)bounds->intersect(tmp); + } + + return true; +} + +static void draw_into_mask(const SkMask& mask, const SkPath& devPath) { + SkBitmap bm; + SkDraw draw; + SkRegion clipRgn; + SkMatrix matrix; + SkPaint paint; + + bm.setConfig(SkBitmap::kA8_Config, mask.fBounds.width(), mask.fBounds.height(), mask.fRowBytes); + bm.setPixels(mask.fImage); + + clipRgn.setRect(0, 0, mask.fBounds.width(), mask.fBounds.height()); + matrix.setTranslate(-SkIntToScalar(mask.fBounds.fLeft), + -SkIntToScalar(mask.fBounds.fTop)); + + draw.fBitmap = &bm; + draw.fClip = &clipRgn; + draw.fMatrix = &matrix; + draw.fBounder = NULL; + paint.setAntiAlias(true); + draw.drawPath(devPath, paint); +} + +bool SkDraw::DrawToMask(const SkPath& devPath, const SkIRect* clipBounds, + SkMaskFilter* filter, const SkMatrix* filterMatrix, + SkMask* mask, SkMask::CreateMode mode) { + if (SkMask::kJustRenderImage_CreateMode != mode) { + if (!compute_bounds(devPath, clipBounds, filter, filterMatrix, &mask->fBounds)) + return false; + } + + if (SkMask::kComputeBoundsAndRenderImage_CreateMode == mode) { + mask->fFormat = SkMask::kA8_Format; + mask->fRowBytes = mask->fBounds.width(); + mask->fImage = SkMask::AllocImage(mask->computeImageSize()); + memset(mask->fImage, 0, mask->computeImageSize()); + } + + if (SkMask::kJustComputeBounds_CreateMode != mode) { + draw_into_mask(*mask, devPath); + } + + return true; +} + diff --git a/skia/sgl/SkDrawProcs.h b/skia/sgl/SkDrawProcs.h new file mode 100644 index 0000000..d64c088 --- /dev/null +++ b/skia/sgl/SkDrawProcs.h @@ -0,0 +1,26 @@ +#ifndef SkDrawProcs_DEFINED +#define SkDrawProcs_DEFINED + +#include "SkDraw.h" + +class SkBlitter; + +struct SkDraw1Glyph { + const SkDraw* fDraw; + SkBounder* fBounder; + const SkRegion* fClip; + SkBlitter* fBlitter; + SkGlyphCache* fCache; + SkIRect fClipBounds; + + typedef void (*Proc)(const SkDraw1Glyph&, const SkGlyph&, int x, int y); + + Proc init(const SkDraw* draw, SkBlitter* blitter, SkGlyphCache* cache); +}; + +struct SkDrawProcs { + SkDraw1Glyph::Proc fD1GProc; +}; + +#endif + diff --git a/skia/sgl/SkEdge.cpp b/skia/sgl/SkEdge.cpp new file mode 100644 index 0000000..3a9474c --- /dev/null +++ b/skia/sgl/SkEdge.cpp @@ -0,0 +1,467 @@ +/* libs/graphics/sgl/SkEdge.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkEdge.h" +#include "SkFDot6.h" +#include <limits> + +/* + In setLine, setQuadratic, setCubic, the first thing we do is to convert + the points into FDot6. This is modulated by the shift parameter, which + will either be 0, or something like 2 for antialiasing. + + In the float case, we want to turn the float into .6 by saying pt * 64, + or pt * 256 for antialiasing. This is implemented as 1 << (shift + 6). + + In the fixed case, we want to turn the fixed into .6 by saying pt >> 10, + or pt >> 8 for antialiasing. This is implemented as pt >> (10 - shift). +*/ + +///////////////////////////////////////////////////////////////////////// + +int SkEdge::setLine(const SkPoint& p0, const SkPoint& p1, const SkIRect* clip, + int shift) { + SkFDot6 x0, y0, x1, y1; + + { +#ifdef SK_SCALAR_IS_FLOAT + float scale = float(1 << (shift + 6)); + x0 = int(p0.fX * scale); + y0 = int(p0.fY * scale); + x1 = int(p1.fX * scale); + y1 = int(p1.fY * scale); +#else + shift = 10 - shift; + x0 = p0.fX >> shift; + y0 = p0.fY >> shift; + x1 = p1.fX >> shift; + y1 = p1.fY >> shift; +#endif + } + + int winding = 1; + + if (y0 > y1) { + SkTSwap(x0, x1); + SkTSwap(y0, y1); + winding = -1; + } + + int top = SkFDot6Round(y0); + int bot = SkFDot6Round(y1); + + // are we a zero-height line? + if (top == bot) { + return 0; + } + // are we completely above or below the clip? + if (NULL != clip && (top >= clip->fBottom || bot <= clip->fTop)) { + return 0; + } + + SkFixed slope = SkFDot6Div(x1 - x0, y1 - y0); + + fX = SkFDot6ToFixed(x0 + SkFixedMul(slope, (32 - y0) & 63)); // + SK_Fixed1/2 + fDX = slope; + fFirstY = (int16_t)(top); // inlined skToS16() + if (top != (long)fFirstY) { + if (fFirstY < top) { + fFirstY = std::numeric_limits<int16_t>::max(); + } else { + fFirstY = std::numeric_limits<int16_t>::min(); + } + fX -= fDX * (top - (long)fFirstY); + } + fLastY = (int16_t)(bot - 1); // inlined SkToS16() + if (bot-1 != (long)fLastY) { + if (fLastY < bot-1) { + fLastY = std::numeric_limits<int16_t>::max(); + } else { + fLastY = std::numeric_limits<int16_t>::min(); + } + } + fCurveCount = 0; + fWinding = SkToS8(winding); + fCurveShift = 0; + + if (clip) { + this->chopLineWithClip(*clip); + } + return 1; +} + +// called from a curve subclass +int SkEdge::updateLine(SkFixed x0, SkFixed y0, SkFixed x1, SkFixed y1) +{ + SkASSERT(fWinding == 1 || fWinding == -1); + SkASSERT(fCurveCount != 0); + SkASSERT(fCurveShift != 0); + + y0 >>= 10; + y1 >>= 10; + + SkASSERT(y0 <= y1); + + int top = SkFDot6Round(y0); + int bot = SkFDot6Round(y1); + +// SkASSERT(top >= fFirstY); + + // are we a zero-height line? + if (top == bot) + return 0; + + x0 >>= 10; + x1 >>= 10; + + SkFixed slope = SkFDot6Div(x1 - x0, y1 - y0); + + fX = SkFDot6ToFixed(x0 + SkFixedMul(slope, (32 - y0) & 63)); // + SK_Fixed1/2 + fDX = slope; + fFirstY = SkToS16(top); + fLastY = SkToS16(bot - 1); + + return 1; +} + +void SkEdge::chopLineWithClip(const SkIRect& clip) +{ + int top = fFirstY; + + SkASSERT(top < clip.fBottom); + + // clip the line to the top + if (top < clip.fTop) + { + SkASSERT(fLastY >= clip.fTop); + fX += fDX * (clip.fTop - top); + fFirstY = clip.fTop; + } +} + +///////////////////////////////////////////////////////////////////////// + +static inline SkFDot6 cheap_distance(SkFDot6 dx, SkFDot6 dy) +{ + dx = SkAbs32(dx); + dy = SkAbs32(dy); + // return max + min/2 + if (dx > dy) + dx += dy >> 1; + else + dx = dy + (dx >> 1); + return dx; +} + +static inline int diff_to_shift(SkFDot6 dx, SkFDot6 dy) +{ + // cheap calc of distance from center of p0-p2 to the center of the curve + SkFDot6 dist = cheap_distance(dx, dy); + + // shift down dist (it is currently in dot6) + // down by 5 should give us 1/2 pixel accuracy (assuming our dist is accurate...) + // this is chosen by heuristic: make it as big as possible (to minimize segments) + // ... but small enough so that our curves still look smooth + dist >>= 5; + + // each subdivision (shift value) cuts this dist (error) by 1/4 + return (32 - SkCLZ(dist)) >> 1; +} + +int SkQuadraticEdge::setQuadratic(const SkPoint pts[3], const SkIRect* clip, int shift) +{ + SkFDot6 x0, y0, x1, y1, x2, y2; + + { +#ifdef SK_SCALAR_IS_FLOAT + float scale = float(1 << (shift + 6)); + x0 = int(pts[0].fX * scale); + y0 = int(pts[0].fY * scale); + x1 = int(pts[1].fX * scale); + y1 = int(pts[1].fY * scale); + x2 = int(pts[2].fX * scale); + y2 = int(pts[2].fY * scale); +#else + shift = 10 - shift; + x0 = pts[0].fX >> shift; + y0 = pts[0].fY >> shift; + x1 = pts[1].fX >> shift; + y1 = pts[1].fY >> shift; + x2 = pts[2].fX >> shift; + y2 = pts[2].fY >> shift; +#endif + } + + int winding = 1; + if (y0 > y2) + { + SkTSwap(x0, x2); + SkTSwap(y0, y2); + winding = -1; + } + SkASSERT(y0 <= y1 && y1 <= y2); + + int top = SkFDot6Round(y0); + int bot = SkFDot6Round(y2); + + // are we a zero-height quad (line)? + if (top == bot) + return 0; + // are we completely above or below the clip? + if (clip && (top >= clip->fBottom || bot <= clip->fTop)) + return 0; + + // compute number of steps needed (1 << shift) + { + SkFDot6 dx = ((x1 << 1) - x0 - x2) >> 2; + SkFDot6 dy = ((y1 << 1) - y0 - y2) >> 2; + shift = diff_to_shift(dx, dy); + } + // need at least 1 subdivision for our bias trick + if (shift == 0) + shift = 1; + + fWinding = SkToS8(winding); + fCurveShift = SkToU8(shift); + fCurveCount = SkToS16(1 << shift); + + SkFixed A = SkFDot6ToFixed(x0 - x1 - x1 + x2); + SkFixed B = SkFDot6ToFixed(x1 - x0 + x1 - x0); + + fQx = SkFDot6ToFixed(x0); + fQDx = B + (A >> shift); // biased by shift + fQDDx = A >> (shift - 1); // biased by shift + + A = SkFDot6ToFixed(y0 - y1 - y1 + y2); + B = SkFDot6ToFixed(y1 - y0 + y1 - y0); + + fQy = SkFDot6ToFixed(y0); + fQDy = B + (A >> shift); // biased by shift + fQDDy = A >> (shift - 1); // biased by shift + + fQLastX = SkFDot6ToFixed(x2); + fQLastY = SkFDot6ToFixed(y2); + + if (clip) + { + do { + for (;!this->updateQuadratic();) + ; + } while (!this->intersectsClip(*clip)); + this->chopLineWithClip(*clip); + return 1; + } + return this->updateQuadratic(); +} + +int SkQuadraticEdge::updateQuadratic() +{ + int success; + int count = fCurveCount; + SkFixed oldx = fQx; + SkFixed oldy = fQy; + SkFixed dx = fQDx; + SkFixed dy = fQDy; + SkFixed newx, newy; + int shift = fCurveShift; + + SkASSERT(count > 0); + + do { + if (--count > 0) + { + newx = oldx + (dx >> shift); + dx += fQDDx; + newy = oldy + (dy >> shift); + dy += fQDDy; + } + else // last segment + { + newx = fQLastX; + newy = fQLastY; + } + success = this->updateLine(oldx, oldy, newx, newy); + oldx = newx; + oldy = newy; + } while (count > 0 && !success); + + fQx = newx; + fQy = newy; + fQDx = dx; + fQDy = dy; + fCurveCount = SkToS16(count); + return success; +} + +///////////////////////////////////////////////////////////////////////// + +/* f(1/3) = (8a + 12b + 6c + d) / 27 + f(2/3) = (a + 6b + 12c + 8d) / 27 + + f(1/3)-b = (8a - 15b + 6c + d) / 27 + f(2/3)-c = (a + 6b - 15c + 8d) / 27 + + use 16/512 to approximate 1/27 +*/ +static SkFDot6 cubic_delta_from_line(SkFDot6 a, SkFDot6 b, SkFDot6 c, SkFDot6 d) +{ + SkFDot6 oneThird = ((a << 3) - ((b << 4) - b) + 6*c + d) * 19 >> 9; + SkFDot6 twoThird = (a + 6*b - ((c << 4) - c) + (d << 3)) * 19 >> 9; + + return SkMax32(SkAbs32(oneThird), SkAbs32(twoThird)); +} + +int SkCubicEdge::setCubic(const SkPoint pts[4], const SkIRect* clip, int shift) +{ + SkFDot6 x0, y0, x1, y1, x2, y2, x3, y3; + + { +#ifdef SK_SCALAR_IS_FLOAT + float scale = float(1 << (shift + 6)); + x0 = int(pts[0].fX * scale); + y0 = int(pts[0].fY * scale); + x1 = int(pts[1].fX * scale); + y1 = int(pts[1].fY * scale); + x2 = int(pts[2].fX * scale); + y2 = int(pts[2].fY * scale); + x3 = int(pts[3].fX * scale); + y3 = int(pts[3].fY * scale); +#else + shift = 10 - shift; + x0 = pts[0].fX >> shift; + y0 = pts[0].fY >> shift; + x1 = pts[1].fX >> shift; + y1 = pts[1].fY >> shift; + x2 = pts[2].fX >> shift; + y2 = pts[2].fY >> shift; + x3 = pts[3].fX >> shift; + y3 = pts[3].fY >> shift; +#endif + } + + int winding = 1; + if (y0 > y3) + { + SkTSwap(x0, x3); + SkTSwap(x1, x2); + SkTSwap(y0, y3); + SkTSwap(y1, y2); + winding = -1; + } + + int top = SkFDot6Round(y0); + int bot = SkFDot6Round(y3); + + // are we a zero-height cubic (line)? + if (top == bot) + return 0; + + // are we completely above or below the clip? + if (clip && (top >= clip->fBottom || bot <= clip->fTop)) + return 0; + + // compute number of steps needed (1 << shift) + { + // Can't use (center of curve - center of baseline), since center-of-curve + // need not be the max delta from the baseline (it could even be coincident) + // so we try just looking at the two off-curve points + SkFDot6 dx = cubic_delta_from_line(x0, x1, x2, x3); + SkFDot6 dy = cubic_delta_from_line(y0, y1, y2, y3); + // add 1 (by observation) + shift = diff_to_shift(dx, dy) + 1; + } + // need at least 1 subdivision for our bias trick + SkASSERT(shift > 0); + + fWinding = SkToS8(winding); + fCurveShift = SkToU8(shift); + fCurveCount = SkToS16(-1 << shift); + + SkFixed B = SkFDot6ToFixed(3 * (x1 - x0)); + SkFixed C = SkFDot6ToFixed(3 * (x0 - x1 - x1 + x2)); + SkFixed D = SkFDot6ToFixed(x3 + 3 * (x1 - x2) - x0); + + fCx = SkFDot6ToFixed(x0); + fCDx = B + (C >> shift) + (D >> 2*shift); // biased by shift + fCDDx = 2*C + (3*D >> (shift - 1)); // biased by 2*shift + fCDDDx = 3*D >> (shift - 1); // biased by 2*shift + + B = SkFDot6ToFixed(3 * (y1 - y0)); + C = SkFDot6ToFixed(3 * (y0 - y1 - y1 + y2)); + D = SkFDot6ToFixed(y3 + 3 * (y1 - y2) - y0); + + fCy = SkFDot6ToFixed(y0); + fCDy = B + (C >> shift) + (D >> 2*shift); // biased by shift + fCDDy = 2*C + (3*D >> (shift - 1)); // biased by 2*shift + fCDDDy = 3*D >> (shift - 1); // biased by 2*shift + + fCLastX = SkFDot6ToFixed(x3); + fCLastY = SkFDot6ToFixed(y3); + + if (clip) + { + do { + for (;!this->updateCubic();) + ; + } while (!this->intersectsClip(*clip)); + this->chopLineWithClip(*clip); + return 1; + } + return this->updateCubic(); +} + +int SkCubicEdge::updateCubic() +{ + int success; + int count = fCurveCount; + SkFixed oldx = fCx; + SkFixed oldy = fCy; + SkFixed newx, newy; + int shift = fCurveShift; + + SkASSERT(count < 0); + + do { + if (++count < 0) + { + newx = oldx + (fCDx >> shift); + fCDx += fCDDx >> shift; + fCDDx += fCDDDx; + + newy = oldy + (fCDy >> shift); + fCDy += fCDDy >> shift; + fCDDy += fCDDDy; + } + else // last segment + { + // SkDebugf("LastX err=%d, LastY err=%d\n", (oldx + (fCDx >> shift) - fLastX), (oldy + (fCDy >> shift) - fLastY)); + newx = fCLastX; + newy = fCLastY; + } + success = this->updateLine(oldx, oldy, newx, newy); + oldx = newx; + oldy = newy; + } while (count < 0 && !success); + + fCx = newx; + fCy = newy; + fCurveCount = SkToS16(count); + return success; +} + + + diff --git a/skia/sgl/SkEdge.h b/skia/sgl/SkEdge.h new file mode 100644 index 0000000..086e5a6 --- /dev/null +++ b/skia/sgl/SkEdge.h @@ -0,0 +1,92 @@ +/* libs/graphics/sgl/SkEdge.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkEdge_DEFINED +#define SkEdge_DEFINED + +#include "SkRect.h" + +struct SkEdge { + enum Type { + kLine_Type, + kQuad_Type, + kCubic_Type + }; + + SkEdge* fNext; + SkEdge* fPrev; + + SkFixed fX; + SkFixed fDX; + int16_t fFirstY; + int16_t fLastY; + int16_t fCurveCount; // only used by kQuad(+) and kCubic(-) + uint8_t fCurveShift; + int8_t fWinding; // 1 or -1 + + int setLine(const SkPoint& p0, const SkPoint& p1, const SkIRect* clip, + int shiftUp); + inline int updateLine(SkFixed ax, SkFixed ay, SkFixed bx, SkFixed by); + void chopLineWithClip(const SkIRect& clip); + + inline bool intersectsClip(const SkIRect& clip) const { + SkASSERT(fFirstY < clip.fBottom); + return fLastY >= clip.fTop; + } + +#ifdef SK_DEBUG + void dump() const { + #ifdef SK_CAN_USE_FLOAT + SkDebugf("edge: firstY:%d lastY:%d x:%g dx:%g w:%d\n", fFirstY, fLastY, SkFixedToFloat(fX), SkFixedToFloat(fDX), fWinding); + #else + SkDebugf("edge: firstY:%d lastY:%d x:%x dx:%x w:%d\n", fFirstY, fLastY, fX, fDX, fWinding); + #endif + } + + void validate() const { + SkASSERT(fPrev && fNext); + SkASSERT(fPrev->fNext == this); + SkASSERT(fNext->fPrev == this); + + SkASSERT(fFirstY <= fLastY); + SkASSERT(SkAbs32(fWinding) == 1); + } +#endif +}; + +struct SkQuadraticEdge : public SkEdge { + SkFixed fQx, fQy; + SkFixed fQDx, fQDy; + SkFixed fQDDx, fQDDy; + SkFixed fQLastX, fQLastY; + + int setQuadratic(const SkPoint pts[3], const SkIRect* clip, int shiftUp); + int updateQuadratic(); +}; + +struct SkCubicEdge : public SkEdge { + SkFixed fCx, fCy; + SkFixed fCDx, fCDy; + SkFixed fCDDx, fCDDy; + SkFixed fCDDDx, fCDDDy; + SkFixed fCLastX, fCLastY; + + int setCubic(const SkPoint pts[4], const SkIRect* clip, int shiftUp); + int updateCubic(); +}; + +#endif diff --git a/skia/sgl/SkFP.h b/skia/sgl/SkFP.h new file mode 100644 index 0000000..b95cba2 --- /dev/null +++ b/skia/sgl/SkFP.h @@ -0,0 +1,87 @@ +/* libs/graphics/sgl/SkFP.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkFP_DEFINED +#define SkFP_DEFINED + +#include "SkMath.h" + +#ifdef SK_SCALAR_IS_FLOAT + + typedef float SkFP; + + #define SkScalarToFP(n) (n) + #define SkFPToScalar(n) (n) + #define SkIntToFP(n) SkIntToScalar(n) + #define SkFPRound(x) SkScalarRound(n) + #define SkFPCeil(x) SkScalarCeil(n) + #define SkFPFloor(x) SkScalarFloor(n) + + #define SkFPNeg(x) (-(x)) + #define SkFPAbs(x) SkScalarAbs(x) + #define SkFPAdd(a, b) ((a) + (b)) + #define SkFPSub(a, b) ((a) - (b)) + #define SkFPMul(a, b) ((a) * (b)) + #define SkFPMulInt(a, n) ((a) * (n)) + #define SkFPDiv(a, b) ((a) / (b)) + #define SkFPDivInt(a, n) ((a) / (n)) + #define SkFPInvert(x) SkScalarInvert(x) + #define SkFPSqrt(x) SkScalarSqrt(x) + #define SkFPCubeRoot(x) pow(x, 1.0f/3) + + #define SkFPLT(a, b) ((a) < (b)) + #define SkFPLE(a, b) ((a) <= (b)) + #define SkFPGT(a, b) ((a) > (b)) + #define SkFPGE(a, b) ((a) >= (b)) + +#else // scalar is fixed + + #include "SkFloat.h" + + typedef int32_t SkFP; + + #define SkScalarToFP(n) SkFloat::SetShift(n, -16) + #define SkFPToScalar(n) SkFloat::GetShift(n, -16) + #define SkIntToFP(n) SkFloat::SetShift(n, 0) + #define SkFPRound(x) SkFloat::Round(x); + #define SkFPCeil(x) SkFloat::Ceil(); + #define SkFPFloor(x) SkFloat::Floor(); + + #define SkFPNeg(x) SkFloat::Neg(x) + #define SkFPAbs(x) SkFloat::Abs(x) + #define SkFPAdd(a, b) SkFloat::Add(a, b) + #define SkFPSub(a, b) SkFloat::Add(a, SkFloat::Neg(b)) + #define SkFPMul(a, b) SkFloat::Mul(a, b) + #define SkFPMulInt(a, n) SkFloat::MulInt(a, n) + #define SkFPDiv(a, b) SkFloat::Div(a, b) + #define SkFPDivInt(a, n) SkFloat::DivInt(a, n) + #define SkFPInvert(x) SkFloat::Invert(x) + #define SkFPSqrt(x) SkFloat::Sqrt(x) + #define SkFPCubeRoot(x) SkFloat::CubeRoot(x) + + #define SkFPLT(a, b) (SkFloat::Cmp(a, b) < 0) + #define SkFPLE(a, b) (SkFloat::Cmp(a, b) <= 0) + #define SkFPGT(a, b) (SkFloat::Cmp(a, b) > 0) + #define SkFPGE(a, b) (SkFloat::Cmp(a, b) >= 0) + +#endif + +#ifdef SK_DEBUG + void SkFP_UnitTest(); +#endif + +#endif diff --git a/skia/sgl/SkFilterProc.cpp b/skia/sgl/SkFilterProc.cpp new file mode 100644 index 0000000..8082a34 --- /dev/null +++ b/skia/sgl/SkFilterProc.cpp @@ -0,0 +1,303 @@ +/* libs/graphics/sgl/SkFilterProc.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkFilterProc.h" + +/* [1-x 1-y] [x 1-y] + [1-x y] [x y] +*/ + +static unsigned bilerp00(unsigned a00, unsigned a01, unsigned a10, unsigned a11) { return a00; } +static unsigned bilerp01(unsigned a00, unsigned a01, unsigned a10, unsigned a11) { return (3 * a00 + a01) >> 2; } +static unsigned bilerp02(unsigned a00, unsigned a01, unsigned a10, unsigned a11) { return (a00 + a01) >> 1; } +static unsigned bilerp03(unsigned a00, unsigned a01, unsigned a10, unsigned a11) { return (a00 + 3 * a01) >> 2; } + +static unsigned bilerp10(unsigned a00, unsigned a01, unsigned a10, unsigned a11) { return (3 * a00 + a10) >> 2; } +static unsigned bilerp11(unsigned a00, unsigned a01, unsigned a10, unsigned a11) { return (9 * a00 + 3 * (a01 + a10) + a11) >> 4; } +static unsigned bilerp12(unsigned a00, unsigned a01, unsigned a10, unsigned a11) { return (3 * (a00 + a01) + a10 + a11) >> 3; } +static unsigned bilerp13(unsigned a00, unsigned a01, unsigned a10, unsigned a11) { return (9 * a01 + 3 * (a00 + a11) + a10) >> 4; } + +static unsigned bilerp20(unsigned a00, unsigned a01, unsigned a10, unsigned a11) { return (a00 + a10) >> 1; } +static unsigned bilerp21(unsigned a00, unsigned a01, unsigned a10, unsigned a11) { return (3 * (a00 + a10) + a01 + a11) >> 3; } +static unsigned bilerp22(unsigned a00, unsigned a01, unsigned a10, unsigned a11) { return (a00 + a01 + a10 + a11) >> 2; } +static unsigned bilerp23(unsigned a00, unsigned a01, unsigned a10, unsigned a11) { return (3 * (a01 + a11) + a00 + a10) >> 3; } + +static unsigned bilerp30(unsigned a00, unsigned a01, unsigned a10, unsigned a11) { return (a00 + 3 * a10) >> 2; } +static unsigned bilerp31(unsigned a00, unsigned a01, unsigned a10, unsigned a11) { return (9 * a10 + 3 * (a00 + a11) + a01) >> 4; } +static unsigned bilerp32(unsigned a00, unsigned a01, unsigned a10, unsigned a11) { return (3 * (a10 + a11) + a00 + a01) >> 3; } +static unsigned bilerp33(unsigned a00, unsigned a01, unsigned a10, unsigned a11) { return (9 * a11 + 3 * (a01 + a10) + a00) >> 4; } + +static const SkFilterProc gBilerpProcs[4 * 4] = { + bilerp00, bilerp01, bilerp02, bilerp03, + bilerp10, bilerp11, bilerp12, bilerp13, + bilerp20, bilerp21, bilerp22, bilerp23, + bilerp30, bilerp31, bilerp32, bilerp33 +}; + +const SkFilterProc* SkGetBilinearFilterProcTable() +{ + return gBilerpProcs; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define MASK 0xFF00FF +#define LO_PAIR(x) ((x) & MASK) +#define HI_PAIR(x) (((x) >> 8) & MASK) +#define COMBINE(lo, hi) (((lo) & ~0xFF00) | (((hi) & ~0xFF00) << 8)) + +/////////////////////////////////////////////////////////////////////////////// + +static unsigned bilerp4_00(uint32_t c00, uint32_t c01, uint32_t c10, uint32_t c11) { + return c00; +} +static unsigned bilerp4_01(uint32_t c00, uint32_t c01, uint32_t c10, uint32_t c11) { + uint32_t lo = (3 * LO_PAIR(c00) + LO_PAIR(c01)) >> 2; + uint32_t hi = (3 * HI_PAIR(c00) + HI_PAIR(c01)) >> 2; + return COMBINE(lo, hi); +} +static unsigned bilerp4_02(uint32_t c00, uint32_t c01, uint32_t c10, uint32_t c11) { + uint32_t lo = (LO_PAIR(c00) + LO_PAIR(c01)) >> 1; + uint32_t hi = (HI_PAIR(c00) + HI_PAIR(c01)) >> 1; + return COMBINE(lo, hi); +} +static unsigned bilerp4_03(uint32_t c00, uint32_t c01, uint32_t c10, uint32_t c11) { + uint32_t lo = (LO_PAIR(c00) + 3 * LO_PAIR(c01)) >> 2; + uint32_t hi = (HI_PAIR(c00) + 3 * HI_PAIR(c01)) >> 2; + return COMBINE(lo, hi); +} + +static unsigned bilerp4_10(uint32_t c00, uint32_t c01, uint32_t c10, uint32_t c11) { + uint32_t lo = (3 * LO_PAIR(c00) + LO_PAIR(c10)) >> 2; + uint32_t hi = (3 * HI_PAIR(c00) + HI_PAIR(c10)) >> 2; + return COMBINE(lo, hi); +} +static unsigned bilerp4_11(uint32_t c00, uint32_t c01, uint32_t c10, uint32_t c11) { + uint32_t lo = (9 * LO_PAIR(c00) + 3 * (LO_PAIR(c01) + LO_PAIR(c10)) + LO_PAIR(c11)) >> 4; + uint32_t hi = (9 * HI_PAIR(c00) + 3 * (HI_PAIR(c01) + HI_PAIR(c10)) + HI_PAIR(c11)) >> 4; + return COMBINE(lo, hi); +} +static unsigned bilerp4_12(uint32_t c00, uint32_t c01, uint32_t c10, uint32_t c11) { + uint32_t lo = (3 * (LO_PAIR(c00) + LO_PAIR(c01)) + LO_PAIR(c10) + LO_PAIR(c11)) >> 3; + uint32_t hi = (3 * (HI_PAIR(c00) + HI_PAIR(c01)) + HI_PAIR(c10) + HI_PAIR(c11)) >> 3; + return COMBINE(lo, hi); +} +static unsigned bilerp4_13(uint32_t c00, uint32_t c01, uint32_t c10, uint32_t c11) { + uint32_t lo = (9 * LO_PAIR(c01) + 3 * (LO_PAIR(c00) + LO_PAIR(c11)) + LO_PAIR(c10)) >> 4; + uint32_t hi = (9 * HI_PAIR(c01) + 3 * (HI_PAIR(c00) + HI_PAIR(c11)) + HI_PAIR(c10)) >> 4; + return COMBINE(lo, hi); +} + +static unsigned bilerp4_20(uint32_t c00, uint32_t c01, uint32_t c10, uint32_t c11) { + uint32_t lo = (LO_PAIR(c00) + LO_PAIR(c10)) >> 1; + uint32_t hi = (HI_PAIR(c00) + HI_PAIR(c10)) >> 1; + return COMBINE(lo, hi); +} +static unsigned bilerp4_21(uint32_t c00, uint32_t c01, uint32_t c10, uint32_t c11) { + uint32_t lo = (3 * (LO_PAIR(c00) + LO_PAIR(c10)) + LO_PAIR(c01) + LO_PAIR(c11)) >> 3; + uint32_t hi = (3 * (HI_PAIR(c00) + HI_PAIR(c10)) + HI_PAIR(c01) + HI_PAIR(c11)) >> 3; + return COMBINE(lo, hi); +} +static unsigned bilerp4_22(uint32_t c00, uint32_t c01, uint32_t c10, uint32_t c11) { + uint32_t lo = (LO_PAIR(c00) + LO_PAIR(c01) + LO_PAIR(c10) + LO_PAIR(c11)) >> 2; + uint32_t hi = (HI_PAIR(c00) + HI_PAIR(c01) + HI_PAIR(c10) + HI_PAIR(c11)) >> 2; + return COMBINE(lo, hi); +} +static unsigned bilerp4_23(uint32_t c00, uint32_t c01, uint32_t c10, uint32_t c11) { + uint32_t lo = (3 * (LO_PAIR(c01) + LO_PAIR(c11)) + LO_PAIR(c00) + LO_PAIR(c10)) >> 3; + uint32_t hi = (3 * (HI_PAIR(c01) + HI_PAIR(c11)) + HI_PAIR(c00) + HI_PAIR(c10)) >> 3; + return COMBINE(lo, hi); +} + +static unsigned bilerp4_30(uint32_t c00, uint32_t c01, uint32_t c10, uint32_t c11) { + uint32_t lo = (LO_PAIR(c00) + 3 * LO_PAIR(c10)) >> 2; + uint32_t hi = (HI_PAIR(c00) + 3 * HI_PAIR(c10)) >> 2; + return COMBINE(lo, hi); +} +static unsigned bilerp4_31(uint32_t c00, uint32_t c01, uint32_t c10, uint32_t c11) { + uint32_t lo = (9 * LO_PAIR(c10) + 3 * (LO_PAIR(c00) + LO_PAIR(c11)) + LO_PAIR(c01)) >> 4; + uint32_t hi = (9 * HI_PAIR(c10) + 3 * (HI_PAIR(c00) + HI_PAIR(c11)) + HI_PAIR(c01)) >> 4; + return COMBINE(lo, hi); +} +static unsigned bilerp4_32(uint32_t c00, uint32_t c01, uint32_t c10, uint32_t c11) { + uint32_t lo = (3 * (LO_PAIR(c10) + LO_PAIR(c11)) + LO_PAIR(c00) + LO_PAIR(c01)) >> 3; + uint32_t hi = (3 * (HI_PAIR(c10) + HI_PAIR(c11)) + HI_PAIR(c00) + HI_PAIR(c01)) >> 3; + return COMBINE(lo, hi); +} +static unsigned bilerp4_33(uint32_t c00, uint32_t c01, uint32_t c10, uint32_t c11) { + uint32_t lo = (9 * LO_PAIR(c11) + 3 * (LO_PAIR(c01) + LO_PAIR(c10)) + LO_PAIR(c00)) >> 4; + uint32_t hi = (9 * HI_PAIR(c11) + 3 * (HI_PAIR(c01) + HI_PAIR(c10)) + HI_PAIR(c00)) >> 4; + return COMBINE(lo, hi); +} + +static const SkFilter32Proc gBilerp32Procs[4 * 4] = { + bilerp4_00, bilerp4_01, bilerp4_02, bilerp4_03, + bilerp4_10, bilerp4_11, bilerp4_12, bilerp4_13, + bilerp4_20, bilerp4_21, bilerp4_22, bilerp4_23, + bilerp4_30, bilerp4_31, bilerp4_32, bilerp4_33 +}; + +const SkFilter32Proc* SkGetFilter32ProcTable() +{ + return gBilerp32Procs; +} + +/////////////////////////////////////////////////////////////////////////////// + +static unsigned bilerptr00(const uint32_t* a00, const uint32_t* a01, const uint32_t* a10, const uint32_t* a11) { + return *a00; +} +static unsigned bilerptr01(const uint32_t* a00, const uint32_t* a01, const uint32_t* a10, const uint32_t* a11) { + uint32_t c00 = *a00; + uint32_t c01 = *a01; + uint32_t lo = (3 * LO_PAIR(c00) + LO_PAIR(c01)) >> 2; + uint32_t hi = (3 * HI_PAIR(c00) + HI_PAIR(c01)) >> 2; + return COMBINE(lo, hi); +} +static unsigned bilerptr02(const uint32_t* a00, const uint32_t* a01, const uint32_t* a10, const uint32_t* a11) { + uint32_t c00 = *a00; + uint32_t c01 = *a01; + uint32_t lo = (LO_PAIR(c00) + LO_PAIR(c01)) >> 1; + uint32_t hi = (HI_PAIR(c00) + HI_PAIR(c01)) >> 1; + return COMBINE(lo, hi); +} +static unsigned bilerptr03(const uint32_t* a00, const uint32_t* a01, const uint32_t* a10, const uint32_t* a11) { + uint32_t c00 = *a00; + uint32_t c01 = *a01; + uint32_t lo = (LO_PAIR(c00) + 3 * LO_PAIR(c01)) >> 2; + uint32_t hi = (HI_PAIR(c00) + 3 * HI_PAIR(c01)) >> 2; + return COMBINE(lo, hi); +} + +static unsigned bilerptr10(const uint32_t* a00, const uint32_t* a01, const uint32_t* a10, const uint32_t* a11) { + uint32_t c00 = *a00; + uint32_t c10 = *a10; + uint32_t lo = (3 * LO_PAIR(c00) + LO_PAIR(c10)) >> 2; + uint32_t hi = (3 * HI_PAIR(c00) + HI_PAIR(c10)) >> 2; + return COMBINE(lo, hi); +} +static unsigned bilerptr11(const uint32_t* a00, const uint32_t* a01, const uint32_t* a10, const uint32_t* a11) { + uint32_t c00 = *a00; + uint32_t c01 = *a01; + uint32_t c10 = *a10; + uint32_t c11 = *a11; + uint32_t lo = (9 * LO_PAIR(c00) + 3 * (LO_PAIR(c01) + LO_PAIR(c10)) + LO_PAIR(c11)) >> 4; + uint32_t hi = (9 * HI_PAIR(c00) + 3 * (HI_PAIR(c01) + HI_PAIR(c10)) + HI_PAIR(c11)) >> 4; + return COMBINE(lo, hi); +} +static unsigned bilerptr12(const uint32_t* a00, const uint32_t* a01, const uint32_t* a10, const uint32_t* a11) { + uint32_t c00 = *a00; + uint32_t c01 = *a01; + uint32_t c10 = *a10; + uint32_t c11 = *a11; + uint32_t lo = (3 * (LO_PAIR(c00) + LO_PAIR(c01)) + LO_PAIR(c10) + LO_PAIR(c11)) >> 3; + uint32_t hi = (3 * (HI_PAIR(c00) + HI_PAIR(c01)) + HI_PAIR(c10) + HI_PAIR(c11)) >> 3; + return COMBINE(lo, hi); +} +static unsigned bilerptr13(const uint32_t* a00, const uint32_t* a01, const uint32_t* a10, const uint32_t* a11) { + uint32_t c00 = *a00; + uint32_t c01 = *a01; + uint32_t c10 = *a10; + uint32_t c11 = *a11; + uint32_t lo = (9 * LO_PAIR(c01) + 3 * (LO_PAIR(c00) + LO_PAIR(c11)) + LO_PAIR(c10)) >> 4; + uint32_t hi = (9 * HI_PAIR(c01) + 3 * (HI_PAIR(c00) + HI_PAIR(c11)) + HI_PAIR(c10)) >> 4; + return COMBINE(lo, hi); +} + +static unsigned bilerptr20(const uint32_t* a00, const uint32_t* a01, const uint32_t* a10, const uint32_t* a11) { + uint32_t c00 = *a00; + uint32_t c10 = *a10; + uint32_t lo = (LO_PAIR(c00) + LO_PAIR(c10)) >> 1; + uint32_t hi = (HI_PAIR(c00) + HI_PAIR(c10)) >> 1; + return COMBINE(lo, hi); +} +static unsigned bilerptr21(const uint32_t* a00, const uint32_t* a01, const uint32_t* a10, const uint32_t* a11) { + uint32_t c00 = *a00; + uint32_t c01 = *a01; + uint32_t c10 = *a10; + uint32_t c11 = *a11; + uint32_t lo = (3 * (LO_PAIR(c00) + LO_PAIR(c10)) + LO_PAIR(c01) + LO_PAIR(c11)) >> 3; + uint32_t hi = (3 * (HI_PAIR(c00) + HI_PAIR(c10)) + HI_PAIR(c01) + HI_PAIR(c11)) >> 3; + return COMBINE(lo, hi); +} +static unsigned bilerptr22(const uint32_t* a00, const uint32_t* a01, const uint32_t* a10, const uint32_t* a11) { + uint32_t c00 = *a00; + uint32_t c01 = *a01; + uint32_t c10 = *a10; + uint32_t c11 = *a11; + uint32_t lo = (LO_PAIR(c00) + LO_PAIR(c01) + LO_PAIR(c10) + LO_PAIR(c11)) >> 2; + uint32_t hi = (HI_PAIR(c00) + HI_PAIR(c01) + HI_PAIR(c10) + HI_PAIR(c11)) >> 2; + return COMBINE(lo, hi); +} +static unsigned bilerptr23(const uint32_t* a00, const uint32_t* a01, const uint32_t* a10, const uint32_t* a11) { + uint32_t c00 = *a00; + uint32_t c01 = *a01; + uint32_t c10 = *a10; + uint32_t c11 = *a11; + uint32_t lo = (3 * (LO_PAIR(c01) + LO_PAIR(c11)) + LO_PAIR(c00) + LO_PAIR(c10)) >> 3; + uint32_t hi = (3 * (HI_PAIR(c01) + HI_PAIR(c11)) + HI_PAIR(c00) + HI_PAIR(c10)) >> 3; + return COMBINE(lo, hi); +} + +static unsigned bilerptr30(const uint32_t* a00, const uint32_t* a01, const uint32_t* a10, const uint32_t* a11) { + uint32_t c00 = *a00; + uint32_t c10 = *a10; + uint32_t lo = (LO_PAIR(c00) + 3 * LO_PAIR(c10)) >> 2; + uint32_t hi = (HI_PAIR(c00) + 3 * HI_PAIR(c10)) >> 2; + return COMBINE(lo, hi); +} +static unsigned bilerptr31(const uint32_t* a00, const uint32_t* a01, const uint32_t* a10, const uint32_t* a11) { + uint32_t c00 = *a00; + uint32_t c01 = *a01; + uint32_t c10 = *a10; + uint32_t c11 = *a11; + uint32_t lo = (9 * LO_PAIR(c10) + 3 * (LO_PAIR(c00) + LO_PAIR(c11)) + LO_PAIR(c01)) >> 4; + uint32_t hi = (9 * HI_PAIR(c10) + 3 * (HI_PAIR(c00) + HI_PAIR(c11)) + HI_PAIR(c01)) >> 4; + return COMBINE(lo, hi); +} +static unsigned bilerptr32(const uint32_t* a00, const uint32_t* a01, const uint32_t* a10, const uint32_t* a11) { + uint32_t c00 = *a00; + uint32_t c01 = *a01; + uint32_t c10 = *a10; + uint32_t c11 = *a11; + uint32_t lo = (3 * (LO_PAIR(c10) + LO_PAIR(c11)) + LO_PAIR(c00) + LO_PAIR(c01)) >> 3; + uint32_t hi = (3 * (HI_PAIR(c10) + HI_PAIR(c11)) + HI_PAIR(c00) + HI_PAIR(c01)) >> 3; + return COMBINE(lo, hi); +} +static unsigned bilerptr33(const uint32_t* a00, const uint32_t* a01, const uint32_t* a10, const uint32_t* a11) { + uint32_t c00 = *a00; + uint32_t c01 = *a01; + uint32_t c10 = *a10; + uint32_t c11 = *a11; + uint32_t lo = (9 * LO_PAIR(c11) + 3 * (LO_PAIR(c01) + LO_PAIR(c10)) + LO_PAIR(c00)) >> 4; + uint32_t hi = (9 * HI_PAIR(c11) + 3 * (HI_PAIR(c01) + HI_PAIR(c10)) + HI_PAIR(c00)) >> 4; + return COMBINE(lo, hi); +} + +static const SkFilterPtrProc gBilerpPtrProcs[4 * 4] = { + bilerptr00, bilerptr01, bilerptr02, bilerptr03, + bilerptr10, bilerptr11, bilerptr12, bilerptr13, + bilerptr20, bilerptr21, bilerptr22, bilerptr23, + bilerptr30, bilerptr31, bilerptr32, bilerptr33 +}; + +const SkFilterPtrProc* SkGetBilinearFilterPtrProcTable() +{ + return gBilerpPtrProcs; +} + diff --git a/skia/sgl/SkFilterProc.h b/skia/sgl/SkFilterProc.h new file mode 100644 index 0000000..5aa59ab --- /dev/null +++ b/skia/sgl/SkFilterProc.h @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2006-2008 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SkFilter_DEFINED +#define SkFilter_DEFINED + +#include "SkMath.h" +#include "SkFixed.h" + +typedef unsigned (*SkFilterProc)(unsigned x00, unsigned x01, + unsigned x10, unsigned x11); + +const SkFilterProc* SkGetBilinearFilterProcTable(); + +inline SkFilterProc SkGetBilinearFilterProc(const SkFilterProc* table, + SkFixed x, SkFixed y) +{ + SkASSERT(table); + + // convert to dot 2 + x = (unsigned)(x << 16) >> 30; + y = (unsigned)(y << 16) >> 30; + return table[(y << 2) | x]; +} + +inline SkFilterProc SkGetBilinearFilterProc22(const SkFilterProc* table, + unsigned x, unsigned y) +{ + SkASSERT(table); + + // extract low 2 bits + x = x << 30 >> 30; + y = y << 30 >> 30; + return table[(y << 2) | x]; +} + +inline const SkFilterProc* SkGetBilinearFilterProc22Row(const SkFilterProc* table, + unsigned y) +{ + SkASSERT(table); + // extract low 2 bits and shift up 2 + return &table[y << 30 >> 28]; +} + +inline SkFilterProc SkGetBilinearFilterProc22RowProc(const SkFilterProc* row, + unsigned x) +{ + SkASSERT(row); + // extract low 2 bits + return row[x << 30 >> 30]; +} + +/////////////////////////////////////////////////////////////////////////////// + +typedef unsigned (*SkFilter32Proc)(uint32_t x00, uint32_t x01, + uint32_t x10, uint32_t x11); + +const SkFilter32Proc* SkGetFilter32ProcTable(); + +inline SkFilter32Proc SkGetFilter32Proc22(const SkFilterProc* table, + unsigned x, unsigned y) +{ + SkASSERT(table); + + // extract low 2 bits + x = x << 30 >> 30; + y = y << 30 >> 30; + return table[(y << 2) | x]; +} + +inline const SkFilter32Proc* SkGetFilter32Proc22Row(const SkFilterProc* table, + unsigned y) +{ + SkASSERT(table); + // extract low 2 bits and shift up 2 + return &table[y << 30 >> 28]; +} + +inline SkFilter32Proc SkGetFilter32Proc22RowProc(const SkFilterProc* row, + unsigned x) +{ + SkASSERT(row); + // extract low 2 bits + return row[x << 30 >> 30]; +} + +/////////////////////////////////////////////////////////////////////////////// + +/** Special version of SkFilterProc. This takes the address of 4 ints, and combines them a byte at a + time. AABBCCDD. +*/ +typedef uint32_t (*SkFilterPtrProc)(const uint32_t*, const uint32_t*, const uint32_t*, const uint32_t*); + +const SkFilterPtrProc* SkGetBilinearFilterPtrProcTable(); +inline SkFilterPtrProc SkGetBilinearFilterPtrProc(const SkFilterPtrProc* table, SkFixed x, SkFixed y) +{ + SkASSERT(table); + + // convert to dot 2 + x = (unsigned)(x << 16) >> 30; + y = (unsigned)(y << 16) >> 30; + return table[(y << 2) | x]; +} + +/** Given a Y value, return a subset of the proc table for that value. + Pass this to SkGetBilinearFilterPtrXProc with the corresponding X value to get the + correct proc. +*/ +inline const SkFilterPtrProc* SkGetBilinearFilterPtrProcYTable(const SkFilterPtrProc* table, SkFixed y) +{ + SkASSERT(table); + + y = (unsigned)(y << 16) >> 30; + return table + (y << 2); +} + +/** Given a subtable returned by SkGetBilinearFilterPtrProcYTable(), return the proc for the + specified X value. +*/ +inline SkFilterPtrProc SkGetBilinearFilterPtrXProc(const SkFilterPtrProc* table, SkFixed x) +{ + SkASSERT(table); + + // convert to dot 2 + x = (unsigned)(x << 16) >> 30; + return table[x]; +} + +#endif + + diff --git a/skia/sgl/SkFlattenable.cpp b/skia/sgl/SkFlattenable.cpp new file mode 100644 index 0000000..08dd59c --- /dev/null +++ b/skia/sgl/SkFlattenable.cpp @@ -0,0 +1,259 @@ +#include "SkFlattenable.h" +#include "SkTypeface.h" + +void SkFlattenable::flatten(SkFlattenableWriteBuffer&) +{ + /* we don't write anything at the moment, but this allows our subclasses + to not know that, since we want them to always call INHERITED::flatten() + in their code. + */ +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +SkFlattenableReadBuffer::SkFlattenableReadBuffer() { + fRCArray = NULL; + fRCCount = 0; + + fTFArray = NULL; + fTFCount = 0; + + fFactoryArray = NULL; + fFactoryCount = 0; +} + +SkFlattenableReadBuffer::SkFlattenableReadBuffer(const void* data) : + INHERITED(data, 1024 * 1024) { + fRCArray = NULL; + fRCCount = 0; + + fTFArray = NULL; + fTFCount = 0; + + fFactoryArray = NULL; + fFactoryCount = 0; +} + +SkFlattenableReadBuffer::SkFlattenableReadBuffer(const void* data, size_t size) + : INHERITED(data, size) { + fRCArray = NULL; + fRCCount = 0; + + fTFArray = NULL; + fTFCount = 0; + + fFactoryArray = NULL; + fFactoryCount = 0; +} + +SkTypeface* SkFlattenableReadBuffer::readTypeface() { + uint32_t index = this->readU32(); + if (0 == index || index > (unsigned)fTFCount) { + if (index) { + SkDebugf("====== typeface index %d\n", index); + } + return NULL; + } else { + SkASSERT(fTFArray); + return fTFArray[index - 1]; + } +} + +SkRefCnt* SkFlattenableReadBuffer::readRefCnt() { + uint32_t index = this->readU32(); + if (0 == index || index > (unsigned)fRCCount) { + return NULL; + } else { + SkASSERT(fRCArray); + return fRCArray[index - 1]; + } +} + +SkFlattenable* SkFlattenableReadBuffer::readFlattenable() { + SkFlattenable::Factory factory = NULL; + + if (fFactoryCount > 0) { + uint32_t index = this->readU32(); + if (index > 0) { + index -= 1; + SkASSERT(index < (unsigned)fFactoryCount); + factory = fFactoryArray[index]; + // if we recorded an index, but failed to get a factory, we need + // to skip the flattened data in the buffer + if (NULL == factory) { + uint32_t size = this->readU32(); + this->skip(size); + // fall through and return NULL for the object + } + } + } else { + factory = (SkFlattenable::Factory)readFunctionPtr(); + } + + SkFlattenable* obj = NULL; + if (factory) { + uint32_t sizeRecorded = this->readU32(); + uint32_t offset = this->offset(); + obj = (*factory)(*this); + // check that we read the amount we expected + uint32_t sizeRead = this->offset() - offset; + if (sizeRecorded != sizeRead) { + // we could try to fix up the offset... + sk_throw(); + } + } + return obj; +} + +void* SkFlattenableReadBuffer::readFunctionPtr() { + void* proc; + this->read(&proc, sizeof(proc)); + return proc; +} + +/////////////////////////////////////////////////////////////////////////////// + +SkFlattenableWriteBuffer::SkFlattenableWriteBuffer(size_t minSize) : + INHERITED(minSize) { + fFlags = (Flags)0; + fRCRecorder = NULL; + fTFRecorder = NULL; + fFactoryRecorder = NULL; +} + +SkFlattenableWriteBuffer::~SkFlattenableWriteBuffer() { + fRCRecorder->safeUnref(); + fTFRecorder->safeUnref(); + fFactoryRecorder->safeUnref(); +} + +SkRefCntRecorder* SkFlattenableWriteBuffer::setRefCntRecorder( + SkRefCntRecorder* rec) { + SkRefCnt_SafeAssign(fRCRecorder, rec); + return rec; +} + +SkRefCntRecorder* SkFlattenableWriteBuffer::setTypefaceRecorder( + SkRefCntRecorder* rec) { + SkRefCnt_SafeAssign(fTFRecorder, rec); + return rec; +} + +SkFactoryRecorder* SkFlattenableWriteBuffer::setFactoryRecorder( + SkFactoryRecorder* rec) { + SkRefCnt_SafeAssign(fFactoryRecorder, rec); + return rec; +} + +void SkFlattenableWriteBuffer::writeTypeface(SkTypeface* obj) { + if (NULL == obj || NULL == fTFRecorder) { + this->write32(0); + } else { + this->write32(fTFRecorder->record(obj)); + } +} + +void SkFlattenableWriteBuffer::writeRefCnt(SkRefCnt* obj) { + if (NULL == obj || NULL == fRCRecorder) { + this->write32(0); + } else { + this->write32(fRCRecorder->record(obj)); + } +} + +void SkFlattenableWriteBuffer::writeFlattenable(SkFlattenable* flattenable) { + SkFlattenable::Factory factory = NULL; + if (flattenable) { + factory = flattenable->getFactory(); + } + + if (fFactoryRecorder) { + this->write32(fFactoryRecorder->record(factory)); + } else { + this->writeFunctionPtr((void*)factory); + } + + if (factory) { + // make room for the size of the flatttened object + (void)this->reserve(sizeof(uint32_t)); + // record the current size, so we can subtract after the object writes. + uint32_t offset = this->size(); + // now flatten the object + flattenable->flatten(*this); + uint32_t objSize = this->size() - offset; + // record the obj's size + *this->peek32(offset - sizeof(uint32_t)) = objSize; + } +} + +void SkFlattenableWriteBuffer::writeFunctionPtr(void* proc) { + *(void**)this->reserve(sizeof(void*)) = proc; +} + +/////////////////////////////////////////////////////////////////////////////// + +SkRefCntRecorder::~SkRefCntRecorder() { + // call this now, while our decPtr() is sill in scope + this->reset(); +} + +void SkRefCntRecorder::incPtr(void* ptr) { + ((SkRefCnt*)ptr)->ref(); +} + +void SkRefCntRecorder::decPtr(void* ptr) { + ((SkRefCnt*)ptr)->unref(); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define MAX_PAIR_COUNT 64 + +struct Pair { + const char* fName; + SkFlattenable::Factory fFactory; +}; + +static int gCount; +static Pair gPairs[MAX_PAIR_COUNT]; + +void SkFlattenable::Register(const char name[], Factory factory) { + SkASSERT(name); + SkASSERT(factory); + + static bool gOnce; + if (!gOnce) { + gCount = 0; + gOnce = true; + } + + SkASSERT(gCount < MAX_PAIR_COUNT); + + gPairs[gCount].fName = name; + gPairs[gCount].fFactory = factory; + gCount += 1; +} + +SkFlattenable::Factory SkFlattenable::NameToFactory(const char name[]) { + const Pair* pairs = gPairs; + for (int i = gCount - 1; i >= 0; --i) { + if (strcmp(pairs[i].fName, name) == 0) { + return pairs[i].fFactory; + } + } + return NULL; +} + +const char* SkFlattenable::FactoryToName(Factory fact) { + const Pair* pairs = gPairs; + for (int i = gCount - 1; i >= 0; --i) { + if (pairs[i].fFactory == fact) { + return pairs[i].fName; + } + } + return NULL; +} + diff --git a/skia/sgl/SkGeometry.cpp b/skia/sgl/SkGeometry.cpp new file mode 100644 index 0000000..65022ce --- /dev/null +++ b/skia/sgl/SkGeometry.cpp @@ -0,0 +1,1072 @@ +/* libs/graphics/sgl/SkGeometry.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkGeometry.h" +#include "Sk64.h" +#include "SkMatrix.h" + +/** If defined, this makes eval_quad and eval_cubic do more setup (sometimes + involving integer multiplies by 2 or 3, but fewer calls to SkScalarMul. + May also introduce overflow of fixed when we compute our setup. +*/ +#ifdef SK_SCALAR_IS_FIXED + #define DIRECT_EVAL_OF_POLYNOMIALS +#endif + +//////////////////////////////////////////////////////////////////////// + +#ifdef SK_SCALAR_IS_FIXED + static int is_not_monotonic(int a, int b, int c, int d) + { + return (((a - b) | (b - c) | (c - d)) & ((b - a) | (c - b) | (d - c))) >> 31; + } + + static int is_not_monotonic(int a, int b, int c) + { + return (((a - b) | (b - c)) & ((b - a) | (c - b))) >> 31; + } +#else + static int is_not_monotonic(float a, float b, float c) + { + float ab = a - b; + float bc = b - c; + if (ab < 0) + bc = -bc; + return ab == 0 || bc < 0; + } +#endif + +//////////////////////////////////////////////////////////////////////// + +static bool is_unit_interval(SkScalar x) +{ + return x > 0 && x < SK_Scalar1; +} + +static int valid_unit_divide(SkScalar numer, SkScalar denom, SkScalar* ratio) +{ + SkASSERT(ratio); + + if (numer < 0) + { + numer = -numer; + denom = -denom; + } + + if (denom == 0 || numer == 0 || numer >= denom) + return 0; + + SkScalar r = SkScalarDiv(numer, denom); + SkASSERT(r >= 0 && r < SK_Scalar1); + if (r == 0) // catch underflow if numer <<<< denom + return 0; + *ratio = r; + return 1; +} + +/** From Numerical Recipes in C. + + Q = -1/2 (B + sign(B) sqrt[B*B - 4*A*C]) + x1 = Q / A + x2 = C / Q +*/ +int SkFindUnitQuadRoots(SkScalar A, SkScalar B, SkScalar C, SkScalar roots[2]) +{ + SkASSERT(roots); + + if (A == 0) + return valid_unit_divide(-C, B, roots); + + SkScalar* r = roots; + +#ifdef SK_SCALAR_IS_FLOAT + float R = B*B - 4*A*C; + if (R < 0) // complex roots + return 0; + R = sk_float_sqrt(R); +#else + Sk64 RR, tmp; + + RR.setMul(B,B); + tmp.setMul(A,C); + tmp.shiftLeft(2); + RR.sub(tmp); + if (RR.isNeg()) + return 0; + SkFixed R = RR.getSqrt(); +#endif + + SkScalar Q = (B < 0) ? -(B-R)/2 : -(B+R)/2; + r += valid_unit_divide(Q, A, r); + r += valid_unit_divide(C, Q, r); + if (r - roots == 2) + { + if (roots[0] > roots[1]) + SkTSwap<SkScalar>(roots[0], roots[1]); + else if (roots[0] == roots[1]) // nearly-equal? + r -= 1; // skip the double root + } + return (int)(r - roots); +} + +#ifdef SK_SCALAR_IS_FIXED +/** Trim A/B/C down so that they are all <= 32bits + and then call SkFindUnitQuadRoots() +*/ +static int Sk64FindFixedQuadRoots(const Sk64& A, const Sk64& B, const Sk64& C, SkFixed roots[2]) +{ + int na = A.shiftToMake32(); + int nb = B.shiftToMake32(); + int nc = C.shiftToMake32(); + + int shift = SkMax32(na, SkMax32(nb, nc)); + SkASSERT(shift >= 0); + + return SkFindUnitQuadRoots(A.getShiftRight(shift), B.getShiftRight(shift), C.getShiftRight(shift), roots); +} +#endif + +///////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////// + +static SkScalar eval_quad(const SkScalar src[], SkScalar t) +{ + SkASSERT(src); + SkASSERT(t >= 0 && t <= SK_Scalar1); + +#ifdef DIRECT_EVAL_OF_POLYNOMIALS + SkScalar C = src[0]; + SkScalar A = src[4] - 2 * src[2] + C; + SkScalar B = 2 * (src[2] - C); + return SkScalarMulAdd(SkScalarMulAdd(A, t, B), t, C); +#else + SkScalar ab = SkScalarInterp(src[0], src[2], t); + SkScalar bc = SkScalarInterp(src[2], src[4], t); + return SkScalarInterp(ab, bc, t); +#endif +} + +static SkScalar eval_quad_derivative(const SkScalar src[], SkScalar t) +{ + SkScalar A = src[4] - 2 * src[2] + src[0]; + SkScalar B = src[2] - src[0]; + + return 2 * SkScalarMulAdd(A, t, B); +} + +static SkScalar eval_quad_derivative_at_half(const SkScalar src[]) +{ + SkScalar A = src[4] - 2 * src[2] + src[0]; + SkScalar B = src[2] - src[0]; + return A + 2 * B; +} + +void SkEvalQuadAt(const SkPoint src[3], SkScalar t, SkPoint* pt, SkVector* tangent) +{ + SkASSERT(src); + SkASSERT(t >= 0 && t <= SK_Scalar1); + + if (pt) + pt->set(eval_quad(&src[0].fX, t), eval_quad(&src[0].fY, t)); + if (tangent) + tangent->set(eval_quad_derivative(&src[0].fX, t), + eval_quad_derivative(&src[0].fY, t)); +} + +void SkEvalQuadAtHalf(const SkPoint src[3], SkPoint* pt, SkVector* tangent) +{ + SkASSERT(src); + + if (pt) + { + SkScalar x01 = SkScalarAve(src[0].fX, src[1].fX); + SkScalar y01 = SkScalarAve(src[0].fY, src[1].fY); + SkScalar x12 = SkScalarAve(src[1].fX, src[2].fX); + SkScalar y12 = SkScalarAve(src[1].fY, src[2].fY); + pt->set(SkScalarAve(x01, x12), SkScalarAve(y01, y12)); + } + if (tangent) + tangent->set(eval_quad_derivative_at_half(&src[0].fX), + eval_quad_derivative_at_half(&src[0].fY)); +} + +static void interp_quad_coords(const SkScalar* src, SkScalar* dst, SkScalar t) +{ + SkScalar ab = SkScalarInterp(src[0], src[2], t); + SkScalar bc = SkScalarInterp(src[2], src[4], t); + + dst[0] = src[0]; + dst[2] = ab; + dst[4] = SkScalarInterp(ab, bc, t); + dst[6] = bc; + dst[8] = src[4]; +} + +void SkChopQuadAt(const SkPoint src[3], SkPoint dst[5], SkScalar t) +{ + SkASSERT(t > 0 && t < SK_Scalar1); + + interp_quad_coords(&src[0].fX, &dst[0].fX, t); + interp_quad_coords(&src[0].fY, &dst[0].fY, t); +} + +void SkChopQuadAtHalf(const SkPoint src[3], SkPoint dst[5]) +{ + SkScalar x01 = SkScalarAve(src[0].fX, src[1].fX); + SkScalar y01 = SkScalarAve(src[0].fY, src[1].fY); + SkScalar x12 = SkScalarAve(src[1].fX, src[2].fX); + SkScalar y12 = SkScalarAve(src[1].fY, src[2].fY); + + dst[0] = src[0]; + dst[1].set(x01, y01); + dst[2].set(SkScalarAve(x01, x12), SkScalarAve(y01, y12)); + dst[3].set(x12, y12); + dst[4] = src[2]; +} + +/** Quad'(t) = At + B, where + A = 2(a - 2b + c) + B = 2(b - a) + Solve for t, only if it fits between 0 < t < 1 +*/ +int SkFindQuadExtrema(SkScalar a, SkScalar b, SkScalar c, SkScalar tValue[1]) +{ + /* At + B == 0 + t = -B / A + */ +#ifdef SK_SCALAR_IS_FIXED + return is_not_monotonic(a, b, c) && valid_unit_divide(a - b, a - b - b + c, tValue); +#else + return valid_unit_divide(a - b, a - b - b + c, tValue); +#endif +} + +static void flatten_double_quad_extrema(SkScalar coords[14]) +{ + coords[2] = coords[6] = coords[4]; +} + +static void force_quad_monotonic_in_y(SkPoint pts[3]) +{ + // zap pts[1].fY to the nearest value + SkScalar ab = SkScalarAbs(pts[0].fY - pts[1].fY); + SkScalar bc = SkScalarAbs(pts[1].fY - pts[2].fY); + pts[1].fY = ab < bc ? pts[0].fY : pts[2].fY; +} + +/* Returns 0 for 1 quad, and 1 for two quads, either way the answer is + stored in dst[]. Guarantees that the 1/2 quads will be monotonic. +*/ +int SkChopQuadAtYExtrema(const SkPoint src[3], SkPoint dst[5]) +{ + SkASSERT(src); + SkASSERT(dst); + +#if 0 + static bool once = true; + if (once) + { + once = false; + SkPoint s[3] = { 0, 26398, 0, 26331, 0, 20621428 }; + SkPoint d[6]; + + int n = SkChopQuadAtYExtrema(s, d); + SkDebugf("chop=%d, Y=[%x %x %x %x %x %x]\n", n, d[0].fY, d[1].fY, d[2].fY, d[3].fY, d[4].fY, d[5].fY); + } +#endif + + SkScalar a = src[0].fY; + SkScalar b = src[1].fY; + SkScalar c = src[2].fY; + + if (is_not_monotonic(a, b, c)) + { + SkScalar tValue; + if (valid_unit_divide(a - b, a - b - b + c, &tValue)) + { + SkChopQuadAt(src, dst, tValue); + flatten_double_quad_extrema(&dst[0].fY); + return 1; + } + // if we get here, we need to force dst to be monotonic, even though + // we couldn't compute a unit_divide value (probably underflow). + b = SkScalarAbs(a - b) < SkScalarAbs(b - c) ? a : c; + } + dst[0].set(src[0].fX, a); + dst[1].set(src[1].fX, b); + dst[2].set(src[2].fX, c); + return 0; +} + +// F(t) = a (1 - t) ^ 2 + 2 b t (1 - t) + c t ^ 2 +// F'(t) = 2 (b - a) + 2 (a - 2b + c) t +// F''(t) = 2 (a - 2b + c) +// +// A = 2 (b - a) +// B = 2 (a - 2b + c) +// +// Maximum curvature for a quadratic means solving +// Fx' Fx'' + Fy' Fy'' = 0 +// +// t = - (Ax Bx + Ay By) / (Bx ^ 2 + By ^ 2) +// +int SkChopQuadAtMaxCurvature(const SkPoint src[3], SkPoint dst[5]) +{ + SkScalar Ax = src[1].fX - src[0].fX; + SkScalar Ay = src[1].fY - src[0].fY; + SkScalar Bx = src[0].fX - src[1].fX - src[1].fX + src[2].fX; + SkScalar By = src[0].fY - src[1].fY - src[1].fY + src[2].fY; + SkScalar t = 0; // 0 means don't chop + +#ifdef SK_SCALAR_IS_FLOAT + (void)valid_unit_divide(-(Ax * Bx + Ay * By), Bx * Bx + By * By, &t); +#else + // !!! should I use SkFloat here? seems like it + Sk64 numer, denom, tmp; + + numer.setMul(Ax, -Bx); + tmp.setMul(Ay, -By); + numer.add(tmp); + + if (numer.isPos()) // do nothing if numer <= 0 + { + denom.setMul(Bx, Bx); + tmp.setMul(By, By); + denom.add(tmp); + SkASSERT(!denom.isNeg()); + if (numer < denom) + { + t = numer.getFixedDiv(denom); + SkASSERT(t >= 0 && t <= SK_Fixed1); // assert that we're numerically stable (ha!) + if ((unsigned)t >= SK_Fixed1) // runtime check for numerical stability + t = 0; // ignore the chop + } + } +#endif + + if (t == 0) + { + memcpy(dst, src, 3 * sizeof(SkPoint)); + return 1; + } + else + { + SkChopQuadAt(src, dst, t); + return 2; + } +} + +//////////////////////////////////////////////////////////////////////////////////////// +///// CUBICS // CUBICS // CUBICS // CUBICS // CUBICS // CUBICS // CUBICS // CUBICS ///// +//////////////////////////////////////////////////////////////////////////////////////// + +static void get_cubic_coeff(const SkScalar pt[], SkScalar coeff[4]) +{ + coeff[0] = pt[6] + 3*(pt[2] - pt[4]) - pt[0]; + coeff[1] = 3*(pt[4] - pt[2] - pt[2] + pt[0]); + coeff[2] = 3*(pt[2] - pt[0]); + coeff[3] = pt[0]; +} + +void SkGetCubicCoeff(const SkPoint pts[4], SkScalar cx[4], SkScalar cy[4]) +{ + SkASSERT(pts); + + if (cx) + get_cubic_coeff(&pts[0].fX, cx); + if (cy) + get_cubic_coeff(&pts[0].fY, cy); +} + +static SkScalar eval_cubic(const SkScalar src[], SkScalar t) +{ + SkASSERT(src); + SkASSERT(t >= 0 && t <= SK_Scalar1); + + if (t == 0) + return src[0]; + +#ifdef DIRECT_EVAL_OF_POLYNOMIALS + SkScalar D = src[0]; + SkScalar A = src[6] + 3*(src[2] - src[4]) - D; + SkScalar B = 3*(src[4] - src[2] - src[2] + D); + SkScalar C = 3*(src[2] - D); + + return SkScalarMulAdd(SkScalarMulAdd(SkScalarMulAdd(A, t, B), t, C), t, D); +#else + SkScalar ab = SkScalarInterp(src[0], src[2], t); + SkScalar bc = SkScalarInterp(src[2], src[4], t); + SkScalar cd = SkScalarInterp(src[4], src[6], t); + SkScalar abc = SkScalarInterp(ab, bc, t); + SkScalar bcd = SkScalarInterp(bc, cd, t); + return SkScalarInterp(abc, bcd, t); +#endif +} + +/** return At^2 + Bt + C +*/ +static SkScalar eval_quadratic(SkScalar A, SkScalar B, SkScalar C, SkScalar t) +{ + SkASSERT(t >= 0 && t <= SK_Scalar1); + + return SkScalarMulAdd(SkScalarMulAdd(A, t, B), t, C); +} + +static SkScalar eval_cubic_derivative(const SkScalar src[], SkScalar t) +{ + SkScalar A = src[6] + 3*(src[2] - src[4]) - src[0]; + SkScalar B = 2*(src[4] - 2 * src[2] + src[0]); + SkScalar C = src[2] - src[0]; + + return eval_quadratic(A, B, C, t); +} + +static SkScalar eval_cubic_2ndDerivative(const SkScalar src[], SkScalar t) +{ + SkScalar A = src[6] + 3*(src[2] - src[4]) - src[0]; + SkScalar B = src[4] - 2 * src[2] + src[0]; + + return SkScalarMulAdd(A, t, B); +} + +void SkEvalCubicAt(const SkPoint src[4], SkScalar t, SkPoint* loc, SkVector* tangent, SkVector* curvature) +{ + SkASSERT(src); + SkASSERT(t >= 0 && t <= SK_Scalar1); + + if (loc) + loc->set(eval_cubic(&src[0].fX, t), eval_cubic(&src[0].fY, t)); + if (tangent) + tangent->set(eval_cubic_derivative(&src[0].fX, t), + eval_cubic_derivative(&src[0].fY, t)); + if (curvature) + curvature->set(eval_cubic_2ndDerivative(&src[0].fX, t), + eval_cubic_2ndDerivative(&src[0].fY, t)); +} + +/** Cubic'(t) = At^2 + Bt + C, where + A = 3(-a + 3(b - c) + d) + B = 6(a - 2b + c) + C = 3(b - a) + Solve for t, keeping only those that fit betwee 0 < t < 1 +*/ +int SkFindCubicExtrema(SkScalar a, SkScalar b, SkScalar c, SkScalar d, SkScalar tValues[2]) +{ +#ifdef SK_SCALAR_IS_FIXED + if (!is_not_monotonic(a, b, c, d)) + return 0; +#endif + + // we divide A,B,C by 3 to simplify + SkScalar A = d - a + 3*(b - c); + SkScalar B = 2*(a - b - b + c); + SkScalar C = b - a; + + return SkFindUnitQuadRoots(A, B, C, tValues); +} + +static void interp_cubic_coords(const SkScalar* src, SkScalar* dst, SkScalar t) +{ + SkScalar ab = SkScalarInterp(src[0], src[2], t); + SkScalar bc = SkScalarInterp(src[2], src[4], t); + SkScalar cd = SkScalarInterp(src[4], src[6], t); + SkScalar abc = SkScalarInterp(ab, bc, t); + SkScalar bcd = SkScalarInterp(bc, cd, t); + SkScalar abcd = SkScalarInterp(abc, bcd, t); + + dst[0] = src[0]; + dst[2] = ab; + dst[4] = abc; + dst[6] = abcd; + dst[8] = bcd; + dst[10] = cd; + dst[12] = src[6]; +} + +void SkChopCubicAt(const SkPoint src[4], SkPoint dst[7], SkScalar t) +{ + SkASSERT(t > 0 && t < SK_Scalar1); + + interp_cubic_coords(&src[0].fX, &dst[0].fX, t); + interp_cubic_coords(&src[0].fY, &dst[0].fY, t); +} + +void SkChopCubicAt(const SkPoint src[4], SkPoint dst[], const SkScalar tValues[], int roots) +{ +#ifdef SK_DEBUG + { + for (int i = 0; i < roots - 1; i++) + { + SkASSERT(is_unit_interval(tValues[i])); + SkASSERT(is_unit_interval(tValues[i+1])); + SkASSERT(tValues[i] < tValues[i+1]); + } + } +#endif + + if (dst) + { + if (roots == 0) // nothing to chop + memcpy(dst, src, 4*sizeof(SkPoint)); + else + { + SkScalar t = tValues[0]; + SkPoint tmp[4]; + + for (int i = 0; i < roots; i++) + { + SkChopCubicAt(src, dst, t); + if (i == roots - 1) + break; + + SkDEBUGCODE(int valid =) valid_unit_divide(tValues[i+1] - tValues[i], SK_Scalar1 - tValues[i], &t); + SkASSERT(valid); + + dst += 3; + memcpy(tmp, dst, 4 * sizeof(SkPoint)); + src = tmp; + } + } + } +} + +void SkChopCubicAtHalf(const SkPoint src[4], SkPoint dst[7]) +{ + SkScalar x01 = SkScalarAve(src[0].fX, src[1].fX); + SkScalar y01 = SkScalarAve(src[0].fY, src[1].fY); + SkScalar x12 = SkScalarAve(src[1].fX, src[2].fX); + SkScalar y12 = SkScalarAve(src[1].fY, src[2].fY); + SkScalar x23 = SkScalarAve(src[2].fX, src[3].fX); + SkScalar y23 = SkScalarAve(src[2].fY, src[3].fY); + + SkScalar x012 = SkScalarAve(x01, x12); + SkScalar y012 = SkScalarAve(y01, y12); + SkScalar x123 = SkScalarAve(x12, x23); + SkScalar y123 = SkScalarAve(y12, y23); + + dst[0] = src[0]; + dst[1].set(x01, y01); + dst[2].set(x012, y012); + dst[3].set(SkScalarAve(x012, x123), SkScalarAve(y012, y123)); + dst[4].set(x123, y123); + dst[5].set(x23, y23); + dst[6] = src[3]; +} + +static void flatten_double_cubic_extrema(SkScalar coords[14]) +{ + coords[4] = coords[8] = coords[6]; +} + +/** Given 4 points on a cubic bezier, chop it into 1, 2, 3 beziers such that + the resulting beziers are monotonic in Y. This is called by the scan converter. + Depending on what is returned, dst[] is treated as follows + 0 dst[0..3] is the original cubic + 1 dst[0..3] and dst[3..6] are the two new cubics + 2 dst[0..3], dst[3..6], dst[6..9] are the three new cubics + If dst == null, it is ignored and only the count is returned. +*/ +int SkChopCubicAtYExtrema(const SkPoint src[4], SkPoint dst[10]) +{ + SkScalar tValues[2]; + int roots = SkFindCubicExtrema(src[0].fY, src[1].fY, src[2].fY, src[3].fY, tValues); + + SkChopCubicAt(src, dst, tValues, roots); + if (dst && roots > 0) + { + // we do some cleanup to ensure our Y extrema are flat + flatten_double_cubic_extrema(&dst[0].fY); + if (roots == 2) + flatten_double_cubic_extrema(&dst[3].fY); + } + return roots; +} + +/** http://www.faculty.idc.ac.il/arik/quality/appendixA.html + + Inflection means that curvature is zero. + Curvature is [F' x F''] / [F'^3] + So we solve F'x X F''y - F'y X F''y == 0 + After some canceling of the cubic term, we get + A = b - a + B = c - 2b + a + C = d - 3c + 3b - a + (BxCy - ByCx)t^2 + (AxCy - AyCx)t + AxBy - AyBx == 0 +*/ +int SkFindCubicInflections(const SkPoint src[4], SkScalar tValues[]) +{ + SkScalar Ax = src[1].fX - src[0].fX; + SkScalar Ay = src[1].fY - src[0].fY; + SkScalar Bx = src[2].fX - 2 * src[1].fX + src[0].fX; + SkScalar By = src[2].fY - 2 * src[1].fY + src[0].fY; + SkScalar Cx = src[3].fX + 3 * (src[1].fX - src[2].fX) - src[0].fX; + SkScalar Cy = src[3].fY + 3 * (src[1].fY - src[2].fY) - src[0].fY; + int count; + +#ifdef SK_SCALAR_IS_FLOAT + count = SkFindUnitQuadRoots(Bx*Cy - By*Cx, Ax*Cy - Ay*Cx, Ax*By - Ay*Bx, tValues); +#else + Sk64 A, B, C, tmp; + + A.setMul(Bx, Cy); + tmp.setMul(By, Cx); + A.sub(tmp); + + B.setMul(Ax, Cy); + tmp.setMul(Ay, Cx); + B.sub(tmp); + + C.setMul(Ax, By); + tmp.setMul(Ay, Bx); + C.sub(tmp); + + count = Sk64FindFixedQuadRoots(A, B, C, tValues); +#endif + + return count; +} + +int SkChopCubicAtInflections(const SkPoint src[], SkPoint dst[10]) +{ + SkScalar tValues[2]; + int count = SkFindCubicInflections(src, tValues); + + if (dst) + { + if (count == 0) + memcpy(dst, src, 4 * sizeof(SkPoint)); + else + SkChopCubicAt(src, dst, tValues, count); + } + return count + 1; +} + +template <typename T> void bubble_sort(T array[], int count) +{ + for (int i = count - 1; i > 0; --i) + for (int j = i; j > 0; --j) + if (array[j] < array[j-1]) + { + T tmp(array[j]); + array[j] = array[j-1]; + array[j-1] = tmp; + } +} + +#include "SkFP.h" + +// newton refinement +#if 0 +static SkScalar refine_cubic_root(const SkFP coeff[4], SkScalar root) +{ + // x1 = x0 - f(t) / f'(t) + + SkFP T = SkScalarToFloat(root); + SkFP N, D; + + // f' = 3*coeff[0]*T^2 + 2*coeff[1]*T + coeff[2] + D = SkFPMul(SkFPMul(coeff[0], SkFPMul(T,T)), 3); + D = SkFPAdd(D, SkFPMulInt(SkFPMul(coeff[1], T), 2)); + D = SkFPAdd(D, coeff[2]); + + if (D == 0) + return root; + + // f = coeff[0]*T^3 + coeff[1]*T^2 + coeff[2]*T + coeff[3] + N = SkFPMul(SkFPMul(SkFPMul(T, T), T), coeff[0]); + N = SkFPAdd(N, SkFPMul(SkFPMul(T, T), coeff[1])); + N = SkFPAdd(N, SkFPMul(T, coeff[2])); + N = SkFPAdd(N, coeff[3]); + + if (N) + { + SkScalar delta = SkFPToScalar(SkFPDiv(N, D)); + + if (delta) + root -= delta; + } + return root; +} +#endif + +#if defined _WIN32 && _MSC_VER >= 1300 && defined SK_SCALAR_IS_FIXED // disable warning : unreachable code if building fixed point for windows desktop +#pragma warning ( disable : 4702 ) +#endif + +/* Solve coeff(t) == 0, returning the number of roots that + lie withing 0 < t < 1. + coeff[0]t^3 + coeff[1]t^2 + coeff[2]t + coeff[3] +*/ +static int solve_cubic_polynomial(const SkFP coeff[4], SkScalar tValues[3]) +{ +#ifndef SK_SCALAR_IS_FLOAT + return 0; // this is not yet implemented for software float +#endif + + if (SkScalarNearlyZero(coeff[0])) // we're just a quadratic + { + return SkFindUnitQuadRoots(coeff[1], coeff[2], coeff[3], tValues); + } + + SkFP a, b, c, Q, R; + + { + SkASSERT(coeff[0] != 0); + + SkFP inva = SkFPInvert(coeff[0]); + a = SkFPMul(coeff[1], inva); + b = SkFPMul(coeff[2], inva); + c = SkFPMul(coeff[3], inva); + } + Q = SkFPDivInt(SkFPSub(SkFPMul(a,a), SkFPMulInt(b, 3)), 9); +// R = (2*a*a*a - 9*a*b + 27*c) / 54; + R = SkFPMulInt(SkFPMul(SkFPMul(a, a), a), 2); + R = SkFPSub(R, SkFPMulInt(SkFPMul(a, b), 9)); + R = SkFPAdd(R, SkFPMulInt(c, 27)); + R = SkFPDivInt(R, 54); + + SkFP Q3 = SkFPMul(SkFPMul(Q, Q), Q); + SkFP R2MinusQ3 = SkFPSub(SkFPMul(R,R), Q3); + SkFP adiv3 = SkFPDivInt(a, 3); + + SkScalar* roots = tValues; + SkScalar r; + + if (SkFPLT(R2MinusQ3, 0)) // we have 3 real roots + { +#ifdef SK_SCALAR_IS_FLOAT + float theta = sk_float_acos(R / sk_float_sqrt(Q3)); + float neg2RootQ = -2 * sk_float_sqrt(Q); + + r = neg2RootQ * sk_float_cos(theta/3) - adiv3; + if (is_unit_interval(r)) + *roots++ = r; + + r = neg2RootQ * sk_float_cos((theta + 2*SK_ScalarPI)/3) - adiv3; + if (is_unit_interval(r)) + *roots++ = r; + + r = neg2RootQ * sk_float_cos((theta - 2*SK_ScalarPI)/3) - adiv3; + if (is_unit_interval(r)) + *roots++ = r; + + // now sort the roots + bubble_sort(tValues, (int)(roots - tValues)); +#endif + } + else // we have 1 real root + { + SkFP A = SkFPAdd(SkFPAbs(R), SkFPSqrt(R2MinusQ3)); + A = SkFPCubeRoot(A); + if (SkFPGT(R, 0)) + A = SkFPNeg(A); + + if (A != 0) + A = SkFPAdd(A, SkFPDiv(Q, A)); + r = SkFPToScalar(SkFPSub(A, adiv3)); + if (is_unit_interval(r)) + *roots++ = r; + } + + return (int)(roots - tValues); +} + +/* Looking for F' dot F'' == 0 + + A = b - a + B = c - 2b + a + C = d - 3c + 3b - a + + F' = 3Ct^2 + 6Bt + 3A + F'' = 6Ct + 6B + + F' dot F'' -> CCt^3 + 3BCt^2 + (2BB + CA)t + AB +*/ +static void formulate_F1DotF2(const SkScalar src[], SkFP coeff[4]) +{ + SkScalar a = src[2] - src[0]; + SkScalar b = src[4] - 2 * src[2] + src[0]; + SkScalar c = src[6] + 3 * (src[2] - src[4]) - src[0]; + + SkFP A = SkScalarToFP(a); + SkFP B = SkScalarToFP(b); + SkFP C = SkScalarToFP(c); + + coeff[0] = SkFPMul(C, C); + coeff[1] = SkFPMulInt(SkFPMul(B, C), 3); + coeff[2] = SkFPMulInt(SkFPMul(B, B), 2); + coeff[2] = SkFPAdd(coeff[2], SkFPMul(C, A)); + coeff[3] = SkFPMul(A, B); +} + +// EXPERIMENTAL: can set this to zero to accept all t-values 0 < t < 1 +//#define kMinTValueForChopping (SK_Scalar1 / 256) +#define kMinTValueForChopping 0 + +/* Looking for F' dot F'' == 0 + + A = b - a + B = c - 2b + a + C = d - 3c + 3b - a + + F' = 3Ct^2 + 6Bt + 3A + F'' = 6Ct + 6B + + F' dot F'' -> CCt^3 + 3BCt^2 + (2BB + CA)t + AB +*/ +int SkFindCubicMaxCurvature(const SkPoint src[4], SkScalar tValues[3]) +{ + SkFP coeffX[4], coeffY[4]; + int i; + + formulate_F1DotF2(&src[0].fX, coeffX); + formulate_F1DotF2(&src[0].fY, coeffY); + + for (i = 0; i < 4; i++) + coeffX[i] = SkFPAdd(coeffX[i],coeffY[i]); + + SkScalar t[3]; + int count = solve_cubic_polynomial(coeffX, t); + int maxCount = 0; + + // now remove extrema where the curvature is zero (mins) + // !!!! need a test for this !!!! + for (i = 0; i < count; i++) + { + // if (not_min_curvature()) + if (t[i] > kMinTValueForChopping && t[i] < SK_Scalar1 - kMinTValueForChopping) + tValues[maxCount++] = t[i]; + } + return maxCount; +} + +int SkChopCubicAtMaxCurvature(const SkPoint src[4], SkPoint dst[13], SkScalar tValues[3]) +{ + SkScalar t_storage[3]; + + if (tValues == NULL) + tValues = t_storage; + + int count = SkFindCubicMaxCurvature(src, tValues); + + if (dst) + { + if (count == 0) + memcpy(dst, src, 4 * sizeof(SkPoint)); + else + SkChopCubicAt(src, dst, tValues, count); + } + return count + 1; +} + +//////////////////////////////////////////////////////////////////////////////// + +/* Find t value for quadratic [a, b, c] = d. + Return 0 if there is no solution within [0, 1) +*/ +static SkScalar quad_solve(SkScalar a, SkScalar b, SkScalar c, SkScalar d) +{ + // At^2 + Bt + C = d + SkScalar A = a - 2 * b + c; + SkScalar B = 2 * (b - a); + SkScalar C = a - d; + + SkScalar roots[2]; + int count = SkFindUnitQuadRoots(A, B, C, roots); + + SkASSERT(count <= 1); + return count == 1 ? roots[0] : 0; +} + +/* given a quad-curve and a point (x,y), chop the quad at that point and return + the new quad's offCurve point. Should only return false if the computed pos + is the start of the curve (i.e. root == 0) +*/ +static bool quad_pt2OffCurve(const SkPoint quad[3], SkScalar x, SkScalar y, SkPoint* offCurve) +{ + const SkScalar* base; + SkScalar value; + + if (SkScalarAbs(x) < SkScalarAbs(y)) { + base = &quad[0].fX; + value = x; + } else { + base = &quad[0].fY; + value = y; + } + + // note: this returns 0 if it thinks value is out of range, meaning the + // root might return something outside of [0, 1) + SkScalar t = quad_solve(base[0], base[2], base[4], value); + + if (t > 0) + { + SkPoint tmp[5]; + SkChopQuadAt(quad, tmp, t); + *offCurve = tmp[1]; + return true; + } else { + /* t == 0 means either the value triggered a root outside of [0, 1) + For our purposes, we can ignore the <= 0 roots, but we want to + catch the >= 1 roots (which given our caller, will basically mean + a root of 1, give-or-take numerical instability). If we are in the + >= 1 case, return the existing offCurve point. + + The test below checks to see if we are close to the "end" of the + curve (near base[4]). Rather than specifying a tolerance, I just + check to see if value is on to the right/left of the middle point + (depending on the direction/sign of the end points). + */ + if ((base[0] < base[4] && value > base[2]) || + (base[0] > base[4] && value < base[2])) // should root have been 1 + { + *offCurve = quad[1]; + return true; + } + } + return false; +} + +static const SkPoint gQuadCirclePts[kSkBuildQuadArcStorage] = { + { SK_Scalar1, 0 }, + { SK_Scalar1, SK_ScalarTanPIOver8 }, + { SK_ScalarRoot2Over2, SK_ScalarRoot2Over2 }, + { SK_ScalarTanPIOver8, SK_Scalar1 }, + + { 0, SK_Scalar1 }, + { -SK_ScalarTanPIOver8, SK_Scalar1 }, + { -SK_ScalarRoot2Over2, SK_ScalarRoot2Over2 }, + { -SK_Scalar1, SK_ScalarTanPIOver8 }, + + { -SK_Scalar1, 0 }, + { -SK_Scalar1, -SK_ScalarTanPIOver8 }, + { -SK_ScalarRoot2Over2, -SK_ScalarRoot2Over2 }, + { -SK_ScalarTanPIOver8, -SK_Scalar1 }, + + { 0, -SK_Scalar1 }, + { SK_ScalarTanPIOver8, -SK_Scalar1 }, + { SK_ScalarRoot2Over2, -SK_ScalarRoot2Over2 }, + { SK_Scalar1, -SK_ScalarTanPIOver8 }, + + { SK_Scalar1, 0 } +}; + +int SkBuildQuadArc(const SkVector& uStart, const SkVector& uStop, + SkRotationDirection dir, const SkMatrix* userMatrix, + SkPoint quadPoints[]) +{ + // rotate by x,y so that uStart is (1.0) + SkScalar x = SkPoint::DotProduct(uStart, uStop); + SkScalar y = SkPoint::CrossProduct(uStart, uStop); + + SkScalar absX = SkScalarAbs(x); + SkScalar absY = SkScalarAbs(y); + + int pointCount; + + // check for (effectively) coincident vectors + // this can happen if our angle is nearly 0 or nearly 180 (y == 0) + // ... we use the dot-prod to distinguish between 0 and 180 (x > 0) + if (absY <= SK_ScalarNearlyZero && x > 0 && + ((y >= 0 && kCW_SkRotationDirection == dir) || + (y <= 0 && kCCW_SkRotationDirection == dir))) { + + // just return the start-point + quadPoints[0].set(SK_Scalar1, 0); + pointCount = 1; + } else { + if (dir == kCCW_SkRotationDirection) + y = -y; + + // what octant (quadratic curve) is [xy] in? + int oct = 0; + bool sameSign = true; + + if (0 == y) + { + oct = 4; // 180 + SkASSERT(SkScalarAbs(x + SK_Scalar1) <= SK_ScalarNearlyZero); + } + else if (0 == x) + { + SkASSERT(absY - SK_Scalar1 <= SK_ScalarNearlyZero); + if (y > 0) + oct = 2; // 90 + else + oct = 6; // 270 + } + else + { + if (y < 0) + oct += 4; + if ((x < 0) != (y < 0)) + { + oct += 2; + sameSign = false; + } + if ((absX < absY) == sameSign) + oct += 1; + } + + int wholeCount = oct << 1; + memcpy(quadPoints, gQuadCirclePts, (wholeCount + 1) * sizeof(SkPoint)); + + const SkPoint* arc = &gQuadCirclePts[wholeCount]; + if (quad_pt2OffCurve(arc, x, y, &quadPoints[wholeCount + 1])) + { + quadPoints[wholeCount + 2].set(x, y); + wholeCount += 2; + } + pointCount = wholeCount + 1; + } + + // now handle counter-clockwise and the initial unitStart rotation + SkMatrix matrix; + matrix.setSinCos(uStart.fY, uStart.fX); + if (dir == kCCW_SkRotationDirection) { + matrix.preScale(SK_Scalar1, -SK_Scalar1); + } + if (userMatrix) { + matrix.postConcat(*userMatrix); + } + matrix.mapPoints(quadPoints, pointCount); + return pointCount; +} + + +///////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_DEBUG + +void SkGeometry::UnitTest() +{ +#ifdef SK_SUPPORT_UNITTEST + SkPoint pts[3], dst[5]; + + pts[0].set(0, 0); + pts[1].set(100, 50); + pts[2].set(0, 100); + + int count = SkChopQuadAtMaxCurvature(pts, dst); + SkASSERT(count == 1 || count == 2); +#endif +} + +#endif + + diff --git a/skia/sgl/SkGeometry.h b/skia/sgl/SkGeometry.h new file mode 100644 index 0000000..d4547a5 --- /dev/null +++ b/skia/sgl/SkGeometry.h @@ -0,0 +1,163 @@ +/* libs/graphics/sgl/SkGeometry.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkGeometry_DEFINED +#define SkGeometry_DEFINED + +#include "SkMatrix.h" + +/** Given a quadratic equation Ax^2 + Bx + C = 0, return 0, 1, 2 roots for the + equation. +*/ +int SkFindUnitQuadRoots(SkScalar A, SkScalar B, SkScalar C, SkScalar roots[2]); + +/////////////////////////////////////////////////////////////////////////////// + +/** Set pt to the point on the src quadratic specified by t. t must be + 0 <= t <= 1.0 +*/ +void SkEvalQuadAt(const SkPoint src[3], SkScalar t, SkPoint* pt, SkVector* tangent = NULL); +void SkEvalQuadAtHalf(const SkPoint src[3], SkPoint* pt, SkVector* tangent = NULL); + +/** Given a src quadratic bezier, chop it at the specified t value, + where 0 < t < 1, and return the two new quadratics in dst: + dst[0..2] and dst[2..4] +*/ +void SkChopQuadAt(const SkPoint src[3], SkPoint dst[5], SkScalar t); + +/** Given a src quadratic bezier, chop it at the specified t == 1/2, + The new quads are returned in dst[0..2] and dst[2..4] +*/ +void SkChopQuadAtHalf(const SkPoint src[3], SkPoint dst[5]); + +/** Given the 3 coefficients for a quadratic bezier (either X or Y values), look + for extrema, and return the number of t-values that are found that represent + these extrema. If the quadratic has no extrema betwee (0..1) exclusive, the + function returns 0. + Returned count tValues[] + 0 ignored + 1 0 < tValues[0] < 1 +*/ +int SkFindQuadExtrema(SkScalar a, SkScalar b, SkScalar c, SkScalar tValues[1]); + +/** Given 3 points on a quadratic bezier, chop it into 1, 2 beziers such that + the resulting beziers are monotonic in Y. This is called by the scan converter. + Depending on what is returned, dst[] is treated as follows + 1 dst[0..2] is the original quad + 2 dst[0..2] and dst[2..4] are the two new quads + If dst == null, it is ignored and only the count is returned. +*/ +int SkChopQuadAtYExtrema(const SkPoint src[3], SkPoint dst[5]); + +/** Given 3 points on a quadratic bezier, divide it into 2 quadratics + if the point of maximum curvature exists on the quad segment. + Depending on what is returned, dst[] is treated as follows + 1 dst[0..2] is the original quad + 2 dst[0..2] and dst[2..4] are the two new quads + If dst == null, it is ignored and only the count is returned. +*/ +int SkChopQuadAtMaxCurvature(const SkPoint src[3], SkPoint dst[5]); + +//////////////////////////////////////////////////////////////////////////////////////// + +/** Convert from parametric from (pts) to polynomial coefficients + coeff[0]*T^3 + coeff[1]*T^2 + coeff[2]*T + coeff[3] +*/ +void SkGetCubicCoeff(const SkPoint pts[4], SkScalar cx[4], SkScalar cy[4]); + +/** Set pt to the point on the src cubic specified by t. t must be + 0 <= t <= 1.0 +*/ +void SkEvalCubicAt(const SkPoint src[4], SkScalar t, SkPoint* locOrNull, SkVector* tangentOrNull, SkVector* curvatureOrNull); + +/** Given a src cubic bezier, chop it at the specified t value, + where 0 < t < 1, and return the two new cubics in dst: + dst[0..3] and dst[3..6] +*/ +void SkChopCubicAt(const SkPoint src[4], SkPoint dst[7], SkScalar t); +void SkChopCubicAt(const SkPoint src[4], SkPoint dst[7], const SkScalar t[], int t_count); + +/** Given a src cubic bezier, chop it at the specified t == 1/2, + The new cubics are returned in dst[0..3] and dst[3..6] +*/ +void SkChopCubicAtHalf(const SkPoint src[4], SkPoint dst[7]); + +/** Given the 4 coefficients for a cubic bezier (either X or Y values), look + for extrema, and return the number of t-values that are found that represent + these extrema. If the cubic has no extrema betwee (0..1) exclusive, the + function returns 0. + Returned count tValues[] + 0 ignored + 1 0 < tValues[0] < 1 + 2 0 < tValues[0] < tValues[1] < 1 +*/ +int SkFindCubicExtrema(SkScalar a, SkScalar b, SkScalar c, SkScalar d, SkScalar tValues[2]); + +/** Given 4 points on a cubic bezier, chop it into 1, 2, 3 beziers such that + the resulting beziers are monotonic in Y. This is called by the scan converter. + Depending on what is returned, dst[] is treated as follows + 1 dst[0..3] is the original cubic + 2 dst[0..3] and dst[3..6] are the two new cubics + 3 dst[0..3], dst[3..6], dst[6..9] are the three new cubics + If dst == null, it is ignored and only the count is returned. +*/ +int SkChopCubicAtYExtrema(const SkPoint src[4], SkPoint dst[10]); + +/** Given a cubic bezier, return 0, 1, or 2 t-values that represent the + inflection points. +*/ +int SkFindCubicInflections(const SkPoint src[4], SkScalar tValues[2]); + +/** Return 1 for no chop, or 2 for having chopped the cubic at its + inflection point. +*/ +int SkChopCubicAtInflections(const SkPoint src[4], SkPoint dst[10]); + +int SkFindCubicMaxCurvature(const SkPoint src[4], SkScalar tValues[3]); +int SkChopCubicAtMaxCurvature(const SkPoint src[4], SkPoint dst[13], SkScalar tValues[3] = NULL); + +/////////////////////////////////////////////////////////////////////////////////////////// + +enum SkRotationDirection { + kCW_SkRotationDirection, + kCCW_SkRotationDirection +}; + +/** Maximum number of points needed in the quadPoints[] parameter for + SkBuildQuadArc() +*/ +#define kSkBuildQuadArcStorage 17 + +/** Given 2 unit vectors and a rotation direction, fill out the specified + array of points with quadratic segments. Return is the number of points + written to, which will be { 0, 3, 5, 7, ... kSkBuildQuadArcStorage } + + matrix, if not null, is appled to the points before they are returned. +*/ +int SkBuildQuadArc(const SkVector& unitStart, const SkVector& unitStop, SkRotationDirection, + const SkMatrix* matrix, SkPoint quadPoints[]); + +////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_DEBUG + class SkGeometry { + public: + static void UnitTest(); + }; +#endif + +#endif diff --git a/skia/sgl/SkGlobals.cpp b/skia/sgl/SkGlobals.cpp new file mode 100644 index 0000000..fb1c948 --- /dev/null +++ b/skia/sgl/SkGlobals.cpp @@ -0,0 +1,92 @@ +/* libs/graphics/sgl/SkGlobals.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkGlobals.h" +#include "SkThread.h" + +SkGlobals::Rec::~Rec() +{ +} + +SkGlobals::Rec* SkGlobals::Find(uint32_t tag, Rec* (*create_proc)()) +{ + SkGlobals::BootStrap& bootstrap = SkGlobals::GetBootStrap(); + + Rec* rec = bootstrap.fHead; + while (rec) + { + if (rec->fTag == tag) + return rec; + rec = rec->fNext; + } + + if (create_proc == NULL) // no create proc, just return not found + return NULL; + + // if we get here, we may need to create one. First grab the mutex, and + // search again, creating one if its not found the 2nd time. + + bootstrap.fMutex.acquire(); + + // search again, now that we have the mutex. Odds are it won't be there, but we check again + // just in case it was added by another thread before we grabbed the mutex + + Rec*& head = bootstrap.fHead; + rec = head; + while (rec) + { + if (rec->fTag == tag) + break; + rec = rec->fNext; + } + + if (rec == NULL && (rec = create_proc()) != NULL) + { + rec->fTag = tag; + rec->fNext = head; + bootstrap.fHead = rec; + } + + bootstrap.fMutex.release(); + return rec; +} + +void SkGlobals::Init() +{ +} + +void SkGlobals::Term() +{ + SkGlobals::BootStrap& bootstrap = SkGlobals::GetBootStrap(); + + bootstrap.fMutex.acquire(); + + Rec*& head = bootstrap.fHead; + Rec* rec = head; + + while (rec) + { + Rec* next = rec->fNext; + SkDELETE(rec); + rec = next; + } + + bootstrap.fHead = NULL; + bootstrap.fMutex.release(); +} + + diff --git a/skia/sgl/SkGlyphCache.cpp b/skia/sgl/SkGlyphCache.cpp new file mode 100644 index 0000000..f67223d --- /dev/null +++ b/skia/sgl/SkGlyphCache.cpp @@ -0,0 +1,598 @@ +/* libs/graphics/sgl/SkGlyphCache.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkGlyphCache.h" +#include "SkFontHost.h" +#include "SkPaint.h" +#include "SkTemplates.h" + +#define SPEW_PURGE_STATUS +#define USE_CACHE_HASHxxxxx + +/////////////////////////////////////////////////////////////////////////////// + +#define kMinGlphAlloc (sizeof(SkGlyph) * 64) +#define kMinImageAlloc (24 * 64) // should be pointsize-dependent + +#define METRICS_RESERVE_COUNT 128 // so we don't grow this array a lot + +SkGlyphCache::SkGlyphCache(const SkDescriptor* desc) + : fGlyphAlloc(kMinGlphAlloc), fImageAlloc(kMinImageAlloc) { + fPrev = fNext = NULL; + + fDesc = desc->copy(); + fScalerContext = SkScalerContext::Create(desc); + fScalerContext->getFontMetrics(NULL, &fFontMetricsY); + + // init to 0 so that all of the pointers will be null + memset(fGlyphHash, 0, sizeof(fGlyphHash)); + // init with 0xFF so that the charCode field will be -1, which is invalid + memset(fCharToGlyphHash, 0xFF, sizeof(fCharToGlyphHash)); + + fMemoryUsed = sizeof(*this) + kMinGlphAlloc + kMinImageAlloc; + + fGlyphArray.setReserve(METRICS_RESERVE_COUNT); + + fMetricsCount = 0; + fAdvanceCount = 0; + fAuxProcList = NULL; +} + +SkGlyphCache::~SkGlyphCache() { + SkGlyph** gptr = fGlyphArray.begin(); + SkGlyph** stop = fGlyphArray.end(); + while (gptr < stop) { + SkPath* path = (*gptr)->fPath; + if (path) { + SkDELETE(path); + } + gptr += 1; + } + SkDescriptor::Free(fDesc); + SkDELETE(fScalerContext); + this->invokeAndRemoveAuxProcs(); +} + +/////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_DEBUG + class AutoCheckForNull { + public: + AutoCheckForNull(const SkTDArray<SkGlyph*>& array) : fArray(array) { + for (int i = 0; i < array.count(); i++) + SkASSERT(array[i]); + } + ~AutoCheckForNull() { + const SkTDArray<SkGlyph*>& array = fArray; + for (int i = 0; i < array.count(); i++) { + SkASSERT(array[i]); + } + } + private: + const SkTDArray<SkGlyph*>& fArray; + }; + #define VALIDATE() AutoCheckForNull acfn(fGlyphArray) +#else + #define VALIDATE() +#endif + +const SkGlyph& SkGlyphCache::getUnicharAdvance(SkUnichar charCode) { + VALIDATE(); + uint32_t id = SkGlyph::MakeID(charCode); + CharGlyphRec* rec = &fCharToGlyphHash[ID2HashIndex(id)]; + + if (rec->fID != id) { + // this ID is based on the UniChar + rec->fID = id; + // this ID is based on the glyph index + id = SkGlyph::MakeID(fScalerContext->charToGlyphID(charCode)); + rec->fGlyph = this->lookupMetrics(id, kJustAdvance_MetricsType); + } + return *rec->fGlyph; +} + +const SkGlyph& SkGlyphCache::getGlyphIDAdvance(uint16_t glyphID) { + VALIDATE(); + uint32_t id = SkGlyph::MakeID(glyphID); + unsigned index = ID2HashIndex(id); + SkGlyph* glyph = fGlyphHash[index]; + + if (NULL == glyph || glyph->fID != id) { + glyph = this->lookupMetrics(glyphID, kJustAdvance_MetricsType); + fGlyphHash[index] = glyph; + } + return *glyph; +} + +/////////////////////////////////////////////////////////////////////////////// + +const SkGlyph& SkGlyphCache::getUnicharMetrics(SkUnichar charCode) { + VALIDATE(); + uint32_t id = SkGlyph::MakeID(charCode); + CharGlyphRec* rec = &fCharToGlyphHash[ID2HashIndex(id)]; + + if (rec->fID != id) { + // this ID is based on the UniChar + rec->fID = id; + // this ID is based on the glyph index + id = SkGlyph::MakeID(fScalerContext->charToGlyphID(charCode)); + rec->fGlyph = this->lookupMetrics(id, kFull_MetricsType); + } else { + if (rec->fGlyph->isJustAdvance()) { + fScalerContext->getMetrics(rec->fGlyph); + } + } + SkASSERT(rec->fGlyph->isFullMetrics()); + return *rec->fGlyph; +} + +const SkGlyph& SkGlyphCache::getUnicharMetrics(SkUnichar charCode, + SkFixed x, SkFixed y) { + VALIDATE(); + uint32_t id = SkGlyph::MakeID(charCode, x, y); + CharGlyphRec* rec = &fCharToGlyphHash[ID2HashIndex(id)]; + + if (rec->fID != id) { + // this ID is based on the UniChar + rec->fID = id; + // this ID is based on the glyph index + id = SkGlyph::MakeID(fScalerContext->charToGlyphID(charCode), x, y); + rec->fGlyph = this->lookupMetrics(id, kFull_MetricsType); + } else { + if (rec->fGlyph->isJustAdvance()) { + fScalerContext->getMetrics(rec->fGlyph); + } + } + SkASSERT(rec->fGlyph->isFullMetrics()); + return *rec->fGlyph; +} + +const SkGlyph& SkGlyphCache::getGlyphIDMetrics(uint16_t glyphID) { + VALIDATE(); + uint32_t id = SkGlyph::MakeID(glyphID); + unsigned index = ID2HashIndex(id); + SkGlyph* glyph = fGlyphHash[index]; + + if (NULL == glyph || glyph->fID != id) { + glyph = this->lookupMetrics(glyphID, kFull_MetricsType); + fGlyphHash[index] = glyph; + } else { + if (glyph->isJustAdvance()) { + fScalerContext->getMetrics(glyph); + } + } + SkASSERT(glyph->isFullMetrics()); + return *glyph; +} + +const SkGlyph& SkGlyphCache::getGlyphIDMetrics(uint16_t glyphID, + SkFixed x, SkFixed y) { + VALIDATE(); + uint32_t id = SkGlyph::MakeID(glyphID, x, y); + unsigned index = ID2HashIndex(id); + SkGlyph* glyph = fGlyphHash[index]; + + if (NULL == glyph || glyph->fID != id) { + glyph = this->lookupMetrics(id, kFull_MetricsType); + fGlyphHash[index] = glyph; + } else { + if (glyph->isJustAdvance()) { + fScalerContext->getMetrics(glyph); + } + } + SkASSERT(glyph->isFullMetrics()); + return *glyph; +} + +SkGlyph* SkGlyphCache::lookupMetrics(uint32_t id, MetricsType mtype) { + SkGlyph* glyph; + + int hi = 0; + int count = fGlyphArray.count(); + + if (count) { + SkGlyph** gptr = fGlyphArray.begin(); + int lo = 0; + + hi = count - 1; + while (lo < hi) { + int mid = (hi + lo) >> 1; + if (gptr[mid]->fID < id) { + lo = mid + 1; + } else { + hi = mid; + } + } + glyph = gptr[hi]; + if (glyph->fID == id) { + if (kFull_MetricsType == mtype && glyph->isJustAdvance()) { + fScalerContext->getMetrics(glyph); + } + return glyph; + } + + // check if we need to bump hi before falling though to the allocator + if (glyph->fID < id) { + hi += 1; + } + } + + // not found, but hi tells us where to inser the new glyph + fMemoryUsed += sizeof(SkGlyph); + + glyph = (SkGlyph*)fGlyphAlloc.alloc(sizeof(SkGlyph), + SkChunkAlloc::kThrow_AllocFailType); + glyph->fID = id; + glyph->fImage = NULL; + glyph->fPath = NULL; + *fGlyphArray.insert(hi) = glyph; + + if (kJustAdvance_MetricsType == mtype) { + fScalerContext->getAdvance(glyph); + fAdvanceCount += 1; + } else { + SkASSERT(kFull_MetricsType == mtype); + fScalerContext->getMetrics(glyph); + fMetricsCount += 1; + } + + return glyph; +} + +const void* SkGlyphCache::findImage(const SkGlyph& glyph) { + if (glyph.fWidth) { + if (glyph.fImage == NULL) { + size_t size = glyph.computeImageSize(); + const_cast<SkGlyph&>(glyph).fImage = fImageAlloc.alloc(size, + SkChunkAlloc::kReturnNil_AllocFailType); + fScalerContext->getImage(glyph); + fMemoryUsed += size; + } + } + return glyph.fImage; +} + +const SkPath* SkGlyphCache::findPath(const SkGlyph& glyph) { + if (glyph.fWidth) { + if (glyph.fPath == NULL) { + const_cast<SkGlyph&>(glyph).fPath = SkNEW(SkPath); + fScalerContext->getPath(glyph, glyph.fPath); + fMemoryUsed += sizeof(SkPath) + + glyph.fPath->getPoints(NULL, 0x7FFFFFFF) * sizeof(SkPoint); + } + } + return glyph.fPath; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool SkGlyphCache::getAuxProcData(void (*proc)(void*), void** dataPtr) const { + const AuxProcRec* rec = fAuxProcList; + while (rec) { + if (rec->fProc == proc) { + if (dataPtr) { + *dataPtr = rec->fData; + } + return true; + } + rec = rec->fNext; + } + return false; +} + +void SkGlyphCache::setAuxProc(void (*proc)(void*), void* data) { + if (proc == NULL) { + return; + } + + AuxProcRec* rec = fAuxProcList; + while (rec) { + if (rec->fProc == proc) { + rec->fData = data; + return; + } + rec = rec->fNext; + } + // not found, create a new rec + rec = SkNEW(AuxProcRec); + rec->fProc = proc; + rec->fData = data; + rec->fNext = fAuxProcList; + fAuxProcList = rec; +} + +void SkGlyphCache::removeAuxProc(void (*proc)(void*)) { + AuxProcRec* rec = fAuxProcList; + AuxProcRec* prev = NULL; + while (rec) { + AuxProcRec* next = rec->fNext; + if (rec->fProc == proc) { + if (prev) { + prev->fNext = next; + } else { + fAuxProcList = next; + } + SkDELETE(rec); + return; + } + prev = rec; + rec = next; + } +} + +void SkGlyphCache::invokeAndRemoveAuxProcs() { + AuxProcRec* rec = fAuxProcList; + while (rec) { + rec->fProc(rec->fData); + AuxProcRec* next = rec->fNext; + SkDELETE(rec); + rec = next; + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#include "SkGlobals.h" +#include "SkThread.h" + +#define SkGlyphCache_GlobalsTag SkSetFourByteTag('g', 'l', 'f', 'c') + +#ifdef USE_CACHE_HASH + #define HASH_BITCOUNT 6 + #define HASH_COUNT (1 << HASH_BITCOUNT) + #define HASH_MASK (HASH_COUNT - 1) + + static unsigned desc_to_hashindex(const SkDescriptor* desc) + { + SkASSERT(HASH_MASK < 256); // since our munging reduces to 8 bits + + uint32_t n = *(const uint32_t*)desc; //desc->getChecksum(); + SkASSERT(n == desc->getChecksum()); + + // don't trust that the low bits of checksum vary enough, so... + n ^= (n >> 24) ^ (n >> 16) ^ (n >> 8) ^ (n >> 30); + + return n & HASH_MASK; + } +#endif + +class SkGlyphCache_Globals : public SkGlobals::Rec { +public: + SkMutex fMutex; + SkGlyphCache* fHead; + size_t fTotalMemoryUsed; +#ifdef USE_CACHE_HASH + SkGlyphCache* fHash[HASH_COUNT]; +#endif + +#ifdef SK_DEBUG + void validate() const; +#else + void validate() const {} +#endif +}; + +#ifdef SK_USE_RUNTIME_GLOBALS + static SkGlobals::Rec* create_globals() { + SkGlyphCache_Globals* rec = SkNEW(SkGlyphCache_Globals); + rec->fHead = NULL; + rec->fTotalMemoryUsed = 0; +#ifdef USE_CACHE_HASH + memset(rec->fHash, 0, sizeof(rec->fHash)); +#endif + return rec; + } + + #define FIND_GC_GLOBALS() *(SkGlyphCache_Globals*)SkGlobals::Find(SkGlyphCache_GlobalsTag, create_globals) + #define GET_GC_GLOBALS() *(SkGlyphCache_Globals*)SkGlobals::Get(SkGlyphCache_GlobalsTag) +#else + static SkGlyphCache_Globals gGCGlobals; + #define FIND_GC_GLOBALS() gGCGlobals + #define GET_GC_GLOBALS() gGCGlobals +#endif + +/* This guy calls the visitor from within the mutext lock, so the visitor + cannot: + - take too much time + - try to acquire the mutext again + - call a fontscaler (which might call into the cache) +*/ +SkGlyphCache* SkGlyphCache::VisitCache(const SkDescriptor* desc, + bool (*proc)(const SkGlyphCache*, void*), + void* context) { + SkASSERT(desc); + + SkGlyphCache_Globals& globals = FIND_GC_GLOBALS(); + SkAutoMutexAcquire ac(globals.fMutex); + SkGlyphCache* cache; + bool insideMutex = true; + + globals.validate(); + +#ifdef USE_CACHE_HASH + SkGlyphCache** hash = globals.fHash; + unsigned index = desc_to_hashindex(desc); + cache = hash[index]; + if (cache && *cache->fDesc == *desc) { + cache->detach(&globals.fHead); + goto FOUND_IT; + } +#endif + + cache = globals.fHead; + for (cache = globals.fHead; cache != NULL; cache = cache->fNext) { + if (cache->fDesc->equals(*desc)) { + cache->detach(&globals.fHead); + goto FOUND_IT; + } + } + + /* Release the mutex now, before we create a new entry (which might have + side-effects like trying to access the cache/mutex (yikes!) + */ + ac.release(); // release the mutex now + insideMutex = false; // can't use globals anymore + + cache = SkNEW_ARGS(SkGlyphCache, (desc)); + +FOUND_IT: + if (proc(cache, context)) { // stay detached + if (insideMutex) { + SkASSERT(globals.fTotalMemoryUsed >= cache->fMemoryUsed); + globals.fTotalMemoryUsed -= cache->fMemoryUsed; +#ifdef USE_CACHE_HASH + hash[index] = NULL; +#endif + } + } else { // reattach + if (insideMutex) { + cache->attachToHead(&globals.fHead); +#ifdef USE_CACHE_HASH + hash[index] = cache; +#endif + } else { + AttachCache(cache); + } + cache = NULL; + } + return cache; +} + +void SkGlyphCache::AttachCache(SkGlyphCache* cache) { + SkASSERT(cache); + SkASSERT(cache->fNext == NULL); + + SkGlyphCache_Globals& globals = GET_GC_GLOBALS(); + SkAutoMutexAcquire ac(globals.fMutex); + + globals.validate(); + + // if we have a fixed budget for our cache, do a purge here + { + size_t allocated = globals.fTotalMemoryUsed + cache->fMemoryUsed; + size_t amountToFree = SkFontHost::ShouldPurgeFontCache(allocated); + if (amountToFree) + (void)InternalFreeCache(&globals, amountToFree); + } + + cache->attachToHead(&globals.fHead); + globals.fTotalMemoryUsed += cache->fMemoryUsed; + +#ifdef USE_CACHE_HASH + unsigned index = desc_to_hashindex(cache->fDesc); + SkASSERT(globals.fHash[index] != cache); + globals.fHash[index] = cache; +#endif + + globals.validate(); +} + +size_t SkGlyphCache::GetCacheUsed() { + SkGlyphCache_Globals& globals = FIND_GC_GLOBALS(); + SkAutoMutexAcquire ac(globals.fMutex); + + return SkGlyphCache::ComputeMemoryUsed(globals.fHead); +} + +bool SkGlyphCache::SetCacheUsed(size_t bytesUsed) { + size_t curr = SkGlyphCache::GetCacheUsed(); + + if (curr > bytesUsed) { + SkGlyphCache_Globals& globals = FIND_GC_GLOBALS(); + SkAutoMutexAcquire ac(globals.fMutex); + + return InternalFreeCache(&globals, curr - bytesUsed) > 0; + } + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +SkGlyphCache* SkGlyphCache::FindTail(SkGlyphCache* cache) { + if (cache) { + while (cache->fNext) { + cache = cache->fNext; + } + } + return cache; +} + +size_t SkGlyphCache::ComputeMemoryUsed(const SkGlyphCache* head) { + size_t size = 0; + + while (head != NULL) { + size += head->fMemoryUsed; + head = head->fNext; + } + return size; +} + +#ifdef SK_DEBUG +void SkGlyphCache_Globals::validate() const { + size_t computed = SkGlyphCache::ComputeMemoryUsed(fHead); + if (fTotalMemoryUsed != computed) { + printf("total %d, computed %d\n", (int)fTotalMemoryUsed, (int)computed); + } + SkASSERT(fTotalMemoryUsed == computed); +} +#endif + +size_t SkGlyphCache::InternalFreeCache(SkGlyphCache_Globals* globals, + size_t bytesNeeded) { + globals->validate(); + + size_t bytesFreed = 0; + int count = 0; + + // don't do any "small" purges + size_t minToPurge = globals->fTotalMemoryUsed >> 2; + if (bytesNeeded < minToPurge) + bytesNeeded = minToPurge; + + SkGlyphCache* cache = FindTail(globals->fHead); + while (cache != NULL && bytesFreed < bytesNeeded) { + SkGlyphCache* prev = cache->fPrev; + bytesFreed += cache->fMemoryUsed; + +#ifdef USE_CACHE_HASH + unsigned index = desc_to_hashindex(cache->fDesc); + if (cache == globals->fHash[index]) { + globals->fHash[index] = NULL; + } +#endif + + cache->detach(&globals->fHead); + SkDELETE(cache); + cache = prev; + count += 1; + } + + SkASSERT(bytesFreed <= globals->fTotalMemoryUsed); + globals->fTotalMemoryUsed -= bytesFreed; + globals->validate(); + +#ifdef SPEW_PURGE_STATUS + if (count) { + SkDebugf("purging %dK from font cache [%d entries]\n", + (int)(bytesFreed >> 10), count); + } +#endif + + return bytesFreed; +} + diff --git a/skia/sgl/SkGlyphCache.h b/skia/sgl/SkGlyphCache.h new file mode 100644 index 0000000..6aef173 --- /dev/null +++ b/skia/sgl/SkGlyphCache.h @@ -0,0 +1,262 @@ +/* libs/graphics/sgl/SkGlyphCache.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkGlyphCache_DEFINED +#define SkGlyphCache_DEFINED + +#include "SkBitmap.h" +#include "SkChunkAlloc.h" +#include "SkDescriptor.h" +#include "SkScalerContext.h" +#include "SkTemplates.h" + +class SkPaint; + +class SkGlyphCache_Globals; + +/** \class SkGlyphCache + + This class represents a strike: a specific combination of typeface, size, + matrix, etc., and holds the glyphs for that strike. Calling any of the + getUnichar.../getGlyphID... methods will return the requested glyph, + either instantly if it is already cahced, or by first generating it and then + adding it to the strike. + + The strikes are held in a global list, available to all threads. To interact + with one, call either VisitCache() or DetachCache(). +*/ +class SkGlyphCache { +public: + /** Returns a glyph with valid fAdvance and fDevKern fields. + The remaining fields may be valid, but that is not guaranteed. If you + require those, call getUnicharMetrics or getGlyphIDMetrics instead. + */ + const SkGlyph& getUnicharAdvance(SkUnichar); + const SkGlyph& getGlyphIDAdvance(uint16_t); + + /** Returns a glyph with all fields valid except fImage and fPath, which + may be null. If they are null, call findImage or findPath for those. + If they are not null, then they are valid. + + This call is potentially slower than the matching ...Advance call. If + you only need the fAdvance/fDevKern fields, call those instead. + */ + const SkGlyph& getUnicharMetrics(SkUnichar); + const SkGlyph& getGlyphIDMetrics(uint16_t); + + /** These are variants that take the device position of the glyph. Call + these only if you are drawing in subpixel mode. Passing 0, 0 is + effectively the same as calling the variants w/o the extra params, tho + a tiny bit slower. + */ + const SkGlyph& getUnicharMetrics(SkUnichar, SkFixed x, SkFixed y); + const SkGlyph& getGlyphIDMetrics(uint16_t, SkFixed x, SkFixed y); + + /** Return the image associated with the glyph. If it has not been generated + this will trigger that. + */ + const void* findImage(const SkGlyph&); + /** Return the Path associated with the glyph. If it has not been generated + this will trigger that. + */ + const SkPath* findPath(const SkGlyph&); + + /** Return the vertical metrics for this strike. + */ + const SkPaint::FontMetrics& getFontMetricsY() const { + return fFontMetricsY; + } + + /* AuxProc/Data allow a client to associate data with this cache entry. + Multiple clients can use this, as their data is keyed with a function + pointer. In addition to serving as a key, the function pointer is called + with the data when the glyphcache object is deleted, so the client can + cleanup their data as well. NOTE: the auxProc must not try to access + this glyphcache in any way, since it may be in the process of being + deleted. + */ + + //! If the proc is found, return true and set *dataPtr to its data + bool getAuxProcData(void (*auxProc)(void*), void** dataPtr) const; + //! Add a proc/data pair to the glyphcache. proc should be non-null + void setAuxProc(void (*auxProc)(void*), void* auxData); + //! If found, remove the proc/data pair from the glyphcache (does not + // call the proc) + void removeAuxProc(void (*auxProc)(void*)); + + /** Find a matching cache entry, and call proc() with it. If none is found + create a new one. If the proc() returns true, detach the cache and + return it, otherwise leave it and return NULL. + */ + static SkGlyphCache* VisitCache(const SkDescriptor* desc, + bool (*proc)(const SkGlyphCache*, void*), + void* context); + + /** Given a strike that was returned by either VisitCache() or DetachCache() + add it back into the global cache list (after which the caller should + not reference it anymore. + */ + static void AttachCache(SkGlyphCache*); + + /** Detach a strike from the global cache matching the specified descriptor. + Once detached, it can be queried/modified by the current thread, and + when finished, be reattached to the global cache with AttachCache(). + While detached, if another request is made with the same descriptor, + a different strike will be generated. This is fine. It does mean we + can have more than 1 strike for the same descriptor, but that will + eventually get purged, and the win is that different thread will never + block each other while a strike is being used. + */ + static SkGlyphCache* DetachCache(const SkDescriptor* desc) { + return VisitCache(desc, DetachProc, NULL); + } + + /** Return the approximate number of bytes used by the font cache + */ + static size_t GetCacheUsed(); + + /** This can be called to purge old font data, in an attempt to free + enough bytes such that the font cache is not using more than the + specified number of bytes. It is thread-safe, and may be called at + any time. + Return true if some amount of the cache was purged. + */ + static bool SetCacheUsed(size_t bytesUsed); + +private: + SkGlyphCache(const SkDescriptor*); + ~SkGlyphCache(); + + enum MetricsType { + kJustAdvance_MetricsType, + kFull_MetricsType + }; + + SkGlyph* lookupMetrics(uint32_t id, MetricsType); + static bool DetachProc(const SkGlyphCache*, void*) { return true; } + + void detach(SkGlyphCache** head) { + if (fPrev) { + fPrev->fNext = fNext; + } else { + *head = fNext; + } + if (fNext) { + fNext->fPrev = fPrev; + } + fPrev = fNext = NULL; + } + + void attachToHead(SkGlyphCache** head) { + SkASSERT(NULL == fPrev && NULL == fNext); + if (*head) { + (*head)->fPrev = this; + fNext = *head; + } + *head = this; + } + + SkGlyphCache* fNext, *fPrev; + SkDescriptor* fDesc; + SkScalerContext* fScalerContext; + SkPaint::FontMetrics fFontMetricsY; + + enum { + kHashBits = 6, + kHashCount = 1 << kHashBits, + kHashMask = kHashCount - 1 + }; + SkGlyph* fGlyphHash[kHashCount]; + SkTDArray<SkGlyph*> fGlyphArray; + SkChunkAlloc fGlyphAlloc; + SkChunkAlloc fImageAlloc; + + int fMetricsCount, fAdvanceCount; + + struct CharGlyphRec { + uint32_t fID; // unichar + subpixel + SkGlyph* fGlyph; + }; + // no reason to use the same kHashCount as fGlyphHash, but we do for now + CharGlyphRec fCharToGlyphHash[kHashCount]; + + enum { + // shift so that the top bits fall into kHashBits region + kShiftForHashIndex = SkGlyph::kSubShift + + SkGlyph::kSubBits*2 - + kHashBits + }; + + static inline unsigned ID2HashIndex(uint32_t id) { + return (id ^ (id >> kShiftForHashIndex)) & kHashMask; + } + + // used to track (approx) how much ram is tied-up in this cache + size_t fMemoryUsed; + + struct AuxProcRec { + AuxProcRec* fNext; + void (*fProc)(void*); + void* fData; + }; + AuxProcRec* fAuxProcList; + void invokeAndRemoveAuxProcs(); + + // This relies on the caller to have already acquired the mutex to access the global cache + static size_t InternalFreeCache(SkGlyphCache_Globals*, size_t bytesNeeded); + + inline static SkGlyphCache* FindTail(SkGlyphCache* head); + static size_t ComputeMemoryUsed(const SkGlyphCache* head); + + friend class SkGlyphCache_Globals; +}; + +class SkAutoGlyphCache { +public: + SkAutoGlyphCache(SkGlyphCache* cache) : fCache(cache) {} + SkAutoGlyphCache(const SkDescriptor* desc) + { + fCache = SkGlyphCache::DetachCache(desc); + } + SkAutoGlyphCache(const SkPaint& paint, const SkMatrix* matrix) + { + fCache = paint.detachCache(matrix); + } + ~SkAutoGlyphCache() + { + if (fCache) + SkGlyphCache::AttachCache(fCache); + } + + SkGlyphCache* getCache() const { return fCache; } + + void release() + { + if (fCache) + { + SkGlyphCache::AttachCache(fCache); + fCache = NULL; + } + } +private: + SkGlyphCache* fCache; + + static bool DetachProc(const SkGlyphCache*, void*); +}; + +#endif + diff --git a/skia/sgl/SkGraphics.cpp b/skia/sgl/SkGraphics.cpp new file mode 100644 index 0000000..1bb3cb3 --- /dev/null +++ b/skia/sgl/SkGraphics.cpp @@ -0,0 +1,404 @@ +/* libs/graphics/sgl/SkGraphics.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkGraphics.h" + +#include "Sk64.h" +#include "SkBlitter.h" +#include "SkCanvas.h" +#include "SkDOM.h" +#include "SkFloat.h" +#include "SkGeometry.h" +#include "SkGlobals.h" +#include "SkMath.h" +#include "SkMatrix.h" +#include "SkPath.h" +#include "SkPathEffect.h" +#include "SkPathMeasure.h" +#include "SkRandom.h" +#include "SkRefCnt.h" +#include "SkScalerContext.h" +#include "SkShader.h" +#include "SkStream.h" +#include "SkTSearch.h" +#include "SkTime.h" +#include "SkUtils.h" +#include "SkXfermode.h" + +#if 0 + +#define SK_SORT_TEMPLATE_TYPE int +#define SK_SORT_TEMPLATE_NAME sort_int +#define SK_SORT_TEMPLATE_CMP(a, b) ((a) - (b)) +#include "SkSortTemplate.h" + +#define SK_SORT_TEMPLATE_TYPE int* +#define SK_SORT_TEMPLATE_NAME sort_intptr +#define SK_SORT_TEMPLATE_CMP(a, b) (*(a) - *(b)) +#include "SkSortTemplate.h" + +static void test_sort() +{ + int array[] = { 4, 3, 7, 5, 2, 5, 1, 2, 9, 6, 7, 4, 5, 3, 1, 0 }; + int* ptr[SK_ARRAY_COUNT(array)]; + int i, N = SK_ARRAY_COUNT(array) - 1; + + for (i = 0; i < N; i++) + printf(" %d", array[i]); + printf("\n"); + + for (i = 0; i < N; i++) + ptr[i] = &array[i]; + sort_intptr(ptr, N); + for (i = 0; i < N; i++) + printf(" %d", *ptr[i]); + printf("\n"); + + sort_int(array, N); + for (i = 0; i < N; i++) + printf(" %d", array[i]); + printf("\n"); + +} +#endif + +#define SPEED_TESTx + +#define typesizeline(type) { #type , sizeof(type) } +#define unittestline(type) { #type , type::UnitTest } + + +#ifdef BUILD_EMBOSS_TABLE + extern void SkEmbossMask_BuildTable(); +#endif + +#ifdef BUILD_RADIALGRADIENT_TABLE + extern void SkRadialGradient_BuildTable(); +#endif + +#define BIG_LOOP_COUNT 1000000 +#define TEXT_LOOP_COUNT 1000 + +#ifdef SPEED_TEST +static int test_s64(int i) +{ + Sk64 a, b, c; + + c.set(0); + a.set(i); + b.setMul(i, i); + a.add(b); + a.add(c); + return c.getFixed(); +} + +static int test_native_64(int i) +{ + int16_t a, b, c; + + c = 0; + a = i; + b = (int64_t)i * i; + a += b; + a += c; + return (int)(c >> 16); +} + +static void test_drawText(SkBitmap::Config config, SkColor color) +{ + SkBitmap bm; + + bm.setConfig(config, 320, 240); + bm.allocPixels(); + + SkCanvas canvas(bm); + SkPaint paint; + + paint.setAntiAlias(true); + paint.setTextSize(SkIntToScalar(12)); + paint.setColor(color); + + SkScalar x = SkIntToScalar(20); + SkScalar y = SkIntToScalar(100); + const char* text = "Hamburgefons"; + size_t len = strlen(text); + + // draw once to populate the cache + canvas.drawText(text, len, x, y, paint); + + SkMSec now = SkTime::GetMSecs(); + for (int i = 0; i < TEXT_LOOP_COUNT; i++) + canvas.drawText(text, len, x, y, paint); + printf("----------- Config: %d, Color=%x, CPS = %g\n", config, color, + len * TEXT_LOOP_COUNT * 1000.0 / (SkTime::GetMSecs() - now)); +} + +#endif + +void SkGraphics::Init(bool runUnitTests) +{ + SkGlobals::Init(); + +#ifdef BUILD_EMBOSS_TABLE + SkEmbossMask_BuildTable(); +#endif +#ifdef BUILD_RADIALGRADIENT_TABLE + SkRadialGradient_BuildTable(); +#endif + +#ifdef SK_SUPPORT_UNITTEST + if (runUnitTests == false) + return; + int i; + + static const struct { + const char* fTypeName; + size_t fSizeOf; + } gTypeSize[] = { + typesizeline(char), + typesizeline(short), + typesizeline(int), + typesizeline(long), + typesizeline(size_t), + typesizeline(void*), + + typesizeline(S8CPU), + typesizeline(U8CPU), + typesizeline(S16CPU), + typesizeline(U16CPU), + + typesizeline(SkPoint), + typesizeline(SkRect), + typesizeline(SkMatrix), + typesizeline(SkPath), + typesizeline(SkGlyph), + typesizeline(SkRefCnt), + + typesizeline(SkPaint), + typesizeline(SkCanvas), + typesizeline(SkBlitter), + typesizeline(SkShader), + typesizeline(SkXfermode), + typesizeline(SkPathEffect) + }; + +#ifdef SK_CPU_BENDIAN + SkDebugf("SkGraphics: big-endian\n"); +#else + SkDebugf("SkGraphics: little-endian\n"); +#endif + + { + char test = 0xFF; + int itest = test; // promote to int, see if it sign-extended + if (itest < 0) + SkDebugf("SkGraphics: char is signed\n"); + else + SkDebugf("SkGraphics: char is unsigned\n"); + } + for (i = 0; i < (int)SK_ARRAY_COUNT(gTypeSize); i++) + SkDebugf("SkGraphics: sizeof(%s) = %d\n", gTypeSize[i].fTypeName, gTypeSize[i].fSizeOf); + + static const struct { + const char* fTypeName; + void (*fUnitTest)(); + } gUnitTests[] = { + unittestline(Sk64), + unittestline(SkMath), + unittestline(SkUtils), + unittestline(SkString), + unittestline(SkFloat), + unittestline(SkMatrix), + unittestline(SkGeometry), + unittestline(SkPath), + unittestline(SkPathMeasure) + }; + + for (i = 0; i < (int)SK_ARRAY_COUNT(gUnitTests); i++) + { + SkDebugf("SkGraphics: Running UnitTest for %s\n", gUnitTests[i].fTypeName); + gUnitTests[i].fUnitTest(); + SkDebugf("SkGraphics: End UnitTest for %s\n", gUnitTests[i].fTypeName); + } + SkQSort_UnitTest(); + +#endif + + if (false) // test asm fixmul + { + int j; + SkMSec now = SkTime::GetMSecs(); + for (j = 0; j < BIG_LOOP_COUNT; j++) { + (void)SkFixedMul_portable(0x8000, 0x150000); + } + SkMSec now2 = SkTime::GetMSecs(); + printf("-------- SkFixedMul_portable = %d\n", now2 - now); + + for (j = 0; j < BIG_LOOP_COUNT; j++) { + (void)SkFixedMul(0x8000, 0x150000); + } + printf("-------- SkFixedMul = %d\n", SkTime::GetMSecs() - now2); + + SkRandom rand; + for (j = 0; j < 10000; j++) { + SkFixed a = rand.nextS() >> 8; + SkFixed b = rand.nextS() >> 8; + SkFixed c1 = SkFixedMul_portable(a, b); + SkFixed c2 = SkFixedMul(a, b); + if (SkAbs32(c1 - c2) > 1) + printf("------ FixMul disagreement: (%x %x) slow=%x fast=%x\n", a, b, c1, c2); + } + } + + if (false) // test asm fractmul + { + int j; + SkMSec now = SkTime::GetMSecs(); + for (j = 0; j < BIG_LOOP_COUNT; j++) { + (void)SkFractMul_portable(0x800000, 0x1500000); + } + SkMSec now2 = SkTime::GetMSecs(); + printf("-------- SkFractMul_portable = %d\n", now2 - now); + + for (j = 0; j < BIG_LOOP_COUNT; j++) { + (void)SkFractMul(0x800000, 0x1500000); + } + printf("-------- SkFractMul = %d\n", SkTime::GetMSecs() - now2); + + SkRandom rand; + for (j = 0; j < 10000; j++) { + SkFixed a = rand.nextS() >> 1; + SkFixed b = rand.nextS() >> 1; + SkFixed c1 = SkFractMul_portable(a, b); + SkFixed c2 = SkFractMul(a, b); + if (SkAbs32(c1 - c2) > 1) + printf("------ FractMul disagreement: (%x %x) slow=%x fast=%x\n", a, b, c1, c2); + } + } + + if (false) // test asm clz + { + int j; + SkMSec now = SkTime::GetMSecs(); + for (j = 0; j < BIG_LOOP_COUNT; j++) { + (void)SkCLZ_portable(now); + } + SkMSec now2 = SkTime::GetMSecs(); + printf("-------- SkCLZ_portable = %d\n", now2 - now); + + for (j = 0; j < BIG_LOOP_COUNT; j++) { + (void)SkCLZ(now); + } + printf("-------- SkCLZ = %d\n", SkTime::GetMSecs() - now2); + + SkRandom rand; + for (j = 0; j < 10000; j++) { + uint32_t a = rand.nextU(); + int c1 = SkCLZ_portable(a); + int c2 = SkCLZ(a); + if (c1 != c2) + printf("------ CLZ disagreement: (%x) slow=%x fast=%x\n", a, c1, c2); + } + } + +#ifdef SPEED_TEST + if (false) { + int i; + int (*proc)(int); + + static const struct { + int (*proc)(int); + const char* name; + } gList[] = { + { test_s64, "Sk64" }, + { test_native_64, "native" } + }; + + for (size_t j = 0; j < SK_ARRAY_COUNT(gList); j++) { + SkMSec now = SkTime::GetMSecs(); + proc = gList[j].proc; + for (i = 0; i < BIG_LOOP_COUNT; i++) { + proc(i); + } + printf("-------- %s = %d\n", gList[j].name, SkTime::GetMSecs() - now); + } + } +#endif + + if (false) { + size_t i, size = 480; + char* buffer = (char*)sk_malloc_throw(size); + uint16_t* buffer16 = (uint16_t*)buffer; + uint32_t* buffer32 = (uint32_t*)buffer; + + SkMSec now = SkTime::GetMSecs(); + for (i = 0; i < 100000; i++) { + sk_memset16(buffer16, (uint16_t)i, size >> 1); + } + SkMSec now2 = SkTime::GetMSecs(); + for (i = 0; i < 100000; i++) { + sk_memset16_portable(buffer16, (uint16_t)i, size >> 1); + } + SkMSec now3 = SkTime::GetMSecs(); + printf("----------- memset16: native %d, portable %d\n", now2 - now, now3 - now2); + + now = SkTime::GetMSecs(); + for (i = 0; i < 100000; i++) { + sk_memset32(buffer32, i, size >> 2); + } + now2 = SkTime::GetMSecs(); + for (i = 0; i < 100000; i++) { + sk_memset32_portable(buffer32, i, size >> 2); + } + now3 = SkTime::GetMSecs(); + printf("----------- memset32: native %d, portable %d\n", now2 - now, now3 - now2); + + sk_free(buffer); + } + +#ifdef SPEED_TEST + if (false) { + test_drawText(SkBitmap::kARGB_8888_Config, SK_ColorBLACK); + test_drawText(SkBitmap::kARGB_8888_Config, SK_ColorRED); + test_drawText(SkBitmap::kRGB_565_Config, SK_ColorBLACK); + test_drawText(SkBitmap::kRGB_565_Config, SK_ColorRED); + } +#endif + +// if (true) { +// test_sort(); +// } +} + +//////////////////////////////////////////////////////////////////////////// + +#include "SkGlyphCache.h" +#include "SkImageDecoder.h" + +void SkGraphics::Term() { + SkGraphics::SetFontCacheUsed(0); + SkGlobals::Term(); +} + +size_t SkGraphics::GetFontCacheUsed() { + return SkGlyphCache::GetCacheUsed(); +} + +bool SkGraphics::SetFontCacheUsed(size_t usageInBytes) { + return SkGlyphCache::SetCacheUsed(usageInBytes); +} + diff --git a/skia/sgl/SkMask.cpp b/skia/sgl/SkMask.cpp new file mode 100644 index 0000000..edc3b0a --- /dev/null +++ b/skia/sgl/SkMask.cpp @@ -0,0 +1,49 @@ +/* libs/graphics/sgl/SkMask.cpp +** +** Copyright 2007, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkMask.h" + +size_t SkMask::computeImageSize() const +{ + return fBounds.height() * fRowBytes; +} + +size_t SkMask::computeTotalImageSize() const +{ + size_t size = this->computeImageSize(); + + if (fFormat == SkMask::k3D_Format) + size *= 3; + return size; +} + +/** We explicitly use this allocator for SkBimap pixels, so that we can + freely assign memory allocated by one class to the other. +*/ +uint8_t* SkMask::AllocImage(size_t size) +{ + return (uint8_t*)sk_malloc_throw(SkAlign4(size)); +} + +/** We explicitly use this allocator for SkBimap pixels, so that we can + freely assign memory allocated by one class to the other. +*/ +void SkMask::FreeImage(void* image) +{ + sk_free(image); +} + diff --git a/skia/sgl/SkMaskFilter.cpp b/skia/sgl/SkMaskFilter.cpp new file mode 100644 index 0000000..9040cfd --- /dev/null +++ b/skia/sgl/SkMaskFilter.cpp @@ -0,0 +1,62 @@ +/* libs/graphics/sgl/SkMaskFilter.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkMaskFilter.h" +#include "SkBlitter.h" +#include "SkBounder.h" +#include "SkBuffer.h" +#include "SkDraw.h" +#include "SkRegion.h" + +bool SkMaskFilter::filterMask(SkMask*, const SkMask&, const SkMatrix&, SkIPoint*) +{ + return false; +} + +bool SkMaskFilter::filterPath(const SkPath& devPath, const SkMatrix& matrix, + const SkRegion& clip, SkBounder* bounder, + SkBlitter* blitter) +{ + SkMask srcM, dstM; + + if (!SkDraw::DrawToMask(devPath, &clip.getBounds(), this, &matrix, &srcM, + SkMask::kComputeBoundsAndRenderImage_CreateMode)) + { + return false; + } + + SkAutoMaskImage autoSrc(&srcM, false); + + if (!this->filterMask(&dstM, srcM, matrix, NULL)) + return false; + + SkAutoMaskImage autoDst(&dstM, false); + SkRegion::Cliperator clipper(clip, dstM.fBounds); + + if (!clipper.done() && (bounder == NULL || bounder->doIRect(dstM.fBounds))) + { + const SkIRect& cr = clipper.rect(); + do { + blitter->blitMask(dstM, cr); + clipper.next(); + } while (!clipper.done()); + } + + return true; +} + + diff --git a/skia/sgl/SkPackBits.cpp b/skia/sgl/SkPackBits.cpp new file mode 100644 index 0000000..3e92841 --- /dev/null +++ b/skia/sgl/SkPackBits.cpp @@ -0,0 +1,407 @@ +#include "SkPackBits.h" + +#define GATHER_STATSx + +static inline void small_memcpy(void* SK_RESTRICT dst, + const void* SK_RESTRICT src, int n) { + SkASSERT(n > 0 && n <= 15); + uint8_t* d = (uint8_t*)dst; + const uint8_t* s = (const uint8_t*)src; + switch (n) { + case 15: *d++ = *s++; + case 14: *d++ = *s++; + case 13: *d++ = *s++; + case 12: *d++ = *s++; + case 11: *d++ = *s++; + case 10: *d++ = *s++; + case 9: *d++ = *s++; + case 8: *d++ = *s++; + case 7: *d++ = *s++; + case 6: *d++ = *s++; + case 5: *d++ = *s++; + case 4: *d++ = *s++; + case 3: *d++ = *s++; + case 2: *d++ = *s++; + case 1: *d++ = *s++; + case 0: break; + } +} + +static inline void small_memset(void* dst, uint8_t value, int n) { + SkASSERT(n > 0 && n <= 15); + uint8_t* d = (uint8_t*)dst; + switch (n) { + case 15: *d++ = value; + case 14: *d++ = value; + case 13: *d++ = value; + case 12: *d++ = value; + case 11: *d++ = value; + case 10: *d++ = value; + case 9: *d++ = value; + case 8: *d++ = value; + case 7: *d++ = value; + case 6: *d++ = value; + case 5: *d++ = value; + case 4: *d++ = value; + case 3: *d++ = value; + case 2: *d++ = value; + case 1: *d++ = value; + case 0: break; + } +} + +// can we do better for small counts with our own inlined memcpy/memset? + +#define PB_MEMSET(addr, value, count) \ +do { \ +if ((count) > 15) { \ +memset(addr, value, count); \ +} else { \ +small_memset(addr, value, count); \ +} \ +} while (0) + +#define PB_MEMCPY(dst, src, count) \ +do { \ + if ((count) > 15) { \ + memcpy(dst, src, count); \ + } else { \ + small_memcpy(dst, src, count); \ + } \ +} while (0) + +/////////////////////////////////////////////////////////////////////////////// + +#ifdef GATHER_STATS + static int gMemSetBuckets[129]; + static int gMemCpyBuckets[129]; + static int gCounter; + +static void register_memset_count(int n) { + SkASSERT((unsigned)n <= 128); + gMemSetBuckets[n] += 1; + gCounter += 1; + + if ((gCounter & 0xFF) == 0) { + SkDebugf("----- packbits memset stats: "); + for (size_t i = 0; i < SK_ARRAY_COUNT(gMemSetBuckets); i++) { + if (gMemSetBuckets[i]) { + SkDebugf(" %d:%d", i, gMemSetBuckets[i]); + } + } + } +} +static void register_memcpy_count(int n) { + SkASSERT((unsigned)n <= 128); + gMemCpyBuckets[n] += 1; + gCounter += 1; + + if ((gCounter & 0x1FF) == 0) { + SkDebugf("----- packbits memcpy stats: "); + for (size_t i = 0; i < SK_ARRAY_COUNT(gMemCpyBuckets); i++) { + if (gMemCpyBuckets[i]) { + SkDebugf(" %d:%d", i, gMemCpyBuckets[i]); + } + } + } +} +#else +#define register_memset_count(n) +#define register_memcpy_count(n) +#endif + + +/////////////////////////////////////////////////////////////////////////////// + +size_t SkPackBits::ComputeMaxSize16(int count) { + // worst case is the number of 16bit values (times 2) + + // 1 byte per (up to) 128 entries. + return ((count + 127) >> 7) + (count << 1); +} + +size_t SkPackBits::ComputeMaxSize8(int count) { + // worst case is the number of 8bit values + 1 byte per (up to) 128 entries. + return ((count + 127) >> 7) + count; +} + +static uint8_t* flush_same16(uint8_t dst[], uint16_t value, int count) { + while (count > 0) { + int n = count; + if (n > 128) { + n = 128; + } + *dst++ = (uint8_t)(n - 1); + *dst++ = (uint8_t)(value >> 8); + *dst++ = (uint8_t)value; + count -= n; + } + return dst; +} + +static uint8_t* flush_same8(uint8_t dst[], uint8_t value, int count) { + while (count > 0) { + int n = count; + if (n > 128) { + n = 128; + } + *dst++ = (uint8_t)(n - 1); + *dst++ = (uint8_t)value; + count -= n; + } + return dst; +} + +static uint8_t* flush_diff16(uint8_t SK_RESTRICT dst[], + const uint16_t SK_RESTRICT src[], int count) { + while (count > 0) { + int n = count; + if (n > 128) { + n = 128; + } + *dst++ = (uint8_t)(n + 127); + PB_MEMCPY(dst, src, n * sizeof(uint16_t)); + src += n; + dst += n * sizeof(uint16_t); + count -= n; + } + return dst; +} + +static uint8_t* flush_diff8(uint8_t SK_RESTRICT dst[], + const uint8_t SK_RESTRICT src[], int count) { + while (count > 0) { + int n = count; + if (n > 128) { + n = 128; + } + *dst++ = (uint8_t)(n + 127); + PB_MEMCPY(dst, src, n); + src += n; + dst += n; + count -= n; + } + return dst; +} + +size_t SkPackBits::Pack16(const uint16_t SK_RESTRICT src[], int count, + uint8_t SK_RESTRICT dst[]) { + uint8_t* origDst = dst; + const uint16_t* stop = src + count; + + for (;;) { + count = stop - src; + SkASSERT(count >= 0); + if (count == 0) { + return dst - origDst; + } + if (1 == count) { + *dst++ = 0; + *dst++ = (uint8_t)(*src >> 8); + *dst++ = (uint8_t)*src; + return dst - origDst; + } + + unsigned value = *src; + const uint16_t* s = src + 1; + + if (*s == value) { // accumulate same values... + do { + s++; + if (s == stop) { + break; + } + } while (*s == value); + dst = flush_same16(dst, value, s - src); + } else { // accumulate diff values... + do { + if (++s == stop) { + goto FLUSH_DIFF; + } + } while (*s != s[-1]); + s -= 1; // back up so we don't grab one of the "same" values that follow + FLUSH_DIFF: + dst = flush_diff16(dst, src, s - src); + } + src = s; + } +} + +size_t SkPackBits::Pack8(const uint8_t SK_RESTRICT src[], int count, + uint8_t SK_RESTRICT dst[]) { + uint8_t* origDst = dst; + const uint8_t* stop = src + count; + + for (;;) { + count = stop - src; + SkASSERT(count >= 0); + if (count == 0) { + return dst - origDst; + } + if (1 == count) { + *dst++ = 0; + *dst++ = *src; + return dst - origDst; + } + + unsigned value = *src; + const uint8_t* s = src + 1; + + if (*s == value) { // accumulate same values... + do { + s++; + if (s == stop) { + break; + } + } while (*s == value); + dst = flush_same8(dst, value, s - src); + } else { // accumulate diff values... + do { + if (++s == stop) { + goto FLUSH_DIFF; + } + // only stop if we hit 3 in a row, + // otherwise we get bigger than compuatemax + } while (*s != s[-1] || s[-1] != s[-2]); + s -= 2; // back up so we don't grab the "same" values that follow + FLUSH_DIFF: + dst = flush_diff8(dst, src, s - src); + } + src = s; + } +} + +#include "SkUtils.h" + +int SkPackBits::Unpack16(const uint8_t SK_RESTRICT src[], size_t srcSize, + uint16_t SK_RESTRICT dst[]) { + uint16_t* origDst = dst; + const uint8_t* stop = src + srcSize; + + while (src < stop) { + unsigned n = *src++; + if (n <= 127) { // repeat count (n + 1) + n += 1; + sk_memset16(dst, (src[0] << 8) | src[1], n); + src += 2; + } else { // same count (n - 127) + n -= 127; + PB_MEMCPY(dst, src, n * sizeof(uint16_t)); + src += n * sizeof(uint16_t); + } + dst += n; + } + SkASSERT(src == stop); + return dst - origDst; +} + +int SkPackBits::Unpack8(const uint8_t SK_RESTRICT src[], size_t srcSize, + uint8_t SK_RESTRICT dst[]) { + uint8_t* origDst = dst; + const uint8_t* stop = src + srcSize; + + while (src < stop) { + unsigned n = *src++; + if (n <= 127) { // repeat count (n + 1) + n += 1; + PB_MEMSET(dst, *src++, n); + } else { // same count (n - 127) + n -= 127; + PB_MEMCPY(dst, src, n); + src += n; + } + dst += n; + } + SkASSERT(src == stop); + return dst - origDst; +} + +enum UnpackState { + CLEAN_STATE, + REPEAT_BYTE_STATE, + COPY_SRC_STATE +}; + +void SkPackBits::Unpack8(uint8_t SK_RESTRICT dst[], size_t dstSkip, + size_t dstWrite, const uint8_t SK_RESTRICT src[]) { + if (dstWrite == 0) { + return; + } + + UnpackState state = CLEAN_STATE; + size_t stateCount = 0; + + // state 1: do the skip-loop + while (dstSkip > 0) { + unsigned n = *src++; + if (n <= 127) { // repeat count (n + 1) + n += 1; + if (n > dstSkip) { + state = REPEAT_BYTE_STATE; + stateCount = n - dstSkip; + n = dstSkip; + // we don't increment src here, since its needed in stage 2 + } else { + src++; // skip the src byte + } + } else { // same count (n - 127) + n -= 127; + if (n > dstSkip) { + state = COPY_SRC_STATE; + stateCount = n - dstSkip; + n = dstSkip; + } + src += n; + } + dstSkip -= n; + } + + // stage 2: perform any catchup from the skip-stage + if (stateCount > dstWrite) { + stateCount = dstWrite; + } + switch (state) { + case REPEAT_BYTE_STATE: + SkASSERT(stateCount > 0); + register_memset_count(stateCount); + PB_MEMSET(dst, *src++, stateCount); + break; + case COPY_SRC_STATE: + SkASSERT(stateCount > 0); + register_memcpy_count(stateCount); + PB_MEMCPY(dst, src, stateCount); + src += stateCount; + break; + default: + SkASSERT(stateCount == 0); + break; + } + dst += stateCount; + dstWrite -= stateCount; + + // copy at most dstWrite bytes into dst[] + while (dstWrite > 0) { + unsigned n = *src++; + if (n <= 127) { // repeat count (n + 1) + n += 1; + if (n > dstWrite) { + n = dstWrite; + } + register_memset_count(n); + PB_MEMSET(dst, *src++, n); + } else { // same count (n - 127) + n -= 127; + if (n > dstWrite) { + n = dstWrite; + } + register_memcpy_count(n); + PB_MEMCPY(dst, src, n); + src += n; + } + dst += n; + dstWrite -= n; + } + SkASSERT(0 == dstWrite); +} + + + diff --git a/skia/sgl/SkPaint.cpp b/skia/sgl/SkPaint.cpp new file mode 100644 index 0000000..71aa230 --- /dev/null +++ b/skia/sgl/SkPaint.cpp @@ -0,0 +1,1547 @@ +/* libs/graphics/sgl/SkPaint.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkPaint.h" +#include "SkColorFilter.h" +#include "SkDrawLooper.h" +#include "SkFontHost.h" +#include "SkMaskFilter.h" +#include "SkPathEffect.h" +#include "SkRasterizer.h" +#include "SkShader.h" +#include "SkScalerContext.h" +#include "SkStroke.h" +#include "SkTypeface.h" +#include "SkXfermode.h" +#include "SkAutoKern.h" + +#define SK_DefaultTextSize SkIntToScalar(12) + +#define SK_DefaultFlags 0 //(kNativeHintsText_Flag) + +SkPaint::SkPaint() +{ + fTypeface = NULL; + fTextSize = SK_DefaultTextSize; + fTextScaleX = SK_Scalar1; + fTextSkewX = 0; + + fPathEffect = NULL; + fShader = NULL; + fXfermode = NULL; + fMaskFilter = NULL; + fColorFilter = NULL; + fRasterizer = NULL; + fLooper = NULL; + + fColor = SK_ColorBLACK; + fWidth = 0; + fMiterLimit = SK_DefaultMiterLimit; + fFlags = SK_DefaultFlags; + fCapType = kDefault_Cap; + fJoinType = kDefault_Join; + fTextAlign = kLeft_Align; + fStyle = kFill_Style; + fTextEncoding = kUTF8_TextEncoding; +} + +SkPaint::SkPaint(const SkPaint& src) +{ + memcpy(this, &src, sizeof(src)); + + fTypeface->safeRef(); + fPathEffect->safeRef(); + fShader->safeRef(); + fXfermode->safeRef(); + fMaskFilter->safeRef(); + fColorFilter->safeRef(); + fRasterizer->safeRef(); + fLooper->safeRef(); +} + +SkPaint::~SkPaint() +{ + fTypeface->safeUnref(); + fPathEffect->safeUnref(); + fShader->safeUnref(); + fXfermode->safeUnref(); + fMaskFilter->safeUnref(); + fColorFilter->safeUnref(); + fRasterizer->safeUnref(); + fLooper->safeUnref(); +} + +SkPaint& SkPaint::operator=(const SkPaint& src) +{ + SkASSERT(&src); + + src.fTypeface->safeRef(); + src.fPathEffect->safeRef(); + src.fShader->safeRef(); + src.fXfermode->safeRef(); + src.fMaskFilter->safeRef(); + src.fColorFilter->safeRef(); + src.fRasterizer->safeRef(); + src.fLooper->safeRef(); + + fTypeface->safeUnref(); + fPathEffect->safeUnref(); + fShader->safeUnref(); + fXfermode->safeUnref(); + fMaskFilter->safeUnref(); + fColorFilter->safeUnref(); + fRasterizer->safeUnref(); + fLooper->safeUnref(); + + memcpy(this, &src, sizeof(src)); + + return *this; +} + +int operator==(const SkPaint& a, const SkPaint& b) +{ + return memcmp(&a, &b, sizeof(a)) == 0; +} + +void SkPaint::reset() +{ + SkPaint init; + + *this = init; +} + +void SkPaint::setFlags(uint32_t flags) +{ + fFlags = flags; +} + +void SkPaint::setAntiAlias(bool doAA) +{ + this->setFlags(SkSetClearMask(fFlags, doAA, kAntiAlias_Flag)); +} + +void SkPaint::setDither(bool doDither) +{ + this->setFlags(SkSetClearMask(fFlags, doDither, kDither_Flag)); +} + +void SkPaint::setSubpixelText(bool doSubpixel) +{ + this->setFlags(SkSetClearMask(fFlags, doSubpixel, kSubpixelText_Flag)); +} + +void SkPaint::setLinearText(bool doLinearText) +{ + this->setFlags(SkSetClearMask(fFlags, doLinearText, kLinearText_Flag)); +} + +void SkPaint::setUnderlineText(bool doUnderline) +{ + this->setFlags(SkSetClearMask(fFlags, doUnderline, kUnderlineText_Flag)); +} + +void SkPaint::setStrikeThruText(bool doStrikeThru) +{ + this->setFlags(SkSetClearMask(fFlags, doStrikeThru, kStrikeThruText_Flag)); +} + +void SkPaint::setFakeBoldText(bool doFakeBold) +{ + this->setFlags(SkSetClearMask(fFlags, doFakeBold, kFakeBoldText_Flag)); +} + +void SkPaint::setDevKernText(bool doDevKern) +{ + this->setFlags(SkSetClearMask(fFlags, doDevKern, kDevKernText_Flag)); +} + +void SkPaint::setFilterBitmap(bool doFilter) +{ + this->setFlags(SkSetClearMask(fFlags, doFilter, kFilterBitmap_Flag)); +} + +void SkPaint::setStyle(Style style) +{ + if ((unsigned)style < kStyleCount) + fStyle = style; +#ifdef SK_DEBUG + else + SkDebugf("SkPaint::setStyle(%d) out of range\n", style); +#endif +} + +void SkPaint::setColor(SkColor color) +{ + fColor = color; +} + +void SkPaint::setAlpha(U8CPU a) +{ + fColor = SkColorSetARGB(a, SkColorGetR(fColor), SkColorGetG(fColor), SkColorGetB(fColor)); +} + +void SkPaint::setARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) +{ + fColor = SkColorSetARGB(a, r, g, b); +} + +void SkPaint::setStrokeWidth(SkScalar width) +{ + if (width >= 0) + fWidth = width; +#ifdef SK_DEBUG + else + SkDebugf("SkPaint::setStrokeWidth() called with negative value\n"); +#endif +} + +void SkPaint::setStrokeMiter(SkScalar limit) +{ + if (limit >= 0) + fMiterLimit = limit; +#ifdef SK_DEBUG + else + SkDebugf("SkPaint::setStrokeMiter() called with negative value\n"); +#endif +} + +void SkPaint::setStrokeCap(Cap ct) +{ + if ((unsigned)ct < kCapCount) + fCapType = SkToU8(ct); +#ifdef SK_DEBUG + else + SkDebugf("SkPaint::setStrokeCap(%d) out of range\n", ct); +#endif +} + +void SkPaint::setStrokeJoin(Join jt) +{ + if ((unsigned)jt < kJoinCount) + fJoinType = SkToU8(jt); +#ifdef SK_DEBUG + else + SkDebugf("SkPaint::setStrokeJoin(%d) out of range\n", jt); +#endif +} + +////////////////////////////////////////////////////////////////// + +void SkPaint::setTextAlign(Align align) +{ + if ((unsigned)align < kAlignCount) + fTextAlign = SkToU8(align); +#ifdef SK_DEBUG + else + SkDebugf("SkPaint::setTextAlign(%d) out of range\n", align); +#endif +} + +void SkPaint::setTextSize(SkScalar ts) +{ + if (ts > 0) + fTextSize = ts; +#ifdef SK_DEBUG + else + SkDebugf("SkPaint::setTextSize() called with non-positive value\n"); +#endif +} + +void SkPaint::setTextScaleX(SkScalar scaleX) +{ + fTextScaleX = scaleX; +} + +void SkPaint::setTextSkewX(SkScalar skewX) +{ + fTextSkewX = skewX; +} + +void SkPaint::setTextEncoding(TextEncoding encoding) +{ + if ((unsigned)encoding <= kGlyphID_TextEncoding) + fTextEncoding = encoding; +#ifdef SK_DEBUG + else + SkDebugf("SkPaint::setTextEncoding(%d) out of range\n", encoding); +#endif +} + +/////////////////////////////////////////////////////////////////////////////////////// + +SkTypeface* SkPaint::setTypeface(SkTypeface* font) +{ + SkRefCnt_SafeAssign(fTypeface, font); + return font; +} + +SkRasterizer* SkPaint::setRasterizer(SkRasterizer* r) +{ + SkRefCnt_SafeAssign(fRasterizer, r); + return r; +} + +SkDrawLooper* SkPaint::setLooper(SkDrawLooper* looper) +{ + SkRefCnt_SafeAssign(fLooper, looper); + return looper; +} + +/////////////////////////////////////////////////////////////////////////////////////// + +#include "SkGlyphCache.h" +#include "SkUtils.h" + +class SkAutoRestoreFlags { +public: + SkAutoRestoreFlags(SkPaint* paint) : fPaint(paint) + { + fFlags = paint->getFlags(); + } + ~SkAutoRestoreFlags() + { + fPaint->setFlags(fFlags); + } +private: + SkPaint* fPaint; + uint32_t fFlags; +}; + +int SkPaint::textToGlyphs(const void* textData, size_t byteLength, uint16_t glyphs[]) const +{ + if (byteLength == 0) + return 0; + + SkASSERT(textData != NULL); + + if (NULL == glyphs) + { + switch (this->getTextEncoding()) { + case kUTF8_TextEncoding: + return SkUTF8_CountUnichars((const char*)textData, byteLength); + case kUTF16_TextEncoding: + return SkUTF16_CountUnichars((const uint16_t*)textData, byteLength >> 1); + case kGlyphID_TextEncoding: + return byteLength >> 1; + default: + SkASSERT(!"unknown text encoding"); + } + return 0; + } + + // if we get here, we have a valid glyphs[] array, so time to fill it in + + if (this->getTextEncoding() == kGlyphID_TextEncoding) + { + // we want to ignore the low bit of byteLength + memcpy(glyphs, textData, byteLength >> 1 << 1); + return byteLength >> 1; + } + + SkAutoRestoreFlags autoRestore((SkPaint*)this); + ((SkPaint*)this)->fFlags &= ~kSubpixelText_Flag; + + SkAutoGlyphCache autoCache(*this, NULL); + SkGlyphCache* cache = autoCache.getCache(); + SkMeasureCacheProc proc; + proc = this->getMeasureCacheProc(kForward_TextBufferDirection, false); + + const char* text = (const char*)textData; + const char* stop = text + byteLength; + uint16_t* gptr = glyphs; + + while (text < stop) + { + // no need to provide x, y + *gptr++ = proc(cache, &text).getGlyphID(); + } + return gptr - glyphs; +} + +////////////////////////////////////////////////////////////////////////////// + +static uint32_t sk_glyphID_next(const char** text) +{ + const uint16_t* glyph = (const uint16_t*)text; + int32_t value = *glyph; + glyph += 1; + *text = (const char*)glyph; + return value; +} + +static uint32_t sk_glyphID_prev(const char** text) +{ + const uint16_t* glyph = (const uint16_t*)text; + glyph -= 1; + int32_t value = *glyph; + *text = (const char*)glyph; + return value; +} + +////////////////////////////////////////////////////////////////////////////// + +static const SkGlyph& sk_getMetrics_utf8_next(SkGlyphCache* cache, const char** text) +{ + SkASSERT(cache != NULL); + SkASSERT(text != NULL); + + return cache->getUnicharMetrics(SkUTF8_NextUnichar(text)); +} + +static const SkGlyph& sk_getMetrics_utf8_prev(SkGlyphCache* cache, const char** text) +{ + SkASSERT(cache != NULL); + SkASSERT(text != NULL); + + return cache->getUnicharMetrics(SkUTF8_PrevUnichar(text)); +} + +static const SkGlyph& sk_getMetrics_utf16_next(SkGlyphCache* cache, const char** text) +{ + SkASSERT(cache != NULL); + SkASSERT(text != NULL); + + return cache->getUnicharMetrics(SkUTF16_NextUnichar((const uint16_t**)text)); +} + +static const SkGlyph& sk_getMetrics_utf16_prev(SkGlyphCache* cache, const char** text) +{ + SkASSERT(cache != NULL); + SkASSERT(text != NULL); + + return cache->getUnicharMetrics(SkUTF16_PrevUnichar((const uint16_t**)text)); +} + +static const SkGlyph& sk_getMetrics_glyph_next(SkGlyphCache* cache, const char** text) +{ + SkASSERT(cache != NULL); + SkASSERT(text != NULL); + + const uint16_t* ptr = *(const uint16_t**)text; + unsigned glyphID = *ptr; + ptr += 1; + *text = (const char*)ptr; + return cache->getGlyphIDMetrics(glyphID); +} + +static const SkGlyph& sk_getMetrics_glyph_prev(SkGlyphCache* cache, const char** text) +{ + SkASSERT(cache != NULL); + SkASSERT(text != NULL); + + const uint16_t* ptr = *(const uint16_t**)text; + ptr -= 1; + unsigned glyphID = *ptr; + *text = (const char*)ptr; + return cache->getGlyphIDMetrics(glyphID); +} + +/// + +static const SkGlyph& sk_getAdvance_utf8_next(SkGlyphCache* cache, const char** text) +{ + SkASSERT(cache != NULL); + SkASSERT(text != NULL); + + return cache->getUnicharAdvance(SkUTF8_NextUnichar(text)); +} + +static const SkGlyph& sk_getAdvance_utf8_prev(SkGlyphCache* cache, const char** text) +{ + SkASSERT(cache != NULL); + SkASSERT(text != NULL); + + return cache->getUnicharAdvance(SkUTF8_PrevUnichar(text)); +} + +static const SkGlyph& sk_getAdvance_utf16_next(SkGlyphCache* cache, const char** text) +{ + SkASSERT(cache != NULL); + SkASSERT(text != NULL); + + return cache->getUnicharAdvance(SkUTF16_NextUnichar((const uint16_t**)text)); +} + +static const SkGlyph& sk_getAdvance_utf16_prev(SkGlyphCache* cache, const char** text) +{ + SkASSERT(cache != NULL); + SkASSERT(text != NULL); + + return cache->getUnicharAdvance(SkUTF16_PrevUnichar((const uint16_t**)text)); +} + +static const SkGlyph& sk_getAdvance_glyph_next(SkGlyphCache* cache, const char** text) +{ + SkASSERT(cache != NULL); + SkASSERT(text != NULL); + + const uint16_t* ptr = *(const uint16_t**)text; + unsigned glyphID = *ptr; + ptr += 1; + *text = (const char*)ptr; + return cache->getGlyphIDAdvance(glyphID); +} + +static const SkGlyph& sk_getAdvance_glyph_prev(SkGlyphCache* cache, const char** text) +{ + SkASSERT(cache != NULL); + SkASSERT(text != NULL); + + const uint16_t* ptr = *(const uint16_t**)text; + ptr -= 1; + unsigned glyphID = *ptr; + *text = (const char*)ptr; + return cache->getGlyphIDAdvance(glyphID); +} + +SkMeasureCacheProc SkPaint::getMeasureCacheProc(TextBufferDirection tbd, + bool needFullMetrics) const +{ + static const SkMeasureCacheProc gMeasureCacheProcs[] = { + sk_getMetrics_utf8_next, + sk_getMetrics_utf16_next, + sk_getMetrics_glyph_next, + + sk_getMetrics_utf8_prev, + sk_getMetrics_utf16_prev, + sk_getMetrics_glyph_prev, + + sk_getAdvance_utf8_next, + sk_getAdvance_utf16_next, + sk_getAdvance_glyph_next, + + sk_getAdvance_utf8_prev, + sk_getAdvance_utf16_prev, + sk_getAdvance_glyph_prev + }; + + unsigned index = this->getTextEncoding(); + + if (kBackward_TextBufferDirection == tbd) + index += 3; + if (!needFullMetrics && !this->isDevKernText()) + index += 6; + + SkASSERT(index < SK_ARRAY_COUNT(gMeasureCacheProcs)); + return gMeasureCacheProcs[index]; +} + +/////////////////////////////////////////////////////////////////////////////// + +static const SkGlyph& sk_getMetrics_utf8_00(SkGlyphCache* cache, + const char** text, SkFixed, SkFixed) +{ + SkASSERT(cache != NULL); + SkASSERT(text != NULL); + + return cache->getUnicharMetrics(SkUTF8_NextUnichar(text)); +} + +static const SkGlyph& sk_getMetrics_utf8_xy(SkGlyphCache* cache, + const char** text, SkFixed x, SkFixed y) +{ + SkASSERT(cache != NULL); + SkASSERT(text != NULL); + + return cache->getUnicharMetrics(SkUTF8_NextUnichar(text), x, y); +} + +static const SkGlyph& sk_getMetrics_utf16_00(SkGlyphCache* cache, const char** text, + SkFixed, SkFixed) +{ + SkASSERT(cache != NULL); + SkASSERT(text != NULL); + + return cache->getUnicharMetrics(SkUTF16_NextUnichar((const uint16_t**)text)); +} + +static const SkGlyph& sk_getMetrics_utf16_xy(SkGlyphCache* cache, + const char** text, SkFixed x, SkFixed y) +{ + SkASSERT(cache != NULL); + SkASSERT(text != NULL); + + return cache->getUnicharMetrics(SkUTF16_NextUnichar((const uint16_t**)text), + x, y); +} + +static const SkGlyph& sk_getMetrics_glyph_00(SkGlyphCache* cache, const char** text, + SkFixed, SkFixed) +{ + SkASSERT(cache != NULL); + SkASSERT(text != NULL); + + const uint16_t* ptr = *(const uint16_t**)text; + unsigned glyphID = *ptr; + ptr += 1; + *text = (const char*)ptr; + return cache->getGlyphIDMetrics(glyphID); +} + +static const SkGlyph& sk_getMetrics_glyph_xy(SkGlyphCache* cache, + const char** text, SkFixed x, SkFixed y) +{ + SkASSERT(cache != NULL); + SkASSERT(text != NULL); + + const uint16_t* ptr = *(const uint16_t**)text; + unsigned glyphID = *ptr; + ptr += 1; + *text = (const char*)ptr; + return cache->getGlyphIDMetrics(glyphID, x, y); +} + +SkDrawCacheProc SkPaint::getDrawCacheProc() const +{ + static const SkDrawCacheProc gDrawCacheProcs[] = { + sk_getMetrics_utf8_00, + sk_getMetrics_utf16_00, + sk_getMetrics_glyph_00, + + sk_getMetrics_utf8_xy, + sk_getMetrics_utf16_xy, + sk_getMetrics_glyph_xy + }; + + unsigned index = this->getTextEncoding(); + if (fFlags & kSubpixelText_Flag) + index += 3; + + SkASSERT(index < SK_ARRAY_COUNT(gDrawCacheProcs)); + return gDrawCacheProcs[index]; +} + +/////////////////////////////////////////////////////////////////////////////// + +class SkAutoRestorePaintTextSizeAndFrame { +public: + SkAutoRestorePaintTextSizeAndFrame(const SkPaint* paint) : fPaint((SkPaint*)paint) + { + fTextSize = paint->getTextSize(); + fStyle = paint->getStyle(); + fPaint->setStyle(SkPaint::kFill_Style); + } + ~SkAutoRestorePaintTextSizeAndFrame() + { + fPaint->setStyle(fStyle); + fPaint->setTextSize(fTextSize); + } + +private: + SkPaint* fPaint; + SkScalar fTextSize; + SkPaint::Style fStyle; +}; + +static void set_bounds(const SkGlyph& g, SkRect* bounds) +{ + bounds->set(SkIntToScalar(g.fLeft), + SkIntToScalar(g.fTop), + SkIntToScalar(g.fLeft + g.fWidth), + SkIntToScalar(g.fTop + g.fHeight)); +} + +static void join_bounds(const SkGlyph& g, SkRect* bounds, SkFixed dx) +{ + SkScalar sx = SkFixedToScalar(dx); + bounds->join(SkIntToScalar(g.fLeft) + sx, + SkIntToScalar(g.fTop), + SkIntToScalar(g.fLeft + g.fWidth) + sx, + SkIntToScalar(g.fTop + g.fHeight)); +} + +SkScalar SkPaint::measure_text(SkGlyphCache* cache, + const char* text, size_t byteLength, + int* count, SkRect* bounds) const +{ + SkASSERT(count); + if (byteLength == 0) + { + *count = 0; + if (bounds) + bounds->setEmpty(); + return 0; + } + + SkMeasureCacheProc glyphCacheProc; + glyphCacheProc = this->getMeasureCacheProc(kForward_TextBufferDirection, + NULL != bounds); + + int n = 1; + const char* stop = (const char*)text + byteLength; + const SkGlyph* g = &glyphCacheProc(cache, &text); + SkFixed x = g->fAdvanceX; + + SkAutoKern autokern; + + if (NULL == bounds) + { + if (this->isDevKernText()) + { + int rsb; + for (; text < stop; n++) { + rsb = g->fRsbDelta; + g = &glyphCacheProc(cache, &text); + x += SkAutoKern_AdjustF(rsb, g->fLsbDelta) + g->fAdvanceX; + } + } + else + { + for (; text < stop; n++) { + x += glyphCacheProc(cache, &text).fAdvanceX; + } + } + } + else + { + set_bounds(*g, bounds); + if (this->isDevKernText()) + { + int rsb; + for (; text < stop; n++) { + rsb = g->fRsbDelta; + g = &glyphCacheProc(cache, &text); + x += SkAutoKern_AdjustF(rsb, g->fLsbDelta); + join_bounds(*g, bounds, x); + x += g->fAdvanceX; + } + } + else + { + for (; text < stop; n++) { + g = &glyphCacheProc(cache, &text); + join_bounds(*g, bounds, x); + x += g->fAdvanceX; + } + } + } + SkASSERT(text == stop); + + *count = n; + return SkFixedToScalar(x); +} + +SkScalar SkPaint::measureText(const void* textData, size_t length, + SkRect* bounds, SkScalar zoom) const +{ + const char* text = (const char*)textData; + SkASSERT(text != NULL || length == 0); + + SkScalar scale = 0; + SkAutoRestorePaintTextSizeAndFrame restore(this); + + if (this->isLinearText()) + { + scale = fTextSize / kCanonicalTextSizeForPaths; + // this gets restored by restore + ((SkPaint*)this)->setTextSize(SkIntToScalar(kCanonicalTextSizeForPaths)); + } + + SkMatrix zoomMatrix, *zoomPtr = NULL; + if (zoom) + { + zoomMatrix.setScale(zoom, zoom); + zoomPtr = &zoomMatrix; + } + + SkAutoGlyphCache autoCache(*this, zoomPtr); + SkGlyphCache* cache = autoCache.getCache(); + + SkScalar width = 0; + + if (length > 0) + { + int tempCount; + + width = this->measure_text(cache, text, length, &tempCount, bounds); + if (scale) + { + width = SkScalarMul(width, scale); + if (bounds) + { + bounds->fLeft = SkScalarMul(bounds->fLeft, scale); + bounds->fTop = SkScalarMul(bounds->fTop, scale); + bounds->fRight = SkScalarMul(bounds->fRight, scale); + bounds->fBottom = SkScalarMul(bounds->fBottom, scale); + } + } + } + return width; +} + +typedef bool (*SkTextBufferPred)(const char* text, const char* stop); + +static bool forward_textBufferPred(const char* text, const char* stop) +{ + return text < stop; +} + +static bool backward_textBufferPred(const char* text, const char* stop) +{ + return text > stop; +} + +static SkTextBufferPred chooseTextBufferPred(SkPaint::TextBufferDirection tbd, + const char** text, size_t length, const char** stop) +{ + if (SkPaint::kForward_TextBufferDirection == tbd) + { + *stop = *text + length; + return forward_textBufferPred; + } + else + { + // text should point to the end of the buffer, and stop to the beginning + *stop = *text; + *text += length; + return backward_textBufferPred; + } +} + +size_t SkPaint::breakText(const void* textD, size_t length, SkScalar maxWidth, + SkScalar* measuredWidth, + TextBufferDirection tbd) const +{ + if (0 == length || 0 >= maxWidth) + { + if (measuredWidth) + *measuredWidth = 0; + return 0; + } + + SkASSERT(textD != NULL); + const char* text = (const char*)textD; + + SkScalar scale = 0; + SkAutoRestorePaintTextSizeAndFrame restore(this); + + if (this->isLinearText()) + { + scale = fTextSize / kCanonicalTextSizeForPaths; + // this gets restored by restore + ((SkPaint*)this)->setTextSize(SkIntToScalar(kCanonicalTextSizeForPaths)); + } + + SkAutoGlyphCache autoCache(*this, NULL); + SkGlyphCache* cache = autoCache.getCache(); + + SkMeasureCacheProc glyphCacheProc = this->getMeasureCacheProc(tbd, false); + const char* stop; + SkTextBufferPred pred = chooseTextBufferPred(tbd, &text, length, &stop); + SkFixed max = SkScalarToFixed(maxWidth); + SkFixed width = 0; + + SkAutoKern autokern; + + if (this->isDevKernText()) + { + int rsb = 0; + while (pred(text, stop)) + { + const char* curr = text; + const SkGlyph& g = glyphCacheProc(cache, &text); + SkFixed x = SkAutoKern_AdjustF(rsb, g.fLsbDelta) + g.fAdvanceX; + if ((width += x) > max) + { + width -= x; + text = curr; + break; + } + rsb = g.fRsbDelta; + } + } + else + { + while (pred(text, stop)) + { + const char* curr = text; + SkFixed x = glyphCacheProc(cache, &text).fAdvanceX; + if ((width += x) > max) + { + width -= x; + text = curr; + break; + } + } + } + + if (measuredWidth) + { + + SkScalar scalarWidth = SkFixedToScalar(width); + if (scale) + scalarWidth = SkScalarMul(scalarWidth, scale); + *measuredWidth = scalarWidth; + } + + // return the number of bytes measured + return (kForward_TextBufferDirection == tbd) ? + text - stop + length : stop - text + length; +} + +/////////////////////////////////////////////////////////////////////////////// + +static bool FontMetricsCacheProc(const SkGlyphCache* cache, void* context) +{ + *(SkPaint::FontMetrics*)context = cache->getFontMetricsY(); + return false; // don't detach the cache +} + +static void FontMetricsDescProc(const SkDescriptor* desc, void* context) +{ + SkGlyphCache::VisitCache(desc, FontMetricsCacheProc, context); +} + +SkScalar SkPaint::getFontMetrics(FontMetrics* metrics, SkScalar zoom) const +{ + SkScalar scale = 0; + SkAutoRestorePaintTextSizeAndFrame restore(this); + + if (this->isLinearText()) + { + scale = fTextSize / kCanonicalTextSizeForPaths; + // this gets restored by restore + ((SkPaint*)this)->setTextSize(SkIntToScalar(kCanonicalTextSizeForPaths)); + } + + SkMatrix zoomMatrix, *zoomPtr = NULL; + if (zoom) + { + zoomMatrix.setScale(zoom, zoom); + zoomPtr = &zoomMatrix; + } + +#if 0 + SkAutoGlyphCache autoCache(*this, zoomPtr); + SkGlyphCache* cache = autoCache.getCache(); + const FontMetrics& my = cache->getFontMetricsY(); +#endif + FontMetrics storage; + if (NULL == metrics) + metrics = &storage; + + this->descriptorProc(zoomPtr, FontMetricsDescProc, metrics); + + if (scale) + { + metrics->fTop = SkScalarMul(metrics->fTop, scale); + metrics->fAscent = SkScalarMul(metrics->fAscent, scale); + metrics->fDescent = SkScalarMul(metrics->fDescent, scale); + metrics->fBottom = SkScalarMul(metrics->fBottom, scale); + metrics->fLeading = SkScalarMul(metrics->fLeading, scale); + } + return metrics->fDescent - metrics->fAscent + metrics->fLeading; +} + +//////////////////////////////////////////////////////////////////////////////////////////// + +static void set_bounds(const SkGlyph& g, SkRect* bounds, SkScalar scale) +{ + bounds->set(g.fLeft * scale, + g.fTop * scale, + (g.fLeft + g.fWidth) * scale, + (g.fTop + g.fHeight) * scale); +} + +int SkPaint::getTextWidths(const void* textData, size_t byteLength, SkScalar widths[], + SkRect bounds[]) const +{ + if (0 == byteLength) + return 0; + + SkASSERT(NULL != textData); + + if (NULL == widths && NULL == bounds) + return this->countText(textData, byteLength); + + SkAutoRestorePaintTextSizeAndFrame restore(this); + SkScalar scale = 0; + + if (this->isLinearText()) + { + scale = fTextSize / kCanonicalTextSizeForPaths; + // this gets restored by restore + ((SkPaint*)this)->setTextSize(SkIntToScalar(kCanonicalTextSizeForPaths)); + } + + SkAutoGlyphCache autoCache(*this, NULL); + SkGlyphCache* cache = autoCache.getCache(); + SkMeasureCacheProc glyphCacheProc; + glyphCacheProc = this->getMeasureCacheProc(kForward_TextBufferDirection, + NULL != bounds); + + const char* text = (const char*)textData; + const char* stop = text + byteLength; + int count = 0; + + if (this->isDevKernText()) + { + // we adjust the widths returned here through auto-kerning + SkAutoKern autokern; + SkFixed prevWidth = 0; + + if (scale) { + while (text < stop) { + const SkGlyph& g = glyphCacheProc(cache, &text); + if (widths) { + SkFixed adjust = autokern.adjust(g); + + if (count > 0) { + SkScalar w = SkFixedToScalar(prevWidth + adjust); + *widths++ = SkScalarMul(w, scale); + } + prevWidth = g.fAdvanceX; + } + if (bounds) { + set_bounds(g, bounds++, scale); + } + ++count; + } + if (count > 0 && widths) { + *widths = SkScalarMul(SkFixedToScalar(prevWidth), scale); + } + } else { + while (text < stop) { + const SkGlyph& g = glyphCacheProc(cache, &text); + if (widths) { + SkFixed adjust = autokern.adjust(g); + + if (count > 0) { + *widths++ = SkFixedToScalar(prevWidth + adjust); + } + prevWidth = g.fAdvanceX; + } + if (bounds) { + set_bounds(g, bounds++); + } + ++count; + } + if (count > 0 && widths) { + *widths = SkFixedToScalar(prevWidth); + } + } + } else { // no devkern + if (scale) { + while (text < stop) { + const SkGlyph& g = glyphCacheProc(cache, &text); + if (widths) { + *widths++ = SkScalarMul(SkFixedToScalar(g.fAdvanceX), + scale); + } + if (bounds) { + set_bounds(g, bounds++, scale); + } + ++count; + } + } else { + while (text < stop) { + const SkGlyph& g = glyphCacheProc(cache, &text); + if (widths) { + *widths++ = SkFixedToScalar(g.fAdvanceX); + } + if (bounds) { + set_bounds(g, bounds++); + } + ++count; + } + } + } + + SkASSERT(text == stop); + return count; +} + +//////////////////////////////////////////////////////////////////////////////////////////// + +#include "SkDraw.h" + +void SkPaint::getTextPath(const void* textData, size_t length, SkScalar x, SkScalar y, SkPath* path) const +{ + const char* text = (const char*)textData; + SkASSERT(length == 0 || text != NULL); + if (text == NULL || length == 0 || path == NULL) + return; + + SkTextToPathIter iter(text, length, *this, false, true); + SkMatrix matrix; + SkScalar prevXPos = 0; + + matrix.setScale(iter.getPathScale(), iter.getPathScale()); + matrix.postTranslate(x, y); + path->reset(); + + SkScalar xpos; + const SkPath* iterPath; + while ((iterPath = iter.next(&xpos)) != NULL) + { + matrix.postTranslate(xpos - prevXPos, 0); + path->addPath(*iterPath, matrix); + prevXPos = xpos; + } +} + +static void add_flattenable(SkDescriptor* desc, uint32_t tag, + SkFlattenableWriteBuffer* buffer) { + buffer->flatten(desc->addEntry(tag, buffer->size(), NULL)); +} + +/* + * interpolates to find the right value for key, in the function represented by the 'length' number of pairs: (keys[i], values[i]) + inspired by a desire to change the multiplier for thickness in fakebold + therefore, i assumed number of pairs (length) will be small, so a linear search is sufficient + repeated keys are allowed for discontinuous functions (so long as keys is monotonically increasing), and if + key is the value of a repeated scalar in keys, the first one will be used + - this may change if a binary search is used + - also, this ensures that there is no divide by zero (an assert also checks for that) +*/ +static SkScalar interpolate(SkScalar key, const SkScalar keys[], const SkScalar values[], int length) +{ + + SkASSERT(length > 0); + SkASSERT(keys != NULL); + SkASSERT(values != NULL); +#ifdef SK_DEBUG + for (int i = 1; i < length; i++) + SkASSERT(keys[i] >= keys[i-1]); +#endif + int right = 0; + while (right < length && key > keys[right]) + right++; + //could use sentinal values to eliminate conditionals + //i assume i am not in control of input values, so i want to make it simple + if (length == right) + return values[length-1]; + if (0 == right) + return values[0]; + //otherwise, we interpolate between right-1 and right + SkScalar rVal = values[right]; + SkScalar lVal = values[right-1]; + SkScalar rightKey = keys[right]; + SkScalar leftKey = keys[right-1]; + SkASSERT(rightKey != leftKey); + //fractional amount which we will multiply by the difference in the left value and right value + SkScalar fract = SkScalarDiv(key-leftKey,rightKey-leftKey); + return lVal + SkScalarMul(fract, rVal-lVal); +} + +//used for interpolating in fakeBold +static const SkScalar pointSizes[] = { SkIntToScalar(9), SkIntToScalar(36) }; +static const SkScalar multipliers[] = { SK_Scalar1/24, SK_Scalar1/32 }; + +static SkMask::Format computeMaskFormat(const SkPaint& paint) +{ + uint32_t flags = paint.getFlags(); + + return (flags & SkPaint::kAntiAlias_Flag) ? SkMask::kA8_Format : SkMask::kBW_Format; +} + +static SkScalerContext::Hints computeScalerHints(const SkPaint& paint) +{ + uint32_t flags = paint.getFlags(); + + if (flags & SkPaint::kLinearText_Flag) + return SkScalerContext::kNo_Hints; + else if (flags & SkPaint::kSubpixelText_Flag) + return SkScalerContext::kSubpixel_Hints; + else + return SkScalerContext::kNormal_Hints; +} + +void SkScalerContext::MakeRec(const SkPaint& paint, const SkMatrix* deviceMatrix, Rec* rec) +{ + SkASSERT(deviceMatrix == NULL || (deviceMatrix->getType() & SkMatrix::kPerspective_Mask) == 0); + + rec->fFontID = SkTypeface::UniqueID(paint.getTypeface()); + rec->fTextSize = paint.getTextSize(); + rec->fPreScaleX = paint.getTextScaleX(); + rec->fPreSkewX = paint.getTextSkewX(); + + if (deviceMatrix) + { + rec->fPost2x2[0][0] = deviceMatrix->getScaleX(); + rec->fPost2x2[0][1] = deviceMatrix->getSkewX(); + rec->fPost2x2[1][0] = deviceMatrix->getSkewY(); + rec->fPost2x2[1][1] = deviceMatrix->getScaleY(); + } + else + { + rec->fPost2x2[0][0] = rec->fPost2x2[1][1] = SK_Scalar1; + rec->fPost2x2[0][1] = rec->fPost2x2[1][0] = 0; + } + + SkPaint::Style style = paint.getStyle(); + SkScalar strokeWidth = paint.getStrokeWidth(); + + if (paint.isFakeBoldText()) + { + SkScalar fakeBoldScale = interpolate(paint.getTextSize(), pointSizes, multipliers, 2); + SkScalar extra = SkScalarMul(paint.getTextSize(), fakeBoldScale); + + if (style == SkPaint::kFill_Style) + { + style = SkPaint::kStrokeAndFill_Style; + strokeWidth = extra; // ignore paint's strokeWidth if it was "fill" + } + else + strokeWidth += extra; + } + + unsigned flags = SkFontHost::ComputeGammaFlag(paint); + + if (paint.isDevKernText()) + flags |= SkScalerContext::kDevKernText_Flag; + + if (style != SkPaint::kFill_Style && strokeWidth > 0) + { + rec->fFrameWidth = strokeWidth; + rec->fMiterLimit = paint.getStrokeMiter(); + rec->fStrokeJoin = SkToU8(paint.getStrokeJoin()); + + if (style == SkPaint::kStrokeAndFill_Style) + flags |= SkScalerContext::kFrameAndFill_Flag; + } + else + { + rec->fFrameWidth = 0; + rec->fMiterLimit = 0; + rec->fStrokeJoin = 0; + } + + rec->fHints = SkToU8(computeScalerHints(paint)); + rec->fMaskFormat = SkToU8(computeMaskFormat(paint)); + rec->fFlags = SkToU8(flags); +} + +#define MIN_SIZE_FOR_EFFECT_BUFFER 1024 + +void SkPaint::descriptorProc(const SkMatrix* deviceMatrix, + void (*proc)(const SkDescriptor*, void*), + void* context) const +{ + SkScalerContext::Rec rec; + + SkScalerContext::MakeRec(*this, deviceMatrix, &rec); + + size_t descSize = sizeof(rec); + int entryCount = 1; + SkPathEffect* pe = this->getPathEffect(); + SkMaskFilter* mf = this->getMaskFilter(); + SkRasterizer* ra = this->getRasterizer(); + + SkFlattenableWriteBuffer peBuffer(MIN_SIZE_FOR_EFFECT_BUFFER); + SkFlattenableWriteBuffer mfBuffer(MIN_SIZE_FOR_EFFECT_BUFFER); + SkFlattenableWriteBuffer raBuffer(MIN_SIZE_FOR_EFFECT_BUFFER); + + if (pe) { + peBuffer.writeFlattenable(pe); + descSize += peBuffer.size(); + entryCount += 1; + rec.fMaskFormat = SkMask::kA8_Format; // force antialiasing when we do the scan conversion + // seems like we could support kLCD as well at this point <reed>... + } + if (mf) { + mfBuffer.writeFlattenable(mf); + descSize += mfBuffer.size(); + entryCount += 1; + rec.fMaskFormat = SkMask::kA8_Format; // force antialiasing with maskfilters + } + if (ra) { + raBuffer.writeFlattenable(ra); + descSize += raBuffer.size(); + entryCount += 1; + rec.fMaskFormat = SkMask::kA8_Format; // force antialiasing when we do the scan conversion + } + descSize += SkDescriptor::ComputeOverhead(entryCount); + + SkAutoDescriptor ad(descSize); + SkDescriptor* desc = ad.getDesc(); + + desc->init(); + desc->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec); + + if (pe) { + add_flattenable(desc, kPathEffect_SkDescriptorTag, &peBuffer); + } + if (mf) { + add_flattenable(desc, kMaskFilter_SkDescriptorTag, &mfBuffer); + } + if (ra) { + add_flattenable(desc, kRasterizer_SkDescriptorTag, &raBuffer); + } + + SkASSERT(descSize == desc->getLength()); + desc->computeChecksum(); + + proc(desc, context); +} + +static void DetachDescProc(const SkDescriptor* desc, void* context) +{ + *((SkGlyphCache**)context) = SkGlyphCache::DetachCache(desc); +} + +SkGlyphCache* SkPaint::detachCache(const SkMatrix* deviceMatrix) const +{ + SkGlyphCache* cache; + this->descriptorProc(deviceMatrix, DetachDescProc, &cache); + return cache; +} + +/////////////////////////////////////////////////////////////////////////////// + +#include "SkStream.h" + +void SkPaint::flatten(SkFlattenableWriteBuffer& buffer) const { + buffer.writeTypeface(this->getTypeface()); + buffer.writeScalar(this->getTextSize()); + buffer.writeScalar(this->getTextScaleX()); + buffer.writeScalar(this->getTextSkewX()); + buffer.writeFlattenable(this->getPathEffect()); + buffer.writeFlattenable(this->getShader()); + buffer.writeFlattenable(this->getXfermode()); + buffer.writeFlattenable(this->getMaskFilter()); + buffer.writeFlattenable(this->getColorFilter()); + buffer.writeFlattenable(this->getRasterizer()); + buffer.writeFlattenable(this->getLooper()); + buffer.write32(this->getColor()); + buffer.writeScalar(this->getStrokeWidth()); + buffer.writeScalar(this->getStrokeMiter()); + buffer.write16(this->getFlags()); + buffer.write8(this->getTextAlign()); + buffer.write8(this->getStrokeCap()); + buffer.write8(this->getStrokeJoin()); + buffer.write8(this->getStyle()); + buffer.write8(this->getTextEncoding()); +} + +void SkPaint::unflatten(SkFlattenableReadBuffer& buffer) { + this->setTypeface(buffer.readTypeface()); + this->setTextSize(buffer.readScalar()); + this->setTextScaleX(buffer.readScalar()); + this->setTextSkewX(buffer.readScalar()); + this->setPathEffect((SkPathEffect*) buffer.readFlattenable())->safeUnref(); + this->setShader((SkShader*) buffer.readFlattenable())->safeUnref(); + this->setXfermode((SkXfermode*) buffer.readFlattenable())->safeUnref(); + this->setMaskFilter((SkMaskFilter*) buffer.readFlattenable())->safeUnref(); + this->setColorFilter((SkColorFilter*) buffer.readFlattenable())->safeUnref(); + this->setRasterizer((SkRasterizer*) buffer.readFlattenable())->safeUnref(); + this->setLooper((SkDrawLooper*) buffer.readFlattenable())->safeUnref(); + this->setColor(buffer.readU32()); + this->setStrokeWidth(buffer.readScalar()); + this->setStrokeMiter(buffer.readScalar()); + this->setFlags(buffer.readU16()); + this->setTextAlign((SkPaint::Align) buffer.readU8()); + this->setStrokeCap((SkPaint::Cap) buffer.readU8()); + this->setStrokeJoin((SkPaint::Join) buffer.readU8()); + this->setStyle((SkPaint::Style) buffer.readU8()); + this->setTextEncoding((SkPaint::TextEncoding) buffer.readU8()); +} + +/////////////////////////////////////////////////////////////////////////////// + +SkShader* SkPaint::setShader(SkShader* shader) +{ + SkRefCnt_SafeAssign(fShader, shader); + return shader; +} + +SkColorFilter* SkPaint::setColorFilter(SkColorFilter* filter) +{ + SkRefCnt_SafeAssign(fColorFilter, filter); + return filter; +} + +SkXfermode* SkPaint::setXfermode(SkXfermode* mode) +{ + SkRefCnt_SafeAssign(fXfermode, mode); + return mode; +} + +SkXfermode* SkPaint::setPorterDuffXfermode(SkPorterDuff::Mode mode) +{ + fXfermode->safeUnref(); + fXfermode = SkPorterDuff::CreateXfermode(mode); + return fXfermode; +} + +SkPathEffect* SkPaint::setPathEffect(SkPathEffect* effect) +{ + SkRefCnt_SafeAssign(fPathEffect, effect); + return effect; +} + +SkMaskFilter* SkPaint::setMaskFilter(SkMaskFilter* filter) +{ + SkRefCnt_SafeAssign(fMaskFilter, filter); + return filter; +} + +//////////////////////////////////////////////////////////////////////////////////////// + +bool SkPaint::getFillPath(const SkPath& src, SkPath* dst) const +{ + SkPath effectPath, strokePath; + const SkPath* path = &src; + + SkScalar width = this->getStrokeWidth(); + + switch (this->getStyle()) { + case SkPaint::kFill_Style: + width = -1; // mark it as no-stroke + break; + case SkPaint::kStrokeAndFill_Style: + if (width == 0) + width = -1; // mark it as no-stroke + break; + case SkPaint::kStroke_Style: + break; + default: + SkASSERT(!"unknown paint style"); + } + + if (this->getPathEffect()) + { + // lie to the pathEffect if our style is strokeandfill, so that it treats us as just fill + if (this->getStyle() == SkPaint::kStrokeAndFill_Style) + width = -1; // mark it as no-stroke + + if (this->getPathEffect()->filterPath(&effectPath, src, &width)) + path = &effectPath; + + // restore the width if we earlier had to lie, and if we're still set to no-stroke + // note: if we're now stroke (width >= 0), then the pathEffect asked for that change + // and we want to respect that (i.e. don't overwrite their setting for width) + if (this->getStyle() == SkPaint::kStrokeAndFill_Style && width < 0) + { + width = this->getStrokeWidth(); + if (width == 0) + width = -1; + } + } + + if (width > 0 && !path->isEmpty()) + { + SkStroke stroker(*this, width); + stroker.strokePath(*path, &strokePath); + path = &strokePath; + } + + if (path == &src) + *dst = src; + else + { + SkASSERT(path == &effectPath || path == &strokePath); + dst->swap(*(SkPath*)path); + } + + return width != 0; // return true if we're filled, or false if we're hairline (width == 0) +} + +//////////////////////////////////////////////////////////////////////////////////////// + +static bool has_thick_frame(const SkPaint& paint) +{ + return paint.getStrokeWidth() > 0 && paint.getStyle() != SkPaint::kFill_Style; +} + +SkTextToPathIter::SkTextToPathIter( const char text[], size_t length, + const SkPaint& paint, + bool applyStrokeAndPathEffects, + bool forceLinearTextOn) + : fPaint(paint) /* make a copy of the paint */ +{ + fGlyphCacheProc = paint.getMeasureCacheProc(SkPaint::kForward_TextBufferDirection, + true); + + if (forceLinearTextOn) + fPaint.setLinearText(true); + fPaint.setMaskFilter(NULL); // don't want this affecting our path-cache lookup + + if (fPaint.getPathEffect() == NULL && !has_thick_frame(fPaint)) + applyStrokeAndPathEffects = false; + + // can't use our canonical size if we need to apply patheffects/strokes + if (fPaint.isLinearText() && !applyStrokeAndPathEffects) + { + fPaint.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths)); + fScale = paint.getTextSize() / SkPaint::kCanonicalTextSizeForPaths; + } + else + fScale = SK_Scalar1; + + if (!applyStrokeAndPathEffects) + { + fPaint.setStyle(SkPaint::kFill_Style); + fPaint.setPathEffect(NULL); + } + + fCache = fPaint.detachCache(NULL); + + SkPaint::Style style = SkPaint::kFill_Style; + SkPathEffect* pe = NULL; + + if (!applyStrokeAndPathEffects) + { + style = paint.getStyle(); // restore + pe = paint.getPathEffect(); // restore + } + fPaint.setStyle(style); + fPaint.setPathEffect(pe); + fPaint.setMaskFilter(paint.getMaskFilter()); // restore + + // now compute fXOffset if needed + + SkScalar xOffset = 0; + if (paint.getTextAlign() != SkPaint::kLeft_Align) // need to measure first + { + int count; + SkScalar width = SkScalarMul(fPaint.measure_text(fCache, text, length, &count, NULL), fScale); + if (paint.getTextAlign() == SkPaint::kCenter_Align) + width = SkScalarHalf(width); + xOffset = -width; + } + fXPos = xOffset; + fPrevAdvance = 0; + + fText = text; + fStop = text + length; +} + +SkTextToPathIter::~SkTextToPathIter() +{ + SkGlyphCache::AttachCache(fCache); +} + +const SkPath* SkTextToPathIter::next(SkScalar* xpos) +{ + while (fText < fStop) + { + const SkGlyph& glyph = fGlyphCacheProc(fCache, &fText); + + fXPos += SkScalarMul(SkFixedToScalar(fPrevAdvance + fAutoKern.adjust(glyph)), fScale); + fPrevAdvance = glyph.fAdvanceX; // + fPaint.getTextTracking(); + + if (glyph.fWidth) + { + if (xpos) + *xpos = fXPos; + return fCache->findPath(glyph); + } + } + return NULL; +} + diff --git a/skia/sgl/SkPath.cpp b/skia/sgl/SkPath.cpp new file mode 100644 index 0000000..afe8662 --- /dev/null +++ b/skia/sgl/SkPath.cpp @@ -0,0 +1,1369 @@ +/* libs/graphics/sgl/SkPath.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkPath.h" +#include "SkFlattenable.h" +#include "SkMath.h" + +/* + Stores the verbs and points as they are given to us, with exceptions: + - we only record "Close" if it was immediately preceeded by Line | Quad | Cubic + - we insert a Move(0,0) if Line | Quad | Cubic is our first command + + The iterator does more cleanup, especially if forceClose == true + 1. if we encounter Close, return a cons'd up Line() first (if the curr-pt != start-pt) + 2. if we encounter Move without a preceeding Close, and forceClose is true, goto #1 + 3. if we encounter Line | Quad | Cubic after Close, cons up a Move +*/ + +//////////////////////////////////////////////////////////////////////////// + +SkPath::SkPath() : fFastBoundsIsDirty(true), fFillType(kWinding_FillType) {} + +SkPath::SkPath(const SkPath& src) { + SkDEBUGCODE(src.validate();) + *this = src; +} + +SkPath::~SkPath() { + SkDEBUGCODE(this->validate();) +} + +SkPath& SkPath::operator=(const SkPath& src) { + SkDEBUGCODE(src.validate();) + + if (this != &src) { + fFastBounds = src.fFastBounds; + fPts = src.fPts; + fVerbs = src.fVerbs; + fFillType = src.fFillType; + fFastBoundsIsDirty = src.fFastBoundsIsDirty; + } + SkDEBUGCODE(this->validate();) + return *this; +} + +void SkPath::swap(SkPath& other) { + SkASSERT(&other != NULL); + + if (this != &other) { + SkTSwap<SkRect>(fFastBounds, other.fFastBounds); + fPts.swap(other.fPts); + fVerbs.swap(other.fVerbs); + SkTSwap<uint8_t>(fFillType, other.fFillType); + SkTSwap<uint8_t>(fFastBoundsIsDirty, other.fFastBoundsIsDirty); + } +} + +void SkPath::reset() { + SkDEBUGCODE(this->validate();) + + fPts.reset(); + fVerbs.reset(); + fFastBoundsIsDirty = true; +} + +void SkPath::rewind() { + SkDEBUGCODE(this->validate();) + + fPts.rewind(); + fVerbs.rewind(); + fFastBoundsIsDirty = true; +} + +bool SkPath::isEmpty() const { + SkDEBUGCODE(this->validate();) + + int count = fVerbs.count(); + return count == 0 || (count == 1 && fVerbs[0] == kMove_Verb); +} + +bool SkPath::isRect(SkRect*) const { + SkDEBUGCODE(this->validate();) + + SkASSERT(!"unimplemented"); + return false; +} + +int SkPath::getPoints(SkPoint copy[], int max) const { + SkDEBUGCODE(this->validate();) + + SkASSERT(max >= 0); + int count = fPts.count(); + if (copy && max > 0 && count > 0) { + memcpy(copy, fPts.begin(), sizeof(SkPoint) * SkMin32(max, count)); + } + return count; +} + +void SkPath::getLastPt(SkPoint* lastPt) const { + SkDEBUGCODE(this->validate();) + + if (lastPt) { + int count = fPts.count(); + if (count == 0) { + lastPt->set(0, 0); + } else { + *lastPt = fPts[count - 1]; + } + } +} + +void SkPath::setLastPt(SkScalar x, SkScalar y) { + SkDEBUGCODE(this->validate();) + + int count = fPts.count(); + if (count == 0) { + this->moveTo(x, y); + } else { + fPts[count - 1].set(x, y); + } +} + +#define ALWAYS_FAST_BOUNDS_FOR_NOW true + +void SkPath::computeBounds(SkRect* bounds, BoundsType bt) const { + SkDEBUGCODE(this->validate();) + + SkASSERT(bounds); + + // we BoundsType for now + + if (fFastBoundsIsDirty) { + fFastBoundsIsDirty = false; + if (fPts.count() <= 1) { // we ignore just 1 point (moveto) + fFastBounds.set(0, 0, 0, 0); + } else { + fFastBounds.set(fPts.begin(), fPts.count()); + } + } +#ifdef SK_DEBUG + else { // check that our cache is correct + SkRect r; + if (fPts.count() <= 1) { // we ignore just 1 point (moveto) + r.set(0, 0, 0, 0); + } else { + r.set(fPts.begin(), fPts.count()); + } + SkASSERT(r == fFastBounds); + } +#endif + + *bounds = fFastBounds; +} + +////////////////////////////////////////////////////////////////////////////// +// Construction methods + +void SkPath::incReserve(U16CPU inc) { + SkDEBUGCODE(this->validate();) + + fVerbs.setReserve(fVerbs.count() + inc); + fPts.setReserve(fPts.count() + inc); + + SkDEBUGCODE(this->validate();) +} + +void SkPath::moveTo(SkScalar x, SkScalar y) { + SkDEBUGCODE(this->validate();) + + int vc = fVerbs.count(); + SkPoint* pt; + + if (vc > 0 && fVerbs[vc - 1] == kMove_Verb) { + pt = &fPts[fPts.count() - 1]; + } else { + pt = fPts.append(); + *fVerbs.append() = kMove_Verb; + } + pt->set(x, y); + + fFastBoundsIsDirty = true; +} + +void SkPath::rMoveTo(SkScalar x, SkScalar y) { + SkPoint pt; + this->getLastPt(&pt); + this->moveTo(pt.fX + x, pt.fY + y); +} + +void SkPath::lineTo(SkScalar x, SkScalar y) { + SkDEBUGCODE(this->validate();) + + if (fVerbs.count() == 0) { + fPts.append()->set(0, 0); + *fVerbs.append() = kMove_Verb; + } + fPts.append()->set(x, y); + *fVerbs.append() = kLine_Verb; + + fFastBoundsIsDirty = true; +} + +void SkPath::rLineTo(SkScalar x, SkScalar y) { + SkPoint pt; + this->getLastPt(&pt); + this->lineTo(pt.fX + x, pt.fY + y); +} + +void SkPath::quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) { + SkDEBUGCODE(this->validate();) + + if (fVerbs.count() == 0) { + fPts.append()->set(0, 0); + *fVerbs.append() = kMove_Verb; + } + + SkPoint* pts = fPts.append(2); + pts[0].set(x1, y1); + pts[1].set(x2, y2); + *fVerbs.append() = kQuad_Verb; + + fFastBoundsIsDirty = true; +} + +void SkPath::rQuadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) { + SkPoint pt; + this->getLastPt(&pt); + this->quadTo(pt.fX + x1, pt.fY + y1, pt.fX + x2, pt.fY + y2); +} + +void SkPath::cubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, + SkScalar x3, SkScalar y3) { + SkDEBUGCODE(this->validate();) + + if (fVerbs.count() == 0) { + fPts.append()->set(0, 0); + *fVerbs.append() = kMove_Verb; + } + SkPoint* pts = fPts.append(3); + pts[0].set(x1, y1); + pts[1].set(x2, y2); + pts[2].set(x3, y3); + *fVerbs.append() = kCubic_Verb; + + fFastBoundsIsDirty = true; +} + +void SkPath::rCubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, + SkScalar x3, SkScalar y3) { + SkPoint pt; + this->getLastPt(&pt); + this->cubicTo(pt.fX + x1, pt.fY + y1, pt.fX + x2, pt.fY + y2, + pt.fX + x3, pt.fY + y3); +} + +void SkPath::close() { + SkDEBUGCODE(this->validate();) + + int count = fVerbs.count(); + if (count > 0) { + switch (fVerbs[count - 1]) { + case kLine_Verb: + case kQuad_Verb: + case kCubic_Verb: + *fVerbs.append() = kClose_Verb; + break; + default: + // don't add a close if the prev wasn't a primitive + break; + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void SkPath::addRect(const SkRect& rect, Direction dir) { + this->addRect(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, dir); +} + +void SkPath::addRect(SkScalar left, SkScalar top, SkScalar right, + SkScalar bottom, Direction dir) { + this->incReserve(5); + + this->moveTo(left, top); + if (dir == kCCW_Direction) { + this->lineTo(left, bottom); + this->lineTo(right, bottom); + this->lineTo(right, top); + } else { + this->lineTo(right, top); + this->lineTo(right, bottom); + this->lineTo(left, bottom); + } + this->close(); +} + +#define CUBIC_ARC_FACTOR ((SK_ScalarSqrt2 - SK_Scalar1) * 4 / 3) + +void SkPath::addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry, + Direction dir) { + SkScalar w = rect.width(); + SkScalar halfW = SkScalarHalf(w); + SkScalar h = rect.height(); + SkScalar halfH = SkScalarHalf(h); + + if (halfW <= 0 || halfH <= 0) { + return; + } + + bool skip_hori = rx >= halfW; + bool skip_vert = ry >= halfH; + + if (skip_hori && skip_vert) { + this->addOval(rect, dir); + return; + } + if (skip_hori) { + rx = halfW; + } else if (skip_vert) { + ry = halfH; + } + + SkScalar sx = SkScalarMul(rx, CUBIC_ARC_FACTOR); + SkScalar sy = SkScalarMul(ry, CUBIC_ARC_FACTOR); + + this->incReserve(17); + this->moveTo(rect.fRight - rx, rect.fTop); + if (dir == kCCW_Direction) { + if (!skip_hori) { + this->lineTo(rect.fLeft + rx, rect.fTop); // top + } + this->cubicTo(rect.fLeft + rx - sx, rect.fTop, + rect.fLeft, rect.fTop + ry - sy, + rect.fLeft, rect.fTop + ry); // top-left + if (!skip_vert) { + this->lineTo(rect.fLeft, rect.fBottom - ry); // left + } + this->cubicTo(rect.fLeft, rect.fBottom - ry + sy, + rect.fLeft + rx - sx, rect.fBottom, + rect.fLeft + rx, rect.fBottom); // bot-left + if (!skip_hori) { + this->lineTo(rect.fRight - rx, rect.fBottom); // bottom + } + this->cubicTo(rect.fRight - rx + sx, rect.fBottom, + rect.fRight, rect.fBottom - ry + sy, + rect.fRight, rect.fBottom - ry); // bot-right + if (!skip_vert) { + this->lineTo(rect.fRight, rect.fTop + ry); + } + this->cubicTo(rect.fRight, rect.fTop + ry - sy, + rect.fRight - rx + sx, rect.fTop, + rect.fRight - rx, rect.fTop); // top-right + } else { + this->cubicTo(rect.fRight - rx + sx, rect.fTop, + rect.fRight, rect.fTop + ry - sy, + rect.fRight, rect.fTop + ry); // top-right + if (!skip_vert) { + this->lineTo(rect.fRight, rect.fBottom - ry); + } + this->cubicTo(rect.fRight, rect.fBottom - ry + sy, + rect.fRight - rx + sx, rect.fBottom, + rect.fRight - rx, rect.fBottom); // bot-right + if (!skip_hori) { + this->lineTo(rect.fLeft + rx, rect.fBottom); // bottom + } + this->cubicTo(rect.fLeft + rx - sx, rect.fBottom, + rect.fLeft, rect.fBottom - ry + sy, + rect.fLeft, rect.fBottom - ry); // bot-left + if (!skip_vert) { + this->lineTo(rect.fLeft, rect.fTop + ry); // left + } + this->cubicTo(rect.fLeft, rect.fTop + ry - sy, + rect.fLeft + rx - sx, rect.fTop, + rect.fLeft + rx, rect.fTop); // top-left + if (!skip_hori) { + this->lineTo(rect.fRight - rx, rect.fTop); // top + } + } + this->close(); +} + +static void add_corner_arc(SkPath* path, const SkRect& rect, + SkScalar rx, SkScalar ry, int startAngle, + bool ccw, bool forceMoveTo = false) { + rx = SkMinScalar(SkScalarHalf(rect.width()), rx); + ry = SkMinScalar(SkScalarHalf(rect.height()), ry); + + SkRect r; + r.set(-rx, -ry, rx, ry); + + switch (startAngle) { + case 0: + r.offset(rect.fRight - r.fRight, rect.fBottom - r.fBottom); + break; + case 90: + r.offset(rect.fLeft - r.fLeft, rect.fBottom - r.fBottom); + break; + case 180: r.offset(rect.fLeft - r.fLeft, rect.fTop - r.fTop); break; + case 270: r.offset(rect.fRight - r.fRight, rect.fTop - r.fTop); break; + default: SkASSERT(!"unexpected startAngle in add_corner_arc"); + } + + SkScalar start = SkIntToScalar(startAngle); + SkScalar sweep = SkIntToScalar(90); + if (ccw) { + start += sweep; + sweep = -sweep; + } + + path->arcTo(r, start, sweep, forceMoveTo); +} + +void SkPath::addRoundRect(const SkRect& rect, const SkScalar rad[], + Direction dir) { + + if (kCW_Direction == dir) { + add_corner_arc(this, rect, rad[0], rad[1], 180, false, true); + add_corner_arc(this, rect, rad[2], rad[3], 270, false); + add_corner_arc(this, rect, rad[4], rad[5], 0, false); + add_corner_arc(this, rect, rad[6], rad[7], 90, false); + } else { + add_corner_arc(this, rect, rad[0], rad[1], 180, true, true); + add_corner_arc(this, rect, rad[6], rad[7], 90, true); + add_corner_arc(this, rect, rad[4], rad[5], 0, true); + add_corner_arc(this, rect, rad[2], rad[3], 270, true); + } +} + +void SkPath::addOval(const SkRect& oval, Direction dir) { + SkScalar cx = oval.centerX(); + SkScalar cy = oval.centerY(); + SkScalar rx = SkScalarHalf(oval.width()); + SkScalar ry = SkScalarHalf(oval.height()); +#if 1 // these seem faster than using quads (1/2 the number of edges) + SkScalar sx = SkScalarMul(rx, CUBIC_ARC_FACTOR); + SkScalar sy = SkScalarMul(ry, CUBIC_ARC_FACTOR); + + this->incReserve(13); + this->moveTo(cx + rx, cy); + if (dir == kCCW_Direction) { + this->cubicTo(cx + rx, cy - sy, cx + sx, cy - ry, cx, cy - ry); + this->cubicTo(cx - sx, cy - ry, cx - rx, cy - sy, cx - rx, cy); + this->cubicTo(cx - rx, cy + sy, cx - sx, cy + ry, cx, cy + ry); + this->cubicTo(cx + sx, cy + ry, cx + rx, cy + sy, cx + rx, cy); + } else { + this->cubicTo(cx + rx, cy + sy, cx + sx, cy + ry, cx, cy + ry); + this->cubicTo(cx - sx, cy + ry, cx - rx, cy + sy, cx - rx, cy); + this->cubicTo(cx - rx, cy - sy, cx - sx, cy - ry, cx, cy - ry); + this->cubicTo(cx + sx, cy - ry, cx + rx, cy - sy, cx + rx, cy); + } +#else + SkScalar sx = SkScalarMul(rx, SK_ScalarTanPIOver8); + SkScalar sy = SkScalarMul(ry, SK_ScalarTanPIOver8); + SkScalar mx = SkScalarMul(rx, SK_ScalarRoot2Over2); + SkScalar my = SkScalarMul(ry, SK_ScalarRoot2Over2); + + this->incReserve(16); + this->moveTo(cx + rx, cy); + if (dir == kCCW_Direction) { + this->quadTo(cx + rx, cy - sy, cx + mx, cy - my); + this->quadTo(cx + sx, cy - ry, cx + 0, cy - ry); + this->quadTo(cx - sx, cy - ry, cx - mx, cy - my); + this->quadTo(cx - rx, cy - sy, cx - rx, cy - 0); + this->quadTo(cx - rx, cy + sy, cx - mx, cy + my); + this->quadTo(cx - sx, cy + ry, cx - 0, cy + ry); + this->quadTo(cx + sx, cy + ry, cx + mx, cy + my); + this->quadTo(cx + rx, cy + sy, cx + rx, cy + 0); + } else { + this->quadTo(cx + rx, cy + sy, cx + mx, cy + my); + this->quadTo(cx + sx, cy + ry, cx - 0, cy + ry); + this->quadTo(cx - sx, cy + ry, cx - mx, cy + my); + this->quadTo(cx - rx, cy + sy, cx - rx, cy - 0); + this->quadTo(cx - rx, cy - sy, cx - mx, cy - my); + this->quadTo(cx - sx, cy - ry, cx + 0, cy - ry); + this->quadTo(cx + sx, cy - ry, cx + mx, cy - my); + this->quadTo(cx + rx, cy - sy, cx + rx, cy + 0); + } +#endif + this->close(); +} + +void SkPath::addCircle(SkScalar x, SkScalar y, SkScalar r, Direction dir) { + if (r > 0) { + SkRect rect; + rect.set(x - r, y - r, x + r, y + r); + this->addOval(rect, dir); + } +} + +#include "SkGeometry.h" + +static int build_arc_points(const SkRect& oval, SkScalar startAngle, + SkScalar sweepAngle, + SkPoint pts[kSkBuildQuadArcStorage]) { + SkVector start, stop; + + start.fY = SkScalarSinCos(SkDegreesToRadians(startAngle), &start.fX); + stop.fY = SkScalarSinCos(SkDegreesToRadians(startAngle + sweepAngle), + &stop.fX); + + SkMatrix matrix; + + matrix.setScale(SkScalarHalf(oval.width()), SkScalarHalf(oval.height())); + matrix.postTranslate(oval.centerX(), oval.centerY()); + + return SkBuildQuadArc(start, stop, + sweepAngle > 0 ? kCW_SkRotationDirection : kCCW_SkRotationDirection, + &matrix, pts); +} + +void SkPath::arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle, + bool forceMoveTo) { + if (oval.width() < 0 || oval.height() < 0) { + return; + } + + SkPoint pts[kSkBuildQuadArcStorage]; + int count = build_arc_points(oval, startAngle, sweepAngle, pts); + SkASSERT((count & 1) == 1); + + if (fVerbs.count() == 0) { + forceMoveTo = true; + } + this->incReserve(count); + forceMoveTo ? this->moveTo(pts[0]) : this->lineTo(pts[0]); + for (int i = 1; i < count; i += 2) { + this->quadTo(pts[i], pts[i+1]); + } +} + +void SkPath::addArc(const SkRect& oval, SkScalar startAngle, + SkScalar sweepAngle) { + if (oval.isEmpty() || 0 == sweepAngle) { + return; + } + + const SkScalar kFullCircleAngle = SkIntToScalar(360); + + if (sweepAngle >= kFullCircleAngle || sweepAngle <= -kFullCircleAngle) { + this->addOval(oval, sweepAngle > 0 ? kCW_Direction : kCCW_Direction); + return; + } + + SkPoint pts[kSkBuildQuadArcStorage]; + int count = build_arc_points(oval, startAngle, sweepAngle, pts); + + this->incReserve(count); + this->moveTo(pts[0]); + for (int i = 1; i < count; i += 2) { + this->quadTo(pts[i], pts[i+1]); + } +} + +/* + Need to handle the case when the angle is sharp, and our computed end-points + for the arc go behind pt1 and/or p2... +*/ +void SkPath::arcTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, + SkScalar radius) { + SkVector before, after; + + // need to know our prev pt so we can construct tangent vectors + { + SkPoint start; + this->getLastPt(&start); + before.setNormalize(x1 - start.fX, y1 - start.fY); + after.setNormalize(x2 - x1, y2 - y1); + } + + SkScalar cosh = SkPoint::DotProduct(before, after); + SkScalar sinh = SkPoint::CrossProduct(before, after); + + if (SkScalarNearlyZero(sinh)) { // angle is too tight + return; + } + + SkScalar dist = SkScalarMulDiv(radius, SK_Scalar1 - cosh, sinh); + if (dist < 0) { + dist = -dist; + } + + SkScalar xx = x1 - SkScalarMul(dist, before.fX); + SkScalar yy = y1 - SkScalarMul(dist, before.fY); + SkRotationDirection arcDir; + + // now turn before/after into normals + if (sinh > 0) { + before.rotateCCW(); + after.rotateCCW(); + arcDir = kCW_SkRotationDirection; + } else { + before.rotateCW(); + after.rotateCW(); + arcDir = kCCW_SkRotationDirection; + } + + SkMatrix matrix; + SkPoint pts[kSkBuildQuadArcStorage]; + + matrix.setScale(radius, radius); + matrix.postTranslate(xx - SkScalarMul(radius, before.fX), + yy - SkScalarMul(radius, before.fY)); + + int count = SkBuildQuadArc(before, after, arcDir, &matrix, pts); + + this->incReserve(count); + // [xx,yy] == pts[0] + this->lineTo(xx, yy); + for (int i = 1; i < count; i += 2) { + this->quadTo(pts[i], pts[i+1]); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void SkPath::addPath(const SkPath& path, SkScalar dx, SkScalar dy) { + SkMatrix matrix; + + matrix.setTranslate(dx, dy); + this->addPath(path, matrix); +} + +void SkPath::addPath(const SkPath& path, const SkMatrix& matrix) { + this->incReserve(path.fPts.count()); + + Iter iter(path, false); + SkPoint pts[4]; + Verb verb; + + SkMatrix::MapPtsProc proc = matrix.getMapPtsProc(); + + while ((verb = iter.next(pts)) != kDone_Verb) { + switch (verb) { + case kMove_Verb: + proc(matrix, &pts[0], &pts[0], 1); + this->moveTo(pts[0]); + break; + case kLine_Verb: + proc(matrix, &pts[1], &pts[1], 1); + this->lineTo(pts[1]); + break; + case kQuad_Verb: + proc(matrix, &pts[1], &pts[1], 2); + this->quadTo(pts[1], pts[2]); + break; + case kCubic_Verb: + proc(matrix, &pts[1], &pts[1], 3); + this->cubicTo(pts[1], pts[2], pts[3]); + break; + case kClose_Verb: + this->close(); + break; + default: + SkASSERT(!"unknown verb"); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static const uint8_t gPtsInVerb[] = { + 1, // kMove + 1, // kLine + 2, // kQuad + 3, // kCubic + 0, // kClose + 0 // kDone +}; + +// ignore the initial moveto, and stop when the 1st contour ends +void SkPath::pathTo(const SkPath& path) { + int i, vcount = path.fVerbs.count(); + if (vcount == 0) { + return; + } + + this->incReserve(vcount); + + const uint8_t* verbs = path.fVerbs.begin(); + const SkPoint* pts = path.fPts.begin() + 1; // 1 for the initial moveTo + + SkASSERT(verbs[0] == kMove_Verb); + for (i = 1; i < vcount; i++) { + switch (verbs[i]) { + case kLine_Verb: + this->lineTo(pts[0].fX, pts[0].fY); + break; + case kQuad_Verb: + this->quadTo(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY); + break; + case kCubic_Verb: + this->cubicTo(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, + pts[2].fX, pts[2].fY); + break; + case kClose_Verb: + return; + } + pts += gPtsInVerb[verbs[i]]; + } +} + +// ignore the last point of the 1st contour +void SkPath::reversePathTo(const SkPath& path) { + int i, vcount = path.fVerbs.count(); + if (vcount == 0) { + return; + } + + this->incReserve(vcount); + + const uint8_t* verbs = path.fVerbs.begin(); + const SkPoint* pts = path.fPts.begin(); + + SkASSERT(verbs[0] == kMove_Verb); + for (i = 1; i < vcount; i++) { + int n = gPtsInVerb[verbs[i]]; + if (n == 0) { + break; + } + pts += n; + } + + while (--i > 0) { + switch (verbs[i]) { + case kLine_Verb: + this->lineTo(pts[-1].fX, pts[-1].fY); + break; + case kQuad_Verb: + this->quadTo(pts[-1].fX, pts[-1].fY, pts[-2].fX, pts[-2].fY); + break; + case kCubic_Verb: + this->cubicTo(pts[-1].fX, pts[-1].fY, pts[-2].fX, pts[-2].fY, + pts[-3].fX, pts[-3].fY); + break; + default: + SkASSERT(!"bad verb"); + break; + } + pts -= gPtsInVerb[verbs[i]]; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void SkPath::offset(SkScalar dx, SkScalar dy, SkPath* dst) const { + SkMatrix matrix; + + matrix.setTranslate(dx, dy); + this->transform(matrix, dst); +} + +#include "SkGeometry.h" + +static void subdivide_quad_to(SkPath* path, const SkPoint pts[3], + int level = 2) { + if (--level >= 0) { + SkPoint tmp[5]; + + SkChopQuadAtHalf(pts, tmp); + subdivide_quad_to(path, &tmp[0], level); + subdivide_quad_to(path, &tmp[2], level); + } else { + path->quadTo(pts[1], pts[2]); + } +} + +static void subdivide_cubic_to(SkPath* path, const SkPoint pts[4], + int level = 2) { + if (--level >= 0) { + SkPoint tmp[7]; + + SkChopCubicAtHalf(pts, tmp); + subdivide_cubic_to(path, &tmp[0], level); + subdivide_cubic_to(path, &tmp[3], level); + } else { + path->cubicTo(pts[1], pts[2], pts[3]); + } +} + +void SkPath::transform(const SkMatrix& matrix, SkPath* dst) const { + if (dst == NULL) { + dst = (SkPath*)this; + } + + if (matrix.getType() & SkMatrix::kPerspective_Mask) { + SkPath tmp; + tmp.fFillType = fFillType; + + SkPath::Iter iter(*this, false); + SkPoint pts[4]; + SkPath::Verb verb; + + while ((verb = iter.next(pts)) != kDone_Verb) { + switch (verb) { + case kMove_Verb: + tmp.moveTo(pts[0]); + break; + case kLine_Verb: + tmp.lineTo(pts[1]); + break; + case kQuad_Verb: + subdivide_quad_to(&tmp, pts); + break; + case kCubic_Verb: + subdivide_cubic_to(&tmp, pts); + break; + case kClose_Verb: + tmp.close(); + break; + default: + SkASSERT(!"unknown verb"); + break; + } + } + + dst->swap(tmp); + matrix.mapPoints(dst->fPts.begin(), dst->fPts.count()); + } else { + // remember that dst might == this, so be sure to check + // fFastBoundsIsDirty before we set it + if (!fFastBoundsIsDirty && matrix.rectStaysRect() && fPts.count() > 1) { + // if we're empty, fastbounds should not be mapped + matrix.mapRect(&dst->fFastBounds, fFastBounds); + dst->fFastBoundsIsDirty = false; + } else { + dst->fFastBoundsIsDirty = true; + } + + if (this != dst) { + dst->fVerbs = fVerbs; + dst->fPts.setCount(fPts.count()); + dst->fFillType = fFillType; + } + matrix.mapPoints(dst->fPts.begin(), fPts.begin(), fPts.count()); + } +} + +void SkPath::updateBoundsCache() const { + if (fFastBoundsIsDirty) { + SkRect r; + this->computeBounds(&r, kFast_BoundsType); + SkASSERT(!fFastBoundsIsDirty); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +enum NeedMoveToState { + kAfterClose_NeedMoveToState, + kAfterCons_NeedMoveToState, + kAfterPrefix_NeedMoveToState +}; + +SkPath::Iter::Iter() { +#ifdef SK_DEBUG + fPts = NULL; + fMoveTo.fX = fMoveTo.fY = fLastPt.fX = fLastPt.fY = 0; + fForceClose = fNeedMoveTo = fCloseLine = false; +#endif + // need to init enough to make next() harmlessly return kDone_Verb + fVerbs = NULL; + fVerbStop = NULL; + fNeedClose = false; +} + +SkPath::Iter::Iter(const SkPath& path, bool forceClose) { + this->setPath(path, forceClose); +} + +void SkPath::Iter::setPath(const SkPath& path, bool forceClose) { + fPts = path.fPts.begin(); + fVerbs = path.fVerbs.begin(); + fVerbStop = path.fVerbs.end(); + fForceClose = SkToU8(forceClose); + fNeedClose = false; + fNeedMoveTo = kAfterPrefix_NeedMoveToState; +} + +bool SkPath::Iter::isClosedContour() const { + if (fVerbs == NULL || fVerbs == fVerbStop) { + return false; + } + if (fForceClose) { + return true; + } + + const uint8_t* verbs = fVerbs; + const uint8_t* stop = fVerbStop; + + if (kMove_Verb == *verbs) { + verbs += 1; // skip the initial moveto + } + + while (verbs < stop) { + unsigned v = *verbs++; + if (kMove_Verb == v) { + break; + } + if (kClose_Verb == v) { + return true; + } + } + return false; +} + +SkPath::Verb SkPath::Iter::autoClose(SkPoint pts[2]) { + if (fLastPt != fMoveTo) { + if (pts) { + pts[0] = fLastPt; + pts[1] = fMoveTo; + } + fLastPt = fMoveTo; + fCloseLine = true; + return kLine_Verb; + } + return kClose_Verb; +} + +bool SkPath::Iter::cons_moveTo(SkPoint pts[1]) { + if (fNeedMoveTo == kAfterClose_NeedMoveToState) { + if (pts) { + *pts = fMoveTo; + } + fNeedClose = fForceClose; + fNeedMoveTo = kAfterCons_NeedMoveToState; + fVerbs -= 1; + return true; + } + + if (fNeedMoveTo == kAfterCons_NeedMoveToState) { + if (pts) { + *pts = fMoveTo; + } + fNeedMoveTo = kAfterPrefix_NeedMoveToState; + } else { + SkASSERT(fNeedMoveTo == kAfterPrefix_NeedMoveToState); + if (pts) { + *pts = fPts[-1]; + } + } + return false; +} + +SkPath::Verb SkPath::Iter::next(SkPoint pts[4]) { + if (fVerbs == fVerbStop) { + if (fNeedClose) { + if (kLine_Verb == this->autoClose(pts)) { + return kLine_Verb; + } + fNeedClose = false; + return kClose_Verb; + } + return kDone_Verb; + } + + unsigned verb = *fVerbs++; + const SkPoint* srcPts = fPts; + + switch (verb) { + case kMove_Verb: + if (fNeedClose) { + fVerbs -= 1; + verb = this->autoClose(pts); + if (verb == kClose_Verb) { + fNeedClose = false; + } + return (Verb)verb; + } + if (fVerbs == fVerbStop) { // might be a trailing moveto + return kDone_Verb; + } + fMoveTo = *srcPts; + if (pts) { + pts[0] = *srcPts; + } + srcPts += 1; + fNeedMoveTo = kAfterCons_NeedMoveToState; + fNeedClose = fForceClose; + break; + case kLine_Verb: + if (this->cons_moveTo(pts)) { + return kMove_Verb; + } + if (pts) { + pts[1] = srcPts[0]; + } + fLastPt = srcPts[0]; + fCloseLine = false; + srcPts += 1; + break; + case kQuad_Verb: + if (this->cons_moveTo(pts)) { + return kMove_Verb; + } + if (pts) { + memcpy(&pts[1], srcPts, 2 * sizeof(SkPoint)); + } + fLastPt = srcPts[1]; + srcPts += 2; + break; + case kCubic_Verb: + if (this->cons_moveTo(pts)) { + return kMove_Verb; + } + if (pts) { + memcpy(&pts[1], srcPts, 3 * sizeof(SkPoint)); + } + fLastPt = srcPts[2]; + srcPts += 3; + break; + case kClose_Verb: + verb = this->autoClose(pts); + if (verb == kLine_Verb) { + fVerbs -= 1; + } else { + fNeedClose = false; + } + fNeedMoveTo = kAfterClose_NeedMoveToState; + break; + } + fPts = srcPts; + return (Verb)verb; +} + +/////////////////////////////////////////////////////////////////////////////// + +static bool exceeds_dist(const SkScalar p[], const SkScalar q[], SkScalar dist, + int count) { + SkASSERT(dist > 0); + + count *= 2; + for (int i = 0; i < count; i++) { + if (SkScalarAbs(p[i] - q[i]) > dist) { + return true; + } + } + return false; +} + +static void subdivide_quad(SkPath* dst, const SkPoint pts[3], SkScalar dist, + int subLevel = 4) { + if (--subLevel >= 0 && exceeds_dist(&pts[0].fX, &pts[1].fX, dist, 4)) { + SkPoint tmp[5]; + SkChopQuadAtHalf(pts, tmp); + + subdivide_quad(dst, &tmp[0], dist, subLevel); + subdivide_quad(dst, &tmp[2], dist, subLevel); + } else { + dst->quadTo(pts[1], pts[2]); + } +} + +static void subdivide_cubic(SkPath* dst, const SkPoint pts[4], SkScalar dist, + int subLevel = 4) { + if (--subLevel >= 0 && exceeds_dist(&pts[0].fX, &pts[1].fX, dist, 6)) { + SkPoint tmp[7]; + SkChopCubicAtHalf(pts, tmp); + + subdivide_cubic(dst, &tmp[0], dist, subLevel); + subdivide_cubic(dst, &tmp[3], dist, subLevel); + } else { + dst->cubicTo(pts[1], pts[2], pts[3]); + } +} + +void SkPath::subdivide(SkScalar dist, bool bendLines, SkPath* dst) const { + SkPath tmpPath; + if (NULL == dst || this == dst) { + dst = &tmpPath; + } + + SkPath::Iter iter(*this, false); + SkPoint pts[4]; + + for (;;) { + switch (iter.next(pts)) { + case SkPath::kMove_Verb: + dst->moveTo(pts[0]); + break; + case SkPath::kLine_Verb: + if (!bendLines) { + dst->lineTo(pts[1]); + break; + } + // construct a quad from the line + pts[2] = pts[1]; + pts[1].set(SkScalarAve(pts[0].fX, pts[2].fX), + SkScalarAve(pts[0].fY, pts[2].fY)); + // fall through to the quad case + case SkPath::kQuad_Verb: + subdivide_quad(dst, pts, dist); + break; + case SkPath::kCubic_Verb: + subdivide_cubic(dst, pts, dist); + break; + case SkPath::kClose_Verb: + dst->close(); + break; + case SkPath::kDone_Verb: + goto DONE; + } + } +DONE: + if (&tmpPath == dst) { // i.e. the dst should be us + dst->swap(*(SkPath*)this); + } +} + +/////////////////////////////////////////////////////////////////////// +/* + Format in flattened buffer: [ptCount, verbCount, pts[], verbs[]] +*/ + +void SkPath::flatten(SkFlattenableWriteBuffer& buffer) const { + SkDEBUGCODE(this->validate();) + + buffer.write32(fPts.count()); + buffer.write32(fVerbs.count()); + buffer.write32(fFillType); + buffer.writeMul4(fPts.begin(), sizeof(SkPoint) * fPts.count()); + buffer.writePad(fVerbs.begin(), fVerbs.count()); +} + +void SkPath::unflatten(SkFlattenableReadBuffer& buffer) { + fPts.setCount(buffer.readS32()); + fVerbs.setCount(buffer.readS32()); + fFillType = buffer.readS32(); + buffer.read(fPts.begin(), sizeof(SkPoint) * fPts.count()); + buffer.read(fVerbs.begin(), fVerbs.count()); + + fFastBoundsIsDirty = true; + + SkDEBUGCODE(this->validate();) +} + +/////////////////////////////////////////////////////////////////////////////// + +#include "SkString.h" +#include "SkStream.h" + +static void write_scalar(SkWStream* stream, SkScalar value) { + char buffer[SkStrAppendScalar_MaxSize]; + char* stop = SkStrAppendScalar(buffer, value); + stream->write(buffer, stop - buffer); +} + +static void append_scalars(SkWStream* stream, char verb, const SkScalar data[], + int count) { + stream->write(&verb, 1); + write_scalar(stream, data[0]); + for (int i = 1; i < count; i++) { + if (data[i] >= 0) { + // can skip the separater if data[i] is negative + stream->write(" ", 1); + } + write_scalar(stream, data[i]); + } +} + +void SkPath::toString(SkString* str) const { + SkDynamicMemoryWStream stream; + + SkPath::Iter iter(*this, false); + SkPoint pts[4]; + + for (;;) { + switch (iter.next(pts)) { + case SkPath::kMove_Verb: + append_scalars(&stream, 'M', &pts[0].fX, 2); + break; + case SkPath::kLine_Verb: + append_scalars(&stream, 'L', &pts[1].fX, 2); + break; + case SkPath::kQuad_Verb: + append_scalars(&stream, 'Q', &pts[1].fX, 4); + break; + case SkPath::kCubic_Verb: + append_scalars(&stream, 'C', &pts[1].fX, 6); + break; + case SkPath::kClose_Verb: + stream.write("Z", 1); + break; + case SkPath::kDone_Verb: + str->resize(stream.getOffset()); + stream.copyTo(str->writable_str()); + return; + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_DEBUG + +void SkPath::validate() const { + SkASSERT(this != NULL); + SkASSERT((fFillType & ~3) == 0); + if (!fFastBoundsIsDirty) { + SkASSERT(fFastBounds.width() >= 0 && fFastBounds.height() >= 0); + } + fPts.validate(); + fVerbs.validate(); +} + +#if 0 // test to ensure that the iterator returns the same data as the path +void SkPath::test() const +{ + Iter iter(*this, false); + SkPoint pts[4]; + Verb verb; + + const uint8_t* verbs = fVerbs.begin(); + const SkPoint* points = fPts.begin(); + + while ((verb = iter.next(pts)) != kDone_Verb) + { + SkASSERT(*verbs == verb); + verbs += 1; + + int count; + switch (verb) { + case kMove_Verb: + count = 1; + break; + case kLine_Verb: + count = 2; + break; + case kQuad_Verb: + count = 3; + break; + case kCubic_Verb: + count = 4; + break; + case kClose_Verb: + default: + count = 0; + break; + } + if (count > 1) + points -= 1; + SkASSERT(memcmp(pts, points, count * sizeof(SkPoint)) == 0); + points += count; + } + + int vc = fVerbs.count(), pc = fPts.count(); + if (vc && fVerbs.begin()[vc-1] == kMove_Verb) + { + vc -= 1; + pc -= 1; + } + SkASSERT(verbs - fVerbs.begin() == vc); + SkASSERT(points - fPts.begin() == pc); +} +#endif + +void SkPath::dump(bool forceClose, const char title[]) const { + Iter iter(*this, forceClose); + SkPoint pts[4]; + Verb verb; + + SkDebugf("path: forceClose=%s %s\n", forceClose ? "true" : "false", + title ? title : ""); + + while ((verb = iter.next(pts)) != kDone_Verb) { + switch (verb) { + case kMove_Verb: +#ifdef SK_CAN_USE_FLOAT + SkDebugf(" path: moveTo [%g %g]\n", + SkScalarToFloat(pts[0].fX), SkScalarToFloat(pts[0].fY)); +#else + SkDebugf(" path: moveTo [%x %x]\n", pts[0].fX, pts[0].fY); +#endif + break; + case kLine_Verb: +#ifdef SK_CAN_USE_FLOAT + SkDebugf(" path: lineTo [%g %g]\n", + SkScalarToFloat(pts[1].fX), SkScalarToFloat(pts[1].fY)); +#else + SkDebugf(" path: lineTo [%x %x]\n", pts[1].fX, pts[1].fY); +#endif + break; + case kQuad_Verb: +#ifdef SK_CAN_USE_FLOAT + SkDebugf(" path: quadTo [%g %g] [%g %g]\n", + SkScalarToFloat(pts[1].fX), SkScalarToFloat(pts[1].fY), + SkScalarToFloat(pts[2].fX), SkScalarToFloat(pts[2].fY)); +#else + SkDebugf(" path: quadTo [%x %x] [%x %x]\n", + pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY); +#endif + break; + case kCubic_Verb: +#ifdef SK_CAN_USE_FLOAT + SkDebugf(" path: cubeTo [%g %g] [%g %g] [%g %g]\n", + SkScalarToFloat(pts[1].fX), SkScalarToFloat(pts[1].fY), + SkScalarToFloat(pts[2].fX), SkScalarToFloat(pts[2].fY), + SkScalarToFloat(pts[3].fX), SkScalarToFloat(pts[3].fY)); +#else + SkDebugf(" path: cubeTo [%x %x] [%x %x] [%x %x]\n", + pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY, + pts[3].fX, pts[3].fY); +#endif + break; + case kClose_Verb: + SkDebugf(" path: close\n"); + break; + default: + SkDebugf(" path: UNKNOWN VERB %d, aborting dump...\n", verb); + verb = kDone_Verb; // stop the loop + break; + } + } + SkDebugf("path: done %s\n", title ? title : ""); +} + +#include "SkTSort.h" + +void SkPath::UnitTest() { +#ifdef SK_SUPPORT_UNITTEST + SkPath p; + SkRect r; + + r.set(0, 0, 10, 20); + p.addRect(r); + p.dump(false); + p.dump(true); + + { + int array[] = { 5, 3, 7, 2, 6, 1, 2, 9, 5, 0 }; + int i; + + for (i = 0; i < (int)SK_ARRAY_COUNT(array); i++) { + SkDebugf(" %d", array[i]); + } + SkDebugf("\n"); + SkTHeapSort<int>(array, SK_ARRAY_COUNT(array)); + for (i = 0; i < (int)SK_ARRAY_COUNT(array); i++) + SkDebugf(" %d", array[i]); + SkDebugf("\n"); + } + + { + SkPath p; + SkPoint pt; + + p.moveTo(SK_Scalar1, 0); + p.getLastPt(&pt); + SkASSERT(pt.fX == SK_Scalar1); + } +#endif +} + +#endif diff --git a/skia/sgl/SkPathEffect.cpp b/skia/sgl/SkPathEffect.cpp new file mode 100644 index 0000000..8321fca --- /dev/null +++ b/skia/sgl/SkPathEffect.cpp @@ -0,0 +1,142 @@ +/* libs/graphics/sgl/SkPathEffect.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkPathEffect.h" +#include "SkPath.h" +#include "SkBuffer.h" + +////////////////////////////////////////////////////////////////////////////////// + +SkPairPathEffect::SkPairPathEffect(SkPathEffect* pe0, SkPathEffect* pe1) + : fPE0(pe0), fPE1(pe1) +{ + SkASSERT(pe0); + SkASSERT(pe1); + fPE0->ref(); + fPE1->ref(); +} + +SkPairPathEffect::~SkPairPathEffect() +{ + fPE0->unref(); + fPE1->unref(); +} + +/* + Format: [oe0-factory][pe1-factory][pe0-size][pe0-data][pe1-data] +*/ +void SkPairPathEffect::flatten(SkFlattenableWriteBuffer& buffer) +{ + buffer.writeFlattenable(fPE0); + buffer.writeFlattenable(fPE1); +} + +SkPairPathEffect::SkPairPathEffect(SkFlattenableReadBuffer& buffer) +{ + fPE0 = (SkPathEffect*)buffer.readFlattenable(); + fPE1 = (SkPathEffect*)buffer.readFlattenable(); +} + +////////////////////////////////////////////////////////////////////////////////// + +bool SkComposePathEffect::filterPath(SkPath* dst, const SkPath& src, SkScalar* width) +{ + SkPath tmp; + const SkPath* ptr = &src; + + if (fPE1->filterPath(&tmp, src, width)) + ptr = &tmp; + return fPE0->filterPath(dst, *ptr, width); +} + +////////////////////////////////////////////////////////////////////////////////// + +bool SkSumPathEffect::filterPath(SkPath* dst, const SkPath& src, SkScalar* width) +{ + // use bit-or so that we always call both, even if the first one succeeds + return fPE0->filterPath(dst, src, width) | fPE1->filterPath(dst, src, width); +} + +///////////////////////////////////////////////////////////////////////////////// + +#include "SkStroke.h" + +SkStrokePathEffect::SkStrokePathEffect(const SkPaint& paint) + : fWidth(paint.getStrokeWidth()), fMiter(paint.getStrokeMiter()), + fStyle(SkToU8(paint.getStyle())), fJoin(SkToU8(paint.getStrokeJoin())), fCap(SkToU8(paint.getStrokeCap())) +{ +} + +SkStrokePathEffect::SkStrokePathEffect(SkScalar width, SkPaint::Style style, SkPaint::Join join, SkPaint::Cap cap, SkScalar miter) + : fWidth(width), fMiter(miter), fStyle(SkToU8(style)), fJoin(SkToU8(join)), fCap(SkToU8(cap)) +{ + if (miter < 0) // signal they want the default + fMiter = SK_DefaultMiterLimit; +} + +bool SkStrokePathEffect::filterPath(SkPath* dst, const SkPath& src, SkScalar* width) +{ + if (fWidth < 0 || fStyle == SkPaint::kFill_Style) + return false; + + if (fStyle == SkPaint::kStroke_Style && fWidth == 0) // hairline + { + *width = 0; + return true; + } + + SkStroke stroke; + + stroke.setWidth(fWidth); + stroke.setMiterLimit(fMiter); + stroke.setJoin((SkPaint::Join)fJoin); + stroke.setCap((SkPaint::Cap)fCap); + stroke.setDoFill(fStyle == SkPaint::kStrokeAndFill_Style); + + stroke.strokePath(src, dst); + return true; +} + +SkFlattenable::Factory SkStrokePathEffect::getFactory() +{ + return CreateProc; +} + +SkFlattenable* SkStrokePathEffect::CreateProc(SkFlattenableReadBuffer& buffer) +{ + return SkNEW_ARGS(SkStrokePathEffect, (buffer)); +} + +void SkStrokePathEffect::flatten(SkFlattenableWriteBuffer& buffer) +{ + buffer.writeScalar(fWidth); + buffer.writeScalar(fMiter); + buffer.write8(fStyle); + buffer.write8(fJoin); + buffer.write8(fCap); +} + +SkStrokePathEffect::SkStrokePathEffect(SkFlattenableReadBuffer& buffer) +{ + fWidth = buffer.readScalar(); + fMiter = buffer.readScalar(); + fStyle = buffer.readU8(); + fJoin = buffer.readU8(); + fCap = buffer.readU8(); +} + + diff --git a/skia/sgl/SkPathMeasure.cpp b/skia/sgl/SkPathMeasure.cpp new file mode 100644 index 0000000..e877d0f --- /dev/null +++ b/skia/sgl/SkPathMeasure.cpp @@ -0,0 +1,598 @@ +/* + * Copyright (C) 2006-2008 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "SkPathMeasure.h" +#include "SkGeometry.h" +#include "SkPath.h" +#include "SkTSearch.h" + +// these must be 0,1,2 since they are in our 2-bit field +enum { + kLine_SegType, + kCloseLine_SegType, + kQuad_SegType, + kCubic_SegType +}; + +#define kMaxTValue 32767 + +static inline SkScalar tValue2Scalar(int t) { + SkASSERT((unsigned)t <= kMaxTValue); + +#ifdef SK_SCALAR_IS_FLOAT + return t * 3.05185e-5f; // t / 32767 +#else + return (t + (t >> 14)) << 1; +#endif +} + +SkScalar SkPathMeasure::Segment::getScalarT() const { + return tValue2Scalar(fTValue); +} + +const SkPathMeasure::Segment* SkPathMeasure::NextSegment(const Segment* seg) { + unsigned ptIndex = seg->fPtIndex; + + do { + ++seg; + } while (seg->fPtIndex == ptIndex); + return seg; +} + +/////////////////////////////////////////////////////////////////////////////// + +static inline int tspan_big_enough(int tspan) { + SkASSERT((unsigned)tspan <= kMaxTValue); + return tspan >> 10; +} + +#if 0 +static inline bool tangents_too_curvy(const SkVector& tan0, SkVector& tan1) { + static const SkScalar kFlatEnoughTangentDotProd = SK_Scalar1 * 99 / 100; + + SkASSERT(kFlatEnoughTangentDotProd > 0 && + kFlatEnoughTangentDotProd < SK_Scalar1); + + return SkPoint::DotProduct(tan0, tan1) < kFlatEnoughTangentDotProd; +} +#endif + +// can't use tangents, since we need [0..1..................2] to be seen +// as definitely not a line (it is when drawn, but not parametrically) +// so we compare midpoints +#define CHEAP_DIST_LIMIT (SK_Scalar1/2) // just made this value up + +static bool quad_too_curvy(const SkPoint pts[3]) { + // diff = (a/4 + b/2 + c/4) - (a/2 + c/2) + // diff = -a/4 + b/2 - c/4 + SkScalar dx = SkScalarHalf(pts[1].fX) - + SkScalarHalf(SkScalarHalf(pts[0].fX + pts[2].fX)); + SkScalar dy = SkScalarHalf(pts[1].fY) - + SkScalarHalf(SkScalarHalf(pts[0].fY + pts[2].fY)); + + SkScalar dist = SkMaxScalar(SkScalarAbs(dx), SkScalarAbs(dy)); + return dist > CHEAP_DIST_LIMIT; +} + +static bool cheap_dist_exceeds_limit(const SkPoint& pt, + SkScalar x, SkScalar y) { + SkScalar dist = SkMaxScalar(SkScalarAbs(x - pt.fX), SkScalarAbs(y - pt.fY)); + // just made up the 1/2 + return dist > CHEAP_DIST_LIMIT; +} + +static bool cubic_too_curvy(const SkPoint pts[4]) { + return cheap_dist_exceeds_limit(pts[1], + SkScalarInterp(pts[0].fX, pts[3].fX, SK_Scalar1/3), + SkScalarInterp(pts[0].fY, pts[3].fY, SK_Scalar1/3)) + || + cheap_dist_exceeds_limit(pts[2], + SkScalarInterp(pts[0].fX, pts[3].fX, SK_Scalar1*2/3), + SkScalarInterp(pts[0].fY, pts[3].fY, SK_Scalar1*2/3)); +} + +SkScalar SkPathMeasure::compute_quad_segs(const SkPoint pts[3], + SkScalar distance, int mint, int maxt, int ptIndex) { + if (tspan_big_enough(maxt - mint) && quad_too_curvy(pts)) { + SkPoint tmp[5]; + int halft = (mint + maxt) >> 1; + + SkChopQuadAtHalf(pts, tmp); + distance = this->compute_quad_segs(tmp, distance, mint, halft, ptIndex); + distance = this->compute_quad_segs(&tmp[2], distance, halft, maxt, ptIndex); + } else { + SkScalar d = SkPoint::Distance(pts[0], pts[2]); + SkASSERT(d >= 0); + if (!SkScalarNearlyZero(d)) { + distance += d; + Segment* seg = fSegments.append(); + seg->fDistance = distance; + seg->fPtIndex = ptIndex; + seg->fType = kQuad_SegType; + seg->fTValue = maxt; + } + } + return distance; +} + +SkScalar SkPathMeasure::compute_cubic_segs(const SkPoint pts[4], + SkScalar distance, int mint, int maxt, int ptIndex) { + if (tspan_big_enough(maxt - mint) && cubic_too_curvy(pts)) { + SkPoint tmp[7]; + int halft = (mint + maxt) >> 1; + + SkChopCubicAtHalf(pts, tmp); + distance = this->compute_cubic_segs(tmp, distance, mint, halft, ptIndex); + distance = this->compute_cubic_segs(&tmp[3], distance, halft, maxt, ptIndex); + } else { + SkScalar d = SkPoint::Distance(pts[0], pts[3]); + SkASSERT(d >= 0); + if (!SkScalarNearlyZero(d)) { + distance += d; + Segment* seg = fSegments.append(); + seg->fDistance = distance; + seg->fPtIndex = ptIndex; + seg->fType = kCubic_SegType; + seg->fTValue = maxt; + } + } + return distance; +} + +void SkPathMeasure::buildSegments() { + SkPoint pts[4]; + int ptIndex = fFirstPtIndex; + SkScalar d, distance = 0; + bool isClosed = fForceClosed; + bool firstMoveTo = ptIndex < 0; + Segment* seg; + + fSegments.reset(); + for (;;) { + switch (fIter.next(pts)) { + case SkPath::kMove_Verb: + if (!firstMoveTo) { + goto DONE; + } + ptIndex += 1; + firstMoveTo = false; + break; + + case SkPath::kLine_Verb: + d = SkPoint::Distance(pts[0], pts[1]); + SkASSERT(d >= 0); + if (!SkScalarNearlyZero(d)) { + distance += d; + seg = fSegments.append(); + seg->fDistance = distance; + seg->fPtIndex = ptIndex; + seg->fType = fIter.isCloseLine() ? + kCloseLine_SegType : kLine_SegType; + seg->fTValue = kMaxTValue; + } + ptIndex += !fIter.isCloseLine(); + break; + + case SkPath::kQuad_Verb: + distance = this->compute_quad_segs(pts, distance, 0, + kMaxTValue, ptIndex); + ptIndex += 2; + break; + + case SkPath::kCubic_Verb: + distance = this->compute_cubic_segs(pts, distance, 0, + kMaxTValue, ptIndex); + ptIndex += 3; + break; + + case SkPath::kClose_Verb: + isClosed = true; + break; + + case SkPath::kDone_Verb: + goto DONE; + } + } +DONE: + fLength = distance; + fIsClosed = isClosed; + fFirstPtIndex = ptIndex + 1; + +#ifdef SK_DEBUG + { + const Segment* seg = fSegments.begin(); + const Segment* stop = fSegments.end(); + unsigned ptIndex = 0; + SkScalar distance = 0; + + while (seg < stop) { + SkASSERT(seg->fDistance > distance); + SkASSERT(seg->fPtIndex >= ptIndex); + SkASSERT(seg->fTValue > 0); + + const Segment* s = seg; + while (s < stop - 1 && s[0].fPtIndex == s[1].fPtIndex) { + SkASSERT(s[0].fType == s[1].fType); + SkASSERT(s[0].fTValue < s[1].fTValue); + s += 1; + } + + distance = seg->fDistance; + ptIndex = seg->fPtIndex; + seg += 1; + } + // SkDebugf("\n"); + } +#endif +} + +// marked as a friend in SkPath.h +const SkPoint* sk_get_path_points(const SkPath& path, int index) { + return &path.fPts[index]; +} + +static void compute_pos_tan(const SkPath& path, int firstPtIndex, int ptIndex, + int segType, SkScalar t, SkPoint* pos, SkVector* tangent) { + const SkPoint* pts = sk_get_path_points(path, ptIndex); + + switch (segType) { + case kLine_SegType: + case kCloseLine_SegType: { + const SkPoint* endp = (segType == kLine_SegType) ? + &pts[1] : + sk_get_path_points(path, firstPtIndex); + + if (pos) { + pos->set(SkScalarInterp(pts[0].fX, endp->fX, t), + SkScalarInterp(pts[0].fY, endp->fY, t)); + } + if (tangent) { + tangent->setNormalize(endp->fX - pts[0].fX, endp->fY - pts[0].fY); + } + break; + } + case kQuad_SegType: + SkEvalQuadAt(pts, t, pos, tangent); + if (tangent) { + tangent->normalize(); + } + break; + case kCubic_SegType: + SkEvalCubicAt(pts, t, pos, tangent, NULL); + if (tangent) { + tangent->normalize(); + } + break; + default: + SkASSERT(!"unknown segType"); + } +} + +static void seg_to(const SkPath& src, int firstPtIndex, int ptIndex, + int segType, SkScalar startT, SkScalar stopT, SkPath* dst) { + SkASSERT(startT >= 0 && startT <= SK_Scalar1); + SkASSERT(stopT >= 0 && stopT <= SK_Scalar1); + SkASSERT(startT <= stopT); + + if (SkScalarNearlyZero(stopT - startT)) { + return; + } + + const SkPoint* pts = sk_get_path_points(src, ptIndex); + SkPoint tmp0[7], tmp1[7]; + + switch (segType) { + case kLine_SegType: + case kCloseLine_SegType: { + const SkPoint* endp = (segType == kLine_SegType) ? + &pts[1] : + sk_get_path_points(src, firstPtIndex); + + if (stopT == kMaxTValue) { + dst->lineTo(*endp); + } else { + dst->lineTo(SkScalarInterp(pts[0].fX, endp->fX, stopT), + SkScalarInterp(pts[0].fY, endp->fY, stopT)); + } + break; + } + case kQuad_SegType: + if (startT == 0) { + if (stopT == SK_Scalar1) { + dst->quadTo(pts[1], pts[2]); + } else { + SkChopQuadAt(pts, tmp0, stopT); + dst->quadTo(tmp0[1], tmp0[2]); + } + } else { + SkChopQuadAt(pts, tmp0, startT); + if (stopT == SK_Scalar1) { + dst->quadTo(tmp0[3], tmp0[4]); + } else { + SkChopQuadAt(&tmp0[2], tmp1, SkScalarDiv(stopT - startT, + SK_Scalar1 - startT)); + dst->quadTo(tmp1[1], tmp1[2]); + } + } + break; + case kCubic_SegType: + if (startT == 0) { + if (stopT == SK_Scalar1) { + dst->cubicTo(pts[1], pts[2], pts[3]); + } else { + SkChopCubicAt(pts, tmp0, stopT); + dst->cubicTo(tmp0[1], tmp0[2], tmp0[3]); + } + } else { + SkChopCubicAt(pts, tmp0, startT); + if (stopT == SK_Scalar1) { + dst->cubicTo(tmp0[4], tmp0[5], tmp0[6]); + } else { + SkChopCubicAt(&tmp0[3], tmp1, SkScalarDiv(stopT - startT, + SK_Scalar1 - startT)); + dst->cubicTo(tmp1[1], tmp1[2], tmp1[3]); + } + } + break; + default: + SkASSERT(!"unknown segType"); + sk_throw(); + } +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +SkPathMeasure::SkPathMeasure() { + fPath = NULL; + fLength = -1; // signal we need to compute it + fForceClosed = false; + fFirstPtIndex = -1; +} + +SkPathMeasure::SkPathMeasure(const SkPath& path, bool forceClosed) { + fPath = &path; + fLength = -1; // signal we need to compute it + fForceClosed = forceClosed; + fFirstPtIndex = -1; + + fIter.setPath(path, forceClosed); +} + +SkPathMeasure::~SkPathMeasure() {} + +/** Assign a new path, or null to have none. +*/ +void SkPathMeasure::setPath(const SkPath* path, bool forceClosed) { + fPath = path; + fLength = -1; // signal we need to compute it + fForceClosed = forceClosed; + fFirstPtIndex = -1; + + if (path) { + fIter.setPath(*path, forceClosed); + } + fSegments.reset(); +} + +SkScalar SkPathMeasure::getLength() { + if (fPath == NULL) { + return 0; + } + if (fLength < 0) { + this->buildSegments(); + } + SkASSERT(fLength >= 0); + return fLength; +} + +const SkPathMeasure::Segment* SkPathMeasure::distanceToSegment( + SkScalar distance, SkScalar* t) { + SkDEBUGCODE(SkScalar length = ) this->getLength(); + SkASSERT(distance >= 0 && distance <= length); + + const Segment* seg = fSegments.begin(); + int count = fSegments.count(); + + int index = SkTSearch<SkScalar>(&seg->fDistance, count, distance, + sizeof(Segment)); + // don't care if we hit an exact match or not, so we xor index if it is negative + index ^= (index >> 31); + seg = &seg[index]; + + // now interpolate t-values with the prev segment (if possible) + SkScalar startT = 0, startD = 0; + // check if the prev segment is legal, and references the same set of points + if (index > 0) { + startD = seg[-1].fDistance; + if (seg[-1].fPtIndex == seg->fPtIndex) { + SkASSERT(seg[-1].fType == seg->fType); + startT = seg[-1].getScalarT(); + } + } + + SkASSERT(seg->getScalarT() > startT); + SkASSERT(distance >= startD); + SkASSERT(seg->fDistance > startD); + + *t = startT + SkScalarMulDiv(seg->getScalarT() - startT, + distance - startD, + seg->fDistance - startD); + return seg; +} + +bool SkPathMeasure::getPosTan(SkScalar distance, SkPoint* pos, + SkVector* tangent) { + SkASSERT(fPath); + if (fPath == NULL) { + EMPTY: + return false; + } + + SkScalar length = this->getLength(); // call this to force computing it + int count = fSegments.count(); + + if (count == 0 || length == 0) { + goto EMPTY; + } + + // pin the distance to a legal range + if (distance < 0) { + distance = 0; + } else if (distance > length) { + distance = length; + } + + SkScalar t; + const Segment* seg = this->distanceToSegment(distance, &t); + + compute_pos_tan(*fPath, fSegments[0].fPtIndex, seg->fPtIndex, seg->fType, + t, pos, tangent); + return true; +} + +bool SkPathMeasure::getMatrix(SkScalar distance, SkMatrix* matrix, + MatrixFlags flags) { + SkPoint position; + SkVector tangent; + + if (this->getPosTan(distance, &position, &tangent)) { + if (matrix) { + if (flags & kGetTangent_MatrixFlag) { + matrix->setSinCos(tangent.fY, tangent.fX, 0, 0); + } else { + matrix->reset(); + } + if (flags & kGetPosition_MatrixFlag) { + matrix->postTranslate(position.fX, position.fY); + } + } + return true; + } + return false; +} + +bool SkPathMeasure::getSegment(SkScalar startD, SkScalar stopD, SkPath* dst, + bool startWithMoveTo) { + SkASSERT(dst); + + SkScalar length = this->getLength(); // ensure we have built our segments + + if (startD < 0) { + startD = 0; + } + if (stopD > length) { + stopD = length; + } + if (startD >= stopD) { + return false; + } + + SkPoint p; + SkScalar startT, stopT; + const Segment* seg = this->distanceToSegment(startD, &startT); + const Segment* stopSeg = this->distanceToSegment(stopD, &stopT); + SkASSERT(seg <= stopSeg); + + if (startWithMoveTo) { + compute_pos_tan(*fPath, fSegments[0].fPtIndex, seg->fPtIndex, + seg->fType, startT, &p, NULL); + dst->moveTo(p); + } + + if (seg->fPtIndex == stopSeg->fPtIndex) { + seg_to(*fPath, fSegments[0].fPtIndex, seg->fPtIndex, seg->fType, + startT, stopT, dst); + } else { + do { + seg_to(*fPath, fSegments[0].fPtIndex, seg->fPtIndex, seg->fType, + startT, SK_Scalar1, dst); + seg = SkPathMeasure::NextSegment(seg); + startT = 0; + } while (seg->fPtIndex < stopSeg->fPtIndex); + seg_to(*fPath, fSegments[0].fPtIndex, seg->fPtIndex, seg->fType, + 0, stopT, dst); + } + return true; +} + +bool SkPathMeasure::isClosed() { + (void)this->getLength(); + return fIsClosed; +} + +/** Move to the next contour in the path. Return true if one exists, or false if + we're done with the path. +*/ +bool SkPathMeasure::nextContour() { + fLength = -1; + return this->getLength() > 0; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_DEBUG + +void SkPathMeasure::dump() { + SkDebugf("pathmeas: length=%g, segs=%d\n", fLength, fSegments.count()); + + for (int i = 0; i < fSegments.count(); i++) { + const Segment* seg = &fSegments[i]; + SkDebugf("pathmeas: seg[%d] distance=%g, point=%d, t=%g, type=%d\n", + i, seg->fDistance, seg->fPtIndex, seg->getScalarT(), + seg->fType); + } +} + +void SkPathMeasure::UnitTest() { +#ifdef SK_SUPPORT_UNITTEST + SkPath path; + + path.moveTo(0, 0); + path.lineTo(SK_Scalar1, 0); + path.lineTo(SK_Scalar1, SK_Scalar1); + path.lineTo(0, SK_Scalar1); + + SkPathMeasure meas(path, true); + SkScalar length = meas.getLength(); + SkASSERT(length == SK_Scalar1*4); + + path.reset(); + path.moveTo(0, 0); + path.lineTo(SK_Scalar1*3, SK_Scalar1*4); + meas.setPath(&path, false); + length = meas.getLength(); + SkASSERT(length == SK_Scalar1*5); + + path.reset(); + path.addCircle(0, 0, SK_Scalar1); + meas.setPath(&path, true); + length = meas.getLength(); + SkDebugf("circle arc-length = %g\n", length); + + for (int i = 0; i < 8; i++) { + SkScalar d = length * i / 8; + SkPoint p; + SkVector v; + meas.getPosTan(d, &p, &v); + SkDebugf("circle arc-length=%g, pos[%g %g] tan[%g %g]\n", + d, p.fX, p.fY, v.fX, v.fY); + } +#endif +} + +#endif diff --git a/skia/sgl/SkPicture.cpp b/skia/sgl/SkPicture.cpp new file mode 100644 index 0000000..0847004 --- /dev/null +++ b/skia/sgl/SkPicture.cpp @@ -0,0 +1,238 @@ +/* +** +** Copyright 2007, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkPictureFlat.h" +#include "SkPicturePlayback.h" +#include "SkPictureRecord.h" + +#include "SkCanvas.h" +#include "SkChunkAlloc.h" +#include "SkPicture.h" +#include "SkRegion.h" +#include "SkStream.h" +#include "SkTDArray.h" +#include "SkTSearch.h" +#include "SkTime.h" + +#include "SkReader32.h" +#include "SkWriter32.h" + +#define DUMP_BUFFER_SIZE 65536 + +//#define ENABLE_TIME_DRAW // dumps milliseconds for each draw + + +#ifdef SK_DEBUG +// enable SK_DEBUG_TRACE to trace DrawType elements when +// recorded and played back +// #define SK_DEBUG_TRACE +// enable SK_DEBUG_SIZE to see the size of picture components +// #define SK_DEBUG_SIZE +// enable SK_DEBUG_DUMP to see the contents of recorded elements +// #define SK_DEBUG_DUMP +// enable SK_DEBUG_VALIDATE to check internal structures for consistency +// #define SK_DEBUG_VALIDATE +#endif + +#if defined SK_DEBUG_TRACE || defined SK_DEBUG_DUMP +const char* DrawTypeToString(DrawType drawType) { + switch (drawType) { + case UNUSED: SkDebugf("DrawType UNUSED\n"); SkASSERT(0); break; + case CLIP_PATH: return "CLIP_PATH"; + case CLIP_REGION: return "CLIP_REGION"; + case CLIP_RECT: return "CLIP_RECT"; + case CONCAT: return "CONCAT"; + case DRAW_BITMAP: return "DRAW_BITMAP"; + case DRAW_BITMAP_MATRIX: return "DRAW_BITMAP_MATRIX"; + case DRAW_BITMAP_RECT: return "DRAW_BITMAP_RECT"; + case DRAW_PAINT: return "DRAW_PAINT"; + case DRAW_PATH: return "DRAW_PATH"; + case DRAW_PICTURE: return "DRAW_PICTURE"; + case DRAW_POINTS: return "DRAW_POINTS"; + case DRAW_POS_TEXT: return "DRAW_POS_TEXT"; + case DRAW_POS_TEXT_H: return "DRAW_POS_TEXT_H"; + case DRAW_RECT_GENERAL: return "DRAW_RECT_GENERAL"; + case DRAW_RECT_SIMPLE: return "DRAW_RECT_SIMPLE"; + case DRAW_SPRITE: return "DRAW_SPRITE"; + case DRAW_TEXT: return "DRAW_TEXT"; + case DRAW_TEXT_ON_PATH: return "DRAW_TEXT_ON_PATH"; + case RESTORE: return "RESTORE"; + case ROTATE: return "ROTATE"; + case SAVE: return "SAVE"; + case SAVE_LAYER: return "SAVE_LAYER"; + case SCALE: return "SCALE"; + case SKEW: return "SKEW"; + case TRANSLATE: return "TRANSLATE"; + default: + SkDebugf("DrawType error 0x%08x\n", drawType); + SkASSERT(0); + break; + } + SkASSERT(0); + return NULL; +} +#endif + +#ifdef SK_DEBUG_VALIDATE +static void validateMatrix(const SkMatrix* matrix) { + SkScalar scaleX = matrix->getScaleX(); + SkScalar scaleY = matrix->getScaleY(); + SkScalar skewX = matrix->getSkewX(); + SkScalar skewY = matrix->getSkewY(); + SkScalar perspX = matrix->getPerspX(); + SkScalar perspY = matrix->getPerspY(); + if (scaleX != 0 && skewX != 0) + SkDebugf("scaleX != 0 && skewX != 0\n"); + SkASSERT(scaleX == 0 || skewX == 0); + SkASSERT(scaleY == 0 || skewY == 0); + SkASSERT(perspX == 0); + SkASSERT(perspY == 0); +} +#endif + + +/////////////////////////////////////////////////////////////////////////////// + +SkPicture::SkPicture() { + fRecord = NULL; + fPlayback = NULL; + fWidth = fHeight = 0; +} + +SkPicture::SkPicture(const SkPicture& src) : SkRefCnt() { + fWidth = src.fWidth; + fHeight = src.fHeight; + fRecord = NULL; + + /* We want to copy the src's playback. However, if that hasn't been built + yet, we need to fake a call to endRecording() without actually calling + it (since it is destructive, and we don't want to change src). + */ + if (src.fPlayback) { + fPlayback = SkNEW_ARGS(SkPicturePlayback, (*src.fPlayback)); + } else if (src.fRecord) { + // here we do a fake src.endRecording() + fPlayback = SkNEW_ARGS(SkPicturePlayback, (*src.fRecord)); + } else { + fPlayback = NULL; + } +} + +SkPicture::~SkPicture() { + fRecord->safeUnref(); + SkDELETE(fPlayback); +} + +void SkPicture::swap(SkPicture& other) { + SkTSwap(fRecord, other.fRecord); + SkTSwap(fPlayback, other.fPlayback); + SkTSwap(fWidth, other.fWidth); + SkTSwap(fHeight, other.fHeight); +} + +/////////////////////////////////////////////////////////////////////////////// + +SkCanvas* SkPicture::beginRecording(int width, int height) { + if (fPlayback) { + SkDELETE(fPlayback); + fPlayback = NULL; + } + + if (NULL != fRecord) { + fRecord->unref(); + fRecord = NULL; + } + + fRecord = SkNEW(SkPictureRecord); + + fWidth = width; + fHeight = height; + + SkBitmap bm; + bm.setConfig(SkBitmap::kNo_Config, width, height); + fRecord->setBitmapDevice(bm); + + return fRecord; +} + +SkCanvas* SkPicture::getRecordingCanvas() const { + // will be null if we are not recording + return fRecord; +} + +void SkPicture::endRecording() { + if (NULL == fPlayback) { + if (NULL != fRecord) { + fPlayback = SkNEW_ARGS(SkPicturePlayback, (*fRecord)); + fRecord->unref(); + fRecord = NULL; + } + } + SkASSERT(NULL == fRecord); +} + +void SkPicture::draw(SkCanvas* surface) { + this->endRecording(); + if (fPlayback) { + fPlayback->draw(*surface); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +#include "SkStream.h" + +#define PICTURE_VERSION 1 + +SkPicture::SkPicture(SkStream* stream) : SkRefCnt() { + if (stream->readU32() != PICTURE_VERSION) { + sk_throw(); + } + + fWidth = stream->readU32(); + fHeight = stream->readU32(); + + fRecord = NULL; + fPlayback = NULL; + + if (stream->readBool()) { + fPlayback = SkNEW_ARGS(SkPicturePlayback, (stream)); + } +} + +void SkPicture::serialize(SkWStream* stream) const { + SkPicturePlayback* playback = fPlayback; + + if (NULL == playback && fRecord) { + playback = SkNEW_ARGS(SkPicturePlayback, (*fRecord)); + } + + stream->write32(PICTURE_VERSION); + stream->write32(fWidth); + stream->write32(fHeight); + if (playback) { + stream->writeBool(true); + playback->serialize(stream); + // delete playback if it is a local version (i.e. cons'd up just now) + if (playback != fPlayback) { + SkDELETE(playback); + } + } else { + stream->writeBool(false); + } +} + diff --git a/skia/sgl/SkPixelRef.cpp b/skia/sgl/SkPixelRef.cpp new file mode 100644 index 0000000..adfc3c0 --- /dev/null +++ b/skia/sgl/SkPixelRef.cpp @@ -0,0 +1,129 @@ +#include "SkPixelRef.h" +#include "SkFlattenable.h" +#include "SkThread.h" + +static SkMutex gPixelRefMutex; +static int32_t gPixelRefGenerationID; + +SkPixelRef::SkPixelRef(SkMutex* mutex) { + if (NULL == mutex) { + mutex = &gPixelRefMutex; + } + fMutex = mutex; + fPixels = NULL; + fColorTable = NULL; // we do not track ownership of this + fLockCount = 0; + fGenerationID = 0; // signal to rebuild + fIsImmutable = false; +} + +SkPixelRef::SkPixelRef(SkFlattenableReadBuffer& buffer, SkMutex* mutex) { + if (NULL == mutex) { + mutex = &gPixelRefMutex; + } + fMutex = mutex; + fPixels = NULL; + fColorTable = NULL; // we do not track ownership of this + fLockCount = 0; + fGenerationID = 0; // signal to rebuild + fIsImmutable = buffer.readBool(); +} + +void SkPixelRef::flatten(SkFlattenableWriteBuffer& buffer) const { + buffer.writeBool(fIsImmutable); +} + +void SkPixelRef::lockPixels() { + SkAutoMutexAcquire ac(*fMutex); + + if (1 == ++fLockCount) { + fPixels = this->onLockPixels(&fColorTable); + } +} + +void SkPixelRef::unlockPixels() { + SkAutoMutexAcquire ac(*fMutex); + + SkASSERT(fLockCount > 0); + if (0 == --fLockCount) { + this->onUnlockPixels(); + fPixels = NULL; + fColorTable = NULL; + } +} + +uint32_t SkPixelRef::getGenerationID() const { + uint32_t genID = fGenerationID; + if (0 == genID) { + // do a loop in case our global wraps around, as we never want to + // return a 0 + do { + genID = sk_atomic_inc(&gPixelRefGenerationID) + 1; + } while (0 == genID); + fGenerationID = genID; + } + return genID; +} + +void SkPixelRef::notifyPixelsChanged() { + if (fIsImmutable) { + SkDebugf("========== notifyPixelsChanged called on immutable pixelref"); + sk_throw(); + } + // this signals us to recompute this next time around + fGenerationID = 0; +} + +void SkPixelRef::setImmutable() { + fIsImmutable = true; +} + +/////////////////////////////////////////////////////////////////////////////// + +#define MAX_PAIR_COUNT 16 + +struct Pair { + const char* fName; + SkPixelRef::Factory fFactory; +}; + +static int gCount; +static Pair gPairs[MAX_PAIR_COUNT]; + +void SkPixelRef::Register(const char name[], Factory factory) { + SkASSERT(name); + SkASSERT(factory); + + static bool gOnce; + if (!gOnce) { + gCount = 0; + gOnce = true; + } + + SkASSERT(gCount < MAX_PAIR_COUNT); + + gPairs[gCount].fName = name; + gPairs[gCount].fFactory = factory; + gCount += 1; +} + +SkPixelRef::Factory SkPixelRef::NameToFactory(const char name[]) { + const Pair* pairs = gPairs; + for (int i = gCount - 1; i >= 0; --i) { + if (strcmp(pairs[i].fName, name) == 0) { + return pairs[i].fFactory; + } + } + return NULL; +} + +const char* SkPixelRef::FactoryToName(Factory fact) { + const Pair* pairs = gPairs; + for (int i = gCount - 1; i >= 0; --i) { + if (pairs[i].fFactory == fact) { + return pairs[i].fName; + } + } + return NULL; +} + diff --git a/skia/sgl/SkProcSpriteBlitter.cpp b/skia/sgl/SkProcSpriteBlitter.cpp new file mode 100644 index 0000000..822c218 --- /dev/null +++ b/skia/sgl/SkProcSpriteBlitter.cpp @@ -0,0 +1,55 @@ +/* libs/graphics/sgl/SkProcSpriteBlitter.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#if 0 // experimental + +class SkProcSpriteBlitter : public SkSpriteBlitter { +public: + typedef void (*Proc)(void* dst, const void* src, int count, const SkPMColor ctable[]); + + SkProcSpriteBlitter(const SkBitmap& source, Proc proc, unsigned srcShift, unsigned dstShift) + : SkSpriteBlitter(source), fProc(proc), fSrcShift(SkToU8(srcShift)), fDstShift(SkToU8(dstShift)) {} + + virtual void blitRect(int x, int y, int width, int height) + { + size_t dstRB = fDevice.rowBytes(); + size_t srcRB = fSource.rowBytes(); + char* dst = (char*)fDevice.getPixels() + y * dstRB + (x << fDstShift); + const char* src = (const char*)fSource.getPixels() + (y - fTop) * srcRB + ((x - fLeft) << fSrcShift); + Proc proc = fProc; + const SkPMColor* ctable = NULL; + + if fSource.getColorTable()) + ctable = fSource.getColorTable()->lockColors(); + + while (--height >= 0) + { + proc(dst, src, width, ctable); + dst += dstRB; + src += srcRB; + } + + if fSource.getColorTable()) + fSource.getColorTable()->unlockColors(false); + } + +private: + Proc fProc; + uint8_t fSrcShift, fDstShift; +}; + +#endif diff --git a/skia/sgl/SkPtrRecorder.cpp b/skia/sgl/SkPtrRecorder.cpp new file mode 100644 index 0000000..4f774ec --- /dev/null +++ b/skia/sgl/SkPtrRecorder.cpp @@ -0,0 +1,53 @@ +#include "SkPtrRecorder.h" +#include "SkTSearch.h" + +void SkPtrRecorder::reset() { + Pair* p = fList.begin(); + Pair* stop = fList.end(); + while (p < stop) { + this->decPtr(p->fPtr); + p += 1; + } + fList.reset(); +} + +int SkPtrRecorder::Cmp(const Pair& a, const Pair& b) { + return (char*)a.fPtr - (char*)b.fPtr; +} + +uint32_t SkPtrRecorder::recordPtr(void* ptr) { + if (NULL == ptr) { + return 0; + } + + int count = fList.count(); + Pair pair; + pair.fPtr = ptr; + + int index = SkTSearch<Pair>(fList.begin(), count, pair, sizeof(pair), &Cmp); + if (index < 0) { + index = ~index; // turn it back into an index for insertion + this->incPtr(ptr); + pair.fIndex = count + 1; + *fList.insert(index) = pair; + return count + 1; + } else { + return fList[index].fIndex; + } +} + +void SkPtrRecorder::getPtrs(void* array[]) const { + int count = fList.count(); + if (count > 0) { + SkASSERT(array); + const Pair* p = fList.begin(); + // p->fIndex is base-1, so we need to subtract to find its slot + for (int i = 0; i < count; i++) { + int index = p[i].fIndex - 1; + SkASSERT((unsigned)index < (unsigned)count); + array[index] = p[i].fPtr; + } + } +} + + diff --git a/skia/sgl/SkRasterizer.cpp b/skia/sgl/SkRasterizer.cpp new file mode 100644 index 0000000..8a46bad --- /dev/null +++ b/skia/sgl/SkRasterizer.cpp @@ -0,0 +1,62 @@ +/* libs/graphics/sgl/SkRasterizer.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkRasterizer.h" +#include "SkDraw.h" +#include "SkMaskFilter.h" +#include "SkPath.h" + +// do nothing for now, since we don't store anything at flatten time +SkRasterizer::SkRasterizer(SkFlattenableReadBuffer&) {} + +bool SkRasterizer::rasterize(const SkPath& fillPath, const SkMatrix& matrix, + const SkIRect* clipBounds, SkMaskFilter* filter, + SkMask* mask, SkMask::CreateMode mode) +{ + SkIRect storage; + + if (clipBounds && filter && SkMask::kJustRenderImage_CreateMode != mode) + { + SkIPoint margin; + SkMask srcM, dstM; + + srcM.fFormat = SkMask::kA8_Format; + srcM.fBounds.set(0, 0, 1, 1); + srcM.fImage = NULL; + if (!filter->filterMask(&dstM, srcM, matrix, &margin)) + return false; + + storage = *clipBounds; + storage.inset(-margin.fX, -margin.fY); + clipBounds = &storage; + } + + return this->onRasterize(fillPath, matrix, clipBounds, mask, mode); +} + +/* Our default implementation of the virtual method just scan converts +*/ +bool SkRasterizer::onRasterize(const SkPath& fillPath, const SkMatrix& matrix, + const SkIRect* clipBounds, + SkMask* mask, SkMask::CreateMode mode) +{ + SkPath devPath; + + fillPath.transform(matrix, &devPath); + return SkDraw::DrawToMask(devPath, clipBounds, NULL, NULL, mask, mode); +} + diff --git a/skia/sgl/SkRefCnt.cpp b/skia/sgl/SkRefCnt.cpp new file mode 100644 index 0000000..2f0babc --- /dev/null +++ b/skia/sgl/SkRefCnt.cpp @@ -0,0 +1,48 @@ +/* libs/graphics/sgl/SkRefCnt.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkRefCnt.h" + +SkAutoUnref::~SkAutoUnref() { + if (fObj) { + fObj->unref(); + } +} + +bool SkAutoUnref::ref() { + if (fObj) { + fObj->ref(); + return true; + } + return false; +} + +bool SkAutoUnref::unref() { + if (fObj) { + fObj->unref(); + fObj = NULL; + return true; + } + return false; +} + +SkRefCnt* SkAutoUnref::detach() { + SkRefCnt* obj = fObj; + fObj = NULL; + return obj; +} + diff --git a/skia/sgl/SkRegion_path.cpp b/skia/sgl/SkRegion_path.cpp new file mode 100644 index 0000000..11e19b9 --- /dev/null +++ b/skia/sgl/SkRegion_path.cpp @@ -0,0 +1,457 @@ +/* libs/graphics/sgl/SkRegion_path.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkRegionPriv.h" +#include "SkBlitter.h" +#include "SkScan.h" +#include "SkTDArray.h" +#include "SkPath.h" + +class SkRgnBuilder : public SkBlitter { +public: + virtual ~SkRgnBuilder(); + + void init(int maxHeight, int maxTransitions); + + void done() { + if (fCurrScanline != NULL) { + fCurrScanline->fXCount = (SkRegion::RunType)((int)(fCurrXPtr - fCurrScanline->firstX())); + if (!this->collapsWithPrev()) { // flush the last line + fCurrScanline = fCurrScanline->nextScanline(); + } + } + } + + int computeRunCount() const; + void copyToRect(SkIRect*) const; + void copyToRgn(SkRegion::RunType runs[]) const; + + virtual void blitH(int x, int y, int width); + +#ifdef SK_DEBUG + void dump() const { + SkDebugf("SkRgnBuilder: Top = %d\n", fTop); + const Scanline* line = (Scanline*)fStorage; + while (line < fCurrScanline) { + SkDebugf("SkRgnBuilder::Scanline: LastY=%d, fXCount=%d", line->fLastY, line->fXCount); + for (int i = 0; i < line->fXCount; i++) { + SkDebugf(" %d", line->firstX()[i]); + } + SkDebugf("\n"); + + line = line->nextScanline(); + } + } +#endif +private: + struct Scanline { + SkRegion::RunType fLastY; + SkRegion::RunType fXCount; + + SkRegion::RunType* firstX() const { return (SkRegion::RunType*)(this + 1); } + Scanline* nextScanline() const { + return (Scanline*)((SkRegion::RunType*)(this + 1) + fXCount); + } + }; + SkRegion::RunType* fStorage; + Scanline* fCurrScanline; + Scanline* fPrevScanline; + // points at next avialable x[] in fCurrScanline + SkRegion::RunType* fCurrXPtr; + SkRegion::RunType fTop; // first Y value + + int fStorageCount; + + bool collapsWithPrev() { + if (fPrevScanline != NULL && + fPrevScanline->fLastY + 1 == fCurrScanline->fLastY && + fPrevScanline->fXCount == fCurrScanline->fXCount && + !memcmp(fPrevScanline->firstX(), + fCurrScanline->firstX(), + fCurrScanline->fXCount * sizeof(SkRegion::RunType))) + { + // update the height of fPrevScanline + fPrevScanline->fLastY = fCurrScanline->fLastY; + return true; + } + return false; + } +}; + +SkRgnBuilder::~SkRgnBuilder() { + sk_free(fStorage); +} + +void SkRgnBuilder::init(int maxHeight, int maxTransitions) { + int count = maxHeight * (3 + maxTransitions); + + // add maxTransitions to have slop for working buffer + fStorageCount = count + 3 + maxTransitions; + fStorage = (SkRegion::RunType*)sk_malloc_throw(fStorageCount * sizeof(SkRegion::RunType)); + + fCurrScanline = NULL; // signal empty collection + fPrevScanline = NULL; // signal first scanline +} + +void SkRgnBuilder::blitH(int x, int y, int width) { + if (fCurrScanline == NULL) { // first time + fTop = (SkRegion::RunType)(y); + fCurrScanline = (Scanline*)fStorage; + fCurrScanline->fLastY = (SkRegion::RunType)(y); + fCurrXPtr = fCurrScanline->firstX(); + } else { + SkASSERT(y >= fCurrScanline->fLastY); + + if (y > fCurrScanline->fLastY) { + // if we get here, we're done with fCurrScanline + fCurrScanline->fXCount = (SkRegion::RunType)((int)(fCurrXPtr - fCurrScanline->firstX())); + + int prevLastY = fCurrScanline->fLastY; + if (!this->collapsWithPrev()) { + fPrevScanline = fCurrScanline; + fCurrScanline = fCurrScanline->nextScanline(); + + } + if (y - 1 > prevLastY) { // insert empty run + fCurrScanline->fLastY = (SkRegion::RunType)(y - 1); + fCurrScanline->fXCount = 0; + fCurrScanline = fCurrScanline->nextScanline(); + } + // setup for the new curr line + fCurrScanline->fLastY = (SkRegion::RunType)(y); + fCurrXPtr = fCurrScanline->firstX(); + } + } + // check if we should extend the current run, or add a new one + if (fCurrXPtr > fCurrScanline->firstX() && fCurrXPtr[-1] == x) { + fCurrXPtr[-1] = (SkRegion::RunType)(x + width); + } else { + fCurrXPtr[0] = (SkRegion::RunType)(x); + fCurrXPtr[1] = (SkRegion::RunType)(x + width); + fCurrXPtr += 2; + } + SkASSERT(fCurrXPtr - fStorage < fStorageCount); +} + +int SkRgnBuilder::computeRunCount() const { + if (fCurrScanline == NULL) { + return 0; + } + + const SkRegion::RunType* line = fStorage; + const SkRegion::RunType* stop = (const SkRegion::RunType*)fCurrScanline; + + return 2 + (int)(stop - line); +} + +void SkRgnBuilder::copyToRect(SkIRect* r) const { + SkASSERT(fCurrScanline != NULL); + SkASSERT((const SkRegion::RunType*)fCurrScanline - fStorage == 4); + + const Scanline* line = (const Scanline*)fStorage; + SkASSERT(line->fXCount == 2); + + r->set(line->firstX()[0], fTop, line->firstX()[1], line->fLastY + 1); +} + +void SkRgnBuilder::copyToRgn(SkRegion::RunType runs[]) const { + SkASSERT(fCurrScanline != NULL); + SkASSERT((const SkRegion::RunType*)fCurrScanline - fStorage > 4); + + const Scanline* line = (const Scanline*)fStorage; + const Scanline* stop = fCurrScanline; + + *runs++ = fTop; + do { + *runs++ = (SkRegion::RunType)(line->fLastY + 1); + int count = line->fXCount; + if (count) { + memcpy(runs, line->firstX(), count * sizeof(SkRegion::RunType)); + runs += count; + } + *runs++ = SkRegion::kRunTypeSentinel; + line = line->nextScanline(); + } while (line < stop); + SkASSERT(line == stop); + *runs = SkRegion::kRunTypeSentinel; +} + +static int count_path_runtype_values(const SkPath& path, int* itop, int* ibot) { + static const uint8_t gPathVerbToInitialLastIndex[] = { + 0, // kMove_Verb + 1, // kLine_Verb + 2, // kQuad_Verb + 3, // kCubic_Verb + 0, // kClose_Verb + 0 // kDone_Verb + }; + + static const uint8_t gPathVerbToMaxEdges[] = { + 0, // kMove_Verb + 1, // kLine_Verb + 2, // kQuad_VerbB + 3, // kCubic_Verb + 0, // kClose_Verb + 0 // kDone_Verb + }; + + SkPath::Iter iter(path, true); + SkPoint pts[4]; + SkPath::Verb verb; + + int maxEdges = 0; + SkScalar top = SkIntToScalar(SK_MaxS16); + SkScalar bot = SkIntToScalar(SK_MinS16); + + while ((verb = iter.next(pts)) != SkPath::kDone_Verb) { + maxEdges += gPathVerbToMaxEdges[verb]; + + int lastIndex = gPathVerbToInitialLastIndex[verb]; + if (lastIndex > 0) { + for (int i = 1; i <= lastIndex; i++) { + if (top > pts[i].fY) { + top = pts[i].fY; + } else if (bot < pts[i].fY) { + bot = pts[i].fY; + } + } + } else if (SkPath::kMove_Verb == verb) { + if (top > pts[0].fY) { + top = pts[0].fY; + } else if (bot < pts[0].fY) { + bot = pts[0].fY; + } + } + } + SkASSERT(top <= bot); + + *itop = SkScalarRound(top); + *ibot = SkScalarRound(bot); + return maxEdges; +} + +bool SkRegion::setPath(const SkPath& path, const SkRegion& clip) { + SkDEBUGCODE(this->validate();) + + if (clip.isEmpty()) { + return this->setEmpty(); + } + + if (path.isEmpty()) { + if (path.isInverseFillType()) { + return this->set(clip); + } else { + return this->setEmpty(); + } + } + + // compute worst-case rgn-size for the path + int pathTop, pathBot; + int pathTransitions = count_path_runtype_values(path, &pathTop, &pathBot); + int clipTop, clipBot; + int clipTransitions; + + clipTransitions = clip.count_runtype_values(&clipTop, &clipBot); + + int top = SkMax32(pathTop, clipTop); + int bot = SkMin32(pathBot, clipBot); + + if (top >= bot) + return this->setEmpty(); + + SkRgnBuilder builder; + + builder.init(bot - top, SkMax32(pathTransitions, clipTransitions)); + SkScan::FillPath(path, clip, &builder); + builder.done(); + + int count = builder.computeRunCount(); + if (count == 0) { + return this->setEmpty(); + } else if (count == kRectRegionRuns) { + builder.copyToRect(&fBounds); + this->setRect(fBounds); + } else { + SkRegion tmp; + + tmp.fRunHead = RunHead::Alloc(count); + builder.copyToRgn(tmp.fRunHead->writable_runs()); + ComputeRunBounds(tmp.fRunHead->readonly_runs(), count, &tmp.fBounds); + this->swap(tmp); + } + SkDEBUGCODE(this->validate();) + return true; +} + +///////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////// + +struct Edge { + enum { + kY0Link = 0x01, + kY1Link = 0x02, + + kCompleteLink = (kY0Link | kY1Link) + }; + + SkRegion::RunType fX; + SkRegion::RunType fY0, fY1; + uint8_t fFlags; + Edge* fNext; + + void set(int x, int y0, int y1) { + SkASSERT(y0 != y1); + + fX = (SkRegion::RunType)(x); + fY0 = (SkRegion::RunType)(y0); + fY1 = (SkRegion::RunType)(y1); + fFlags = 0; + SkDEBUGCODE(fNext = NULL;) + } + + int top() const { + return SkFastMin32(fY0, fY1); + } +}; + +static void find_link(Edge* base, Edge* stop) { + SkASSERT(base < stop); + + if (base->fFlags == Edge::kCompleteLink) { + SkASSERT(base->fNext); + return; + } + + SkASSERT(base + 1 < stop); + + int y0 = base->fY0; + int y1 = base->fY1; + + Edge* e = base; + if ((base->fFlags & Edge::kY0Link) == 0) { + for (;;) { + e += 1; + if ((e->fFlags & Edge::kY1Link) == 0 && y0 == e->fY1) { + SkASSERT(NULL == e->fNext); + e->fNext = base; + e->fFlags = SkToU8(e->fFlags | Edge::kY1Link); + break; + } + } + } + + e = base; + if ((base->fFlags & Edge::kY1Link) == 0) { + for (;;) { + e += 1; + if ((e->fFlags & Edge::kY0Link) == 0 && y1 == e->fY0) { + SkASSERT(NULL == base->fNext); + base->fNext = e; + e->fFlags = SkToU8(e->fFlags | Edge::kY0Link); + break; + } + } + } + + base->fFlags = Edge::kCompleteLink; +} + +static int extract_path(Edge* edge, Edge* stop, SkPath* path) { + while (0 == edge->fFlags) { + edge++; // skip over "used" edges + } + + SkASSERT(edge < stop); + + Edge* base = edge; + Edge* prev = edge; + edge = edge->fNext; + SkASSERT(edge != base); + + int count = 1; + path->moveTo(SkIntToScalar(prev->fX), SkIntToScalar(prev->fY0)); + prev->fFlags = 0; + do { + if (prev->fX != edge->fX || prev->fY1 != edge->fY0) { // skip collinear + path->lineTo(SkIntToScalar(prev->fX), SkIntToScalar(prev->fY1)); // V + path->lineTo(SkIntToScalar(edge->fX), SkIntToScalar(edge->fY0)); // H + } + prev = edge; + edge = edge->fNext; + count += 1; + prev->fFlags = 0; + } while (edge != base); + path->lineTo(SkIntToScalar(prev->fX), SkIntToScalar(prev->fY1)); // V + path->close(); + return count; +} + +#include "SkTSearch.h" + +static int EdgeProc(const Edge* a, const Edge* b) { + return (a->fX == b->fX) ? a->top() - b->top() : a->fX - b->fX; +} + +bool SkRegion::getBoundaryPath(SkPath* path) const { + if (this->isEmpty()) { + return false; + } + + const SkIRect& bounds = this->getBounds(); + + if (this->isRect()) { + SkRect r; + r.set(bounds); // this converts the ints to scalars + path->addRect(r); + return true; + } + + SkRegion::Iterator iter(*this); + SkTDArray<Edge> edges; + + for (const SkIRect& r = iter.rect(); !iter.done(); iter.next()) { + Edge* edge = edges.append(2); + edge[0].set(r.fLeft, r.fBottom, r.fTop); + edge[1].set(r.fRight, r.fTop, r.fBottom); + } + SkQSort(edges.begin(), edges.count(), sizeof(Edge), (SkQSortCompareProc)EdgeProc); + + int count = edges.count(); + Edge* start = edges.begin(); + Edge* stop = start + count; + Edge* e; + + for (e = start; e != stop; e++) { + find_link(e, stop); + } + +#ifdef SK_DEBUG + for (e = start; e != stop; e++) { + SkASSERT(e->fNext != NULL); + SkASSERT(e->fFlags == Edge::kCompleteLink); + } +#endif + + path->incReserve(count << 1); + do { + SkASSERT(count > 1); + count -= extract_path(start, stop, path); + } while (count > 0); + + return true; +} + diff --git a/skia/sgl/SkScalerContext.cpp b/skia/sgl/SkScalerContext.cpp new file mode 100644 index 0000000..5f4abfd --- /dev/null +++ b/skia/sgl/SkScalerContext.cpp @@ -0,0 +1,540 @@ +/* libs/graphics/sgl/SkScalerContext.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkScalerContext.h" +#include "SkDescriptor.h" +#include "SkDraw.h" +#include "SkFontHost.h" +#include "SkMaskFilter.h" +#include "SkPathEffect.h" +#include "SkRasterizer.h" +#include "SkRegion.h" +#include "SkStroke.h" +#include "SkThread.h" + +#ifdef SK_DEBUG +// #define TRACK_MISSING_CHARS +#endif + +#define ComputeBWRowBytes(width) (((unsigned)(width) + 7) >> 3) + +static const uint8_t* gBlackGammaTable; +static const uint8_t* gWhiteGammaTable; + +void SkGlyph::toMask(SkMask* mask) const { + SkASSERT(mask); + + mask->fImage = (uint8_t*)fImage; + mask->fBounds.set(fLeft, fTop, fLeft + fWidth, fTop + fHeight); + mask->fRowBytes = this->rowBytes(); + mask->fFormat = fMaskFormat; +} + +size_t SkGlyph::computeImageSize() const { + size_t size = this->rowBytes() * fHeight; + if (fMaskFormat == SkMask::k3D_Format) { + size *= 3; + } + return size; +} + +#ifdef SK_DEBUG + #define DUMP_RECx +#endif + +static SkFlattenable* load_flattenable(const SkDescriptor* desc, uint32_t tag) { + SkFlattenable* obj = NULL; + uint32_t len; + const void* data = desc->findEntry(tag, &len); + + if (data) { + SkFlattenableReadBuffer buffer(data, len); + obj = buffer.readFlattenable(); + SkASSERT(buffer.offset() == buffer.size()); + } + return obj; +} + +SkScalerContext::SkScalerContext(const SkDescriptor* desc) + : fPathEffect(NULL), fMaskFilter(NULL) +{ + static bool gHaveGammaTables; + if (!gHaveGammaTables) { + const uint8_t* tables[2]; + SkFontHost::GetGammaTables(tables); + gBlackGammaTable = tables[0]; + gWhiteGammaTable = tables[1]; + gHaveGammaTables = true; + } + + fBaseGlyphCount = 0; + fAuxScalerContext = NULL; + + const Rec* rec = (const Rec*)desc->findEntry(kRec_SkDescriptorTag, NULL); + SkASSERT(rec); + + fRec = *rec; + +#ifdef DUMP_REC + desc->assertChecksum(); + SkDebugf("SkScalarContext checksum %x count %d length %d\n", desc->getChecksum(), desc->getCount(), desc->getLength()); + SkDebugf(" textsize %g prescale %g preskew %g post [%g %g %g %g]\n", + rec->fTextSize, rec->fPreScaleX, rec->fPreSkewX, rec->fPost2x2[0][0], + rec->fPost2x2[0][1], rec->fPost2x2[1][0], rec->fPost2x2[1][1]); + SkDebugf(" frame %g miter %g hints %d framefill %d format %d join %d\n", + rec->fFrameWidth, rec->fMiterLimit, rec->fHints, rec->fFrameAndFill, + rec->fMaskFormat, rec->fStrokeJoin); + SkDebugf(" pathEffect %x maskFilter %x\n", desc->findEntry(kPathEffect_SkDescriptorTag, NULL), + desc->findEntry(kMaskFilter_SkDescriptorTag, NULL)); +#endif + + fPathEffect = (SkPathEffect*)load_flattenable(desc, kPathEffect_SkDescriptorTag); + fMaskFilter = (SkMaskFilter*)load_flattenable(desc, kMaskFilter_SkDescriptorTag); + fRasterizer = (SkRasterizer*)load_flattenable(desc, kRasterizer_SkDescriptorTag); +} + +SkScalerContext::~SkScalerContext() { + fPathEffect->safeUnref(); + fMaskFilter->safeUnref(); + fRasterizer->safeUnref(); + + SkDELETE(fAuxScalerContext); +} + +SkScalerContext* SkScalerContext::loadAuxContext() const { + if (NULL == fAuxScalerContext) { + fAuxScalerContext = SkFontHost::CreateFallbackScalerContext(fRec); + if (NULL != fAuxScalerContext) { + fAuxScalerContext->setBaseGlyphCount(this->getGlyphCount()); + } + } + return fAuxScalerContext; +} + +#ifdef TRACK_MISSING_CHARS + static uint8_t gMissingChars[1 << 13]; +#endif + +uint16_t SkScalerContext::charToGlyphID(SkUnichar uni) { + unsigned glyphID = this->generateCharToGlyph(uni); + + if (0 == glyphID) { // try auxcontext + SkScalerContext* ctx = this->loadAuxContext(); + if (NULL != ctx) { + glyphID = ctx->generateCharToGlyph(uni); + if (0 != glyphID) { // only fiddle with it if its not missing + glyphID += this->getGlyphCount(); + if (glyphID > 0xFFFF) { + glyphID = 0; + } + } + } + } +#ifdef TRACK_MISSING_CHARS + if (0 == glyphID) { + bool announce = false; + if (uni > 0xFFFF) { // we don't record these + announce = true; + } else { + unsigned index = uni >> 3; + unsigned mask = 1 << (uni & 7); + SkASSERT(index < SK_ARRAY_COUNT(gMissingChars)); + if ((gMissingChars[index] & mask) == 0) { + gMissingChars[index] |= mask; + announce = true; + } + } + if (announce) { + printf(">>> MISSING CHAR <<< 0x%04X\n", uni); + } + } +#endif + return SkToU16(glyphID); +} + +/* Internal routine to resolve auxContextID into a real context. + Only makes sense to call once the glyph has been given a + valid auxGlyphID. +*/ +SkScalerContext* SkScalerContext::getGlyphContext(const SkGlyph& glyph) const { + SkScalerContext* ctx = const_cast<SkScalerContext*>(this); + + if (glyph.getGlyphID() >= this->getGlyphCount()) { + ctx = this->loadAuxContext(); + if (NULL == ctx) { // if no aux, just return us + ctx = const_cast<SkScalerContext*>(this); + } + } + return ctx; +} + +static int plus_minus_pin(int value, int max) { + SkASSERT(max >= 0); + + if (value > max) { + value = max; + } else if (value < -max) { + value = -max; + } + return value; +} + +void SkScalerContext::getAdvance(SkGlyph* glyph) { + // mark us as just having a valid advance + glyph->fMaskFormat = MASK_FORMAT_JUST_ADVANCE; + // we mark the format before making the call, in case the impl + // internally ends up calling its generateMetrics, which is OK + // albeit slower than strictly necessary + this->getGlyphContext(*glyph)->generateAdvance(glyph); +} + +void SkScalerContext::getMetrics(SkGlyph* glyph) { + this->getGlyphContext(*glyph)->generateMetrics(glyph); + + // for now we have separate cache entries for devkerning on and off + // in the future we might share caches, but make our measure/draw + // code make the distinction. Thus we zap the values if the caller + // has not asked for them. + if ((fRec.fFlags & SkScalerContext::kDevKernText_Flag) == 0) { + // no devkern, so zap the fields + glyph->fLsbDelta = glyph->fRsbDelta = 0; + } + + // if either dimension is empty, zap the image bounds of the glyph + if (0 == glyph->fWidth || 0 == glyph->fHeight) { + glyph->fWidth = 0; + glyph->fHeight = 0; + glyph->fTop = 0; + glyph->fLeft = 0; + glyph->fMaskFormat = 0; + return; + } + + if (fRec.fFrameWidth > 0 || fPathEffect != NULL || fRasterizer != NULL) { + SkPath devPath, fillPath; + SkMatrix fillToDevMatrix; + + this->internalGetPath(*glyph, &fillPath, &devPath, &fillToDevMatrix); + + if (fRasterizer) { + SkMask mask; + + if (fRasterizer->rasterize(fillPath, fillToDevMatrix, NULL, + fMaskFilter, &mask, + SkMask::kJustComputeBounds_CreateMode)) { + glyph->fLeft = mask.fBounds.fLeft; + glyph->fTop = mask.fBounds.fTop; + glyph->fWidth = SkToU16(mask.fBounds.width()); + glyph->fHeight = SkToU16(mask.fBounds.height()); + } else { + // draw nothing 'cause we failed + glyph->fLeft = 0; + glyph->fTop = 0; + glyph->fWidth = 0; + glyph->fHeight = 0; + return; + } + } else { + // just use devPath + SkRect r; + SkIRect ir; + + devPath.computeBounds(&r, SkPath::kExact_BoundsType); + r.roundOut(&ir); + + glyph->fLeft = ir.fLeft; + glyph->fTop = ir.fTop; + glyph->fWidth = SkToU16(ir.width()); + glyph->fHeight = SkToU16(ir.height()); + } + } + + glyph->fMaskFormat = fRec.fMaskFormat; + + if (fMaskFilter) { + SkMask src, dst; + SkMatrix matrix; + + glyph->toMask(&src); + fRec.getMatrixFrom2x2(&matrix); + + src.fImage = NULL; // only want the bounds from the filter + if (fMaskFilter->filterMask(&dst, src, matrix, NULL)) { + SkASSERT(dst.fImage == NULL); + glyph->fLeft = dst.fBounds.fLeft; + glyph->fTop = dst.fBounds.fTop; + glyph->fWidth = SkToU16(dst.fBounds.width()); + glyph->fHeight = SkToU16(dst.fBounds.height()); + glyph->fMaskFormat = dst.fFormat; + } + } +} + +void SkScalerContext::getImage(const SkGlyph& origGlyph) { + const SkGlyph* glyph = &origGlyph; + SkGlyph tmpGlyph; + + if (fMaskFilter) { // restore the prefilter bounds + tmpGlyph.fID = origGlyph.fID; + + // need the original bounds, sans our maskfilter + SkMaskFilter* mf = fMaskFilter; + fMaskFilter = NULL; // temp disable + this->getMetrics(&tmpGlyph); + fMaskFilter = mf; // restore + + tmpGlyph.fImage = origGlyph.fImage; + + // we need the prefilter bounds to be <= filter bounds + SkASSERT(tmpGlyph.fWidth <= origGlyph.fWidth); + SkASSERT(tmpGlyph.fHeight <= origGlyph.fHeight); + glyph = &tmpGlyph; + } + + if (fRec.fFrameWidth > 0 || fPathEffect != NULL || fRasterizer != NULL) { + SkPath devPath, fillPath; + SkMatrix fillToDevMatrix; + + this->internalGetPath(*glyph, &fillPath, &devPath, &fillToDevMatrix); + + if (fRasterizer) { + SkMask mask; + + glyph->toMask(&mask); + mask.fFormat = SkMask::kA8_Format; + bzero(glyph->fImage, mask.computeImageSize()); + + if (!fRasterizer->rasterize(fillPath, fillToDevMatrix, NULL, + fMaskFilter, &mask, + SkMask::kJustRenderImage_CreateMode)) { + return; + } + } else { + SkBitmap bm; + SkBitmap::Config config; + SkMatrix matrix; + SkRegion clip; + SkPaint paint; + SkDraw draw; + + if (SkMask::kA8_Format == fRec.fMaskFormat) { + config = SkBitmap::kA8_Config; + paint.setAntiAlias(true); + } else { + SkASSERT(SkMask::kBW_Format == fRec.fMaskFormat); + config = SkBitmap::kA1_Config; + paint.setAntiAlias(false); + } + + clip.setRect(0, 0, glyph->fWidth, glyph->fHeight); + matrix.setTranslate(-SkIntToScalar(glyph->fLeft), + -SkIntToScalar(glyph->fTop)); + bm.setConfig(config, glyph->fWidth, glyph->fHeight, + glyph->rowBytes()); + bm.setPixels(glyph->fImage); + bzero(glyph->fImage, bm.height() * bm.rowBytes()); + + draw.fClip = &clip; + draw.fMatrix = &matrix; + draw.fBitmap = &bm; + draw.fBounder = NULL; + draw.drawPath(devPath, paint); + } + } else { + this->getGlyphContext(*glyph)->generateImage(*glyph); + } + + if (fMaskFilter) { + SkMask srcM, dstM; + SkMatrix matrix; + + // the src glyph image shouldn't be 3D + SkASSERT(SkMask::k3D_Format != glyph->fMaskFormat); + glyph->toMask(&srcM); + fRec.getMatrixFrom2x2(&matrix); + + if (fMaskFilter->filterMask(&dstM, srcM, matrix, NULL)) { + int width = SkFastMin32(origGlyph.fWidth, dstM.fBounds.width()); + int height = SkFastMin32(origGlyph.fHeight, dstM.fBounds.height()); + int dstRB = origGlyph.rowBytes(); + int srcRB = dstM.fRowBytes; + + const uint8_t* src = (const uint8_t*)dstM.fImage; + uint8_t* dst = (uint8_t*)origGlyph.fImage; + + if (SkMask::k3D_Format == dstM.fFormat) { + // we have to copy 3 times as much + height *= 3; + } + + // clean out our glyph, since it may be larger than dstM + //bzero(dst, height * dstRB); + + while (--height >= 0) { + memcpy(dst, src, width); + src += srcRB; + dst += dstRB; + } + SkMask::FreeImage(dstM.fImage); + } + } + + // check to see if we should filter the alpha channel + + if (fRec.fMaskFormat != SkMask::kBW_Format && + (fRec.fFlags & (kGammaForBlack_Flag | kGammaForWhite_Flag)) != 0) + { + const uint8_t* table = (fRec.fFlags & kGammaForBlack_Flag) ? gBlackGammaTable : gWhiteGammaTable; + if (NULL != table) + { + uint8_t* dst = (uint8_t*)glyph->fImage; + unsigned rowBytes = glyph->rowBytes(); + + for (int y = glyph->fHeight - 1; y >= 0; --y) + { + for (int x = glyph->fWidth - 1; x >= 0; --x) + dst[x] = table[dst[x]]; + dst += rowBytes; + } + } + } +} + +void SkScalerContext::getPath(const SkGlyph& glyph, SkPath* path) +{ + this->internalGetPath(glyph, NULL, path, NULL); +} + +void SkScalerContext::getFontMetrics(SkPaint::FontMetrics* mx, SkPaint::FontMetrics* my) +{ + this->generateFontMetrics(mx, my); +} + +/////////////////////////////////////////////////////////////////////// + +void SkScalerContext::internalGetPath(const SkGlyph& glyph, SkPath* fillPath, SkPath* devPath, SkMatrix* fillToDevMatrix) +{ + SkPath path; + + this->getGlyphContext(glyph)->generatePath(glyph, &path); + + if (fRec.fFrameWidth > 0 || fPathEffect != NULL) + { + // need the path in user-space, with only the point-size applied + // so that our stroking and effects will operate the same way they + // would if the user had extracted the path themself, and then + // called drawPath + SkPath localPath; + SkMatrix matrix, inverse; + + fRec.getMatrixFrom2x2(&matrix); + matrix.invert(&inverse); + path.transform(inverse, &localPath); + // now localPath is only affected by the paint settings, and not the canvas matrix + + SkScalar width = fRec.fFrameWidth; + + if (fPathEffect) + { + SkPath effectPath; + + if (fPathEffect->filterPath(&effectPath, localPath, &width)) + localPath.swap(effectPath); + } + + if (width > 0) + { + SkStroke stroker; + SkPath outline; + + stroker.setWidth(width); + stroker.setMiterLimit(fRec.fMiterLimit); + stroker.setJoin((SkPaint::Join)fRec.fStrokeJoin); + stroker.setDoFill(SkToBool(fRec.fFlags & kFrameAndFill_Flag)); + stroker.strokePath(localPath, &outline); + localPath.swap(outline); + } + + // now return stuff to the caller + if (fillToDevMatrix) + *fillToDevMatrix = matrix; + + if (devPath) + localPath.transform(matrix, devPath); + + if (fillPath) + fillPath->swap(localPath); + } + else // nothing tricky to do + { + if (fillToDevMatrix) + fillToDevMatrix->reset(); + + if (devPath) + { + if (fillPath == NULL) + devPath->swap(path); + else + *devPath = path; + } + + if (fillPath) + fillPath->swap(path); + } + + if (devPath) + devPath->updateBoundsCache(); + if (fillPath) + fillPath->updateBoundsCache(); +} + + +void SkScalerContext::Rec::getMatrixFrom2x2(SkMatrix* dst) const +{ + dst->reset(); + dst->setScaleX(fPost2x2[0][0]); + dst->setSkewX( fPost2x2[0][1]); + dst->setSkewY( fPost2x2[1][0]); + dst->setScaleY(fPost2x2[1][1]); +} + +void SkScalerContext::Rec::getLocalMatrix(SkMatrix* m) const +{ + m->setScale(SkScalarMul(fTextSize, fPreScaleX), fTextSize); + if (fPreSkewX) + m->postSkew(fPreSkewX, 0); +} + +void SkScalerContext::Rec::getSingleMatrix(SkMatrix* m) const +{ + this->getLocalMatrix(m); + + // now concat the device matrix + { + SkMatrix deviceMatrix; + this->getMatrixFrom2x2(&deviceMatrix); + m->postConcat(deviceMatrix); + } +} + +#include "SkFontHost.h" + +SkScalerContext* SkScalerContext::Create(const SkDescriptor* desc) +{ + return SkFontHost::CreateScalerContext(desc); +} + diff --git a/skia/sgl/SkScan.cpp b/skia/sgl/SkScan.cpp new file mode 100644 index 0000000..1d80bb1 --- /dev/null +++ b/skia/sgl/SkScan.cpp @@ -0,0 +1,75 @@ +/* libs/graphics/sgl/SkScan.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkScan.h" +#include "SkBlitter.h" +#include "SkRegion.h" + +static inline void blitrect(SkBlitter* blitter, const SkIRect& r) { + blitter->blitRect(r.fLeft, r.fTop, r.width(), r.height()); +} + +void SkScan::FillIRect(const SkIRect& r, const SkRegion* clip, + SkBlitter* blitter) { + if (!r.isEmpty()) { + if (clip) { + if (clip->isRect()) { + const SkIRect& clipBounds = clip->getBounds(); + + if (clipBounds.contains(r)) { + blitrect(blitter, r); + } else { + SkIRect rr = r; + if (rr.intersect(clipBounds)) { + blitrect(blitter, rr); + } + } + } else { + SkRegion::Cliperator cliper(*clip, r); + const SkIRect& rr = cliper.rect(); + + while (!cliper.done()) { + blitrect(blitter, rr); + cliper.next(); + } + } + } else { + blitrect(blitter, r); + } + } +} + +void SkScan::FillXRect(const SkXRect& xr, const SkRegion* clip, + SkBlitter* blitter) { + SkIRect r; + + XRect_round(xr, &r); + SkScan::FillIRect(r, clip, blitter); +} + +#ifdef SK_SCALAR_IS_FLOAT + +void SkScan::FillRect(const SkRect& r, const SkRegion* clip, + SkBlitter* blitter) { + SkXRect xr; + + XRect_set(&xr, r); + SkScan::FillXRect(xr, clip, blitter); +} + +#endif + diff --git a/skia/sgl/SkScan.h b/skia/sgl/SkScan.h new file mode 100644 index 0000000..c4cefeb --- /dev/null +++ b/skia/sgl/SkScan.h @@ -0,0 +1,123 @@ +/* libs/graphics/sgl/SkScan.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkScan_DEFINED +#define SkScan_DEFINED + +#include "SkRect.h" + +class SkRegion; +class SkBlitter; +class SkPath; + +/** Defines a fixed-point rectangle, identical to the integer SkIRect, but its + coordinates are treated as SkFixed rather than int32_t. +*/ +typedef SkIRect SkXRect; + +class SkScan { +public: + static void FillIRect(const SkIRect&, const SkRegion* clip, SkBlitter*); + static void FillXRect(const SkXRect&, const SkRegion* clip, SkBlitter*); + +#ifdef SK_SCALAR_IS_FIXED + static void FillRect(const SkRect& rect, const SkRegion* clip, + SkBlitter* blitter) { + SkScan::FillXRect(*(const SkXRect*)&rect, clip, blitter); + } +#else + static void FillRect(const SkRect&, const SkRegion* clip, SkBlitter*); +#endif + + static void FillTriangle(const SkPoint pts[], const SkRegion*, SkBlitter*); + static void FillPath(const SkPath&, const SkRegion& clip, SkBlitter*); + + static void FillTriangle(const SkPoint& a, const SkPoint& b, + const SkPoint& c, const SkRegion* clip, + SkBlitter* blitter) { + SkPoint pts[3]; + pts[0] = a; + pts[1] = b; + pts[2] = c; + FillTriangle(pts, clip, blitter); + } + + static void HairLine(const SkPoint&, const SkPoint&, const SkRegion* clip, SkBlitter*); + static void HairRect(const SkRect&, const SkRegion* clip, SkBlitter*); + static void HairPath(const SkPath&, const SkRegion* clip, SkBlitter*); + + static void FrameRect(const SkRect&, SkScalar width, const SkRegion* clip, SkBlitter*); + + static void AntiFillXRect(const SkXRect&, const SkRegion* clip, SkBlitter*); +#ifdef SK_SCALAR_IS_FIXED + static void AntiFillRect(const SkRect& rect, const SkRegion* clip, + SkBlitter* blitter) { + SkScan::AntiFillXRect(*(const SkXRect*)&rect, clip, blitter); + } +#else + static void AntiFillRect(const SkRect&, const SkRegion* clip, SkBlitter*); +#endif + + static void AntiFillPath(const SkPath&, const SkRegion& clip, SkBlitter*); + + static void AntiHairLine(const SkPoint&, const SkPoint&, const SkRegion* clip, SkBlitter*); + static void AntiHairRect(const SkRect&, const SkRegion* clip, SkBlitter*); + static void AntiHairPath(const SkPath&, const SkRegion* clip, SkBlitter*); +}; + +/** Assign an SkXRect from a SkIRect, by promoting the src rect's coordinates + from int to SkFixed. Does not check for overflow if the src coordinates + exceed 32K +*/ +static void XRect_set(SkXRect* xr, const SkIRect& src) { + xr->fLeft = SkIntToFixed(src.fLeft); + xr->fTop = SkIntToFixed(src.fTop); + xr->fRight = SkIntToFixed(src.fRight); + xr->fBottom = SkIntToFixed(src.fBottom); +} + +/** Assign an SkXRect from a SkRect, by promoting the src rect's coordinates + from SkScalar to SkFixed. Does not check for overflow if the src coordinates + exceed 32K +*/ +static void XRect_set(SkXRect* xr, const SkRect& src) { + xr->fLeft = SkScalarToFixed(src.fLeft); + xr->fTop = SkScalarToFixed(src.fTop); + xr->fRight = SkScalarToFixed(src.fRight); + xr->fBottom = SkScalarToFixed(src.fBottom); +} + +/** Round the SkXRect coordinates, and store the result in the SkIRect. +*/ +static void XRect_round(const SkXRect& xr, SkIRect* dst) { + dst->fLeft = SkFixedRound(xr.fLeft); + dst->fTop = SkFixedRound(xr.fTop); + dst->fRight = SkFixedRound(xr.fRight); + dst->fBottom = SkFixedRound(xr.fBottom); +} + +/** Round the SkXRect coordinates out (i.e. use floor for left/top, and ceiling + for right/bottom), and store the result in the SkIRect. +*/ +static void XRect_roundOut(const SkXRect& xr, SkIRect* dst) { + dst->fLeft = SkFixedFloor(xr.fLeft); + dst->fTop = SkFixedFloor(xr.fTop); + dst->fRight = SkFixedCeil(xr.fRight); + dst->fBottom = SkFixedCeil(xr.fBottom); +} + +#endif diff --git a/skia/sgl/SkScanPriv.h b/skia/sgl/SkScanPriv.h new file mode 100644 index 0000000..de7e3e7 --- /dev/null +++ b/skia/sgl/SkScanPriv.h @@ -0,0 +1,48 @@ +/* libs/graphics/sgl/SkScanPriv.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkScanPriv_DEFINED +#define SkScanPriv_DEFINED + +#include "SkScan.h" +#include "SkBlitter.h" + +class SkScanClipper { +public: + SkScanClipper(SkBlitter* blitter, const SkRegion* clip, const SkIRect& bounds); + + SkBlitter* getBlitter() const { return fBlitter; } + const SkIRect* getClipRect() const { return fClipRect; } + +private: + SkRectClipBlitter fRectBlitter; + SkRgnClipBlitter fRgnBlitter; + SkBlitter* fBlitter; + const SkIRect* fClipRect; +}; + +// clipRect == null means path is entirely inside the clip +void sk_fill_path(const SkPath& path, const SkIRect* clipRect, + SkBlitter* blitter, int stop_y, int shiftEdgesUp, + const SkRegion& clipRgn); + +// blit the rects above and below avoid, clipped to clp +void sk_blit_above_and_below(SkBlitter* blitter, const SkIRect& avoid, + const SkRegion& clip); + +#endif + diff --git a/skia/sgl/SkScan_AntiPath.cpp b/skia/sgl/SkScan_AntiPath.cpp new file mode 100644 index 0000000..b603f15 --- /dev/null +++ b/skia/sgl/SkScan_AntiPath.cpp @@ -0,0 +1,406 @@ +/* libs/graphics/sgl/SkScan_AntiPath.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkScanPriv.h" +#include "SkPath.h" +#include "SkMatrix.h" +#include "SkBlitter.h" +#include "SkRegion.h" +#include "SkAntiRun.h" + +#define SHIFT 2 +#define SCALE (1 << SHIFT) +#define MASK (SCALE - 1) + +/////////////////////////////////////////////////////////////////////////////////////////// + +class BaseSuperBlitter : public SkBlitter { +public: + BaseSuperBlitter(SkBlitter* realBlitter, const SkIRect& ir, + const SkRegion& clip); + + virtual void blitAntiH(int x, int y, const SkAlpha antialias[], + const int16_t runs[]) { + SkASSERT(!"How did I get here?"); + } + virtual void blitV(int x, int y, int height, SkAlpha alpha) { + SkASSERT(!"How did I get here?"); + } + virtual void blitRect(int x, int y, int width, int height) { + SkASSERT(!"How did I get here?"); + } + +protected: + SkBlitter* fRealBlitter; + int fCurrIY; + int fWidth, fLeft, fSuperLeft; + + SkDEBUGCODE(int fCurrX;) + SkDEBUGCODE(int fCurrY;) +}; + +BaseSuperBlitter::BaseSuperBlitter(SkBlitter* realBlitter, const SkIRect& ir, + const SkRegion& clip) { + fRealBlitter = realBlitter; + + // take the union of the ir bounds and clip, since we may be called with an + // inverse filltype + const int left = SkMin32(ir.fLeft, clip.getBounds().fLeft); + const int right = SkMax32(ir.fRight, clip.getBounds().fRight); + + fLeft = left; + fSuperLeft = left << SHIFT; + fWidth = right - left; + fCurrIY = -1; + SkDEBUGCODE(fCurrX = -1; fCurrY = -1;) +} + +class SuperBlitter : public BaseSuperBlitter { +public: + SuperBlitter(SkBlitter* realBlitter, const SkIRect& ir, + const SkRegion& clip); + + virtual ~SuperBlitter() { + this->flush(); + sk_free(fRuns.fRuns); + } + + void flush(); + + virtual void blitH(int x, int y, int width); + +private: + SkAlphaRuns fRuns; +}; + +SuperBlitter::SuperBlitter(SkBlitter* realBlitter, const SkIRect& ir, + const SkRegion& clip) + : BaseSuperBlitter(realBlitter, ir, clip) { + const int width = fWidth; + + // extra one to store the zero at the end + fRuns.fRuns = (int16_t*)sk_malloc_throw((width + 1 + (width + 2)/2) * sizeof(int16_t)); + fRuns.fAlpha = (uint8_t*)(fRuns.fRuns + width + 1); + fRuns.reset(width); +} + +void SuperBlitter::flush() +{ + if (fCurrIY >= 0) + { + if (!fRuns.empty()) + { + // SkDEBUGCODE(fRuns.dump();) + fRealBlitter->blitAntiH(fLeft, fCurrIY, fRuns.fAlpha, fRuns.fRuns); + fRuns.reset(fWidth); + } + fCurrIY = -1; + SkDEBUGCODE(fCurrX = -1;) + } +} + +static inline int coverage_to_alpha(int aa) +{ + aa <<= 8 - 2*SHIFT; + aa -= aa >> (8 - SHIFT - 1); + return aa; +} + +#define SUPER_Mask ((1 << SHIFT) - 1) + +void SuperBlitter::blitH(int x, int y, int width) +{ + int iy = y >> SHIFT; + SkASSERT(iy >= fCurrIY); + + x -= fSuperLeft; + // hack, until I figure out why my cubics (I think) go beyond the bounds + if (x < 0) + { + width += x; + x = 0; + } + +#ifdef SK_DEBUG + SkASSERT(y >= fCurrY); + SkASSERT(y != fCurrY || x >= fCurrX); + fCurrY = y; +#endif + + if (iy != fCurrIY) // new scanline + { + this->flush(); + fCurrIY = iy; + } + + // we sub 1 from maxValue 1 time for each block, so that we don't + // hit 256 as a summed max, but 255. +// int maxValue = (1 << (8 - SHIFT)) - (((y & MASK) + 1) >> SHIFT); + +#if 0 + SkAntiRun<SHIFT> arun; + arun.set(x, x + width); + fRuns.add(x >> SHIFT, arun.getStartAlpha(), arun.getMiddleCount(), arun.getStopAlpha(), maxValue); +#else + { + int start = x; + int stop = x + width; + + SkASSERT(start >= 0 && stop > start); + int fb = start & SUPER_Mask; + int fe = stop & SUPER_Mask; + int n = (stop >> SHIFT) - (start >> SHIFT) - 1; + + if (n < 0) + { + fb = fe - fb; + n = 0; + fe = 0; + } + else + { + if (fb == 0) + n += 1; + else + fb = (1 << SHIFT) - fb; + } + fRuns.add(x >> SHIFT, coverage_to_alpha(fb), n, coverage_to_alpha(fe), + (1 << (8 - SHIFT)) - (((y & MASK) + 1) >> SHIFT)); + } +#endif + +#ifdef SK_DEBUG + fRuns.assertValid(y & MASK, (1 << (8 - SHIFT))); + fCurrX = x + width; +#endif +} + +/////////////////////////////////////////////////////////////////////////////// + +class MaskSuperBlitter : public BaseSuperBlitter { +public: + MaskSuperBlitter(SkBlitter* realBlitter, const SkIRect& ir, + const SkRegion& clip); + virtual ~MaskSuperBlitter() { + fRealBlitter->blitMask(fMask, fClipRect); + } + + virtual void blitH(int x, int y, int width); + + static bool CanHandleRect(const SkIRect& bounds) + { + int width = bounds.width(); + int rb = SkAlign4(width); + + return (width <= MaskSuperBlitter::kMAX_WIDTH) && + (rb * bounds.height() <= MaskSuperBlitter::kMAX_STORAGE); + } + +private: + enum { + kMAX_WIDTH = 32, // so we don't try to do very wide things, where the RLE blitter would be faster + kMAX_STORAGE = 1024 + }; + + SkMask fMask; + SkIRect fClipRect; + // we add 1 because add_aa_span can write (unchanged) 1 extra byte at the end, rather than + // perform a test to see if stopAlpha != 0 + uint32_t fStorage[(kMAX_STORAGE >> 2) + 1]; +}; + +MaskSuperBlitter::MaskSuperBlitter(SkBlitter* realBlitter, const SkIRect& ir, + const SkRegion& clip) + : BaseSuperBlitter(realBlitter, ir, clip) { + SkASSERT(CanHandleRect(ir)); + + fMask.fImage = (uint8_t*)fStorage; + fMask.fBounds = ir; + fMask.fRowBytes = ir.width(); + fMask.fFormat = SkMask::kA8_Format; + + fClipRect = ir; + fClipRect.intersect(clip.getBounds()); + + // For valgrind, write 1 extra byte at the end so we don't read + // uninitialized memory. See comment in add_aa_span and fStorage[]. + memset(fStorage, 0, fMask.fBounds.height() * fMask.fRowBytes + 1); +} + +static void add_aa_span(uint8_t* alpha, U8CPU startAlpha) +{ + /* I should be able to just add alpha[x] + startAlpha. + However, if the trailing edge of the previous span and the leading + edge of the current span round to the same super-sampled x value, + I might overflow to 256 with this add, hence the funny subtract. + */ + unsigned tmp = *alpha + startAlpha; + SkASSERT(tmp <= 256); + *alpha = SkToU8(tmp - (tmp >> 8)); +} + +static void add_aa_span(uint8_t* alpha, U8CPU startAlpha, int middleCount, U8CPU stopAlpha, U8CPU maxValue) +{ + SkASSERT(middleCount >= 0); + + /* I should be able to just add alpha[x] + startAlpha. + However, if the trailing edge of the previous span and the leading + edge of the current span round to the same super-sampled x value, + I might overflow to 256 with this add, hence the funny subtract. + */ + unsigned tmp = *alpha + startAlpha; + SkASSERT(tmp <= 256); + *alpha++ = SkToU8(tmp - (tmp >> 8)); + + while (--middleCount >= 0) + { + alpha[0] = SkToU8(alpha[0] + maxValue); + alpha += 1; + } + + // potentially this can be off the end of our "legal" alpha values, but that + // only happens if stopAlpha is also 0. Rather than test for stopAlpha != 0 + // every time (slow), we just do it, and ensure that we've allocated extra space + // (see the + 1 comment in fStorage[] + *alpha = SkToU8(*alpha + stopAlpha); +} + +void MaskSuperBlitter::blitH(int x, int y, int width) +{ + int iy = (y >> SHIFT); + + SkASSERT(iy >= fMask.fBounds.fTop && iy < fMask.fBounds.fBottom); + iy -= fMask.fBounds.fTop; // make it relative to 0 + +#ifdef SK_DEBUG + { + int ix = x >> SHIFT; + SkASSERT(ix >= fMask.fBounds.fLeft && ix < fMask.fBounds.fRight); + } +#endif + + x -= (fMask.fBounds.fLeft << SHIFT); + + // hack, until I figure out why my cubics (I think) go beyond the bounds + if (x < 0) + { + width += x; + x = 0; + } + + // we sub 1 from maxValue 1 time for each block, so that we don't + // hit 256 as a summed max, but 255. +// int maxValue = (1 << (8 - SHIFT)) - (((y & MASK) + 1) >> SHIFT); + + uint8_t* row = fMask.fImage + iy * fMask.fRowBytes + (x >> SHIFT); + + int start = x; + int stop = x + width; + + SkASSERT(start >= 0 && stop > start); + int fb = start & SUPER_Mask; + int fe = stop & SUPER_Mask; + int n = (stop >> SHIFT) - (start >> SHIFT) - 1; + + + if (n < 0) + { + add_aa_span(row, coverage_to_alpha(fe - fb)); + } + else + { + fb = (1 << SHIFT) - fb; + add_aa_span(row, coverage_to_alpha(fb), n, coverage_to_alpha(fe), + (1 << (8 - SHIFT)) - (((y & MASK) + 1) >> SHIFT)); + } + +#ifdef SK_DEBUG + fCurrX = x + width; +#endif +} + +/////////////////////////////////////////////////////////////////////////////// + +static int overflows_short(int value) { + return value - (short)value; +} + +void SkScan::AntiFillPath(const SkPath& path, const SkRegion& clip, + SkBlitter* blitter) { + if (clip.isEmpty()) { + return; + } + + SkRect r; + SkIRect ir; + + path.computeBounds(&r, SkPath::kFast_BoundsType); + r.roundOut(&ir); + if (ir.isEmpty()) { + return; + } + + if (overflows_short(ir.fLeft << SHIFT) || + overflows_short(ir.fRight << SHIFT) || + overflows_short(ir.width() << SHIFT) || + overflows_short(ir.fTop << SHIFT) || + overflows_short(ir.fBottom << SHIFT) || + overflows_short(ir.height() << SHIFT)) { + // can't supersample, try drawing w/o antialiasing + SkScan::FillPath(path, clip, blitter); + return; + } + + SkScanClipper clipper(blitter, &clip, ir); + const SkIRect* clipRect = clipper.getClipRect(); + + if (clipper.getBlitter() == NULL) { // clipped out + if (path.isInverseFillType()) { + blitter->blitRegion(clip); + } + return; + } + + // now use the (possibly wrapped) blitter + blitter = clipper.getBlitter(); + + if (path.isInverseFillType()) { + sk_blit_above_and_below(blitter, ir, clip); + } + + SkIRect superRect, *superClipRect = NULL; + + if (clipRect) + { + superRect.set( clipRect->fLeft << SHIFT, clipRect->fTop << SHIFT, + clipRect->fRight << SHIFT, clipRect->fBottom << SHIFT); + superClipRect = &superRect; + } + + // MaskSuperBlitter can't handle drawing outside of ir, so we can't use it + // if we're an inverse filltype + if (!path.isInverseFillType() && MaskSuperBlitter::CanHandleRect(ir)) + { + MaskSuperBlitter superBlit(blitter, ir, clip); + sk_fill_path(path, superClipRect, &superBlit, ir.fBottom, SHIFT, clip); + } + else + { + SuperBlitter superBlit(blitter, ir, clip); + sk_fill_path(path, superClipRect, &superBlit, ir.fBottom, SHIFT, clip); + } +} diff --git a/skia/sgl/SkScan_Antihair.cpp b/skia/sgl/SkScan_Antihair.cpp new file mode 100644 index 0000000..ca36739 --- /dev/null +++ b/skia/sgl/SkScan_Antihair.cpp @@ -0,0 +1,549 @@ +/* libs/graphics/sgl/SkScan_Antihair.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkScan.h" +#include "SkBlitter.h" +#include "SkColorPriv.h" +#include "SkRegion.h" +#include "SkFDot6.h" + +#define HLINE_STACK_BUFFER 100 + +//#define TEST_GAMMA + +#ifdef TEST_GAMMA + static uint8_t gGammaTable[256]; + #define ApplyGamma(table, alpha) (table)[alpha] + + static void build_gamma_table() + { + static bool gInit = false; + + if (gInit == false) + { + for (int i = 0; i < 256; i++) + { + SkFixed n = i * 257; + n += n >> 15; + SkASSERT(n >= 0 && n <= SK_Fixed1); + n = SkFixedSqrt(n); + n = n * 255 >> 16; + // SkDebugf("morph %d -> %d\n", i, n); + gGammaTable[i] = SkToU8(n); + } + gInit = true; + } + } +#else + #define ApplyGamma(table, alpha) SkToU8(alpha) +#endif + + +static void call_hline_blitter(SkBlitter* blitter, int x, int y, int count, uint8_t alpha) +{ + SkASSERT(count > 0); + + int16_t runs[HLINE_STACK_BUFFER + 1]; + uint8_t aa[HLINE_STACK_BUFFER]; + + aa[0] = ApplyGamma(gGammaTable, alpha); + do { + int n = count; + if (n > HLINE_STACK_BUFFER) + n = HLINE_STACK_BUFFER; + + runs[0] = SkToS16(n); + runs[n] = 0; + blitter->blitAntiH(x, y, aa, runs); + x += n; + count -= n; + } while (count > 0); +} + +static void hline(int x, int stopx, SkFixed fy, SkFixed /*slope*/, SkBlitter* blitter) +{ + SkASSERT(x < stopx); + int count = stopx - x; + fy += SK_Fixed1/2; + + int y = fy >> 16; + uint8_t a = (uint8_t)(fy >> 8); + + // lower line + if (a) + call_hline_blitter(blitter, x, y, count, a); + + // upper line + a = (uint8_t)(255 - a); + if (a) + call_hline_blitter(blitter, x, y - 1, count, a); +} + +static void horish(int x, int stopx, SkFixed fy, SkFixed dy, SkBlitter* blitter) +{ + SkASSERT(x < stopx); + +#ifdef TEST_GAMMA + const uint8_t* gamma = gGammaTable; +#endif + int16_t runs[2]; + uint8_t aa[1]; + + runs[0] = 1; + runs[1] = 0; + + fy += SK_Fixed1/2; + do { + int lower_y = fy >> 16; + uint8_t a = (uint8_t)(fy >> 8); + + if (a) + { + aa[0] = ApplyGamma(gamma, a); + blitter->blitAntiH(x, lower_y, aa, runs); + // the clipping blitters might edit runs, but should not affect us + SkASSERT(runs[0] == 1); + SkASSERT(runs[1] == 0); + } + a = (uint8_t)(255 - a); + if (a) + { + aa[0] = ApplyGamma(gamma, a); + blitter->blitAntiH(x, lower_y - 1, aa, runs); + // the clipping blitters might edit runs, but should not affect us + SkASSERT(runs[0] == 1); + SkASSERT(runs[1] == 0); + } + fy += dy; + } while (++x < stopx); +} + +static void vline(int y, int stopy, SkFixed fx, SkFixed /*slope*/, SkBlitter* blitter) +{ + SkASSERT(y < stopy); + fx += SK_Fixed1/2; + + int x = fx >> 16; + int a = (uint8_t)(fx >> 8); + + if (a) + blitter->blitV(x, y, stopy - y, ApplyGamma(gGammaTable, a)); + a = 255 - a; + if (a) + blitter->blitV(x - 1, y, stopy - y, ApplyGamma(gGammaTable, a)); +} + +static void vertish(int y, int stopy, SkFixed fx, SkFixed dx, SkBlitter* blitter) +{ + SkASSERT(y < stopy); +#ifdef TEST_GAMMA + const uint8_t* gamma = gGammaTable; +#endif + int16_t runs[3]; + uint8_t aa[2]; + + runs[0] = 1; + runs[2] = 0; + + fx += SK_Fixed1/2; + do { + int x = fx >> 16; + uint8_t a = (uint8_t)(fx >> 8); + + aa[0] = ApplyGamma(gamma, 255 - a); + aa[1] = ApplyGamma(gamma, a); + // the clippng blitters might overwrite this guy, so we have to reset it each time + runs[1] = 1; + blitter->blitAntiH(x - 1, y, aa, runs); + // the clipping blitters might edit runs, but should not affect us + SkASSERT(runs[0] == 1); + SkASSERT(runs[2] == 0); + fx += dx; + } while (++y < stopy); +} + +typedef void (*LineProc)(int istart, int istop, SkFixed fstart, SkFixed slope, SkBlitter*); + +static inline SkFixed fastfixdiv(SkFDot6 a, SkFDot6 b) +{ + SkASSERT((a << 16 >> 16) == a); + SkASSERT(b != 0); + return (a << 16) / b; +} +static inline SkFDot6 fastfixmul(SkFixed fixed, SkFDot6 b) +{ + SkASSERT(SkAbs32(fixed) <= SK_Fixed1 && SkAbs32(b) <= SkIntToFDot6(511)); + return (fixed * b + 0x8000) >> 16; +} + +static void do_anti_hairline(SkFDot6 x0, SkFDot6 y0, SkFDot6 x1, SkFDot6 y1, + const SkIRect* clip, SkBlitter* blitter) +{ + // check that we're no larger than 511 pixels (so we can do a faster div). + // if we are, subdivide and call again + + if (SkAbs32(x1 - x0) > SkIntToFDot6(511) || SkAbs32(y1 - y0) > SkIntToFDot6(511)) + { + int hx = (x0 + x1) >> 1; + int hy = (y0 + y1) >> 1; + do_anti_hairline(x0, y0, hx, hy, clip, blitter); + do_anti_hairline(hx, hy, x1, y1, clip, blitter); + return; + } + + int istart, istop; + SkFixed fstart, slope; + LineProc proc; + + if (SkAbs32(x1 - x0) > SkAbs32(y1 - y0)) // mostly horizontal + { + if (x0 > x1) // we want to go left-to-right + { + SkTSwap<SkFDot6>(x0, x1); + SkTSwap<SkFDot6>(y0, y1); + } + istart = SkFDot6Round(x0); + istop = SkFDot6Round(x1); + if (istart == istop) // too short to draw + return; + + if (y0 == y1) // completely horizontal, take fast case + { + slope = 0; + fstart = SkFDot6ToFixed(y0); + proc = hline; + } + else + { + slope = fastfixdiv(y1 - y0, x1 - x0); + SkASSERT(slope >= -SK_Fixed1 && slope <= SK_Fixed1); + fstart = SkFDot6ToFixed(y0 + fastfixmul(slope, (32 - x0) & 63)); + proc = horish; + } + + if (clip) + { + if (istart >= clip->fRight || istop <= clip->fLeft) + return; + if (istart < clip->fLeft) + { + fstart += slope * (clip->fLeft - istart); + istart = clip->fLeft; + } + if (istop > clip->fRight) + istop = clip->fRight; + SkASSERT(istart <= istop); + if (istart == istop) + return; + // now test if our Y values are completely inside the clip + int top, bottom; + if (slope >= 0) // T2B + { + top = SkFixedFloor(fstart - SK_FixedHalf); + bottom = SkFixedCeil(fstart + (istop - istart - 1) * slope + SK_FixedHalf); + } + else // B2T + { + bottom = SkFixedCeil(fstart + SK_FixedHalf); + top = SkFixedFloor(fstart + (istop - istart - 1) * slope - SK_FixedHalf); + } + if (top >= clip->fBottom || bottom <= clip->fTop) + return; + if (clip->fTop <= top && clip->fBottom >= bottom) + clip = NULL; + } + } + else // mostly vertical + { + if (y0 > y1) // we want to go top-to-bottom + { + SkTSwap<SkFDot6>(x0, x1); + SkTSwap<SkFDot6>(y0, y1); + } + istart = SkFDot6Round(y0); + istop = SkFDot6Round(y1); + if (istart == istop) // too short to draw + return; + + if (x0 == x1) + { + slope = 0; + fstart = SkFDot6ToFixed(x0); + proc = vline; + } + else + { + slope = fastfixdiv(x1 - x0, y1 - y0); + SkASSERT(slope <= SK_Fixed1 && slope >= -SK_Fixed1); + fstart = SkFDot6ToFixed(x0 + fastfixmul(slope, (32 - y0) & 63)); + proc = vertish; + } + + if (clip) + { + if (istart >= clip->fBottom || istop <= clip->fTop) + return; + if (istart < clip->fTop) + { + fstart += slope * (clip->fTop - istart); + istart = clip->fTop; + } + if (istop > clip->fBottom) + istop = clip->fBottom; + SkASSERT(istart <= istop); + if (istart == istop) + return; + // now test if our X values are completely inside the clip + int left, right; + if (slope >= 0) // L2R + { + left = SkFixedFloor(fstart - SK_FixedHalf); + right = SkFixedCeil(fstart + (istop - istart - 1) * slope + SK_FixedHalf); + } + else // R2L + { + right = SkFixedCeil(fstart + SK_FixedHalf); + left = SkFixedFloor(fstart + (istop - istart - 1) * slope - SK_FixedHalf); + } + if (left >= clip->fRight || right <= clip->fLeft) + return; + if (clip->fLeft <= left && clip->fRight >= right) + clip = NULL; + } + } + + SkRectClipBlitter rectClipper; + if (clip) + { + rectClipper.init(blitter, *clip); + blitter = &rectClipper; + } + proc(istart, istop, fstart, slope, blitter); +} + +void SkScan::AntiHairLine(const SkPoint& pt0, const SkPoint& pt1, + const SkRegion* clip, SkBlitter* blitter) +{ + if (clip && clip->isEmpty()) + return; + + SkASSERT(clip == NULL || !clip->getBounds().isEmpty()); + +#ifdef TEST_GAMMA + build_gamma_table(); +#endif + + SkFDot6 x0 = SkScalarToFDot6(pt0.fX); + SkFDot6 y0 = SkScalarToFDot6(pt0.fY); + SkFDot6 x1 = SkScalarToFDot6(pt1.fX); + SkFDot6 y1 = SkScalarToFDot6(pt1.fY); + + if (clip) + { + SkFDot6 left = SkMin32(x0, x1); + SkFDot6 top = SkMin32(y0, y1); + SkFDot6 right = SkMax32(x0, x1); + SkFDot6 bottom = SkMax32(y0, y1); + SkIRect ir; + + ir.set( SkFDot6Round(left) - 1, + SkFDot6Round(top) - 1, + SkFDot6Round(right) + 1, + SkFDot6Round(bottom) + 1); + + if (clip->quickReject(ir)) + return; + if (!clip->quickContains(ir)) + { + SkRegion::Cliperator iter(*clip, ir); + const SkIRect* r = &iter.rect(); + + while (!iter.done()) + { + do_anti_hairline(x0, y0, x1, y1, r, blitter); + iter.next(); + } + return; + } + // fall through to no-clip case + } + do_anti_hairline(x0, y0, x1, y1, NULL, blitter); +} + +void SkScan::AntiHairRect(const SkRect& rect, const SkRegion* clip, SkBlitter* blitter) +{ + if (clip) + { + SkIRect ir; + SkRect r = rect; + + r.inset(-SK_Scalar1/2, -SK_Scalar1/2); + r.roundOut(&ir); + if (clip->quickReject(ir)) + return; + if (clip->quickContains(ir)) + clip = NULL; + } + + SkPoint p0, p1; + + p0.set(rect.fLeft, rect.fTop); + p1.set(rect.fRight, rect.fTop); + SkScan::AntiHairLine(p0, p1, clip, blitter); + p0.set(rect.fRight, rect.fBottom); + SkScan::AntiHairLine(p0, p1, clip, blitter); + p1.set(rect.fLeft, rect.fBottom); + SkScan::AntiHairLine(p0, p1, clip, blitter); + p0.set(rect.fLeft, rect.fTop); + SkScan::AntiHairLine(p0, p1, clip, blitter); +} + +////////////////////////////////////////////////////////////////////////////////////////// + +typedef int FDot8; // 24.8 integer fixed point + +static inline FDot8 SkFixedToFDot8(SkFixed x) { + return (x + 0x80) >> 8; +} + +static void do_scanline(FDot8 L, int top, FDot8 R, U8CPU alpha, SkBlitter* blitter) +{ + SkASSERT(L < R); + + if ((L >> 8) == ((R - 1) >> 8)) // 1x1 pixel + { + blitter->blitV(L >> 8, top, 1, SkAlphaMul(alpha, R - L)); + return; + } + + int left = L >> 8; + + if (L & 0xFF) + { + blitter->blitV(left, top, 1, SkAlphaMul(alpha, 256 - (L & 0xFF))); + left += 1; + } + + int rite = R >> 8; + int width = rite - left; + if (width > 0) + call_hline_blitter(blitter, left, top, width, alpha); + + if (R & 0xFF) + blitter->blitV(rite, top, 1, SkAlphaMul(alpha, R & 0xFF)); +} + +static void antifillrect(const SkXRect& xr, SkBlitter* blitter) +{ + FDot8 L = SkFixedToFDot8(xr.fLeft); + FDot8 T = SkFixedToFDot8(xr.fTop); + FDot8 R = SkFixedToFDot8(xr.fRight); + FDot8 B = SkFixedToFDot8(xr.fBottom); + + // check for empty now that we're in our reduced precision space + if (L >= R || T >= B) + return; + + int top = T >> 8; + if (top == ((B - 1) >> 8)) // just one scanline high + { + do_scanline(L, top, R, B - T - 1, blitter); + return; + } + + if (T & 0xFF) + { + do_scanline(L, top, R, 256 - (T & 0xFF), blitter); + top += 1; + } + + int bot = B >> 8; + int height = bot - top; + if (height > 0) + { + int left = L >> 8; + if (L & 0xFF) + { + blitter->blitV(left, top, height, 256 - (L & 0xFF)); + left += 1; + } + int rite = R >> 8; + int width = rite - left; + if (width > 0) + blitter->blitRect(left, top, width, height); + if (R & 0xFF) + blitter->blitV(rite, top, height, R & 0xFF); + } + + if (B & 0xFF) + do_scanline(L, bot, R, B & 0xFF, blitter); +} + +/////////////////////////////////////////////////////////////////////////////// + +void SkScan::AntiFillXRect(const SkXRect& xr, const SkRegion* clip, + SkBlitter* blitter) { + if (clip) { + SkIRect outerBounds; + XRect_roundOut(xr, &outerBounds); + + if (clip->isRect()) { + const SkIRect& clipBounds = clip->getBounds(); + + if (clipBounds.contains(outerBounds)) { + antifillrect(xr, blitter); + } else { + SkXRect tmpR; + // this keeps our original edges fractional + XRect_set(&tmpR, clipBounds); + if (tmpR.intersect(xr)) { + antifillrect(tmpR, blitter); + } + } + } else { + SkRegion::Cliperator clipper(*clip, outerBounds); + const SkIRect& rr = clipper.rect(); + + while (!clipper.done()) { + SkXRect tmpR; + + // this keeps our original edges fractional + XRect_set(&tmpR, rr); + if (tmpR.intersect(xr)) { + antifillrect(tmpR, blitter); + } + clipper.next(); + } + } + } else { + antifillrect(xr, blitter); + } +} + +#ifdef SK_SCALAR_IS_FLOAT + +void SkScan::AntiFillRect(const SkRect& r, const SkRegion* clip, + SkBlitter* blitter) { + SkXRect xr; + + XRect_set(&xr, r); + SkScan::AntiFillXRect(xr, clip, blitter); +} + +#endif + + diff --git a/skia/sgl/SkScan_Hairline.cpp b/skia/sgl/SkScan_Hairline.cpp new file mode 100644 index 0000000..5312f25 --- /dev/null +++ b/skia/sgl/SkScan_Hairline.cpp @@ -0,0 +1,302 @@ +/* libs/graphics/sgl/SkScan_Hairline.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkScan.h" +#include "SkBlitter.h" +#include "SkRegion.h" +#include "SkFDot6.h" + +static void horiline(int x, int stopx, SkFixed fy, SkFixed dy, SkBlitter* blitter) +{ + SkASSERT(x < stopx); + + do { + blitter->blitH(x, fy >> 16, 1); + fy += dy; + } while (++x < stopx); +} + +static void vertline(int y, int stopy, SkFixed fx, SkFixed dx, SkBlitter* blitter) +{ + SkASSERT(y < stopy); + + do { + blitter->blitH(fx >> 16, y, 1); + fx += dx; + } while (++y < stopy); +} + +void SkScan::HairLine(const SkPoint& pt0, const SkPoint& pt1, const SkRegion* clip, SkBlitter* blitter) +{ + SkBlitterClipper clipper; + + SkFDot6 x0 = SkScalarToFDot6(pt0.fX); + SkFDot6 y0 = SkScalarToFDot6(pt0.fY); + SkFDot6 x1 = SkScalarToFDot6(pt1.fX); + SkFDot6 y1 = SkScalarToFDot6(pt1.fY); + + if (clip) + { + SkRect r; + SkIRect ir; + SkPoint pts[2]; + + pts[0] = pt0; + pts[1] = pt1; + r.set(pts, 2); + r.roundOut(&ir); + + // if we're given a horizontal or vertical line + // this rect could be empty (in area), in which case + // clip->quickReject() will always return true. + // hence we bloat the rect to avoid that case + if (ir.width() == 0) + ir.fRight += 1; + if (ir.height() == 0) + ir.fBottom += 1; + + if (clip->quickReject(ir)) + return; + if (clip->quickContains(ir)) + clip = NULL; + else + { + blitter = clipper.apply(blitter, clip); + } + } + + SkFDot6 dx = x1 - x0; + SkFDot6 dy = y1 - y0; + + if (SkAbs32(dx) > SkAbs32(dy)) // mostly horizontal + { + if (x0 > x1) // we want to go left-to-right + { + SkTSwap<SkFDot6>(x0, x1); + SkTSwap<SkFDot6>(y0, y1); + } + int ix0 = SkFDot6Round(x0); + int ix1 = SkFDot6Round(x1); + if (ix0 == ix1) // too short to draw + return; + + SkFixed slope = SkFixedDiv(dy, dx); + SkFixed startY = SkFDot6ToFixed(y0 + SkFixedMul(slope, (32 - x0) & 63)); + + horiline(ix0, ix1, startY, slope, blitter); + } + else // mostly vertical + { + if (y0 > y1) // we want to go top-to-bottom + { + SkTSwap<SkFDot6>(x0, x1); + SkTSwap<SkFDot6>(y0, y1); + } + int iy0 = SkFDot6Round(y0); + int iy1 = SkFDot6Round(y1); + if (iy0 == iy1) // too short to draw + return; + + SkFixed slope = SkFixedDiv(dx, dy); + SkFixed startX = SkFDot6ToFixed(x0 + SkFixedMul(slope, (32 - y0) & 63)); + + vertline(iy0, iy1, startX, slope, blitter); + } +} + +// we don't just draw 4 lines, 'cause that can leave a gap in the bottom-right +// and double-hit the top-left. +void SkScan::HairRect(const SkRect& rect, const SkRegion* clip, SkBlitter* blitter) +{ + SkBlitterClipper clipper; + SkIRect r; + + r.set(SkScalarToFixed(rect.fLeft) >> 16, + SkScalarToFixed(rect.fTop) >> 16, + (SkScalarToFixed(rect.fRight) >> 16) + 1, + (SkScalarToFixed(rect.fBottom) >> 16) + 1); + + if (clip) + { + if (clip->quickReject(r)) + return; + if (!clip->quickContains(r)) + blitter = clipper.apply(blitter, clip); + } + + int width = r.width(); + int height = r.height(); + + if ((width | height) == 0) + return; + if (width <= 2 || height <= 2) + { + blitter->blitRect(r.fLeft, r.fTop, width, height); + return; + } + // if we get here, we know we have 4 segments to draw + blitter->blitH(r.fLeft, r.fTop, width); // top + blitter->blitRect(r.fLeft, r.fTop + 1, 1, height - 2); // left + blitter->blitRect(r.fRight - 1, r.fTop + 1, 1, height - 2); // right + blitter->blitH(r.fLeft, r.fBottom - 1, width); // bottom +} + +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "SkPath.h" +#include "SkGeometry.h" + +static bool quad_too_curvy(const SkPoint pts[3]) +{ + return true; +} + +static void hairquad(const SkPoint pts[3], const SkRegion* clip, SkBlitter* blitter, int level, + void (*lineproc)(const SkPoint&, const SkPoint&, const SkRegion* clip, SkBlitter*)) +{ +#if 1 + if (level > 0 && quad_too_curvy(pts)) + { + SkPoint tmp[5]; + + SkChopQuadAtHalf(pts, tmp); + hairquad(tmp, clip, blitter, level - 1, lineproc); + hairquad(&tmp[2], clip, blitter, level - 1, lineproc); + } + else + lineproc(pts[0], pts[2], clip, blitter); +#else + lineproc(pts[0], pts[1], clip, blitter); + lineproc(pts[1], pts[2], clip, blitter); +#endif +} + +static bool cubic_too_curvy(const SkPoint pts[4]) +{ + return true; +} + +static void haircubic(const SkPoint pts[4], const SkRegion* clip, SkBlitter* blitter, int level, + void (*lineproc)(const SkPoint&, const SkPoint&, const SkRegion*, SkBlitter*)) +{ + if (level > 0 && cubic_too_curvy(pts)) + { + SkPoint tmp[7]; + + SkChopCubicAt(pts, tmp, SK_Scalar1/2); + haircubic(tmp, clip, blitter, level - 1, lineproc); + haircubic(&tmp[3], clip, blitter, level - 1, lineproc); + } + else + lineproc(pts[0], pts[3], clip, blitter); +} + +#define kMaxCubicSubdivideLevel 6 +#define kMaxQuadSubdivideLevel 5 + +static void hair_path(const SkPath& path, const SkRegion* clip, SkBlitter* blitter, + void (*lineproc)(const SkPoint&, const SkPoint&, const SkRegion*, SkBlitter*)) +{ + if (path.isEmpty()) + return; + + const SkIRect* clipR = NULL; + + if (clip) + { + SkRect bounds; + SkIRect ibounds; + + path.computeBounds(&bounds, SkPath::kFast_BoundsType); + bounds.roundOut(&ibounds); + ibounds.inset(-1, -1); + + if (clip->quickReject(ibounds)) + return; + + if (clip->quickContains(ibounds)) + clip = NULL; + else + clipR = &clip->getBounds(); + } + + SkPath::Iter iter(path, false); + SkPoint pts[4]; + SkPath::Verb verb; + + while ((verb = iter.next(pts)) != SkPath::kDone_Verb) + { + switch (verb) { + case SkPath::kLine_Verb: + lineproc(pts[0], pts[1], clip, blitter); + break; + case SkPath::kQuad_Verb: + hairquad(pts, clip, blitter, kMaxQuadSubdivideLevel, lineproc); + break; + case SkPath::kCubic_Verb: + haircubic(pts, clip, blitter, kMaxCubicSubdivideLevel, lineproc); + break; + default: + break; + } + } +} + +void SkScan::HairPath(const SkPath& path, const SkRegion* clip, SkBlitter* blitter) +{ + hair_path(path, clip, blitter, SkScan::HairLine); +} + +void SkScan::AntiHairPath(const SkPath& path, const SkRegion* clip, SkBlitter* blitter) +{ + hair_path(path, clip, blitter, SkScan::AntiHairLine); +} + +//////////////////////////////////////////////////////////////////////////////// + +void SkScan::FrameRect(const SkRect& r, SkScalar diameter, const SkRegion* clip, SkBlitter* blitter) +{ + SkASSERT(diameter > 0); + + if (r.isEmpty()) + return; + + SkScalar radius = diameter / 2; + SkRect outer, tmp; + + outer.set( r.fLeft - radius, r.fTop - radius, + r.fRight + radius, r.fBottom + radius); + + if (r.width() <= diameter || r.height() <= diameter) + { + SkScan::FillRect(outer, clip, blitter); + return; + } + + tmp.set(outer.fLeft, outer.fTop, outer.fRight, outer.fTop + diameter); + SkScan::FillRect(tmp, clip, blitter); + tmp.fTop = outer.fBottom - diameter; + tmp.fBottom = outer.fBottom; + SkScan::FillRect(tmp, clip, blitter); + + tmp.set(outer.fLeft, outer.fTop + diameter, outer.fLeft + diameter, outer.fBottom - diameter); + SkScan::FillRect(tmp, clip, blitter); + tmp.fLeft = outer.fRight - diameter; + tmp.fRight = outer.fRight; + SkScan::FillRect(tmp, clip, blitter); +} + diff --git a/skia/sgl/SkScan_Path.cpp b/skia/sgl/SkScan_Path.cpp new file mode 100644 index 0000000..81b0233 --- /dev/null +++ b/skia/sgl/SkScan_Path.cpp @@ -0,0 +1,671 @@ +/* libs/graphics/sgl/SkScan_Path.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkScanPriv.h" +#include "SkBlitter.h" +#include "SkEdge.h" +#include "SkGeometry.h" +#include "SkPath.h" +#include "SkRegion.h" +#include "SkTemplates.h" + +#define kEDGE_HEAD_Y SK_MinS16 +#define kEDGE_TAIL_Y SK_MaxS16 + +#ifdef SK_DEBUG + static void validate_sort(const SkEdge* edge) + { + int y = kEDGE_HEAD_Y; + + while (edge->fFirstY != SK_MaxS16) + { + edge->validate(); + SkASSERT(y <= edge->fFirstY); + + y = edge->fFirstY; + edge = edge->fNext; + } + } +#else + #define validate_sort(edge) +#endif + +static inline void remove_edge(SkEdge* edge) +{ + edge->fPrev->fNext = edge->fNext; + edge->fNext->fPrev = edge->fPrev; +} + +static inline void swap_edges(SkEdge* prev, SkEdge* next) +{ + SkASSERT(prev->fNext == next && next->fPrev == prev); + + // remove prev from the list + prev->fPrev->fNext = next; + next->fPrev = prev->fPrev; + + // insert prev after next + prev->fNext = next->fNext; + next->fNext->fPrev = prev; + next->fNext = prev; + prev->fPrev = next; +} + +static void backward_insert_edge_based_on_x(SkEdge* edge SkDECLAREPARAM(int, curr_y)) +{ + SkFixed x = edge->fX; + + for (;;) + { + SkEdge* prev = edge->fPrev; + + // add 1 to curr_y since we may have added new edges (built from curves) + // that start on the next scanline + SkASSERT(prev && prev->fFirstY <= curr_y + 1); + + if (prev->fX <= x) + break; + + swap_edges(prev, edge); + } +} + +static void insert_new_edges(SkEdge* newEdge, int curr_y) +{ + SkASSERT(newEdge->fFirstY >= curr_y); + + while (newEdge->fFirstY == curr_y) + { + SkEdge* next = newEdge->fNext; + backward_insert_edge_based_on_x(newEdge SkPARAM(curr_y)); + newEdge = next; + } +} + +#ifdef SK_DEBUG +static void validate_edges_for_y(const SkEdge* edge, int curr_y) +{ + while (edge->fFirstY <= curr_y) + { + SkASSERT(edge->fPrev && edge->fNext); + SkASSERT(edge->fPrev->fNext == edge); + SkASSERT(edge->fNext->fPrev == edge); + SkASSERT(edge->fFirstY <= edge->fLastY); + + SkASSERT(edge->fPrev->fX <= edge->fX); + edge = edge->fNext; + } +} +#else + #define validate_edges_for_y(edge, curr_y) +#endif + +#if defined _WIN32 && _MSC_VER >= 1300 // disable warning : local variable used without having been initialized +#pragma warning ( push ) +#pragma warning ( disable : 4701 ) +#endif + +typedef void (*PrePostProc)(SkBlitter* blitter, int y, bool isStartOfScanline); +#define PREPOST_START true +#define PREPOST_END false + +static void walk_edges(SkEdge* prevHead, SkPath::FillType fillType, + SkBlitter* blitter, int stop_y, PrePostProc proc) +{ + validate_sort(prevHead->fNext); + + int curr_y = prevHead->fNext->fFirstY; + // returns 1 for evenodd, -1 for winding, regardless of inverse-ness + int windingMask = (fillType & 1) ? 1 : -1; + + for (;;) + { + int w = 0; + int left SK_INIT_TO_AVOID_WARNING; + bool in_interval = false; + SkEdge* currE = prevHead->fNext; + SkFixed prevX = prevHead->fX; + + validate_edges_for_y(currE, curr_y); + + if (proc) { + proc(blitter, curr_y, PREPOST_START); // pre-proc + } + + while (currE->fFirstY <= curr_y) + { + SkASSERT(currE->fLastY >= curr_y); + + int x = (currE->fX + SK_Fixed1/2) >> 16; + w += currE->fWinding; + if ((w & windingMask) == 0) // we finished an interval + { + SkASSERT(in_interval); + int width = x - left; + SkASSERT(width >= 0); + if (width) + blitter->blitH(left, curr_y, width); + in_interval = false; + } + else if (!in_interval) + { + left = x; + in_interval = true; + } + + SkEdge* next = currE->fNext; + SkFixed newX; + + if (currE->fLastY == curr_y) // are we done with this edge? + { + if (currE->fCurveCount < 0) + { + if (((SkCubicEdge*)currE)->updateCubic()) + { + SkASSERT(currE->fFirstY == curr_y + 1); + + newX = currE->fX; + goto NEXT_X; + } + } + else if (currE->fCurveCount > 0) + { + if (((SkQuadraticEdge*)currE)->updateQuadratic()) + { + newX = currE->fX; + goto NEXT_X; + } + } + remove_edge(currE); + } + else + { + SkASSERT(currE->fLastY > curr_y); + newX = currE->fX + currE->fDX; + currE->fX = newX; + NEXT_X: + if (newX < prevX) // ripple currE backwards until it is x-sorted + backward_insert_edge_based_on_x(currE SkPARAM(curr_y)); + else + prevX = newX; + } + currE = next; + SkASSERT(currE); + } + + if (proc) { + proc(blitter, curr_y, PREPOST_END); // post-proc + } + + curr_y += 1; + if (curr_y >= stop_y) + break; + + // now currE points to the first edge with a Yint larger than curr_y + insert_new_edges(currE, curr_y); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +// this guy overrides blitH, and will call its proxy blitter with the inverse +// of the spans it is given (clipped to the left/right of the cliprect) +// +// used to implement inverse filltypes on paths +// +class InverseBlitter : public SkBlitter { +public: + void setBlitter(SkBlitter* blitter, const SkIRect& clip, int shift) { + fBlitter = blitter; + fFirstX = clip.fLeft << shift; + fLastX = clip.fRight << shift; + } + void prepost(int y, bool isStart) { + if (isStart) { + fPrevX = fFirstX; + } else { + int invWidth = fLastX - fPrevX; + if (invWidth > 0) { + fBlitter->blitH(fPrevX, y, invWidth); + } + } + } + + // overrides + virtual void blitH(int x, int y, int width) { + int invWidth = x - fPrevX; + if (invWidth > 0) { + fBlitter->blitH(fPrevX, y, invWidth); + } + fPrevX = x + width; + } + + // we do not expect to get called with these entrypoints + virtual void blitAntiH(int, int, const SkAlpha[], const int16_t runs[]) { + SkASSERT(!"blitAntiH unexpected"); + } + virtual void blitV(int x, int y, int height, SkAlpha alpha) { + SkASSERT(!"blitV unexpected"); + } + virtual void blitRect(int x, int y, int width, int height) { + SkASSERT(!"blitRect unexpected"); + } + virtual void blitMask(const SkMask&, const SkIRect& clip) { + SkASSERT(!"blitMask unexpected"); + } + virtual const SkBitmap* justAnOpaqueColor(uint32_t* value) { + SkASSERT(!"justAnOpaqueColor unexpected"); + return NULL; + } + +private: + SkBlitter* fBlitter; + int fFirstX, fLastX, fPrevX; +}; + +static void PrePostInverseBlitterProc(SkBlitter* blitter, int y, bool isStart) { + ((InverseBlitter*)blitter)->prepost(y, isStart); +} + +/////////////////////////////////////////////////////////////////////////////// + +#if defined _WIN32 && _MSC_VER >= 1300 +#pragma warning ( pop ) +#endif + +/* Our line edge relies on the maximum span being <= 512, so that it can + use FDot6 and keep the dx,dy in 16bits (for much faster slope divide). + This function returns true if the specified line is too big. +*/ +static inline bool line_too_big(const SkPoint pts[2]) +{ + SkScalar dx = pts[1].fX - pts[0].fX; + SkScalar dy = pts[1].fY - pts[0].fY; + + return SkScalarAbs(dx) > SkIntToScalar(511) || + SkScalarAbs(dy) > SkIntToScalar(511); +} + +static int build_edges(SkEdge edge[], const SkPath& path, + const SkIRect* clipRect, SkEdge* list[], int shiftUp) { + SkEdge** start = list; + SkPath::Iter iter(path, true); + SkPoint pts[4]; + SkPath::Verb verb; + + while ((verb = iter.next(pts)) != SkPath::kDone_Verb) { + switch (verb) { + case SkPath::kLine_Verb: + if (edge->setLine(pts[0], pts[1], clipRect, shiftUp)) { + *list++ = edge; + edge = (SkEdge*)((char*)edge + sizeof(SkEdge)); + } + break; + case SkPath::kQuad_Verb: { + SkPoint tmp[5]; + SkPoint* p = tmp; + int count = SkChopQuadAtYExtrema(pts, tmp); + + do { + if (((SkQuadraticEdge*)edge)->setQuadratic(p, clipRect, + shiftUp)) + { + *list++ = edge; + edge = (SkEdge*)((char*)edge + sizeof(SkQuadraticEdge)); + } + p += 2; + } while (--count >= 0); + break; + } + case SkPath::kCubic_Verb: { + SkPoint tmp[10]; + SkPoint* p = tmp; + int count = SkChopCubicAtYExtrema(pts, tmp); + SkASSERT(count >= 0 && count <= 2); + + do { + if (((SkCubicEdge*)edge)->setCubic(p, clipRect, shiftUp)) + { + *list++ = edge; + edge = (SkEdge*)((char*)edge + sizeof(SkCubicEdge)); + } + p += 3; + } while (--count >= 0); + break; + } + default: + break; + } + } + return (int)(list - start); +} + +extern "C" { + static int edge_compare(const void* a, const void* b) + { + const SkEdge* edgea = *(const SkEdge**)a; + const SkEdge* edgeb = *(const SkEdge**)b; + + int valuea = edgea->fFirstY; + int valueb = edgeb->fFirstY; + + if (valuea == valueb) + { + valuea = edgea->fX; + valueb = edgeb->fX; + } + return valuea - valueb; + } +} + +static SkEdge* sort_edges(SkEdge* list[], int count, SkEdge** last) +{ + qsort(list, count, sizeof(SkEdge*), edge_compare); + + // now make the edges linked in sorted order + for (int i = 1; i < count; i++) + { + list[i - 1]->fNext = list[i]; + list[i]->fPrev = list[i - 1]; + } + + *last = list[count - 1]; + return list[0]; +} + +/* 'quick' computation of the max sized needed to allocated for + our edgelist. +*/ +static int worst_case_edge_count(const SkPath& path, size_t* storage) +{ + size_t size = 0; + int edgeCount = 0; + + SkPath::Iter iter(path, true); + SkPath::Verb verb; + + while ((verb = iter.next(NULL)) != SkPath::kDone_Verb) + { + switch (verb) { + case SkPath::kLine_Verb: + edgeCount += 1; + size += sizeof(SkQuadraticEdge); // treat line like Quad (in case its > 512) + break; + case SkPath::kQuad_Verb: + edgeCount += 2; // might need 2 edges when we chop on Y extrema + size += 2 * sizeof(SkQuadraticEdge); + break; + case SkPath::kCubic_Verb: + edgeCount += 3; // might need 3 edges when we chop on Y extrema + size += 3 * sizeof(SkCubicEdge); + break; + default: + break; + } + } + + SkASSERT(storage); + *storage = size; + return edgeCount; +} + +/* Much faster than worst_case_edge_count, but over estimates even more +*/ +static int cheap_worst_case_edge_count(const SkPath& path, size_t* storage) +{ + int ptCount = path.getPoints(NULL, 0); + int edgeCount = ptCount; + *storage = edgeCount * sizeof(SkCubicEdge); + return edgeCount; +} + +// clipRect may be null, even though we always have a clip. This indicates that +// the path is contained in the clip, and so we can ignore it during the blit +// +// clipRect (if no null) has already been shifted up +// +void sk_fill_path(const SkPath& path, const SkIRect* clipRect, SkBlitter* blitter, + int stop_y, int shiftEdgesUp, const SkRegion& clipRgn) +{ + SkASSERT(&path && blitter); + + size_t size; + int maxCount = cheap_worst_case_edge_count(path, &size); + +#ifdef SK_DEBUG + { + size_t size2; + int maxCount2 = worst_case_edge_count(path, &size2); + + SkASSERT(maxCount >= maxCount2 && size >= size2); + } +#endif + + SkAutoMalloc memory(maxCount * sizeof(SkEdge*) + size); + SkEdge** list = (SkEdge**)memory.get(); + SkEdge* edge = (SkEdge*)(list + maxCount); + int count = build_edges(edge, path, clipRect, list, shiftEdgesUp); + SkEdge headEdge, tailEdge, *last; + + SkASSERT(count <= maxCount); + if (count == 0) { + return; + } + SkASSERT(count > 1); + + // this returns the first and last edge after they're sorted into a dlink list + edge = sort_edges(list, count, &last); + + headEdge.fPrev = NULL; + headEdge.fNext = edge; + headEdge.fFirstY = kEDGE_HEAD_Y; + headEdge.fX = SK_MinS32; + edge->fPrev = &headEdge; + + tailEdge.fPrev = last; + tailEdge.fNext = NULL; + tailEdge.fFirstY = kEDGE_TAIL_Y; + last->fNext = &tailEdge; + + // now edge is the head of the sorted linklist + + stop_y <<= shiftEdgesUp; + if (clipRect && stop_y > clipRect->fBottom) { + stop_y = clipRect->fBottom; + } + + InverseBlitter ib; + PrePostProc proc = NULL; + + if (path.isInverseFillType()) { + ib.setBlitter(blitter, clipRgn.getBounds(), shiftEdgesUp); + blitter = &ib; + proc = PrePostInverseBlitterProc; + } + + walk_edges(&headEdge, path.getFillType(), blitter, stop_y, proc); +} + +void sk_blit_above_and_below(SkBlitter* blitter, const SkIRect& ir, + const SkRegion& clip) { + const SkIRect& cr = clip.getBounds(); + SkIRect tmp; + + tmp.fLeft = cr.fLeft; + tmp.fRight = cr.fRight; + + tmp.fTop = cr.fTop; + tmp.fBottom = ir.fTop; + if (!tmp.isEmpty()) { + blitter->blitRectRegion(tmp, clip); + } + + tmp.fTop = ir.fBottom; + tmp.fBottom = cr.fBottom; + if (!tmp.isEmpty()) { + blitter->blitRectRegion(tmp, clip); + } +} + +///////////////////////////////////////////////////////////////////////////////////// + +SkScanClipper::SkScanClipper(SkBlitter* blitter, const SkRegion* clip, const SkIRect& ir) +{ + fBlitter = NULL; // null means blit nothing + fClipRect = NULL; + + if (clip) + { + fClipRect = &clip->getBounds(); + if (!SkIRect::Intersects(*fClipRect, ir)) // completely clipped out + return; + + if (clip->isRect()) + { + if (fClipRect->contains(ir)) + fClipRect = NULL; + else + { + // only need a wrapper blitter if we're horizontally clipped + if (fClipRect->fLeft > ir.fLeft || fClipRect->fRight < ir.fRight) + { + fRectBlitter.init(blitter, *fClipRect); + blitter = &fRectBlitter; + } + } + } + else + { + fRgnBlitter.init(blitter, clip); + blitter = &fRgnBlitter; + } + } + fBlitter = blitter; +} + +/////////////////////////////////////////////////////////////////////////////// + +void SkScan::FillPath(const SkPath& path, const SkRegion& clip, + SkBlitter* blitter) { + if (clip.isEmpty()) { + return; + } + + SkRect r; + SkIRect ir; + + path.computeBounds(&r, SkPath::kFast_BoundsType); + r.round(&ir); + if (ir.isEmpty()) { + if (path.isInverseFillType()) { + blitter->blitRegion(clip); + } + return; + } + + SkScanClipper clipper(blitter, &clip, ir); + + blitter = clipper.getBlitter(); + if (blitter) { + if (path.isInverseFillType()) { + sk_blit_above_and_below(blitter, ir, clip); + } + sk_fill_path(path, clipper.getClipRect(), blitter, ir.fBottom, 0, clip); + } else { + // what does it mean to not have a blitter if path.isInverseFillType??? + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static int build_tri_edges(SkEdge edge[], const SkPoint pts[], + const SkIRect* clipRect, SkEdge* list[]) { + SkEdge** start = list; + + if (edge->setLine(pts[0], pts[1], clipRect, 0)) { + *list++ = edge; + edge = (SkEdge*)((char*)edge + sizeof(SkEdge)); + } + if (edge->setLine(pts[1], pts[2], clipRect, 0)) { + *list++ = edge; + edge = (SkEdge*)((char*)edge + sizeof(SkEdge)); + } + if (edge->setLine(pts[2], pts[0], clipRect, 0)) { + *list++ = edge; + } + return (int)(list - start); +} + + +void sk_fill_triangle(const SkPoint pts[], const SkIRect* clipRect, + SkBlitter* blitter, const SkIRect& ir) { + SkASSERT(pts && blitter); + + SkEdge edgeStorage[3]; + SkEdge* list[3]; + + int count = build_tri_edges(edgeStorage, pts, clipRect, list); + if (count < 2) { + return; + } + + SkEdge headEdge, tailEdge, *last; + + // this returns the first and last edge after they're sorted into a dlink list + SkEdge* edge = sort_edges(list, count, &last); + + headEdge.fPrev = NULL; + headEdge.fNext = edge; + headEdge.fFirstY = kEDGE_HEAD_Y; + headEdge.fX = SK_MinS32; + edge->fPrev = &headEdge; + + tailEdge.fPrev = last; + tailEdge.fNext = NULL; + tailEdge.fFirstY = kEDGE_TAIL_Y; + last->fNext = &tailEdge; + + // now edge is the head of the sorted linklist + int stop_y = ir.fBottom; + if (clipRect && stop_y > clipRect->fBottom) { + stop_y = clipRect->fBottom; + } + walk_edges(&headEdge, SkPath::kEvenOdd_FillType, blitter, stop_y, NULL); +} + +void SkScan::FillTriangle(const SkPoint pts[], const SkRegion* clip, + SkBlitter* blitter) { + if (clip && clip->isEmpty()) { + return; + } + + SkRect r; + SkIRect ir; + r.set(pts, 3); + r.round(&ir); + if (ir.isEmpty()) { + return; + } + + SkScanClipper clipper(blitter, clip, ir); + + blitter = clipper.getBlitter(); + if (NULL != blitter) { + sk_fill_triangle(pts, clipper.getClipRect(), blitter, ir); + } +} + diff --git a/skia/sgl/SkShader.cpp b/skia/sgl/SkShader.cpp new file mode 100644 index 0000000..9a90959 --- /dev/null +++ b/skia/sgl/SkShader.cpp @@ -0,0 +1,284 @@ +/* libs/graphics/sgl/SkShader.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkShader.h" +#include "SkPaint.h" + +SkShader::SkShader() : fLocalMatrix(NULL) { + SkDEBUGCODE(fInSession = false;) +} + +SkShader::SkShader(SkFlattenableReadBuffer& buffer) + : INHERITED(buffer), fLocalMatrix(NULL) { + if (buffer.readBool()) { + SkMatrix matrix; + buffer.read(&matrix, sizeof(matrix)); + setLocalMatrix(matrix); + } + SkDEBUGCODE(fInSession = false;) +} + +SkShader::~SkShader() { + SkASSERT(!fInSession); + sk_free(fLocalMatrix); +} + +void SkShader::beginSession() { + SkASSERT(!fInSession); + SkDEBUGCODE(fInSession = true;) +} + +void SkShader::endSession() { + SkASSERT(fInSession); + SkDEBUGCODE(fInSession = false;) +} + +void SkShader::flatten(SkFlattenableWriteBuffer& buffer) { + this->INHERITED::flatten(buffer); + buffer.writeBool(fLocalMatrix != NULL); + if (fLocalMatrix) { + buffer.writeMul4(fLocalMatrix, sizeof(SkMatrix)); + } +} + +bool SkShader::getLocalMatrix(SkMatrix* localM) const { + if (fLocalMatrix) { + if (localM) { + *localM = *fLocalMatrix; + } + return true; + } else { + if (localM) { + localM->reset(); + } + return false; + } +} + +void SkShader::setLocalMatrix(const SkMatrix& localM) { + if (localM.isIdentity()) { + this->resetLocalMatrix(); + } else { + if (fLocalMatrix == NULL) { + fLocalMatrix = (SkMatrix*)sk_malloc_throw(sizeof(SkMatrix)); + } + *fLocalMatrix = localM; + } +} + +void SkShader::resetLocalMatrix() { + if (fLocalMatrix) { + sk_free(fLocalMatrix); + fLocalMatrix = NULL; + } +} + +bool SkShader::setContext(const SkBitmap& device, + const SkPaint& paint, + const SkMatrix& matrix) { + const SkMatrix* m = &matrix; + SkMatrix total; + + fDeviceConfig = SkToU8(device.getConfig()); + fPaintAlpha = paint.getAlpha(); + if (fLocalMatrix) { + total.setConcat(matrix, *fLocalMatrix); + m = &total; + } + if (m->invert(&fTotalInverse)) { + fTotalInverseClass = (uint8_t)ComputeMatrixClass(fTotalInverse); + return true; + } + return false; +} + +#include "SkColorPriv.h" + +void SkShader::shadeSpan16(int x, int y, uint16_t span16[], int count) { + SkASSERT(span16); + SkASSERT(count > 0); + SkASSERT(this->canCallShadeSpan16()); + + // basically, if we get here, the subclass screwed up + SkASSERT(!"kHasSpan16 flag is set, but shadeSpan16() not implemented"); +} + +#define kTempColorQuadCount 6 // balance between speed (larger) and saving stack-space +#define kTempColorCount (kTempColorQuadCount << 2) + +#ifdef SK_CPU_BENDIAN + #define SkU32BitShiftToByteOffset(shift) (3 - ((shift) >> 3)) +#else + #define SkU32BitShiftToByteOffset(shift) ((shift) >> 3) +#endif + +void SkShader::shadeSpanAlpha(int x, int y, uint8_t alpha[], int count) { + SkASSERT(count > 0); + + SkPMColor colors[kTempColorCount]; + + while ((count -= kTempColorCount) >= 0) { + this->shadeSpan(x, y, colors, kTempColorCount); + x += kTempColorCount; + + const uint8_t* srcA = (const uint8_t*)colors + SkU32BitShiftToByteOffset(SK_A32_SHIFT); + int quads = kTempColorQuadCount; + do { + U8CPU a0 = srcA[0]; + U8CPU a1 = srcA[4]; + U8CPU a2 = srcA[8]; + U8CPU a3 = srcA[12]; + srcA += 4*4; + *alpha++ = SkToU8(a0); + *alpha++ = SkToU8(a1); + *alpha++ = SkToU8(a2); + *alpha++ = SkToU8(a3); + } while (--quads != 0); + } + SkASSERT(count < 0); + SkASSERT(count + kTempColorCount >= 0); + if (count += kTempColorCount) { + this->shadeSpan(x, y, colors, count); + + const uint8_t* srcA = (const uint8_t*)colors + SkU32BitShiftToByteOffset(SK_A32_SHIFT); + do { + *alpha++ = *srcA; + srcA += 4; + } while (--count != 0); + } +#if 0 + do { + int n = count; + if (n > kTempColorCount) + n = kTempColorCount; + SkASSERT(n > 0); + + this->shadeSpan(x, y, colors, n); + x += n; + count -= n; + + const uint8_t* srcA = (const uint8_t*)colors + SkU32BitShiftToByteOffset(SK_A32_SHIFT); + do { + *alpha++ = *srcA; + srcA += 4; + } while (--n != 0); + } while (count > 0); +#endif +} + +SkShader::MatrixClass SkShader::ComputeMatrixClass(const SkMatrix& mat) { + MatrixClass mc = kLinear_MatrixClass; + + if (mat.getType() & SkMatrix::kPerspective_Mask) { + if (mat.fixedStepInX(0, NULL, NULL)) { + mc = kFixedStepInX_MatrixClass; + } else { + mc = kPerspective_MatrixClass; + } + } + return mc; +} + +////////////////////////////////////////////////////////////////////////////// + +bool SkShader::asABitmap(SkBitmap*, SkMatrix*, TileMode*) { + return false; +} + +SkShader* SkShader::CreateBitmapShader(const SkBitmap& src, + TileMode tmx, TileMode tmy) { + return SkShader::CreateBitmapShader(src, tmx, tmy, NULL, 0); +} + +////////////////////////////////////////////////////////////////////////////// + +#include "SkColorShader.h" +#include "SkUtils.h" + +SkColorShader::SkColorShader(SkFlattenableReadBuffer& b) : INHERITED(b) { + fInheritColor = b.readU8(); + if (fInheritColor) { + return; + } + fColor = b.readU32(); +} + +void SkColorShader::flatten(SkFlattenableWriteBuffer& buffer) { + this->INHERITED::flatten(buffer); + buffer.write8(fInheritColor); + if (fInheritColor) { + return; + } + buffer.write32(fColor); +} + +uint32_t SkColorShader::getFlags() { + return (SkGetPackedA32(fPMColor) == 255 ? kOpaqueAlpha_Flag : 0) | + kHasSpan16_Flag; +} + +uint8_t SkColorShader::getSpan16Alpha() const { + return SkGetPackedA32(fPMColor); +} + +bool SkColorShader::setContext(const SkBitmap& device, const SkPaint& paint, + const SkMatrix& matrix) { + if (!this->INHERITED::setContext(device, paint, matrix)) { + return false; + } + + SkColor c; + unsigned a; + + if (fInheritColor) { + c = paint.getColor(); + a = SkColorGetA(c); + } else { + c = fColor; + a = SkAlphaMul(SkColorGetA(c), SkAlpha255To256(paint.getAlpha())); + } + + unsigned r = SkColorGetR(c); + unsigned g = SkColorGetG(c); + unsigned b = SkColorGetB(c); + + // we want this before we apply any alpha + fColor16 = SkPack888ToRGB16(r, g, b); + + if (a != 255) { + a = SkAlpha255To256(a); + r = SkAlphaMul(r, a); + g = SkAlphaMul(g, a); + b = SkAlphaMul(b, a); + } + fPMColor = SkPackARGB32(a, r, g, b); + + return true; +} + +void SkColorShader::shadeSpan(int x, int y, SkPMColor span[], int count) { + sk_memset32(span, fPMColor, count); +} + +void SkColorShader::shadeSpan16(int x, int y, uint16_t span[], int count) { + sk_memset16(span, fColor16, count); +} + +void SkColorShader::shadeSpanAlpha(int x, int y, uint8_t alpha[], int count) { + memset(alpha, SkGetPackedA32(fPMColor), count); +} + diff --git a/skia/sgl/SkSpriteBlitter.h b/skia/sgl/SkSpriteBlitter.h new file mode 100644 index 0000000..7447389 --- /dev/null +++ b/skia/sgl/SkSpriteBlitter.h @@ -0,0 +1,55 @@ +/* libs/graphics/sgl/SkSpriteBlitter.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkSpriteBlitter_DEFINED +#define SkSpriteBlitter_DEFINED + +#include "SkBlitter.h" +#include "SkBitmap.h" + +class SkPaint; + +class SkSpriteBlitter : public SkBlitter { +public: + SkSpriteBlitter(const SkBitmap& source); + virtual ~SkSpriteBlitter(); + + virtual void setup(const SkBitmap& device, int left, int top, + const SkPaint& paint); + + // overrides +#ifdef SK_DEBUG + virtual void blitH(int x, int y, int width); + virtual void blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]); + virtual void blitV(int x, int y, int height, SkAlpha alpha); + virtual void blitMask(const SkMask&, const SkIRect& clip); +#endif + + static SkSpriteBlitter* ChooseD16(const SkBitmap& source, const SkPaint&, + void* storage, size_t storageSize); + static SkSpriteBlitter* ChooseD32(const SkBitmap& source, const SkPaint&, + void* storage, size_t storageSize); + +protected: + const SkBitmap* fDevice; + const SkBitmap* fSource; + int fLeft, fTop; + const SkPaint* fPaint; +}; + +#endif + diff --git a/skia/sgl/SkSpriteBlitterTemplate.h b/skia/sgl/SkSpriteBlitterTemplate.h new file mode 100644 index 0000000..267931e --- /dev/null +++ b/skia/sgl/SkSpriteBlitterTemplate.h @@ -0,0 +1,91 @@ +/* libs/graphics/sgl/SkSpriteBlitterTemplate.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + + +class SkSPRITE_CLASSNAME : public SkSpriteBlitter { +public: + SkSPRITE_CLASSNAME(const SkBitmap& source SkSPRITE_ARGS) + : SkSpriteBlitter(source) { + SkSPRITE_INIT + } + + virtual void blitRect(int x, int y, int width, int height) { + SkASSERT(width > 0 && height > 0); + int srcX = x - fLeft; + int srcY = y - fTop; + SK_RESTRICT SkSPRITE_DST_TYPE* dst =fDevice->SkSPRITE_DST_GETADDR(x, y); + const SK_RESTRICT SkSPRITE_SRC_TYPE* src = + fSource->SkSPRITE_SRC_GETADDR(srcX, srcY); + unsigned dstRB = fDevice->rowBytes(); + unsigned srcRB = fSource->rowBytes(); + + SkDEBUGCODE((void)fDevice->SkSPRITE_DST_GETADDR(x + width - 1, y + height - 1);) + SkDEBUGCODE((void)fSource->SkSPRITE_SRC_GETADDR(srcX + width - 1, srcY + height - 1);) + + SkSPRITE_PREAMBLE((*fSource), srcX, srcY); + + do { + SkSPRITE_DST_TYPE* d = dst; + const SkSPRITE_SRC_TYPE* s = src; +#ifdef SkSPRITE_BEGIN_ROW + SkSPRITE_BEGIN_ROW +#endif + +#ifdef SkSPRITE_ROW_PROC + SkSPRITE_ROW_PROC(d, s, width, x, y); +#else + int w = width; + do { + SkSPRITE_SRC_TYPE sc = *s++; + SkSPRITE_BLIT_PIXEL(d, sc); + d += 1; + } while (--w != 0); +#endif + dst = (SK_RESTRICT SkSPRITE_DST_TYPE*)((char*)dst + dstRB); + src = (const SK_RESTRICT SkSPRITE_SRC_TYPE*) + ((const char*)src + srcRB); + SkSPRITE_NEXT_ROW +#ifdef SkSPRITE_ROW_PROC + y += 1; +#endif + } while (--height != 0); + + SkSPRITE_POSTAMBLE((*fSource)); + } + +private: + SkSPRITE_FIELDS +}; + +#undef SkSPRITE_BLIT_PIXEL +#undef SkSPRITE_CLASSNAME +#undef SkSPRITE_DST_TYPE +#undef SkSPRITE_SRC_TYPE +#undef SkSPRITE_DST_GETADDR +#undef SkSPRITE_SRC_GETADDR +#undef SkSPRITE_PREAMBLE +#undef SkSPRITE_POSTAMBLE +#undef SkSPRITE_ARGS +#undef SkSPRITE_FIELDS +#undef SkSPRITE_INIT +#undef SkSPRITE_NEXT_ROW +#undef SkSPRITE_BEGIN_ROW + +#ifdef SkSPRITE_ROW_PROC + #undef SkSPRITE_ROW_PROC +#endif + diff --git a/skia/sgl/SkSpriteBlitter_ARGB32.cpp b/skia/sgl/SkSpriteBlitter_ARGB32.cpp new file mode 100644 index 0000000..ede7450 --- /dev/null +++ b/skia/sgl/SkSpriteBlitter_ARGB32.cpp @@ -0,0 +1,314 @@ +/* libs/graphics/sgl/SkSpriteBlitter_ARGB32.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkSpriteBlitter.h" +#include "SkTemplates.h" +#include "SkUtils.h" +#include "SkColorPriv.h" + +#define D32_S32A_Opaque_Pixel(dst, sc) \ +do { \ + if (sc) { \ + unsigned srcA = SkGetPackedA32(sc); \ + uint32_t result = sc; \ + if (srcA != 0xFF) { \ + result += SkAlphaMulQ(*dst, SkAlpha255To256(255 - srcA)); \ + } \ + *dst = result; \ + } \ +} while (0) + +#define SkSPRITE_CLASSNAME Sprite_D32_S32A_Opaque +#define SkSPRITE_ARGS +#define SkSPRITE_FIELDS +#define SkSPRITE_INIT +#define SkSPRITE_DST_TYPE uint32_t +#define SkSPRITE_SRC_TYPE uint32_t +#define SkSPRITE_DST_GETADDR getAddr32 +#define SkSPRITE_SRC_GETADDR getAddr32 +#define SkSPRITE_PREAMBLE(srcBM, x, y) +#define SkSPRITE_BLIT_PIXEL(dst, src) D32_S32A_Opaque_Pixel(dst, src) +#define SkSPRITE_NEXT_ROW +#define SkSPRITE_POSTAMBLE(srcBM) +#include "SkSpriteBlitterTemplate.h" + +/////////////////////////////////////////////////////////////////////////////// + +class Sprite_D32_S32_Opaque : public SkSpriteBlitter { +public: + Sprite_D32_S32_Opaque(const SkBitmap& source) : SkSpriteBlitter(source) {} + + virtual void blitRect(int x, int y, int width, int height) { + SkASSERT(width > 0 && height > 0); + SK_RESTRICT uint32_t* dst = fDevice->getAddr32(x, y); + const SK_RESTRICT uint32_t* src = fSource->getAddr32(x - fLeft, + y - fTop); + unsigned dstRB = fDevice->rowBytes(); + unsigned srcRB = fSource->rowBytes(); + size_t size = width * sizeof(uint32_t); + + do { + memcpy(dst, src, size); + dst = (SK_RESTRICT uint32_t*)((char*)dst + dstRB); + src = (const SK_RESTRICT uint32_t*)((const char*)src + srcRB); + } while (--height != 0); + } +}; + +/////////////////////////////////////////////////////////////////////////////// + +#include "SkColorFilter.h" +#include "SkXfermode.h" + +class Sprite_D32_XferFilter : public SkSpriteBlitter { +public: + Sprite_D32_XferFilter(const SkBitmap& source, const SkPaint& paint) + : SkSpriteBlitter(source) { + fColorFilter = paint.getColorFilter(); + fColorFilter->safeRef(); + + fXfermode = paint.getXfermode(); + fXfermode->safeRef(); + + fBufferSize = 0; + fBuffer = NULL; + } + + virtual ~Sprite_D32_XferFilter() { + delete[] fBuffer; + fXfermode->safeUnref(); + fColorFilter->safeUnref(); + } + + virtual void setup(const SkBitmap& device, int left, int top, + const SkPaint& paint) { + this->INHERITED::setup(device, left, top, paint); + + int width = device.width(); + if (width > fBufferSize) { + fBufferSize = width; + delete[] fBuffer; + fBuffer = new SkPMColor[width]; + } + } + +protected: + SkColorFilter* fColorFilter; + SkXfermode* fXfermode; + int fBufferSize; + SkPMColor* fBuffer; + +private: + typedef SkSpriteBlitter INHERITED; +}; + +/////////////////////////////////////////////////////////////////////////////// + +class Sprite_D32_S32A_XferFilter : public Sprite_D32_XferFilter { +public: + Sprite_D32_S32A_XferFilter(const SkBitmap& source, const SkPaint& paint) + : Sprite_D32_XferFilter(source, paint) {} + + virtual void blitRect(int x, int y, int width, int height) { + SkASSERT(width > 0 && height > 0); + SK_RESTRICT uint32_t* dst = fDevice->getAddr32(x, y); + const SK_RESTRICT uint32_t* src = fSource->getAddr32(x - fLeft, + y - fTop); + unsigned dstRB = fDevice->rowBytes(); + unsigned srcRB = fSource->rowBytes(); + SkColorFilter* colorFilter = fColorFilter; + SkXfermode* xfermode = fXfermode; + + do { + const SkPMColor* tmp = src; + + if (NULL != colorFilter) { + colorFilter->filterSpan(src, width, fBuffer); + tmp = fBuffer; + } + + if (NULL != xfermode) { + xfermode->xfer32(dst, tmp, width, NULL); + } else { + for (int i = 0; i < width; i++) { + dst[i] = SkPMSrcOver(tmp[i], dst[i]); + } + } + + dst = (SK_RESTRICT uint32_t*)((char*)dst + dstRB); + src = (const SK_RESTRICT uint32_t*)((const char*)src + srcRB); + } while (--height != 0); + } + +private: + typedef Sprite_D32_XferFilter INHERITED; +}; + +static void fillbuffer(SK_RESTRICT SkPMColor dst[], + const SK_RESTRICT SkPMColor16 src[], int count) { + SkASSERT(count > 0); + + do { + *dst++ = SkPixel4444ToPixel32(*src++); + } while (--count != 0); +} + +class Sprite_D32_S4444_XferFilter : public Sprite_D32_XferFilter { +public: + Sprite_D32_S4444_XferFilter(const SkBitmap& source, const SkPaint& paint) + : Sprite_D32_XferFilter(source, paint) {} + + virtual void blitRect(int x, int y, int width, int height) { + SkASSERT(width > 0 && height > 0); + SK_RESTRICT SkPMColor* dst = fDevice->getAddr32(x, y); + const SK_RESTRICT SkPMColor16* src = fSource->getAddr16(x - fLeft, + y - fTop); + unsigned dstRB = fDevice->rowBytes(); + unsigned srcRB = fSource->rowBytes(); + SK_RESTRICT SkPMColor* buffer = fBuffer; + SkColorFilter* colorFilter = fColorFilter; + SkXfermode* xfermode = fXfermode; + + do { + fillbuffer(buffer, src, width); + + if (NULL != colorFilter) { + colorFilter->filterSpan(buffer, width, buffer); + } + if (NULL != xfermode) { + xfermode->xfer32(dst, buffer, width, NULL); + } else { + for (int i = 0; i < width; i++) { + dst[i] = SkPMSrcOver(buffer[i], dst[i]); + } + } + + dst = (SK_RESTRICT SkPMColor*)((char*)dst + dstRB); + src = (const SK_RESTRICT SkPMColor16*)((const char*)src + srcRB); + } while (--height != 0); + } + +private: + typedef Sprite_D32_XferFilter INHERITED; +}; + +/////////////////////////////////////////////////////////////////////////////// + +static void src_row(SK_RESTRICT SkPMColor dst[], + const SK_RESTRICT SkPMColor16 src[], int count) { + do { + *dst = SkPixel4444ToPixel32(*src); + src += 1; + dst += 1; + } while (--count != 0); +} + +class Sprite_D32_S4444_Opaque : public SkSpriteBlitter { +public: + Sprite_D32_S4444_Opaque(const SkBitmap& source) : SkSpriteBlitter(source) {} + + virtual void blitRect(int x, int y, int width, int height) { + SkASSERT(width > 0 && height > 0); + SK_RESTRICT SkPMColor* dst = fDevice->getAddr32(x, y); + const SK_RESTRICT SkPMColor16* src = fSource->getAddr16(x - fLeft, + y - fTop); + unsigned dstRB = fDevice->rowBytes(); + unsigned srcRB = fSource->rowBytes(); + + do { + src_row(dst, src, width); + dst = (SK_RESTRICT SkPMColor*)((char*)dst + dstRB); + src = (const SK_RESTRICT SkPMColor16*)((const char*)src + srcRB); + } while (--height != 0); + } +}; + +static void srcover_row(SK_RESTRICT SkPMColor dst[], + const SK_RESTRICT SkPMColor16 src[], int count) { + do { + *dst = SkPMSrcOver(SkPixel4444ToPixel32(*src), *dst); + src += 1; + dst += 1; + } while (--count != 0); +} + +class Sprite_D32_S4444 : public SkSpriteBlitter { +public: + Sprite_D32_S4444(const SkBitmap& source) : SkSpriteBlitter(source) {} + + virtual void blitRect(int x, int y, int width, int height) { + SkASSERT(width > 0 && height > 0); + SK_RESTRICT SkPMColor* dst = fDevice->getAddr32(x, y); + const SK_RESTRICT SkPMColor16* src = fSource->getAddr16(x - fLeft, + y - fTop); + unsigned dstRB = fDevice->rowBytes(); + unsigned srcRB = fSource->rowBytes(); + + do { + srcover_row(dst, src, width); + dst = (SK_RESTRICT SkPMColor*)((char*)dst + dstRB); + src = (const SK_RESTRICT SkPMColor16*)((const char*)src + srcRB); + } while (--height != 0); + } +}; + +/////////////////////////////////////////////////////////////////////////////// + +#include "SkTemplatesPriv.h" + +SkSpriteBlitter* SkSpriteBlitter::ChooseD32(const SkBitmap& source, + const SkPaint& paint, + void* storage, size_t storageSize) { + if (paint.getMaskFilter() != NULL || paint.getAlpha() != 0xFF) { + return NULL; + } + + SkXfermode* xfermode = paint.getXfermode(); + SkColorFilter* filter = paint.getColorFilter(); + SkSpriteBlitter* blitter = NULL; + + switch (source.getConfig()) { + case SkBitmap::kARGB_4444_Config: + if (xfermode || filter) { + SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D32_S4444_XferFilter, + storage, storageSize, (source, paint)); + } else if (source.isOpaque()) { + SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D32_S4444_Opaque, + storage, storageSize, (source)); + } else { + SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D32_S4444, + storage, storageSize, (source)); + } + break; + case SkBitmap::kARGB_8888_Config: + if (xfermode || filter) { + SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D32_S32A_XferFilter, + storage, storageSize, (source, paint)); + } else if (source.isOpaque()) { + SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D32_S32_Opaque, + storage, storageSize, (source)); + } else { + SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D32_S32A_Opaque, + storage, storageSize, (source)); + } + break; + default: + break; + } + return blitter; +} + diff --git a/skia/sgl/SkSpriteBlitter_RGB16.cpp b/skia/sgl/SkSpriteBlitter_RGB16.cpp new file mode 100644 index 0000000..3ffea44 --- /dev/null +++ b/skia/sgl/SkSpriteBlitter_RGB16.cpp @@ -0,0 +1,325 @@ +/* libs/graphics/sgl/SkSpriteBlitter_RGB16.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkSpriteBlitter.h" +#include "SkBlitRow.h" +#include "SkTemplates.h" +#include "SkUtils.h" +#include "SkColorPriv.h" + +#define D16_S32A_Opaque_Pixel(dst, sc) \ +do { \ + if (sc) { \ + unsigned sa = SkGetPackedA32(sc); \ + unsigned result = SkPixel32ToPixel16(sc); \ + if (sa != 0xFF) { \ + result += SkAlphaMulRGB16_ToU16(*dst, SkAlpha255To256(255 - sa)); \ + } \ + *dst = SkToU16(result); \ + } \ +} while (0) + +static inline void D16_S32A_Blend_Pixel_helper(uint16_t* dst, SkPMColor sc, + unsigned src_scale) { + uint16_t dc = *dst; + unsigned sa = SkGetPackedA32(sc); + unsigned dr, dg, db; + + if (255 == sa) { + dr = SkAlphaBlend(SkPacked32ToR16(sc), SkGetPackedR16(dc), src_scale); + dg = SkAlphaBlend(SkPacked32ToG16(sc), SkGetPackedG16(dc), src_scale); + db = SkAlphaBlend(SkPacked32ToB16(sc), SkGetPackedB16(dc), src_scale); + } else { + unsigned dst_scale = 255 - SkAlphaMul(sa, src_scale); + dr = (SkPacked32ToR16(sc) * src_scale + + SkGetPackedR16(dc) * dst_scale) >> 8; + dg = (SkPacked32ToG16(sc) * src_scale + + SkGetPackedG16(dc) * dst_scale) >> 8; + db = (SkPacked32ToB16(sc) * src_scale + + SkGetPackedB16(dc) * dst_scale) >> 8; + } + *dst = SkPackRGB16(dr, dg, db); +} + +#define D16_S32A_Blend_Pixel(dst, sc, src_scale) \ + do { if (sc) D16_S32A_Blend_Pixel_helper(dst, sc, src_scale); } while (0) + + +/////////////////////////////////////////////////////////////////////////////// + +class Sprite_D16_S16_Opaque : public SkSpriteBlitter { +public: + Sprite_D16_S16_Opaque(const SkBitmap& source) + : SkSpriteBlitter(source) {} + + // overrides + virtual void blitRect(int x, int y, int width, int height) { + SK_RESTRICT uint16_t* dst = fDevice->getAddr16(x, y); + const SK_RESTRICT uint16_t* src = fSource->getAddr16(x - fLeft, + y - fTop); + unsigned dstRB = fDevice->rowBytes(); + unsigned srcRB = fSource->rowBytes(); + + while (--height >= 0) { + memcpy(dst, src, width << 1); + dst = (uint16_t*)((char*)dst + dstRB); + src = (const uint16_t*)((const char*)src + srcRB); + } + } +}; + +#define D16_S16_Blend_Pixel(dst, sc, scale) \ + do { \ + uint16_t dc = *dst; \ + *dst = SkBlendRGB16(sc, dc, scale); \ + } while (0) + +#define SkSPRITE_CLASSNAME Sprite_D16_S16_Blend +#define SkSPRITE_ARGS , uint8_t alpha +#define SkSPRITE_FIELDS uint8_t fSrcAlpha; +#define SkSPRITE_INIT fSrcAlpha = alpha; +#define SkSPRITE_DST_TYPE uint16_t +#define SkSPRITE_SRC_TYPE uint16_t +#define SkSPRITE_DST_GETADDR getAddr16 +#define SkSPRITE_SRC_GETADDR getAddr16 +#define SkSPRITE_PREAMBLE(srcBM, x, y) int scale = SkAlpha255To256(fSrcAlpha); +#define SkSPRITE_BLIT_PIXEL(dst, src) D16_S16_Blend_Pixel(dst, src, scale) +#define SkSPRITE_NEXT_ROW +#define SkSPRITE_POSTAMBLE(srcBM) +#include "SkSpriteBlitterTemplate.h" + +/////////////////////////////////////////////////////////////////////////////// + +#define D16_S4444_Opaque(dst, sc) \ + do { \ + uint16_t dc = *dst; \ + *dst = SkSrcOver4444To16(sc, dc); \ + } while (0) + +#define SkSPRITE_CLASSNAME Sprite_D16_S4444_Opaque +#define SkSPRITE_ARGS +#define SkSPRITE_FIELDS +#define SkSPRITE_INIT +#define SkSPRITE_DST_TYPE uint16_t +#define SkSPRITE_SRC_TYPE SkPMColor16 +#define SkSPRITE_DST_GETADDR getAddr16 +#define SkSPRITE_SRC_GETADDR getAddr16 +#define SkSPRITE_PREAMBLE(srcBM, x, y) +#define SkSPRITE_BLIT_PIXEL(dst, src) D16_S4444_Opaque(dst, src) +#define SkSPRITE_NEXT_ROW +#define SkSPRITE_POSTAMBLE(srcBM) +#include "SkSpriteBlitterTemplate.h" + +#define D16_S4444_Blend(dst, sc, scale16) \ + do { \ + uint16_t dc = *dst; \ + *dst = SkBlend4444To16(sc, dc, scale16); \ + } while (0) + + +#define SkSPRITE_CLASSNAME Sprite_D16_S4444_Blend +#define SkSPRITE_ARGS , uint8_t alpha +#define SkSPRITE_FIELDS uint8_t fSrcAlpha; +#define SkSPRITE_INIT fSrcAlpha = alpha; +#define SkSPRITE_DST_TYPE uint16_t +#define SkSPRITE_SRC_TYPE uint16_t +#define SkSPRITE_DST_GETADDR getAddr16 +#define SkSPRITE_SRC_GETADDR getAddr16 +#define SkSPRITE_PREAMBLE(srcBM, x, y) int scale = SkAlpha15To16(fSrcAlpha); +#define SkSPRITE_BLIT_PIXEL(dst, src) D16_S4444_Blend(dst, src, scale) +#define SkSPRITE_NEXT_ROW +#define SkSPRITE_POSTAMBLE(srcBM) +#include "SkSpriteBlitterTemplate.h" + +/////////////////////////////////////////////////////////////////////////////// + +#define SkSPRITE_CLASSNAME Sprite_D16_SIndex8A_Opaque +#define SkSPRITE_ARGS +#define SkSPRITE_FIELDS +#define SkSPRITE_INIT +#define SkSPRITE_DST_TYPE uint16_t +#define SkSPRITE_SRC_TYPE uint8_t +#define SkSPRITE_DST_GETADDR getAddr16 +#define SkSPRITE_SRC_GETADDR getAddr8 +#define SkSPRITE_PREAMBLE(srcBM, x, y) const SkPMColor* ctable = srcBM.getColorTable()->lockColors() +#define SkSPRITE_BLIT_PIXEL(dst, src) D16_S32A_Opaque_Pixel(dst, ctable[src]) +#define SkSPRITE_NEXT_ROW +#define SkSPRITE_POSTAMBLE(srcBM) srcBM.getColorTable()->unlockColors(false) +#include "SkSpriteBlitterTemplate.h" + +#define SkSPRITE_CLASSNAME Sprite_D16_SIndex8A_Blend +#define SkSPRITE_ARGS , uint8_t alpha +#define SkSPRITE_FIELDS uint8_t fSrcAlpha; +#define SkSPRITE_INIT fSrcAlpha = alpha; +#define SkSPRITE_DST_TYPE uint16_t +#define SkSPRITE_SRC_TYPE uint8_t +#define SkSPRITE_DST_GETADDR getAddr16 +#define SkSPRITE_SRC_GETADDR getAddr8 +#define SkSPRITE_PREAMBLE(srcBM, x, y) const SkPMColor* ctable = srcBM.getColorTable()->lockColors(); unsigned src_scale = SkAlpha255To256(fSrcAlpha); +#define SkSPRITE_BLIT_PIXEL(dst, src) D16_S32A_Blend_Pixel(dst, ctable[src], src_scale) +#define SkSPRITE_NEXT_ROW +#define SkSPRITE_POSTAMBLE(srcBM) srcBM.getColorTable()->unlockColors(false); +#include "SkSpriteBlitterTemplate.h" + +/////////////////////////////////////////////////////////////////////////////// + +#define SkSPRITE_CLASSNAME Sprite_D16_SIndex8_Opaque +#define SkSPRITE_ARGS +#define SkSPRITE_FIELDS +#define SkSPRITE_INIT +#define SkSPRITE_DST_TYPE uint16_t +#define SkSPRITE_SRC_TYPE uint8_t +#define SkSPRITE_DST_GETADDR getAddr16 +#define SkSPRITE_SRC_GETADDR getAddr8 +#define SkSPRITE_PREAMBLE(srcBM, x, y) const uint16_t* ctable = srcBM.getColorTable()->lock16BitCache() +#define SkSPRITE_BLIT_PIXEL(dst, src) *dst = ctable[src] +#define SkSPRITE_NEXT_ROW +#define SkSPRITE_POSTAMBLE(srcBM) srcBM.getColorTable()->unlock16BitCache() +#include "SkSpriteBlitterTemplate.h" + +#define SkSPRITE_CLASSNAME Sprite_D16_SIndex8_Blend +#define SkSPRITE_ARGS , uint8_t alpha +#define SkSPRITE_FIELDS uint8_t fSrcAlpha; +#define SkSPRITE_INIT fSrcAlpha = alpha; +#define SkSPRITE_DST_TYPE uint16_t +#define SkSPRITE_SRC_TYPE uint8_t +#define SkSPRITE_DST_GETADDR getAddr16 +#define SkSPRITE_SRC_GETADDR getAddr8 +#define SkSPRITE_PREAMBLE(srcBM, x, y) const uint16_t* ctable = srcBM.getColorTable()->lock16BitCache(); unsigned src_scale = SkAlpha255To256(fSrcAlpha); +#define SkSPRITE_BLIT_PIXEL(dst, src) D16_S16_Blend_Pixel(dst, ctable[src], src_scale) +#define SkSPRITE_NEXT_ROW +#define SkSPRITE_POSTAMBLE(srcBM) srcBM.getColorTable()->unlock16BitCache(); +#include "SkSpriteBlitterTemplate.h" + +/////////////////////////////////////////////////////////////////////////////// + +class Sprite_D16_S32_BlitRowProc : public SkSpriteBlitter { +public: + Sprite_D16_S32_BlitRowProc(const SkBitmap& source) + : SkSpriteBlitter(source) {} + + // overrides + + virtual void setup(const SkBitmap& device, int left, int top, + const SkPaint& paint) { + this->INHERITED::setup(device, left, top, paint); + + unsigned flags = 0; + + if (paint.getAlpha() < 0xFF) { + flags |= SkBlitRow::kGlobalAlpha_Flag; + } + if (!fSource->isOpaque()) { + flags |= SkBlitRow::kSrcPixelAlpha_Flag; + } + if (paint.isDither()) { + flags |= SkBlitRow::kDither_Flag; + } + fProc = SkBlitRow::Factory(flags, SkBitmap::kRGB_565_Config); + } + + virtual void blitRect(int x, int y, int width, int height) { + SK_RESTRICT uint16_t* dst = fDevice->getAddr16(x, y); + const SK_RESTRICT SkPMColor* src = fSource->getAddr32(x - fLeft, + y - fTop); + unsigned dstRB = fDevice->rowBytes(); + unsigned srcRB = fSource->rowBytes(); + SkBlitRow::Proc proc = fProc; + U8CPU alpha = fPaint->getAlpha(); + + while (--height >= 0) { + proc(dst, src, width, alpha, x, y); + y += 1; + dst = (SK_RESTRICT uint16_t*)((char*)dst + dstRB); + src = (const SK_RESTRICT SkPMColor*)((const char*)src + srcRB); + } + } + +private: + SkBlitRow::Proc fProc; + + typedef SkSpriteBlitter INHERITED; +}; + +/////////////////////////////////////////////////////////////////////////////// + +#include "SkTemplatesPriv.h" + +SkSpriteBlitter* SkSpriteBlitter::ChooseD16(const SkBitmap& source, + const SkPaint& paint, + void* storage, size_t storageSize) { + if (paint.getMaskFilter() != NULL) { // may add cases for this + return NULL; + } + if (paint.getXfermode() != NULL) { // may add cases for this + return NULL; + } + if (paint.getColorFilter() != NULL) { // may add cases for this + return NULL; + } + + SkSpriteBlitter* blitter = NULL; + unsigned alpha = paint.getAlpha(); + + switch (source.getConfig()) { + case SkBitmap::kARGB_8888_Config: + SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D16_S32_BlitRowProc, + storage, storageSize, (source)); + break; + case SkBitmap::kARGB_4444_Config: + if (255 == alpha) { + SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D16_S4444_Opaque, + storage, storageSize, (source)); + } else { + SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D16_S4444_Blend, + storage, storageSize, (source, alpha >> 4)); + } + break; + case SkBitmap::kRGB_565_Config: + if (255 == alpha) { + SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D16_S16_Opaque, + storage, storageSize, (source)); + } else { + SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D16_S16_Blend, + storage, storageSize, (source, alpha)); + } + break; + case SkBitmap::kIndex8_Config: + if (source.isOpaque()) { + if (255 == alpha) { + SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D16_SIndex8_Opaque, + storage, storageSize, (source)); + } else { + SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D16_SIndex8_Blend, + storage, storageSize, (source, alpha)); + } + } else { + if (255 == alpha) { + SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D16_SIndex8A_Opaque, + storage, storageSize, (source)); + } else { + SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D16_SIndex8A_Blend, + storage, storageSize, (source, alpha)); + } + } + break; + default: + break; + } + return blitter; +} + diff --git a/skia/sgl/SkString.cpp b/skia/sgl/SkString.cpp new file mode 100644 index 0000000..839f5c4 --- /dev/null +++ b/skia/sgl/SkString.cpp @@ -0,0 +1,625 @@ +/* libs/graphics/sgl/SkString.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkString.h" +#include "SkFixed.h" +#include "SkUtils.h" +#include <stdarg.h> + +bool SkStrStartsWith(const char string[], const char prefix[]) +{ + SkASSERT(string); + SkASSERT(prefix); + return !strncmp(string, prefix, strlen(prefix)); +} + +bool SkStrEndsWith(const char string[], const char suffix[]) +{ + SkASSERT(string); + SkASSERT(suffix); + size_t strLen = strlen(string); + size_t suffixLen = strlen(suffix); + return strLen >= suffixLen && + !strncmp(string + strLen - suffixLen, suffix, suffixLen); +} + +int SkStrStartsWithOneOf(const char string[], const char prefixes[]) +{ + int index = 0; + do { + const char* limit = strchr(prefixes, '\0'); + if (!strncmp(string, prefixes, limit - prefixes)) + return index; + prefixes = limit + 1; + index++; + } while (prefixes[0]); + return -1; +} + +char* SkStrAppendS32(char string[], int32_t dec) +{ + SkDEBUGCODE(char* start = string;) + + char buffer[SkStrAppendS32_MaxSize]; + char* p = buffer + sizeof(buffer); + bool neg = false; + + if (dec < 0) + { + neg = true; + dec = -dec; + } + do { + *--p = SkToU8('0' + dec % 10); + dec /= 10; + } while (dec != 0); + if (neg) + *--p = '-'; + + SkASSERT(p >= buffer); + char* stop = buffer + sizeof(buffer); + while (p < stop) + *string++ = *p++; + + SkASSERT(string - start <= SkStrAppendS32_MaxSize); + return string; +} + +char* SkStrAppendScalar(char string[], SkScalar value) +{ + SkDEBUGCODE(char* start = string;) + + SkFixed x = SkScalarToFixed(value); + + if (x < 0) + { + *string++ = '-'; + x = -x; + } + + unsigned frac = x & 0xFFFF; + x >>= 16; + if (frac == 0xFFFF) // need to do this to "round up", since 65535/65536 is closer to 1 than to .9999 + { + x += 1; + frac = 0; + } + string = SkStrAppendS32(string, x); + + // now handle the fractional part (if any) + if (frac) + { + static const uint16_t gTens[] = { 1000, 100, 10, 1 }; + const uint16_t* tens = gTens; + + x = SkFixedRound(frac * 10000); + SkASSERT(x < 10000); + *string++ = '.'; + do { + unsigned powerOfTen = *tens++; + *string++ = SkToU8('0' + x / powerOfTen); + x %= powerOfTen; + } while (x != 0); + } + + SkASSERT(string - start <= SkStrAppendScalar_MaxSize); + return string; +} + +//////////////////////////////////////////////////////////////////////////////////// + +#define kMaxRefCnt_SkString SK_MaxU16 + +// the 3 values are [length] [refcnt] [terminating zero data] +const SkString::Rec SkString::gEmptyRec = { 0, 0, 0 }; + +#define SizeOfRec() (gEmptyRec.data() - (const char*)&gEmptyRec) + +SkString::Rec* SkString::AllocRec(const char text[], U16CPU len) +{ + Rec* rec; + + if (len == 0) + rec = const_cast<Rec*>(&gEmptyRec); + else + { + // add 1 for terminating 0, then align4 so we can have some slop when growing the string + rec = (Rec*)sk_malloc_throw(SizeOfRec() + SkAlign4(len + 1)); + rec->fLength = SkToU16(len); + rec->fRefCnt = 1; + if (text) + memcpy(rec->data(), text, len); + rec->data()[len] = 0; + } + return rec; +} + +SkString::Rec* SkString::RefRec(Rec* src) +{ + if (src != &gEmptyRec) + { + if (src->fRefCnt == kMaxRefCnt_SkString) { + src = AllocRec(src->data(), src->fLength); + } else + src->fRefCnt += 1; + } + return src; +} + +#ifdef SK_DEBUG +void SkString::validate() const +{ + // make sure know one has written over our global + SkASSERT(gEmptyRec.fLength == 0); + SkASSERT(gEmptyRec.fRefCnt == 0); + SkASSERT(gEmptyRec.data()[0] == 0); + + if (fRec != &gEmptyRec) + { + SkASSERT(fRec->fLength > 0); + SkASSERT(fRec->fRefCnt > 0); + SkASSERT(fRec->data()[fRec->fLength] == 0); + } + SkASSERT(fStr == c_str()); +} +#endif + +/////////////////////////////////////////////////////////////////////// + +SkString::SkString() : fRec(const_cast<Rec*>(&gEmptyRec)) { +#ifdef SK_DEBUG + fStr = fRec->data(); +#endif +} + +SkString::SkString(size_t len) +{ + SkASSERT(SkToU16(len) == len); // can't handle larger than 64K + + fRec = AllocRec(NULL, (U16CPU)len); +#ifdef SK_DEBUG + fStr = fRec->data(); +#endif +} + +SkString::SkString(const char text[]) +{ + size_t len = text ? strlen(text) : 0; + + fRec = AllocRec(text, (U16CPU)len); +#ifdef SK_DEBUG + fStr = fRec->data(); +#endif +} + +SkString::SkString(const char text[], size_t len) +{ + fRec = AllocRec(text, (U16CPU)len); +#ifdef SK_DEBUG + fStr = fRec->data(); +#endif +} + +SkString::SkString(const SkString& src) +{ + src.validate(); + + fRec = RefRec(src.fRec); +#ifdef SK_DEBUG + fStr = fRec->data(); +#endif +} + +SkString::~SkString() +{ + this->validate(); + + if (fRec->fLength) + { + SkASSERT(fRec->fRefCnt > 0); + if (--fRec->fRefCnt == 0) + sk_free(fRec); + } +} + +bool SkString::equals(const SkString& src) const +{ + return fRec == src.fRec || this->equals(src.c_str(), src.size()); +} + +bool SkString::equals(const char text[]) const +{ + return this->equals(text, text ? strlen(text) : 0); +} + +bool SkString::equals(const char text[], size_t len) const +{ + SkASSERT(len == 0 || text != NULL); + + return fRec->fLength == len && !memcmp(fRec->data(), text, len); +} + +SkString& SkString::operator=(const SkString& src) +{ + this->validate(); + + if (fRec != src.fRec) + { + SkString tmp(src); + this->swap(tmp); + } + return *this; +} + +void SkString::reset() +{ + this->validate(); + + if (fRec->fLength) + { + SkASSERT(fRec->fRefCnt > 0); + if (--fRec->fRefCnt == 0) + sk_free(fRec); + } + + fRec = const_cast<Rec*>(&gEmptyRec); +#ifdef SK_DEBUG + fStr = fRec->data(); +#endif +} + +char* SkString::writable_str() +{ + this->validate(); + + if (fRec->fLength) + { + if (fRec->fRefCnt > 1) + { + fRec->fRefCnt -= 1; + fRec = AllocRec(fRec->data(), fRec->fLength); + #ifdef SK_DEBUG + fStr = fRec->data(); + #endif + } + } + return fRec->data(); +} + +void SkString::set(const char text[]) +{ + this->set(text, text ? strlen(text) : 0); +} + +void SkString::set(const char text[], size_t len) +{ + if (len == 0) + this->reset(); + else if (fRec->fRefCnt == 1 && len <= fRec->fLength) // should we resize if len <<<< fLength, to save RAM? (e.g. len < (fLength>>1)) + { + // just use less of the buffer without allocating a smaller one + char* p = this->writable_str(); + if (text) + memcpy(p, text, len); + p[len] = 0; + fRec->fLength = SkToU16(len); + } + else if (fRec->fRefCnt == 1 && ((unsigned)fRec->fLength >> 2) == (len >> 2)) + { + // we have spare room in the current allocation, so don't alloc a larger one + char* p = this->writable_str(); + if (text) + memcpy(p, text, len); + p[len] = 0; + fRec->fLength = SkToU16(len); + } + else + { + SkString tmp(text, len); + this->swap(tmp); + } +} + +void SkString::setUTF16(const uint16_t src[]) +{ + int count = 0; + + while (src[count]) + count += 1; + setUTF16(src, count); +} + +void SkString::setUTF16(const uint16_t src[], size_t count) +{ + if (count == 0) + this->reset(); + else if (count <= fRec->fLength) // should we resize if len <<<< fLength, to save RAM? (e.g. len < (fLength>>1)) + { + if (count < fRec->fLength) + this->resize(count); + char* p = this->writable_str(); + for (size_t i = 0; i < count; i++) + p[i] = SkToU8(src[i]); + p[count] = 0; + } + else + { + SkString tmp(count); // puts a null terminator at the end of the string + char* p = tmp.writable_str(); + + for (size_t i = 0; i < count; i++) + p[i] = SkToU8(src[i]); + + this->swap(tmp); + } +} + +void SkString::insert(size_t offset, const char text[]) +{ + this->insert(offset, text, text ? strlen(text) : 0); +} + +void SkString::insert(size_t offset, const char text[], size_t len) +{ + if (len) + { + size_t length = fRec->fLength; + if (offset > length) + offset = length; + + /* If we're the only owner, and we have room in our allocation for the insert, + do it in place, rather than allocating a new buffer. + + To know we have room, compare the allocated sizes + beforeAlloc = SkAlign4(length + 1) + afterAlloc = SkAligh4(length + 1 + len) + but SkAlign4(x) is (x + 3) >> 2 << 2 + which is equivalent for testing to (length + 1 + 3) >> 2 == (length + 1 + 3 + len) >> 2 + and we can then eliminate the +1+3 since that doesn't affec the answer + */ + if (fRec->fRefCnt == 1 && (length >> 2) == ((length + len) >> 2)) + { + char* dst = this->writable_str(); + + if (offset < length) + memmove(dst + offset + len, dst + offset, length - offset); + memcpy(dst + offset, text, len); + + dst[length + len] = 0; + fRec->fLength = SkToU16(length + len); + } + else + { + /* Seems we should use realloc here, since that is safe if it fails + (we have the original data), and might be faster than alloc/copy/free. + */ + SkString tmp(fRec->fLength + len); + char* dst = tmp.writable_str(); + + if (offset > 0) + memcpy(dst, fRec->data(), offset); + memcpy(dst + offset, text, len); + if (offset < fRec->fLength) + memcpy(dst + offset + len, fRec->data() + offset, fRec->fLength - offset); + + this->swap(tmp); + } + } +} + +void SkString::insertUnichar(size_t offset, SkUnichar uni) +{ + char buffer[kMaxBytesInUTF8Sequence]; + size_t len = SkUTF8_FromUnichar(uni, buffer); + + if (len) + this->insert(offset, buffer, len); +} + +void SkString::insertS32(size_t offset, int32_t dec) +{ + char buffer[SkStrAppendS32_MaxSize]; + char* stop = SkStrAppendS32(buffer, dec); + this->insert(offset, buffer, stop - buffer); +} + +void SkString::insertHex(size_t offset, uint32_t hex, int minDigits) +{ + minDigits = SkPin32(minDigits, 0, 8); + + static const char gHex[] = "0123456789ABCDEF"; + + char buffer[8]; + char* p = buffer + sizeof(buffer); + + do { + *--p = gHex[hex & 0xF]; + hex >>= 4; + minDigits -= 1; + } while (hex != 0); + while (--minDigits >= 0) + *--p = '0'; + + SkASSERT(p >= buffer); + this->insert(offset, p, buffer + sizeof(buffer) - p); +} + +void SkString::insertScalar(size_t offset, SkScalar value) +{ + char buffer[SkStrAppendScalar_MaxSize]; + char* stop = SkStrAppendScalar(buffer, value); + this->insert(offset, buffer, stop - buffer); +} + +/////////////////////////////////////////////////////////////////////////// + +//#include <stdarg.h> +#if defined(SK_BUILD_FOR_WIN) || defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_UNIX) + #include <stdio.h> +#endif + +void SkString::printf(const char format[], ...) +{ + static const size_t kBufferSize = 100; + + char buffer[kBufferSize + 1]; + +#ifdef SK_BUILD_FOR_WIN + va_list args; + va_start(args, format); + _vsnprintf(buffer, kBufferSize, format, args); + va_end(args); +#elif defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_UNIX) + va_list args; + va_start(args, format); + vsnprintf(buffer, kBufferSize, format, args); + va_end(args); +#else + buffer[0] = 0; +#endif + + this->set(buffer, strlen(buffer)); +} + +/////////////////////////////////////////////////////////////////////////// + +void SkString::remove(size_t offset, size_t length) +{ + size_t size = this->size(); + + if (offset < size) + { + if (offset + length > size) + length = size - offset; + if (length > 0) + { + SkASSERT(size > length); + SkString tmp(size - length); + char* dst = tmp.writable_str(); + const char* src = this->c_str(); + + if (offset) + { + SkASSERT(offset <= tmp.size()); + memcpy(dst, src, offset); + } + size_t tail = size - offset - length; + SkASSERT((int32_t)tail >= 0); + if (tail) + { + // SkASSERT(offset + length <= tmp.size()); + memcpy(dst + offset, src + offset + length, tail); + } + SkASSERT(dst[tmp.size()] == 0); + this->swap(tmp); + } + } +} + +void SkString::swap(SkString& other) +{ + this->validate(); + other.validate(); + + SkTSwap<Rec*>(fRec, other.fRec); +#ifdef SK_DEBUG + SkTSwap<const char*>(fStr, other.fStr); +#endif +} + +///////////////////////////////////////////////////////////////////////////////// + +SkAutoUCS2::SkAutoUCS2(const char utf8[]) +{ + size_t len = strlen(utf8); + fUCS2 = (uint16_t*)sk_malloc_throw((len + 1) * sizeof(uint16_t)); + + uint16_t* dst = fUCS2; + for (;;) + { + SkUnichar uni = SkUTF8_NextUnichar(&utf8); + *dst++ = SkToU16(uni); + if (uni == 0) + break; + } + fCount = (int)(dst - fUCS2); +} + +SkAutoUCS2::~SkAutoUCS2() +{ + delete[] fUCS2; +} + +///////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_DEBUG + +void SkString::UnitTest() +{ +#ifdef SK_SUPPORT_UNITTEST + SkString a; + SkString b((size_t)0); + SkString c(""); + SkString d(NULL, 0); + + SkASSERT(a.isEmpty()); + SkASSERT(a == b && a == c && a == d); + + a.set("hello"); + b.set("hellox", 5); + c.set(a); + d.resize(5); + memcpy(d.writable_str(), "helloz", 5); + + SkASSERT(!a.isEmpty()); + SkASSERT(a.size() == 5); + SkASSERT(a == b && a == c && a == d); + SkASSERT(a.equals("hello", 5)); + SkASSERT(a.equals("hello")); + SkASSERT(!a.equals("help")); + + SkString e(a); + SkString f("hello"); + SkString g("helloz", 5); + + SkASSERT(a == e && a == f && a == g); + + b.set("world"); + c = b; + SkASSERT(a != b && a != c && b == c); + + a.append(" world"); + e.append("worldz", 5); + e.insert(5, " "); + f.set("world"); + f.prepend("hello "); + SkASSERT(a.equals("hello world") && a == e && a == f); + + a.reset(); + b.resize(0); + SkASSERT(a.isEmpty() && b.isEmpty() && a == b); + + a.set("a"); + a.set("ab"); + a.set("abc"); + a.set("abcd"); +#endif +} + +#endif + diff --git a/skia/sgl/SkStroke.cpp b/skia/sgl/SkStroke.cpp new file mode 100644 index 0000000..530f0fa --- /dev/null +++ b/skia/sgl/SkStroke.cpp @@ -0,0 +1,585 @@ +/* + * Copyright (C) 2006-2008 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "SkStrokerPriv.h" +#include "SkGeometry.h" +#include "SkPath.h" + +#define kMaxQuadSubdivide 5 +#define kMaxCubicSubdivide 4 + +static inline bool degenerate_vector(const SkVector& v) { + return SkScalarNearlyZero(v.fX) && SkScalarNearlyZero(v.fY); +} + +static inline bool degenerate_line(const SkPoint& a, const SkPoint& b, + SkScalar tolerance = SK_ScalarNearlyZero) { + return SkScalarNearlyZero(a.fX - b.fX, tolerance) && + SkScalarNearlyZero(a.fY - b.fY, tolerance); +} + +static inline bool normals_too_curvy(const SkVector& norm0, SkVector& norm1) { + /* root2/2 is a 45-degree angle + make this constant bigger for more subdivisions (but not >= 1) + */ + static const SkScalar kFlatEnoughNormalDotProd = + SK_ScalarSqrt2/2 + SK_Scalar1/10; + + SkASSERT(kFlatEnoughNormalDotProd > 0 && + kFlatEnoughNormalDotProd < SK_Scalar1); + + return SkPoint::DotProduct(norm0, norm1) <= kFlatEnoughNormalDotProd; +} + +static inline bool normals_too_pinchy(const SkVector& norm0, SkVector& norm1) { + static const SkScalar kTooPinchyNormalDotProd = -SK_Scalar1 * 999 / 1000; + + return SkPoint::DotProduct(norm0, norm1) <= kTooPinchyNormalDotProd; +} + +static bool set_normal_unitnormal(const SkPoint& before, const SkPoint& after, + SkScalar radius, + SkVector* normal, SkVector* unitNormal) { + if (!unitNormal->setNormalize(after.fX - before.fX, after.fY - before.fY)) { + return false; + } + unitNormal->rotateCCW(); + unitNormal->scale(radius, normal); + return true; +} + +static bool set_normal_unitnormal(const SkVector& vec, + SkScalar radius, + SkVector* normal, SkVector* unitNormal) { + if (!unitNormal->setNormalize(vec.fX, vec.fY)) { + return false; + } + unitNormal->rotateCCW(); + unitNormal->scale(radius, normal); + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +class SkPathStroker { +public: + SkPathStroker(SkScalar radius, SkScalar miterLimit, SkPaint::Cap cap, + SkPaint::Join join); + + void moveTo(const SkPoint&); + void lineTo(const SkPoint&); + void quadTo(const SkPoint&, const SkPoint&); + void cubicTo(const SkPoint&, const SkPoint&, const SkPoint&); + void close(bool isLine) { this->finishContour(true, isLine); } + + void done(SkPath* dst, bool isLine) { + this->finishContour(false, isLine); + fOuter.addPath(fExtra); + dst->swap(fOuter); + } + +private: + SkScalar fRadius; + SkScalar fInvMiterLimit; + + SkVector fFirstNormal, fPrevNormal, fFirstUnitNormal, fPrevUnitNormal; + SkPoint fFirstPt, fPrevPt; // on original path + SkPoint fFirstOuterPt; + int fSegmentCount; + bool fPrevIsLine; + + SkStrokerPriv::CapProc fCapper; + SkStrokerPriv::JoinProc fJoiner; + + SkPath fInner, fOuter; // outer is our working answer, inner is temp + SkPath fExtra; // added as extra complete contours + + void finishContour(bool close, bool isLine); + void preJoinTo(const SkPoint&, SkVector* normal, SkVector* unitNormal, + bool isLine); + void postJoinTo(const SkPoint&, const SkVector& normal, + const SkVector& unitNormal); + + void line_to(const SkPoint& currPt, const SkVector& normal); + void quad_to(const SkPoint pts[3], + const SkVector& normalAB, const SkVector& unitNormalAB, + SkVector* normalBC, SkVector* unitNormalBC, + int subDivide); + void cubic_to(const SkPoint pts[4], + const SkVector& normalAB, const SkVector& unitNormalAB, + SkVector* normalCD, SkVector* unitNormalCD, + int subDivide); +}; + +/////////////////////////////////////////////////////////////////////////////// + +void SkPathStroker::preJoinTo(const SkPoint& currPt, SkVector* normal, + SkVector* unitNormal, bool currIsLine) { + SkASSERT(fSegmentCount >= 0); + + SkScalar prevX = fPrevPt.fX; + SkScalar prevY = fPrevPt.fY; + + SkAssertResult(set_normal_unitnormal(fPrevPt, currPt, fRadius, normal, + unitNormal)); + + if (fSegmentCount == 0) { + fFirstNormal = *normal; + fFirstUnitNormal = *unitNormal; + fFirstOuterPt.set(prevX + normal->fX, prevY + normal->fY); + + fOuter.moveTo(fFirstOuterPt.fX, fFirstOuterPt.fY); + fInner.moveTo(prevX - normal->fX, prevY - normal->fY); + } else { // we have a previous segment + fJoiner(&fOuter, &fInner, fPrevUnitNormal, fPrevPt, *unitNormal, + fRadius, fInvMiterLimit, fPrevIsLine, currIsLine); + } + fPrevIsLine = currIsLine; +} + +void SkPathStroker::postJoinTo(const SkPoint& currPt, const SkVector& normal, + const SkVector& unitNormal) { + fPrevPt = currPt; + fPrevUnitNormal = unitNormal; + fPrevNormal = normal; + fSegmentCount += 1; +} + +void SkPathStroker::finishContour(bool close, bool currIsLine) { + if (fSegmentCount > 0) { + SkPoint pt; + + if (close) { + fJoiner(&fOuter, &fInner, fPrevUnitNormal, fPrevPt, + fFirstUnitNormal, fRadius, fInvMiterLimit, + fPrevIsLine, currIsLine); + fOuter.close(); + // now add fInner as its own contour + fInner.getLastPt(&pt); + fOuter.moveTo(pt.fX, pt.fY); + fOuter.reversePathTo(fInner); + fOuter.close(); + } else { // add caps to start and end + // cap the end + fInner.getLastPt(&pt); + fCapper(&fOuter, fPrevPt, fPrevNormal, pt, + currIsLine ? &fInner : NULL); + fOuter.reversePathTo(fInner); + // cap the start + fCapper(&fOuter, fFirstPt, -fFirstNormal, fFirstOuterPt, + fPrevIsLine ? &fInner : NULL); + fOuter.close(); + } + } + fInner.reset(); + fSegmentCount = -1; +} + +/////////////////////////////////////////////////////////////////////////////// + +SkPathStroker::SkPathStroker(SkScalar radius, SkScalar miterLimit, + SkPaint::Cap cap, SkPaint::Join join) + : fRadius(radius) { + + /* This is only used when join is miter_join, but we initialize it here + so that it is always defined, to fis valgrind warnings. + */ + fInvMiterLimit = 0; + + if (join == SkPaint::kMiter_Join) { + if (miterLimit <= SK_Scalar1) { + join = SkPaint::kBevel_Join; + } else { + fInvMiterLimit = SkScalarInvert(miterLimit); + } + } + fCapper = SkStrokerPriv::CapFactory(cap); + fJoiner = SkStrokerPriv::JoinFactory(join); + fSegmentCount = -1; + fPrevIsLine = false; +} + +void SkPathStroker::moveTo(const SkPoint& pt) { + if (fSegmentCount > 0) { + this->finishContour(false, false); + } + fSegmentCount = 0; + fFirstPt = fPrevPt = pt; +} + +void SkPathStroker::line_to(const SkPoint& currPt, const SkVector& normal) { + fOuter.lineTo(currPt.fX + normal.fX, currPt.fY + normal.fY); + fInner.lineTo(currPt.fX - normal.fX, currPt.fY - normal.fY); +} + +void SkPathStroker::lineTo(const SkPoint& currPt) { + if (degenerate_line(fPrevPt, currPt)) { + return; + } + SkVector normal, unitNormal; + + this->preJoinTo(currPt, &normal, &unitNormal, true); + this->line_to(currPt, normal); + this->postJoinTo(currPt, normal, unitNormal); +} + +void SkPathStroker::quad_to(const SkPoint pts[3], + const SkVector& normalAB, const SkVector& unitNormalAB, + SkVector* normalBC, SkVector* unitNormalBC, + int subDivide) { + if (!set_normal_unitnormal(pts[1], pts[2], fRadius, + normalBC, unitNormalBC)) { + // pts[1] nearly equals pts[2], so just draw a line to pts[2] + this->line_to(pts[2], normalAB); + *normalBC = normalAB; + *unitNormalBC = unitNormalAB; + return; + } + + if (--subDivide >= 0 && normals_too_curvy(unitNormalAB, *unitNormalBC)) { + SkPoint tmp[5]; + SkVector norm, unit; + + SkChopQuadAtHalf(pts, tmp); + this->quad_to(&tmp[0], normalAB, unitNormalAB, &norm, &unit, subDivide); + this->quad_to(&tmp[2], norm, unit, normalBC, unitNormalBC, subDivide); + } else { + SkVector normalB, unitB; + SkAssertResult(set_normal_unitnormal(pts[0], pts[2], fRadius, + &normalB, &unitB)); + + fOuter.quadTo( pts[1].fX + normalB.fX, pts[1].fY + normalB.fY, + pts[2].fX + normalBC->fX, pts[2].fY + normalBC->fY); + fInner.quadTo( pts[1].fX - normalB.fX, pts[1].fY - normalB.fY, + pts[2].fX - normalBC->fX, pts[2].fY - normalBC->fY); + } +} + +void SkPathStroker::cubic_to(const SkPoint pts[4], + const SkVector& normalAB, const SkVector& unitNormalAB, + SkVector* normalCD, SkVector* unitNormalCD, + int subDivide) { + SkVector ab = pts[1] - pts[0]; + SkVector cd = pts[3] - pts[2]; + SkVector normalBC, unitNormalBC; + + bool degenerateAB = degenerate_vector(ab); + bool degenerateCD = degenerate_vector(cd); + + if (degenerateAB && degenerateCD) { +DRAW_LINE: + this->line_to(pts[3], normalAB); + *normalCD = normalAB; + *unitNormalCD = unitNormalAB; + return; + } + + if (degenerateAB) { + ab = pts[2] - pts[0]; + degenerateAB = degenerate_vector(ab); + } + if (degenerateCD) { + cd = pts[3] - pts[1]; + degenerateCD = degenerate_vector(cd); + } + if (degenerateAB || degenerateCD) { + goto DRAW_LINE; + } + SkAssertResult(set_normal_unitnormal(cd, fRadius, normalCD, unitNormalCD)); + bool degenerateBC = !set_normal_unitnormal(pts[1], pts[2], fRadius, + &normalBC, &unitNormalBC); + + if (--subDivide >= 0 && + (degenerateBC || normals_too_curvy(unitNormalAB, unitNormalBC) || + normals_too_curvy(unitNormalBC, *unitNormalCD))) { + SkPoint tmp[7]; + SkVector norm, unit, dummy, unitDummy; + + SkChopCubicAtHalf(pts, tmp); + this->cubic_to(&tmp[0], normalAB, unitNormalAB, &norm, &unit, + subDivide); + // we use dummys since we already have a valid (and more accurate) + // normals for CD + this->cubic_to(&tmp[3], norm, unit, &dummy, &unitDummy, subDivide); + } else { + SkVector normalB, normalC; + + // need normals to inset/outset the off-curve pts B and C + + if (0) { // this is normal to the line between our adjacent pts + normalB = pts[2] - pts[0]; + normalB.rotateCCW(); + SkAssertResult(normalB.setLength(fRadius)); + + normalC = pts[3] - pts[1]; + normalC.rotateCCW(); + SkAssertResult(normalC.setLength(fRadius)); + } else { // miter-join + SkVector unitBC = pts[2] - pts[1]; + unitBC.normalize(); + unitBC.rotateCCW(); + + normalB = unitNormalAB + unitBC; + normalC = *unitNormalCD + unitBC; + + SkScalar dot = SkPoint::DotProduct(unitNormalAB, unitBC); + SkAssertResult(normalB.setLength(SkScalarDiv(fRadius, + SkScalarSqrt((SK_Scalar1 + dot)/2)))); + dot = SkPoint::DotProduct(*unitNormalCD, unitBC); + SkAssertResult(normalC.setLength(SkScalarDiv(fRadius, + SkScalarSqrt((SK_Scalar1 + dot)/2)))); + } + + fOuter.cubicTo( pts[1].fX + normalB.fX, pts[1].fY + normalB.fY, + pts[2].fX + normalC.fX, pts[2].fY + normalC.fY, + pts[3].fX + normalCD->fX, pts[3].fY + normalCD->fY); + + fInner.cubicTo( pts[1].fX - normalB.fX, pts[1].fY - normalB.fY, + pts[2].fX - normalC.fX, pts[2].fY - normalC.fY, + pts[3].fX - normalCD->fX, pts[3].fY - normalCD->fY); + } +} + +void SkPathStroker::quadTo(const SkPoint& pt1, const SkPoint& pt2) { + bool degenerateAB = degenerate_line(fPrevPt, pt1); + bool degenerateBC = degenerate_line(pt1, pt2); + + if (degenerateAB | degenerateBC) { + if (degenerateAB ^ degenerateBC) { + this->lineTo(pt2); + } + return; + } + + SkVector normalAB, unitAB, normalBC, unitBC; + + this->preJoinTo(pt1, &normalAB, &unitAB, false); + + { + SkPoint pts[3], tmp[5]; + pts[0] = fPrevPt; + pts[1] = pt1; + pts[2] = pt2; + + if (SkChopQuadAtMaxCurvature(pts, tmp) == 2) { + unitBC.setNormalize(pts[2].fX - pts[1].fX, pts[2].fY - pts[1].fY); + unitBC.rotateCCW(); + if (normals_too_pinchy(unitAB, unitBC)) { + normalBC = unitBC; + normalBC.scale(fRadius); + + fOuter.lineTo(tmp[2].fX + normalAB.fX, tmp[2].fY + normalAB.fY); + fOuter.lineTo(tmp[2].fX + normalBC.fX, tmp[2].fY + normalBC.fY); + fOuter.lineTo(tmp[4].fX + normalBC.fX, tmp[4].fY + normalBC.fY); + + fInner.lineTo(tmp[2].fX - normalAB.fX, tmp[2].fY - normalAB.fY); + fInner.lineTo(tmp[2].fX - normalBC.fX, tmp[2].fY - normalBC.fY); + fInner.lineTo(tmp[4].fX - normalBC.fX, tmp[4].fY - normalBC.fY); + + fExtra.addCircle(tmp[2].fX, tmp[2].fY, fRadius, + SkPath::kCW_Direction); + } else { + this->quad_to(&tmp[0], normalAB, unitAB, &normalBC, &unitBC, + kMaxQuadSubdivide); + SkVector n = normalBC; + SkVector u = unitBC; + this->quad_to(&tmp[2], n, u, &normalBC, &unitBC, + kMaxQuadSubdivide); + } + } else { + this->quad_to(pts, normalAB, unitAB, &normalBC, &unitBC, + kMaxQuadSubdivide); + } + } + + this->postJoinTo(pt2, normalBC, unitBC); +} + +void SkPathStroker::cubicTo(const SkPoint& pt1, const SkPoint& pt2, + const SkPoint& pt3) { + bool degenerateAB = degenerate_line(fPrevPt, pt1); + bool degenerateBC = degenerate_line(pt1, pt2); + bool degenerateCD = degenerate_line(pt2, pt3); + + if (degenerateAB + degenerateBC + degenerateCD >= 2) { + this->lineTo(pt3); + return; + } + + SkVector normalAB, unitAB, normalCD, unitCD; + + // find the first tangent (which might be pt1 or pt2 + { + const SkPoint* nextPt = &pt1; + if (degenerateAB) + nextPt = &pt2; + this->preJoinTo(*nextPt, &normalAB, &unitAB, false); + } + + { + SkPoint pts[4], tmp[13]; + int i, count; + SkVector n, u; + SkScalar tValues[3]; + + pts[0] = fPrevPt; + pts[1] = pt1; + pts[2] = pt2; + pts[3] = pt3; + +#if 1 + count = SkChopCubicAtMaxCurvature(pts, tmp, tValues); +#else + count = 1; + memcpy(tmp, pts, 4 * sizeof(SkPoint)); +#endif + n = normalAB; + u = unitAB; + for (i = 0; i < count; i++) { + this->cubic_to(&tmp[i * 3], n, u, &normalCD, &unitCD, + kMaxCubicSubdivide); + if (i == count - 1) { + break; + } + n = normalCD; + u = unitCD; + + } + + // check for too pinchy + for (i = 1; i < count; i++) { + SkPoint p; + SkVector v, c; + + SkEvalCubicAt(pts, tValues[i - 1], &p, &v, &c); + + SkScalar dot = SkPoint::DotProduct(c, c); + v.scale(SkScalarInvert(dot)); + + if (SkScalarNearlyZero(v.fX) && SkScalarNearlyZero(v.fY)) { + fExtra.addCircle(p.fX, p.fY, fRadius, SkPath::kCW_Direction); + } + } + + } + + this->postJoinTo(pt3, normalCD, unitCD); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#include "SkPaint.h" + +SkStroke::SkStroke() { + fWidth = SK_DefaultStrokeWidth; + fMiterLimit = SK_DefaultMiterLimit; + fCap = SkPaint::kDefault_Cap; + fJoin = SkPaint::kDefault_Join; + fDoFill = false; +} + +SkStroke::SkStroke(const SkPaint& p) { + fWidth = p.getStrokeWidth(); + fMiterLimit = p.getStrokeMiter(); + fCap = (uint8_t)p.getStrokeCap(); + fJoin = (uint8_t)p.getStrokeJoin(); + fDoFill = SkToU8(p.getStyle() == SkPaint::kStrokeAndFill_Style); +} + +SkStroke::SkStroke(const SkPaint& p, SkScalar width) { + fWidth = width; + fMiterLimit = p.getStrokeMiter(); + fCap = (uint8_t)p.getStrokeCap(); + fJoin = (uint8_t)p.getStrokeJoin(); + fDoFill = SkToU8(p.getStyle() == SkPaint::kStrokeAndFill_Style); +} + +void SkStroke::setWidth(SkScalar width) { + SkASSERT(width >= 0); + fWidth = width; +} + +void SkStroke::setMiterLimit(SkScalar miterLimit) { + SkASSERT(miterLimit >= 0); + fMiterLimit = miterLimit; +} + +void SkStroke::setCap(SkPaint::Cap cap) { + SkASSERT((unsigned)cap < SkPaint::kCapCount); + fCap = SkToU8(cap); +} + +void SkStroke::setJoin(SkPaint::Join join) { + SkASSERT((unsigned)join < SkPaint::kJoinCount); + fJoin = SkToU8(join); +} + +void SkStroke::strokePath(const SkPath& src, SkPath* dst) const { + SkASSERT(&src != NULL && dst != NULL); + + dst->reset(); + if (SkScalarHalf(fWidth) <= 0) { + return; + } + + SkPathStroker stroker(SkScalarHalf(fWidth), fMiterLimit, this->getCap(), + this->getJoin()); + + SkPath::Iter iter(src, false); + SkPoint pts[4]; + SkPath::Verb verb, lastSegment = SkPath::kMove_Verb; + + while ((verb = iter.next(pts)) != SkPath::kDone_Verb) { + switch (verb) { + case SkPath::kMove_Verb: + stroker.moveTo(pts[0]); + break; + case SkPath::kLine_Verb: + stroker.lineTo(pts[1]); + lastSegment = verb; + break; + case SkPath::kQuad_Verb: + stroker.quadTo(pts[1], pts[2]); + lastSegment = verb; + break; + case SkPath::kCubic_Verb: + stroker.cubicTo(pts[1], pts[2], pts[3]); + lastSegment = verb; + break; + case SkPath::kClose_Verb: + stroker.close(lastSegment == SkPath::kLine_Verb); + break; + default: + break; + } + } + stroker.done(dst, lastSegment == SkPath::kLine_Verb); + + if (fDoFill) { + dst->addPath(src); + } +} + +void SkStroke::strokeLine(const SkPoint& p0, const SkPoint& p1, + SkPath* dst) const { + SkPath tmp; + + tmp.moveTo(p0); + tmp.lineTo(p1); + this->strokePath(tmp, dst); +} + diff --git a/skia/sgl/SkStrokerPriv.cpp b/skia/sgl/SkStrokerPriv.cpp new file mode 100644 index 0000000..28276cb --- /dev/null +++ b/skia/sgl/SkStrokerPriv.cpp @@ -0,0 +1,275 @@ +/* libs/graphics/sgl/SkStrokerPriv.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkStrokerPriv.h" +#include "SkGeometry.h" +#include "SkPath.h" + +static void ButtCapper(SkPath* path, const SkPoint& pivot, + const SkVector& normal, const SkPoint& stop, + SkPath*) +{ + path->lineTo(stop.fX, stop.fY); +} + +static void RoundCapper(SkPath* path, const SkPoint& pivot, + const SkVector& normal, const SkPoint& stop, + SkPath*) +{ + SkScalar px = pivot.fX; + SkScalar py = pivot.fY; + SkScalar nx = normal.fX; + SkScalar ny = normal.fY; + SkScalar sx = SkScalarMul(nx, CUBIC_ARC_FACTOR); + SkScalar sy = SkScalarMul(ny, CUBIC_ARC_FACTOR); + + path->cubicTo(px + nx + CWX(sx, sy), py + ny + CWY(sx, sy), + px + CWX(nx, ny) + sx, py + CWY(nx, ny) + sy, + px + CWX(nx, ny), py + CWY(nx, ny)); + path->cubicTo(px + CWX(nx, ny) - sx, py + CWY(nx, ny) - sy, + px - nx + CWX(sx, sy), py - ny + CWY(sx, sy), + stop.fX, stop.fY); +} + +static void SquareCapper(SkPath* path, const SkPoint& pivot, + const SkVector& normal, const SkPoint& stop, + SkPath* otherPath) +{ + SkVector parallel; + normal.rotateCW(¶llel); + + if (otherPath) + { + path->setLastPt(pivot.fX + normal.fX + parallel.fX, pivot.fY + normal.fY + parallel.fY); + path->lineTo(pivot.fX - normal.fX + parallel.fX, pivot.fY - normal.fY + parallel.fY); + } + else + { + path->lineTo(pivot.fX + normal.fX + parallel.fX, pivot.fY + normal.fY + parallel.fY); + path->lineTo(pivot.fX - normal.fX + parallel.fX, pivot.fY - normal.fY + parallel.fY); + path->lineTo(stop.fX, stop.fY); + } +} + +///////////////////////////////////////////////////////////////////////////// + +static bool is_clockwise(const SkVector& before, const SkVector& after) +{ + return SkScalarMul(before.fX, after.fY) - SkScalarMul(before.fY, after.fX) > 0; +} + +enum AngleType { + kNearly180_AngleType, + kSharp_AngleType, + kShallow_AngleType, + kNearlyLine_AngleType +}; + +static AngleType Dot2AngleType(SkScalar dot) +{ +// need more precise fixed normalization +// SkASSERT(SkScalarAbs(dot) <= SK_Scalar1 + SK_ScalarNearlyZero); + + if (dot >= 0) // shallow or line + return SkScalarNearlyZero(SK_Scalar1 - dot) ? kNearlyLine_AngleType : kShallow_AngleType; + else // sharp or 180 + return SkScalarNearlyZero(SK_Scalar1 + dot) ? kNearly180_AngleType : kSharp_AngleType; +} + +static void HandleInnerJoin(SkPath* inner, const SkPoint& pivot, const SkVector& after) +{ +#if 1 + /* In the degenerate case that the stroke radius is larger than our segments + just connecting the two inner segments may "show through" as a funny + diagonal. To pseudo-fix this, we go through the pivot point. This adds + an extra point/edge, but I can't see a cheap way to know when this is + not needed :( + */ + inner->lineTo(pivot.fX, pivot.fY); +#endif + + inner->lineTo(pivot.fX - after.fX, pivot.fY - after.fY); +} + +static void BluntJoiner(SkPath* outer, SkPath* inner, const SkVector& beforeUnitNormal, + const SkPoint& pivot, const SkVector& afterUnitNormal, + SkScalar radius, SkScalar invMiterLimit, bool, bool) +{ + SkVector after; + afterUnitNormal.scale(radius, &after); + + if (!is_clockwise(beforeUnitNormal, afterUnitNormal)) + { + SkTSwap<SkPath*>(outer, inner); + after.negate(); + } + + outer->lineTo(pivot.fX + after.fX, pivot.fY + after.fY); + HandleInnerJoin(inner, pivot, after); +} + +static void RoundJoiner(SkPath* outer, SkPath* inner, const SkVector& beforeUnitNormal, + const SkPoint& pivot, const SkVector& afterUnitNormal, + SkScalar radius, SkScalar invMiterLimit, bool, bool) +{ + SkScalar dotProd = SkPoint::DotProduct(beforeUnitNormal, afterUnitNormal); + AngleType angleType = Dot2AngleType(dotProd); + + if (angleType == kNearlyLine_AngleType) + return; + + SkVector before = beforeUnitNormal; + SkVector after = afterUnitNormal; + SkRotationDirection dir = kCW_SkRotationDirection; + + if (!is_clockwise(before, after)) + { + SkTSwap<SkPath*>(outer, inner); + before.negate(); + after.negate(); + dir = kCCW_SkRotationDirection; + } + + SkPoint pts[kSkBuildQuadArcStorage]; + SkMatrix matrix; + matrix.setScale(radius, radius); + matrix.postTranslate(pivot.fX, pivot.fY); + int count = SkBuildQuadArc(before, after, dir, &matrix, pts); + SkASSERT((count & 1) == 1); + + if (count > 1) + { + for (int i = 1; i < count; i += 2) + outer->quadTo(pts[i].fX, pts[i].fY, pts[i+1].fX, pts[i+1].fY); + + after.scale(radius); + HandleInnerJoin(inner, pivot, after); + } +} + +#ifdef SK_SCALAR_IS_FLOAT + #define kOneOverSqrt2 (0.707106781f) +#else + #define kOneOverSqrt2 (46341) +#endif + +static void MiterJoiner(SkPath* outer, SkPath* inner, const SkVector& beforeUnitNormal, + const SkPoint& pivot, const SkVector& afterUnitNormal, + SkScalar radius, SkScalar invMiterLimit, + bool prevIsLine, bool currIsLine) +{ + // negate the dot since we're using normals instead of tangents + SkScalar dotProd = SkPoint::DotProduct(beforeUnitNormal, afterUnitNormal); + AngleType angleType = Dot2AngleType(dotProd); + SkVector before = beforeUnitNormal; + SkVector after = afterUnitNormal; + SkVector mid; + SkScalar sinHalfAngle; + bool ccw; + + if (angleType == kNearlyLine_AngleType) + return; + if (angleType == kNearly180_AngleType) + { + currIsLine = false; + goto DO_BLUNT; + } + + ccw = !is_clockwise(before, after); + if (ccw) + { + SkTSwap<SkPath*>(outer, inner); + before.negate(); + after.negate(); + } + + /* Before we enter the world of square-roots and divides, + check if we're trying to join an upright right angle + (common case for stroking rectangles). If so, special case + that (for speed an accuracy). + Note: we only need to check one normal if dot==0 + */ + if (0 == dotProd && invMiterLimit <= kOneOverSqrt2) + { + mid.set(SkScalarMul(before.fX + after.fX, radius), + SkScalarMul(before.fY + after.fY, radius)); + goto DO_MITER; + } + + /* midLength = radius / sinHalfAngle + if (midLength > miterLimit * radius) abort + if (radius / sinHalf > miterLimit * radius) abort + if (1 / sinHalf > miterLimit) abort + if (1 / miterLimit > sinHalf) abort + My dotProd is opposite sign, since it is built from normals and not tangents + hence 1 + dot instead of 1 - dot in the formula + */ + sinHalfAngle = SkScalarSqrt(SkScalarHalf(SK_Scalar1 + dotProd)); + if (sinHalfAngle < invMiterLimit) + { + currIsLine = false; + goto DO_BLUNT; + } + + // choose the most accurate way to form the initial mid-vector + if (angleType == kSharp_AngleType) + { + mid.set(after.fY - before.fY, before.fX - after.fX); + if (ccw) + mid.negate(); + } + else + mid.set(before.fX + after.fX, before.fY + after.fY); + + mid.setLength(SkScalarDiv(radius, sinHalfAngle)); +DO_MITER: + if (prevIsLine) + outer->setLastPt(pivot.fX + mid.fX, pivot.fY + mid.fY); + else + outer->lineTo(pivot.fX + mid.fX, pivot.fY + mid.fY); + +DO_BLUNT: + after.scale(radius); + if (!currIsLine) + outer->lineTo(pivot.fX + after.fX, pivot.fY + after.fY); + HandleInnerJoin(inner, pivot, after); +} + +///////////////////////////////////////////////////////////////////////////// + +SkStrokerPriv::CapProc SkStrokerPriv::CapFactory(SkPaint::Cap cap) +{ + static const SkStrokerPriv::CapProc gCappers[] = { + ButtCapper, RoundCapper, SquareCapper + }; + + SkASSERT((unsigned)cap < SkPaint::kCapCount); + return gCappers[cap]; +} + +SkStrokerPriv::JoinProc SkStrokerPriv::JoinFactory(SkPaint::Join join) +{ + static const SkStrokerPriv::JoinProc gJoiners[] = { + MiterJoiner, RoundJoiner, BluntJoiner + }; + + SkASSERT((unsigned)join < SkPaint::kJoinCount); + return gJoiners[join]; +} + + + diff --git a/skia/sgl/SkStrokerPriv.h b/skia/sgl/SkStrokerPriv.h new file mode 100644 index 0000000..1d1eb89 --- /dev/null +++ b/skia/sgl/SkStrokerPriv.h @@ -0,0 +1,50 @@ +/* libs/graphics/sgl/SkStrokerPriv.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkStrokerPriv_DEFINED +#define SkStrokerPriv_DEFINED + +#include "SkStroke.h" + +#define CWX(x, y) (-y) +#define CWY(x, y) (x) +#define CCWX(x, y) (y) +#define CCWY(x, y) (-x) + +#define CUBIC_ARC_FACTOR ((SK_ScalarSqrt2 - SK_Scalar1) * 4 / 3) + +class SkStrokerPriv { +public: + typedef void (*CapProc)(SkPath* path, + const SkPoint& pivot, + const SkVector& normal, + const SkPoint& stop, + SkPath* otherPath); + + typedef void (*JoinProc)(SkPath* outer, SkPath* inner, + const SkVector& beforeUnitNormal, + const SkPoint& pivot, + const SkVector& afterUnitNormal, + SkScalar radius, SkScalar invMiterLimit, + bool prevIsLine, bool currIsLine); + + static CapProc CapFactory(SkPaint::Cap); + static JoinProc JoinFactory(SkPaint::Join); +}; + +#endif + diff --git a/skia/sgl/SkTSearch.cpp b/skia/sgl/SkTSearch.cpp new file mode 100644 index 0000000..3a1a7d4 --- /dev/null +++ b/skia/sgl/SkTSearch.cpp @@ -0,0 +1,219 @@ +/* libs/graphics/sgl/SkTSearch.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkTSearch.h" +#include <ctype.h> + +static inline const char* index_into_base(const char*const* base, int index, + size_t elemSize) +{ + return *(const char*const*)((const char*)base + index * elemSize); +} + +int SkStrSearch(const char*const* base, int count, const char target[], + size_t target_len, size_t elemSize) +{ + if (count <= 0) + return ~0; + + SkASSERT(base != NULL); + + int lo = 0; + int hi = count - 1; + + while (lo < hi) + { + int mid = (hi + lo) >> 1; + const char* elem = index_into_base(base, mid, elemSize); + + int cmp = strncmp(elem, target, target_len); + if (cmp < 0) + lo = mid + 1; + else if (cmp > 0 || strlen(elem) > target_len) + hi = mid; + else + return mid; + } + + const char* elem = index_into_base(base, hi, elemSize); + int cmp = strncmp(elem, target, target_len); + if (cmp || strlen(elem) > target_len) + { + if (cmp < 0) + hi += 1; + hi = ~hi; + } + return hi; +} + +int SkStrSearch(const char*const* base, int count, const char target[], + size_t elemSize) +{ + return SkStrSearch(base, count, target, strlen(target), elemSize); +} + +int SkStrLCSearch(const char*const* base, int count, const char target[], + size_t len, size_t elemSize) +{ + SkASSERT(target); + + SkAutoAsciiToLC tolc(target, len); + + return SkStrSearch(base, count, tolc.lc(), len, elemSize); +} + +int SkStrLCSearch(const char*const* base, int count, const char target[], + size_t elemSize) +{ + return SkStrLCSearch(base, count, target, strlen(target), elemSize); +} + +////////////////////////////////////////////////////////////////////////////// + +SkAutoAsciiToLC::SkAutoAsciiToLC(const char str[], size_t len) +{ + // see if we need to compute the length + if ((long)len < 0) { + len = strlen(str); + } + fLength = len; + + // assign lc to our preallocated storage if len is small enough, or allocate + // it on the heap + char* lc; + if (len <= STORAGE) { + lc = fStorage; + } else { + lc = (char*)sk_malloc_throw(len + 1); + } + fLC = lc; + + // convert any asii to lower-case. we let non-ascii (utf8) chars pass + // through unchanged + for (int i = (int)(len - 1); i >= 0; --i) { + int c = str[i]; + if ((c & 0x80) == 0) { // is just ascii + c = tolower(c); + } + lc[i] = c; + } + lc[len] = 0; +} + +SkAutoAsciiToLC::~SkAutoAsciiToLC() +{ + if (fLC != fStorage) { + sk_free(fLC); + } +} + +////////////////////////////////////////////////////////////////////////////// + +#define SK_QSortTempSize 16 + +static inline void sk_qsort_swap(char a[], char b[], size_t elemSize) +{ + char tmp[SK_QSortTempSize]; + + while (elemSize > 0) + { + size_t size = elemSize; + if (size > SK_QSortTempSize) + size = SK_QSortTempSize; + elemSize -= size; + + memcpy(tmp, a, size); + memcpy(a, b, size); + memcpy(b, tmp, size); + a += size; + b += size; + } +} + +static void SkQSort_Partition(char* first, char* last, size_t elemSize, SkQSortCompareProc compare) +{ + char* left = first; + char* rite = last; + char* pivot = left; + + while (left <= rite) + { + while (left < last && compare(left, pivot) < 0) + left += elemSize; + while (first < rite && compare(rite, pivot) > 0) + rite -= elemSize; + if (left <= rite) + { + if (left < rite) + { + SkASSERT(compare(left, rite) >= 0); + sk_qsort_swap(left, rite, elemSize); + } + left += elemSize; + rite -= elemSize; + } + } + if (first < rite) + SkQSort_Partition(first, rite, elemSize, compare); + if (left < last) + SkQSort_Partition(left, last, elemSize, compare); +} + +void SkQSort(void* base, size_t count, size_t elemSize, SkQSortCompareProc compare) +{ + SkASSERT(base); + SkASSERT(compare); + SkASSERT(elemSize > 0); + + if (count <= 1) + return; + + SkQSort_Partition((char*)base, (char*)base + (count - 1) * elemSize, elemSize, compare); +} + +#ifdef SK_DEBUG + +#include "SkRandom.h" + +#ifdef SK_SUPPORT_UNITTEST +extern "C" { + int compare_int(const void* a, const void* b) + { + return *(const int*)a - *(const int*)b; + } +} +#endif + +void SkQSort_UnitTest() +{ +#ifdef SK_SUPPORT_UNITTEST + int array[100]; + SkRandom rand; + + for (int i = 0; i < 1000; i++) + { + int j, count = rand.nextRangeU(1, SK_ARRAY_COUNT(array)); + for (j = 0; j < count; j++) + array[j] = rand.nextS() & 0xFF; + SkQSort(array, count, sizeof(int), compare_int); + for (j = 1; j < count; j++) + SkASSERT(array[j-1] <= array[j]); + } +#endif +} + +#endif diff --git a/skia/sgl/SkTSort.h b/skia/sgl/SkTSort.h new file mode 100644 index 0000000..660b689 --- /dev/null +++ b/skia/sgl/SkTSort.h @@ -0,0 +1,65 @@ +/* libs/graphics/sgl/SkTSort.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkTSort_DEFINED +#define SkTSort_DEFINED + +#include "SkTypes.h" + +template <typename T> +void SkTHeapSort_SiftDown(T array[], int root, int bottom) +{ + int root2 = root << 1; + + while (root2 <= bottom) + { + int maxChild; + + if (root2 == bottom) + maxChild = root2; + else if (array[root2] > array[root2 + 1]) + maxChild = root2; + else + maxChild = root2 + 1; + + if (array[root] < array[maxChild]) + { + SkTSwap<T>(array[root], array[maxChild]); + root = maxChild; + root2 = root << 1; + } + else + break; + } +} + +template <typename T> +void SkTHeapSort(T array[], int count) +{ + int i; + + for (i = count/2 - 1; i >= 0; --i) + SkTHeapSort_SiftDown<T>(array, i, count); + + for (i = count - 2; i >= 0; --i) + { + SkTSwap<T>(array[0], array[i + 1]); + SkTHeapSort_SiftDown<T>(array, 0, i); + } +} + +#endif diff --git a/skia/sgl/SkTemplatesPriv.h b/skia/sgl/SkTemplatesPriv.h new file mode 100644 index 0000000..91ecd51 --- /dev/null +++ b/skia/sgl/SkTemplatesPriv.h @@ -0,0 +1,84 @@ +/* libs/graphics/sgl/SkTemplatesPriv.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkTemplatesPriv_DEFINED +#define SkTemplatesPriv_DEFINED + +#include "SkTemplates.h" + +//////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_BUILD_FOR_WIN32 + #define SK_PLACEMENT_NEW(result, classname, storage, storageSize) \ + result = SkNEW(classname) + + #define SK_PLACEMENT_NEW_ARGS(result, classname, storage, storageSize, args) \ + result = SkNEW_ARGS(classname, args) +#else + #include <new> + #define SK_PLACEMENT_NEW(result, classname, storage, storagesize) \ + do { \ + if (storagesize) \ + { \ + SkASSERT(storageSize >= sizeof(classname)); \ + result = new(storage) classname; \ + } \ + else \ + result = SkNEW(classname); \ + } while (0) + + #define SK_PLACEMENT_NEW_ARGS(result, classname, storage, storagesize, args) \ + do { \ + if (storagesize) \ + { \ + SkASSERT(storageSize >= sizeof(classname)); \ + result = new(storage) classname args; \ + } \ + else \ + result = SkNEW_ARGS(classname, args); \ + } while (0) +#endif + +//////////////////////////////////////////////////////////////////////////////// + +template <class T> class SkAutoTPlacementDelete { +public: + SkAutoTPlacementDelete(T* obj, void* storage) : fObj(obj), fStorage(storage) + { + } + ~SkAutoTPlacementDelete() + { + if (fObj) + { + if (fObj == fStorage) + fObj->~T(); + else + delete fObj; + } + } + T* detach() + { + T* obj = fObj; + fObj = NULL; + return obj; + } +private: + T* fObj; + void* fStorage; +}; + +#endif diff --git a/skia/sgl/SkTypeface.cpp b/skia/sgl/SkTypeface.cpp new file mode 100644 index 0000000..9821c51 --- /dev/null +++ b/skia/sgl/SkTypeface.cpp @@ -0,0 +1,64 @@ +#include "SkTypeface.h" +#include "SkFontHost.h" + +static const SkTypeface* resolve_null_typeface(const SkTypeface* face) +{ + if (NULL == face) { + face = SkFontHost::FindTypeface(NULL, NULL, SkTypeface::kNormal); + SkASSERT(face); + } + return face; +} + +uint32_t SkTypeface::UniqueID(const SkTypeface* face) +{ + return resolve_null_typeface(face)->uniqueID(); +} + +bool SkTypeface::Equal(const SkTypeface* facea, const SkTypeface* faceb) +{ + return resolve_null_typeface(facea)->uniqueID() == + resolve_null_typeface(faceb)->uniqueID(); +} + +/////////////////////////////////////////////////////////////////////////////// + +SkTypeface* SkTypeface::Create(const char name[], Style style) +{ + SkTypeface* face = SkFontHost::FindTypeface(NULL, name, style); + face->ref(); + return face; +} + +SkTypeface* SkTypeface::CreateFromTypeface(const SkTypeface* family, Style s) +{ + family = resolve_null_typeface(family); + SkTypeface* face = SkFontHost::FindTypeface(family, NULL, s); + face->ref(); + return face; +} + +SkTypeface* SkTypeface::CreateFromStream(SkStream* stream) +{ + return SkFontHost::CreateTypeface(stream); +} + +#include "SkMMapStream.h" +SkTypeface* SkTypeface::CreateFromFile(const char path[]) +{ + return SkFontHost::CreateTypeface(SkNEW_ARGS(SkMMAPStream, (path))); +} + +/////////////////////////////////////////////////////////////////////////////// + +void SkTypeface::serialize(SkWStream* stream) const { + SkFontHost::Serialize(this, stream); +} + +SkTypeface* SkTypeface::Deserialize(SkStream* stream) { + SkTypeface* face = SkFontHost::Deserialize(stream); + face->ref(); + return face; +} + + diff --git a/skia/sgl/SkTypeface_fake.cpp b/skia/sgl/SkTypeface_fake.cpp new file mode 100644 index 0000000..2564dd2 --- /dev/null +++ b/skia/sgl/SkTypeface_fake.cpp @@ -0,0 +1,17 @@ +#include "SkTypeface.h" + +// ===== Begin Chrome-specific definitions ===== + +uint32_t SkTypeface::UniqueID(const SkTypeface* face) +{ + return NULL; +} + +void SkTypeface::serialize(SkWStream* stream) const { +} + +SkTypeface* SkTypeface::Deserialize(SkStream* stream) { + return NULL; +} + +// ===== End Chrome-specific definitions ===== diff --git a/skia/sgl/SkUnPreMultiply.cpp b/skia/sgl/SkUnPreMultiply.cpp new file mode 100644 index 0000000..371af32 --- /dev/null +++ b/skia/sgl/SkUnPreMultiply.cpp @@ -0,0 +1,73 @@ +#include "SkUnPreMultiply.h" +#include "SkColorPriv.h" + +SkColor SkUnPreMultiply::PMColorToColor(SkPMColor c) { + const unsigned a = SkGetPackedA32(c); + const Scale scale = GetScale(a); + return SkColorSetARGB(a, + ApplyScale(scale, SkGetPackedR32(c)), + ApplyScale(scale, SkGetPackedG32(c)), + ApplyScale(scale, SkGetPackedB32(c))); +} + +const uint32_t SkUnPreMultiply::gTable[] = { + 0x00000000, 0xFF000000, 0x7F800000, 0x55000000, 0x3FC00000, 0x33000000, 0x2A800000, 0x246DB6DB, + 0x1FE00000, 0x1C555555, 0x19800000, 0x172E8BA3, 0x15400000, 0x139D89D9, 0x1236DB6E, 0x11000000, + 0x0FF00000, 0x0F000000, 0x0E2AAAAB, 0x0D6BCA1B, 0x0CC00000, 0x0C249249, 0x0B9745D1, 0x0B1642C8, + 0x0AA00000, 0x0A333333, 0x09CEC4EC, 0x0971C71C, 0x091B6DB7, 0x08CB08D4, 0x08800000, 0x0839CE74, + 0x07F80000, 0x07BA2E8C, 0x07800000, 0x07492492, 0x07155555, 0x06E45307, 0x06B5E50D, 0x0689D89E, + 0x06600000, 0x063831F4, 0x06124925, 0x05EE23B9, 0x05CBA2E9, 0x05AAAAAB, 0x058B2164, 0x056CEFA9, + 0x05500000, 0x05343EB2, 0x0519999A, 0x05000000, 0x04E76276, 0x04CFB2B8, 0x04B8E38E, 0x04A2E8BA, + 0x048DB6DB, 0x0479435E, 0x0465846A, 0x045270D0, 0x04400000, 0x042E29F8, 0x041CE73A, 0x040C30C3, + 0x03FC0000, 0x03EC4EC5, 0x03DD1746, 0x03CE540F, 0x03C00000, 0x03B21643, 0x03A49249, 0x03976FC6, + 0x038AAAAB, 0x037E3F20, 0x03722983, 0x03666666, 0x035AF287, 0x034FCACE, 0x0344EC4F, 0x033A5441, + 0x03300000, 0x0325ED09, 0x031C18FA, 0x0312818B, 0x03092492, 0x03000000, 0x02F711DC, 0x02EE5847, + 0x02E5D174, 0x02DD7BAF, 0x02D55555, 0x02CD5CD6, 0x02C590B2, 0x02BDEF7C, 0x02B677D4, 0x02AF286C, + 0x02A80000, 0x02A0FD5C, 0x029A1F59, 0x029364D9, 0x028CCCCD, 0x0286562E, 0x02800000, 0x0279C952, + 0x0273B13B, 0x026DB6DB, 0x0267D95C, 0x026217ED, 0x025C71C7, 0x0256E62A, 0x0251745D, 0x024C1BAD, + 0x0246DB6E, 0x0241B2F9, 0x023CA1AF, 0x0237A6F5, 0x0232C235, 0x022DF2DF, 0x02293868, 0x02249249, + 0x02200000, 0x021B810F, 0x021714FC, 0x0212BB51, 0x020E739D, 0x020A3D71, 0x02061862, 0x02020408, + 0x01FE0000, 0x01FA0BE8, 0x01F62762, 0x01F25214, 0x01EE8BA3, 0x01EAD3BB, 0x01E72A08, 0x01E38E39, + 0x01E00000, 0x01DC7F11, 0x01D90B21, 0x01D5A3EA, 0x01D24925, 0x01CEFA8E, 0x01CBB7E3, 0x01C880E5, + 0x01C55555, 0x01C234F7, 0x01BF1F90, 0x01BC14E6, 0x01B914C2, 0x01B61EED, 0x01B33333, 0x01B05161, + 0x01AD7943, 0x01AAAAAB, 0x01A7E567, 0x01A5294A, 0x01A27627, 0x019FCBD2, 0x019D2A20, 0x019A90E8, + 0x01980000, 0x01957741, 0x0192F685, 0x01907DA5, 0x018E0C7D, 0x018BA2E9, 0x018940C5, 0x0186E5F1, + 0x01849249, 0x018245AE, 0x01800000, 0x017DC11F, 0x017B88EE, 0x0179574E, 0x01772C23, 0x01750750, + 0x0172E8BA, 0x0170D045, 0x016EBDD8, 0x016CB157, 0x016AAAAB, 0x0168A9B9, 0x0166AE6B, 0x0164B8A8, + 0x0162C859, 0x0160DD68, 0x015EF7BE, 0x015D1746, 0x015B3BEA, 0x01596596, 0x01579436, 0x0155C7B5, + 0x01540000, 0x01523D04, 0x01507EAE, 0x014EC4EC, 0x014D0FAC, 0x014B5EDD, 0x0149B26D, 0x01480A4B, + 0x01466666, 0x0144C6B0, 0x01432B17, 0x0141938C, 0x01400000, 0x013E7064, 0x013CE4A9, 0x013B5CC1, + 0x0139D89E, 0x01385831, 0x0136DB6E, 0x01356246, 0x0133ECAE, 0x01327A97, 0x01310BF6, 0x012FA0BF, + 0x012E38E4, 0x012CD45A, 0x012B7315, 0x012A150B, 0x0128BA2F, 0x01276276, 0x01260DD6, 0x0124BC45, + 0x01236DB7, 0x01222222, 0x0120D97D, 0x011F93BC, 0x011E50D8, 0x011D10C5, 0x011BD37A, 0x011A98EF, + 0x0119611A, 0x01182BF3, 0x0116F970, 0x0115C988, 0x01149C34, 0x0113716B, 0x01124925, 0x01112359, + 0x01100000, 0x010EDF12, 0x010DC087, 0x010CA458, 0x010B8A7E, 0x010A72F0, 0x01095DA9, 0x01084AA0, + 0x010739CE, 0x01062B2E, 0x01051EB8, 0x01041466, 0x01030C31, 0x01020612, 0x01010204, 0x01000000 +}; + +#ifdef BUILD_DIVIDE_TABLE +void SkUnPreMultiply_BuildTable() { + for (unsigned i = 0; i <= 255; i++) { + uint32_t scale; + + if (0 == i) { + scale = 0; + } else { + scale = ((255 << 24) + (i >> 1)) / i; + } + + SkDebugf(" 0x%08X,", scale); + if ((i & 7) == 7) { + SkDebugf("\n"); + } + + // test the result + for (int j = 1; j <= i; j++) { + uint32_t test = (j * scale + (1 << 23)) >> 24; + uint32_t div = roundf(j * 255.0f / i); + int diff = SkAbs32(test - div); + SkASSERT(diff <= 1 && test <= 255); + } + } +} +#endif diff --git a/skia/sgl/SkUtils.cpp b/skia/sgl/SkUtils.cpp new file mode 100644 index 0000000..d541a1d --- /dev/null +++ b/skia/sgl/SkUtils.cpp @@ -0,0 +1,574 @@ +/* libs/graphics/sgl/SkUtils.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkUtils.h" + +#if 0 +#define assign_16_longs(dst, value) \ + do { \ + (dst)[0] = value; (dst)[1] = value; \ + (dst)[2] = value; (dst)[3] = value; \ + (dst)[4] = value; (dst)[5] = value; \ + (dst)[6] = value; (dst)[7] = value; \ + (dst)[8] = value; (dst)[9] = value; \ + (dst)[10] = value; (dst)[11] = value; \ + (dst)[12] = value; (dst)[13] = value; \ + (dst)[14] = value; (dst)[15] = value; \ + } while (0) +#else +#define assign_16_longs(dst, value) \ + do { \ + *(dst)++ = value; *(dst)++ = value; \ + *(dst)++ = value; *(dst)++ = value; \ + *(dst)++ = value; *(dst)++ = value; \ + *(dst)++ = value; *(dst)++ = value; \ + *(dst)++ = value; *(dst)++ = value; \ + *(dst)++ = value; *(dst)++ = value; \ + *(dst)++ = value; *(dst)++ = value; \ + *(dst)++ = value; *(dst)++ = value; \ + } while (0) +#endif + +/////////////////////////////////////////////////////////////////////////// + +void sk_memset16_portable(uint16_t dst[], uint16_t value, int count) +{ + SkASSERT(dst != NULL && count >= 0); + + if (count <= 0) + return; + + // not sure if this helps to short-circuit on small values of count + if (count < 8) + { + do { + *dst++ = (uint16_t)value; + } while (--count != 0); + return; + } + + // ensure we're on a long boundary + if ((size_t)dst & 2) + { + *dst++ = (uint16_t)value; + count -= 1; + } + + uint32_t value32 = ((uint32_t)value << 16) | value; + + // handle the bulk with our unrolled macro + { + int sixteenlongs = count >> 5; + if (sixteenlongs) + { + uint32_t* dst32 = (uint32_t*)dst; + do { + assign_16_longs(dst32, value32); + } while (--sixteenlongs != 0); + dst = (uint16_t*)dst32; + count &= 31; + } + } + + // handle (most) of the rest + { + int longs = count >> 1; + if (longs) + { + do { + *(uint32_t*)dst = value32; + dst += 2; + } while (--longs != 0); + } + } + + // cleanup a possible trailing short + if (count & 1) + *dst = (uint16_t)value; +} + +void sk_memset32_portable(uint32_t dst[], uint32_t value, int count) +{ + SkASSERT(dst != NULL && count >= 0); + + { + int sixteenlongs = count >> 4; + if (sixteenlongs) + { + do { + assign_16_longs(dst, value); + } while (--sixteenlongs != 0); + count &= 15; + } + } + + if (count) + { + do { + *dst++ = value; + } while (--count != 0); + } +} + +////////////////////////////////////////////////////////////////////////////// + +/* 0xxxxxxx 1 total + 10xxxxxx // never a leading byte + 110xxxxx 2 total + 1110xxxx 3 total + 11110xxx 4 total + + 11 10 01 01 xx xx xx xx 0... + 0xE5XX0000 + 0xE5 << 24 +*/ + +#ifdef SK_DEBUG + static void assert_utf8_leadingbyte(unsigned c) + { + SkASSERT(c <= 0xF7); // otherwise leading byte is too big (more than 4 bytes) + SkASSERT((c & 0xC0) != 0x80); // can't begin with a middle char + } + + int SkUTF8_LeadByteToCount(unsigned c) + { + assert_utf8_leadingbyte(c); + return (((0xE5 << 24) >> (c >> 4 << 1)) & 3) + 1; + } +#else + #define assert_utf8_leadingbyte(c) +#endif + +int SkUTF8_CountUnichars(const char utf8[]) +{ + SkASSERT(utf8); + + int count = 0; + + for (;;) + { + int c = *(const uint8_t*)utf8; + if (c == 0) + break; + + utf8 += SkUTF8_LeadByteToCount(c); + count += 1; + } + return count; +} + +int SkUTF8_CountUnichars(const char utf8[], size_t byteLength) +{ + SkASSERT(NULL != utf8 || 0 == byteLength); + + int count = 0; + const char* stop = utf8 + byteLength; + + while (utf8 < stop) + { + utf8 += SkUTF8_LeadByteToCount(*(const uint8_t*)utf8); + count += 1; + } + return count; +} + +SkUnichar SkUTF8_ToUnichar(const char utf8[]) +{ + SkASSERT(NULL != utf8); + + const uint8_t* p = (const uint8_t*)utf8; + int c = *p; + int hic = c << 24; + + assert_utf8_leadingbyte(c); + + if (hic < 0) + { + uint32_t mask = (uint32_t)~0x3F; + hic <<= 1; + do { + c = (c << 6) | (*++p & 0x3F); + mask <<= 5; + } while ((hic <<= 1) < 0); + c &= ~mask; + } + return c; +} + +SkUnichar SkUTF8_NextUnichar(const char** ptr) +{ + SkASSERT(NULL != ptr && NULL != *ptr); + + const uint8_t* p = (const uint8_t*)*ptr; + int c = *p; + int hic = c << 24; + + assert_utf8_leadingbyte(c); + + if (hic < 0) + { + uint32_t mask = (uint32_t)~0x3F; + hic <<= 1; + do { + c = (c << 6) | (*++p & 0x3F); + mask <<= 5; + } while ((hic <<= 1) < 0); + c &= ~mask; + } + *ptr = (char*)p + 1; + return c; +} + +SkUnichar SkUTF8_PrevUnichar(const char** ptr) +{ + SkASSERT(NULL != ptr && NULL != *ptr); + + const char* p = *ptr; + + if (*--p & 0x80) + while (*--p & 0x40) + ; + + *ptr = (char*)p; + return SkUTF8_NextUnichar(&p); +} + +size_t SkUTF8_FromUnichar(SkUnichar uni, char utf8[]) +{ + if ((uint32_t)uni > 0x10FFFF) + { + SkASSERT(!"bad unichar"); + return 0; + } + + if (uni <= 127) + { + if (utf8) + *utf8 = (char)uni; + return 1; + } + + char tmp[4]; + char* p = tmp; + size_t count = 1; + + SkDEBUGCODE(SkUnichar orig = uni;) + + while (uni > 0x3F) + { + *p++ = (char)(0x80 | (uni & 0x3F)); + uni >>= 6; + count += 1; + } + + if (utf8) + { + p = tmp; + utf8 += count; + while (p < tmp + count - 1) + *--utf8 = *p++; + *--utf8 = (char)(~(0xFF >> count) | uni); + } + + SkASSERT(utf8 == NULL || orig == SkUTF8_ToUnichar(utf8)); + return count; +} + +//////////////////////////////////////////////////////////////////////////////////// + +int SkUTF16_CountUnichars(const uint16_t src[]) +{ + SkASSERT(src); + + int count = 0; + unsigned c; + while ((c = *src++) != 0) + { + SkASSERT(!SkUTF16_IsLowSurrogate(c)); + if (SkUTF16_IsHighSurrogate(c)) + { + c = *src++; + SkASSERT(SkUTF16_IsLowSurrogate(c)); + } + count += 1; + } + return count; +} + +int SkUTF16_CountUnichars(const uint16_t src[], int numberOf16BitValues) +{ + SkASSERT(src); + + const uint16_t* stop = src + numberOf16BitValues; + int count = 0; + while (src < stop) + { + unsigned c = *src++; + SkASSERT(!SkUTF16_IsLowSurrogate(c)); + if (SkUTF16_IsHighSurrogate(c)) + { + SkASSERT(src < stop); + c = *src++; + SkASSERT(SkUTF16_IsLowSurrogate(c)); + } + count += 1; + } + return count; +} + +SkUnichar SkUTF16_NextUnichar(const uint16_t** srcPtr) +{ + SkASSERT(srcPtr && *srcPtr); + + const uint16_t* src = *srcPtr; + SkUnichar c = *src++; + + SkASSERT(!SkUTF16_IsLowSurrogate(c)); + if (SkUTF16_IsHighSurrogate(c)) + { + unsigned c2 = *src++; + SkASSERT(SkUTF16_IsLowSurrogate(c2)); + + // c = ((c & 0x3FF) << 10) + (c2 & 0x3FF) + 0x10000 + // c = (((c & 0x3FF) + 64) << 10) + (c2 & 0x3FF) + c = (c << 10) + c2 + (0x10000 - (0xD800 << 10) - 0xDC00); + } + *srcPtr = src; + return c; +} + +SkUnichar SkUTF16_PrevUnichar(const uint16_t** srcPtr) +{ + SkASSERT(srcPtr && *srcPtr); + + const uint16_t* src = *srcPtr; + SkUnichar c = *--src; + + SkASSERT(!SkUTF16_IsHighSurrogate(c)); + if (SkUTF16_IsLowSurrogate(c)) + { + unsigned c2 = *--src; + SkASSERT(SkUTF16_IsHighSurrogate(c2)); + c = (c2 << 10) + c + (0x10000 - (0xD800 << 10) - 0xDC00); + } + *srcPtr = src; + return c; +} + +size_t SkUTF16_FromUnichar(SkUnichar uni, uint16_t dst[]) +{ + SkASSERT((unsigned)uni <= 0x10FFFF); + + int extra = (uni > 0xFFFF); + + if (dst) + { + if (extra) + { + // dst[0] = SkToU16(0xD800 | ((uni - 0x10000) >> 10)); + // dst[0] = SkToU16(0xD800 | ((uni >> 10) - 64)); + dst[0] = SkToU16((0xD800 - 64) + (uni >> 10)); + dst[1] = SkToU16(0xDC00 | (uni & 0x3FF)); + + SkASSERT(SkUTF16_IsHighSurrogate(dst[0])); + SkASSERT(SkUTF16_IsLowSurrogate(dst[1])); + } + else + { + dst[0] = SkToU16(uni); + SkASSERT(!SkUTF16_IsHighSurrogate(dst[0])); + SkASSERT(!SkUTF16_IsLowSurrogate(dst[0])); + } + } + return 1 + extra; +} + +size_t SkUTF16_ToUTF8(const uint16_t utf16[], int numberOf16BitValues, char utf8[]) +{ + SkASSERT(numberOf16BitValues >= 0); + if (numberOf16BitValues <= 0) + return 0; + + SkASSERT(utf16 != NULL); + + const uint16_t* stop = utf16 + numberOf16BitValues; + size_t size = 0; + + if (utf8 == NULL) // just count + { + while (utf16 < stop) + size += SkUTF8_FromUnichar(SkUTF16_NextUnichar(&utf16), NULL); + } + else + { + char* start = utf8; + while (utf16 < stop) + utf8 += SkUTF8_FromUnichar(SkUTF16_NextUnichar(&utf16), utf8); + size = utf8 - start; + } + return size; +} + +//////////////////////////////////////////////////////////////////////////////////// + +#include <stdlib.h> + +static int round_to_K(size_t bytes) +{ + return (bytes + 512) >> 10; +} + +SkAutoMemoryUsageProbe::SkAutoMemoryUsageProbe(const char label[]) + : fLabel(label) +{ +#if 0 + struct mallinfo mi = mallinfo(); + + fBytesAllocated = mi.uordblks; +#endif +} + +SkAutoMemoryUsageProbe::~SkAutoMemoryUsageProbe() +{ +#if 0 + struct mallinfo mi = mallinfo(); + + printf("SkAutoMemoryUsageProbe "); + if (fLabel) + printf("<%s> ", fLabel); + printf("delta %dK, current total allocated %dK\n", + round_to_K(mi.uordblks - fBytesAllocated), + round_to_K(mi.uordblks)); +#endif +} + +//////////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_DEBUG + +#include "SkRandom.h" +#include "SkTSearch.h" +#include "SkTSort.h" + +#define kSEARCH_COUNT 91 + +#ifdef SK_SUPPORT_UNITTEST +static void test_search() +{ + int i, array[kSEARCH_COUNT]; + SkRandom rand; + + for (i = 0; i < kSEARCH_COUNT; i++) + array[i] = rand.nextS(); + + SkTHeapSort<int>(array, kSEARCH_COUNT); + // make sure we got sorted properly + for (i = 1; i < kSEARCH_COUNT; i++) + SkASSERT(array[i-1] <= array[i]); + + // make sure we can find all of our values + for (i = 0; i < kSEARCH_COUNT; i++) + { + int index = SkTSearch<int>(array, kSEARCH_COUNT, array[i], sizeof(int)); + SkASSERT(index == i); + } + + // make sure that random values are either found, or the correct + // insertion index is returned + for (i = 0; i < 10000; i++) + { + int value = rand.nextS(); + int index = SkTSearch<int>(array, kSEARCH_COUNT, value, sizeof(int)); + + if (index >= 0) + SkASSERT(index < kSEARCH_COUNT && array[index] == value); + else + { + index = ~index; + SkASSERT(index <= kSEARCH_COUNT); + if (index < kSEARCH_COUNT) + { + SkASSERT(value < array[index]); + if (index > 0) + SkASSERT(value > array[index - 1]); + } + else // we should append the new value + { + SkASSERT(value > array[kSEARCH_COUNT - 1]); + } + } + } +} + +static void test_utf16() +{ + static const SkUnichar gUni[] = { + 0x10000, 0x18080, 0x20202, 0xFFFFF, 0x101234 + }; + + uint16_t buf[2]; + + for (unsigned i = 0; i < SK_ARRAY_COUNT(gUni); i++) + { + size_t count = SkUTF16_FromUnichar(gUni[i], buf); + SkASSERT(count == 2); + size_t count2 = SkUTF16_CountUnichars(buf, 2); + SkASSERT(count2 == 1); + const uint16_t* ptr = buf; + SkUnichar c = SkUTF16_NextUnichar(&ptr); + SkASSERT(c == gUni[i]); + SkASSERT(ptr - buf == 2); + } +} + +#endif + +void SkUtils::UnitTest() +{ +#ifdef SK_SUPPORT_UNITTEST + static const struct { + const char* fUtf8; + SkUnichar fUni; + } gTest[] = { + { "a", 'a' }, + { "\xC3\x83", (3 << 6) | 3 }, + { "\xE3\x83\x83", (3 << 12) | (3 << 6) | 3 }, + { "\xF3\x83\x83\x83", (3 << 18) | (3 << 12) | (3 << 6) | 3 } + }; + + for (unsigned i = 0; i < SK_ARRAY_COUNT(gTest); i++) + { + const char* p = gTest[i].fUtf8; + int n = SkUTF8_CountUnichars(p); + SkUnichar u0 = SkUTF8_ToUnichar(gTest[i].fUtf8); + SkUnichar u1 = SkUTF8_NextUnichar(&p); + + SkASSERT(n == 1); + SkASSERT(u0 == u1); + SkASSERT(u0 == gTest[i].fUni); + SkASSERT(p - gTest[i].fUtf8 == (int)strlen(gTest[i].fUtf8)); + } + + test_utf16(); + + test_search(); +#endif +} + +#endif + + diff --git a/skia/sgl/SkWriter32.cpp b/skia/sgl/SkWriter32.cpp new file mode 100644 index 0000000..61d0051 --- /dev/null +++ b/skia/sgl/SkWriter32.cpp @@ -0,0 +1,170 @@ +#include "SkWriter32.h" + +struct SkWriter32::Block { + Block* fNext; + size_t fSize; + size_t fAllocated; + + size_t available() const { return fSize - fAllocated; } + char* base() { return (char*)(this + 1); } + const char* base() const { return (const char*)(this + 1); } + + uint32_t* alloc(size_t size) + { + SkASSERT(SkAlign4(size) == size); + SkASSERT(this->available() >= size); + void* ptr = this->base() + fAllocated; + fAllocated += size; + SkASSERT(fAllocated <= fSize); + return (uint32_t*)ptr; + } + + uint32_t* peek32(size_t offset) + { + SkASSERT(offset <= fAllocated + 4); + void* ptr = this->base() + offset; + return (uint32_t*)ptr; + } + + static Block* Create(size_t size) + { + SkASSERT(SkAlign4(size) == size); + Block* block = (Block*)sk_malloc_throw(sizeof(Block) + size); + block->fNext = NULL; + block->fSize = size; + block->fAllocated = 0; + return block; + } +}; + +static size_t compute_block_size(size_t currSize, size_t minSize) +{ + if (currSize < minSize) + currSize = minSize; + + currSize += (currSize >> 1); + return SkAlign4(currSize); +} + +/////////////////////////////////////////////////////////////////////////////// + +SkWriter32::~SkWriter32() +{ + this->reset(); +} + +void SkWriter32::reset() +{ + Block* block = fHead; + while (block) + { + Block* next = block->fNext; + sk_free(block); + block = next; + } + fHead = fTail = NULL; + fSize = 0; +} + +uint32_t* SkWriter32::reserve(size_t size) +{ + SkASSERT(SkAlign4(size) == size); + + Block* block = fTail; + + if (NULL == block) + { + SkASSERT(NULL == fHead); + fHead = fTail = block = Block::Create(SkMax32(size, fMinSize)); + } + else if (block->available() < size) + { + fTail = Block::Create(SkMax32(size, fMinSize)); + block->fNext = fTail; + block = fTail; + } + + fSize += size; + + return block->alloc(size); +} + +uint32_t* SkWriter32::peek32(size_t offset) +{ + SkASSERT(SkAlign4(offset) == offset); + SkASSERT(offset <= fSize); + + Block* block = fHead; + SkASSERT(NULL != block); + + while (offset >= block->fAllocated) + { + offset -= block->fAllocated; + block = block->fNext; + SkASSERT(NULL != block); + } + return block->peek32(offset); +} + +void SkWriter32::flatten(void* dst) const +{ + const Block* block = fHead; + SkDEBUGCODE(size_t total = 0;) + + while (block) + { + size_t allocated = block->fAllocated; + memcpy(dst, block->base(), allocated); + dst = (char*)dst + allocated; + block = block->fNext; + + SkDEBUGCODE(total += allocated;) + SkASSERT(total <= fSize); + } + SkASSERT(total == fSize); +} + +void SkWriter32::writePad(const void* src, size_t size) { + size_t alignedSize = SkAlign4(size); + char* dst = (char*)this->reserve(alignedSize); + memcpy(dst, src, size); + dst += size; + int n = alignedSize - size; + while (--n >= 0) { + *dst++ = 0; + } +} + +#include "SkStream.h" + +size_t SkWriter32::readFromStream(SkStream* stream, size_t length) { + char scratch[1024]; + const size_t MAX = sizeof(scratch); + size_t remaining = length; + + while (remaining != 0) { + size_t n = remaining; + if (n > MAX) { + n = MAX; + } + size_t bytes = stream->read(scratch, n); + this->writePad(scratch, bytes); + remaining -= bytes; + if (bytes != n) { + break; + } + } + return length - remaining; +} + +bool SkWriter32::writeToStream(SkWStream* stream) { + const Block* block = fHead; + while (block) { + if (!stream->write(block->base(), block->fAllocated)) { + return false; + } + block = block->fNext; + } + return true; +} + diff --git a/skia/sgl/SkXfermode.cpp b/skia/sgl/SkXfermode.cpp new file mode 100644 index 0000000..6225e6e --- /dev/null +++ b/skia/sgl/SkXfermode.cpp @@ -0,0 +1,965 @@ +/* + * Copyright (C) 2006 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "SkXfermode.h" +#include "SkColorPriv.h" + +#define SkAlphaMulAlpha(a, b) SkMulDiv255Round(a, b) + +static SkPMColor SkFourByteInterp(SkPMColor src, SkPMColor dst, U8CPU alpha) { + unsigned scale = SkAlpha255To256(alpha); + + unsigned a = SkAlphaBlend(SkGetPackedA32(src), SkGetPackedA32(dst), scale); + unsigned r = SkAlphaBlend(SkGetPackedR32(src), SkGetPackedR32(dst), scale); + unsigned g = SkAlphaBlend(SkGetPackedG32(src), SkGetPackedG32(dst), scale); + unsigned b = SkAlphaBlend(SkGetPackedB32(src), SkGetPackedB32(dst), scale); + + return SkPackARGB32(a, r, g, b); +} + +/////////////////////////////////////////////////////////////////////////////// + +bool SkXfermode::asCoeff(Coeff* src, Coeff* dst) { + return false; +} + +SkPMColor SkXfermode::xferColor(SkPMColor src, SkPMColor dst) { + // no-op. subclasses should override this + return dst; +} + +void SkXfermode::xfer32(SK_RESTRICT SkPMColor dst[], + const SK_RESTRICT SkPMColor src[], int count, + const SK_RESTRICT SkAlpha aa[]) { + SkASSERT(dst && src && count >= 0); + + if (NULL == aa) { + for (int i = count - 1; i >= 0; --i) { + dst[i] = this->xferColor(src[i], dst[i]); + } + } else { + for (int i = count - 1; i >= 0; --i) { + unsigned a = aa[i]; + if (0 != a) { + SkPMColor dstC = dst[i]; + SkPMColor C = this->xferColor(src[i], dstC); + if (0xFF != a) { + C = SkFourByteInterp(C, dstC, a); + } + dst[i] = C; + } + } + } +} + +void SkXfermode::xfer16(SK_RESTRICT uint16_t dst[], + const SK_RESTRICT SkPMColor src[], int count, + const SK_RESTRICT SkAlpha aa[]) { + SkASSERT(dst && src && count >= 0); + + if (NULL == aa) { + for (int i = count - 1; i >= 0; --i) { + SkPMColor dstC = SkPixel16ToPixel32(dst[i]); + dst[i] = SkPixel32ToPixel16_ToU16(this->xferColor(src[i], dstC)); + } + } else { + for (int i = count - 1; i >= 0; --i) { + unsigned a = aa[i]; + if (0 != a) { + SkPMColor dstC = SkPixel16ToPixel32(dst[i]); + SkPMColor C = this->xferColor(src[i], dstC); + if (0xFF != a) { + C = SkFourByteInterp(C, dstC, a); + } + dst[i] = SkPixel32ToPixel16_ToU16(C); + } + } + } +} + +void SkXfermode::xfer4444(SK_RESTRICT SkPMColor16 dst[], + const SK_RESTRICT SkPMColor src[], int count, + const SK_RESTRICT SkAlpha aa[]) +{ + SkASSERT(dst && src && count >= 0); + + if (NULL == aa) { + for (int i = count - 1; i >= 0; --i) { + SkPMColor dstC = SkPixel4444ToPixel32(dst[i]); + dst[i] = SkPixel32ToPixel4444(this->xferColor(src[i], dstC)); + } + } else { + for (int i = count - 1; i >= 0; --i) { + unsigned a = aa[i]; + if (0 != a) { + SkPMColor dstC = SkPixel4444ToPixel32(dst[i]); + SkPMColor C = this->xferColor(src[i], dstC); + if (0xFF != a) { + C = SkFourByteInterp(C, dstC, a); + } + dst[i] = SkPixel32ToPixel4444(C); + } + } + } +} + +void SkXfermode::xferA8(SK_RESTRICT SkAlpha dst[], + const SkPMColor src[], int count, + const SK_RESTRICT SkAlpha aa[]) +{ + SkASSERT(dst && src && count >= 0); + + if (NULL == aa) { + for (int i = count - 1; i >= 0; --i) { + SkPMColor res = this->xferColor(src[i], (dst[i] << SK_A32_SHIFT)); + dst[i] = SkToU8(SkGetPackedA32(res)); + } + } else { + for (int i = count - 1; i >= 0; --i) { + unsigned a = aa[i]; + if (0 != a) { + SkAlpha dstA = dst[i]; + unsigned A = SkGetPackedA32(this->xferColor(src[i], + (SkPMColor)(dstA << SK_A32_SHIFT))); + if (0xFF != a) { + A = SkAlphaBlend(A, dstA, SkAlpha255To256(a)); + } + dst[i] = SkToU8(A); + } + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void SkProcXfermode::xfer32(SK_RESTRICT SkPMColor dst[], + const SK_RESTRICT SkPMColor src[], int count, + const SK_RESTRICT SkAlpha aa[]) { + SkASSERT(dst && src && count >= 0); + + SkXfermodeProc proc = fProc; + + if (NULL != proc) { + if (NULL == aa) { + for (int i = count - 1; i >= 0; --i) { + dst[i] = proc(src[i], dst[i]); + } + } else { + for (int i = count - 1; i >= 0; --i) { + unsigned a = aa[i]; + if (0 != a) { + SkPMColor dstC = dst[i]; + SkPMColor C = proc(src[i], dstC); + if (a != 0xFF) { + C = SkFourByteInterp(C, dstC, a); + } + dst[i] = C; + } + } + } + } +} + +void SkProcXfermode::xfer16(SK_RESTRICT uint16_t dst[], + const SK_RESTRICT SkPMColor src[], int count, + const SK_RESTRICT SkAlpha aa[]) { + SkASSERT(dst && src && count >= 0); + + SkXfermodeProc proc = fProc; + + if (NULL != proc) { + if (NULL == aa) { + for (int i = count - 1; i >= 0; --i) { + SkPMColor dstC = SkPixel16ToPixel32(dst[i]); + dst[i] = SkPixel32ToPixel16_ToU16(proc(src[i], dstC)); + } + } else { + for (int i = count - 1; i >= 0; --i) { + unsigned a = aa[i]; + if (0 != a) { + SkPMColor dstC = SkPixel16ToPixel32(dst[i]); + SkPMColor C = proc(src[i], dstC); + if (0xFF != a) { + C = SkFourByteInterp(C, dstC, a); + } + dst[i] = SkPixel32ToPixel16_ToU16(C); + } + } + } + } +} + +void SkProcXfermode::xfer4444(SK_RESTRICT SkPMColor16 dst[], + const SK_RESTRICT SkPMColor src[], int count, + const SK_RESTRICT SkAlpha aa[]) { + SkASSERT(dst && src && count >= 0); + + SkXfermodeProc proc = fProc; + + if (NULL != proc) { + if (NULL == aa) { + for (int i = count - 1; i >= 0; --i) { + SkPMColor dstC = SkPixel4444ToPixel32(dst[i]); + dst[i] = SkPixel32ToPixel4444(proc(src[i], dstC)); + } + } else { + for (int i = count - 1; i >= 0; --i) { + unsigned a = aa[i]; + if (0 != a) { + SkPMColor dstC = SkPixel4444ToPixel32(dst[i]); + SkPMColor C = proc(src[i], dstC); + if (0xFF != a) { + C = SkFourByteInterp(C, dstC, a); + } + dst[i] = SkPixel32ToPixel4444(C); + } + } + } + } +} + +void SkProcXfermode::xferA8(SK_RESTRICT SkAlpha dst[], + const SK_RESTRICT SkPMColor src[], int count, + const SK_RESTRICT SkAlpha aa[]) { + SkASSERT(dst && src && count >= 0); + + SkXfermodeProc proc = fProc; + + if (NULL != proc) { + if (NULL == aa) { + for (int i = count - 1; i >= 0; --i) { + SkPMColor res = proc(src[i], dst[i] << SK_A32_SHIFT); + dst[i] = SkToU8(SkGetPackedA32(res)); + } + } else { + for (int i = count - 1; i >= 0; --i) { + unsigned a = aa[i]; + if (0 != a) { + SkAlpha dstA = dst[i]; + SkPMColor res = proc(src[i], dstA << SK_A32_SHIFT); + unsigned A = SkGetPackedA32(res); + if (0xFF != a) { + A = SkAlphaBlend(A, dstA, SkAlpha255To256(a)); + } + dst[i] = SkToU8(A); + } + } + } + } +} + +SkProcXfermode::SkProcXfermode(SkFlattenableReadBuffer& buffer) + : SkXfermode(buffer) { + fProc = (SkXfermodeProc)buffer.readFunctionPtr(); +} + +void SkProcXfermode::flatten(SkFlattenableWriteBuffer& buffer) { + buffer.writeFunctionPtr((void*)fProc); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +class SkProcCoeffXfermode : public SkProcXfermode { +public: + SkProcCoeffXfermode(SkXfermodeProc proc, Coeff sc, Coeff dc) + : INHERITED(proc), fSrcCoeff(sc), fDstCoeff(dc) { + } + + virtual bool asCoeff(Coeff* sc, Coeff* dc) { + if (sc) { + *sc = fSrcCoeff; + } + if (dc) { + *dc = fDstCoeff; + } + return true; + } + + virtual Factory getFactory() { return CreateProc; } + virtual void flatten(SkFlattenableWriteBuffer& buffer) { + this->INHERITED::flatten(buffer); + buffer.write32(fSrcCoeff); + buffer.write32(fDstCoeff); + } + +protected: + SkProcCoeffXfermode(SkFlattenableReadBuffer& buffer) + : INHERITED(buffer) { + fSrcCoeff = (Coeff)buffer.readU32(); + fDstCoeff = (Coeff)buffer.readU32(); + } + +private: + Coeff fSrcCoeff, fDstCoeff; + + static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) { + return SkNEW_ARGS(SkProcCoeffXfermode, (buffer)); } + + typedef SkProcXfermode INHERITED; +}; + +/////////////////////////////////////////////////////////////////////////////// + +// kClear_Mode, //!< [0, 0] +static SkPMColor clear_modeproc(SkPMColor src, SkPMColor dst) { + return 0; +} + +// kSrc_Mode, //!< [Sa, Sc] +static SkPMColor src_modeproc(SkPMColor src, SkPMColor dst) { + return src; +} + +// kDst_Mode, //!< [Da, Dc] +static SkPMColor dst_modeproc(SkPMColor src, SkPMColor dst) { + return dst; +} + +// kSrcOver_Mode, //!< [Sa + (1 - Sa)*Da, Sc + (1 - Sa)*Dc] +static SkPMColor srcover_modeproc(SkPMColor src, SkPMColor dst) { + return src + SkAlphaMulQ(dst, SkAlpha255To256(255 - SkGetPackedA32(src))); +} + +// kDstOver_Mode, //!< [Sa + (1 - Sa)*Da, Dc + (1 - Da)*Sc] +static SkPMColor dstover_modeproc(SkPMColor src, SkPMColor dst) { + unsigned sa = SkGetPackedA32(src); + unsigned da = SkGetPackedA32(dst); + unsigned ida = 255 - da; + + return SkPackARGB32(sa + da - SkAlphaMulAlpha(sa, da), + SkGetPackedR32(dst) + SkAlphaMulAlpha(ida, SkGetPackedR32(src)), + SkGetPackedG32(dst) + SkAlphaMulAlpha(ida, SkGetPackedG32(src)), + SkGetPackedB32(dst) + SkAlphaMulAlpha(ida, SkGetPackedB32(src))); +} + +// kSrcIn_Mode, //!< [Sa * Da, Sc * Da] +static SkPMColor srcin_modeproc(SkPMColor src, SkPMColor dst) { + return SkAlphaMulQ(src, SkAlpha255To256(SkGetPackedA32(dst))); +} + +// kDstIn_Mode, //!< [Sa * Da, Sa * Dc] +static SkPMColor dstin_modeproc(SkPMColor src, SkPMColor dst) { + return SkAlphaMulQ(dst, SkAlpha255To256(SkGetPackedA32(src))); +} + +// kSrcOut_Mode, //!< [Sa * (1 - Da), Sc * (1 - Da)] +static SkPMColor srcout_modeproc(SkPMColor src, SkPMColor dst) { + return SkAlphaMulQ(src, SkAlpha255To256(255 - SkGetPackedA32(dst))); +} + +// kDstOut_Mode, //!< [Da * (1 - Sa), Dc * (1 - Sa)] +static SkPMColor dstout_modeproc(SkPMColor src, SkPMColor dst) { + return SkAlphaMulQ(dst, SkAlpha255To256(255 - SkGetPackedA32(src))); +} + +// kSrcATop_Mode, //!< [Da, Sc * Da + (1 - Sa) * Dc] +static SkPMColor srcatop_modeproc(SkPMColor src, SkPMColor dst) { + unsigned sa = SkGetPackedA32(src); + unsigned da = SkGetPackedA32(dst); + unsigned isa = 255 - sa; + + return SkPackARGB32(da, + SkAlphaMulAlpha(da, SkGetPackedR32(src)) + + SkAlphaMulAlpha(isa, SkGetPackedR32(dst)), + SkAlphaMulAlpha(da, SkGetPackedG32(src)) + + SkAlphaMulAlpha(isa, SkGetPackedG32(dst)), + SkAlphaMulAlpha(da, SkGetPackedB32(src)) + + SkAlphaMulAlpha(isa, SkGetPackedB32(dst))); +} + +// kDstATop_Mode, //!< [Sa, Sa * Dc + Sc * (1 - Da)] +static SkPMColor dstatop_modeproc(SkPMColor src, SkPMColor dst) { + unsigned sa = SkGetPackedA32(src); + unsigned da = SkGetPackedA32(dst); + unsigned ida = 255 - da; + + return SkPackARGB32(sa, + SkAlphaMulAlpha(ida, SkGetPackedR32(src)) + + SkAlphaMulAlpha(sa, SkGetPackedR32(dst)), + SkAlphaMulAlpha(ida, SkGetPackedG32(src)) + + SkAlphaMulAlpha(sa, SkGetPackedG32(dst)), + SkAlphaMulAlpha(ida, SkGetPackedB32(src)) + + SkAlphaMulAlpha(sa, SkGetPackedB32(dst))); +} + +// kXor_Mode [Sa + Da - 2 * Sa * Da, Sc * (1 - Da) + (1 - Sa) * Dc] +static SkPMColor xor_modeproc(SkPMColor src, SkPMColor dst) { + unsigned sa = SkGetPackedA32(src); + unsigned da = SkGetPackedA32(dst); + unsigned isa = 255 - sa; + unsigned ida = 255 - da; + + return SkPackARGB32(sa + da - (SkAlphaMulAlpha(sa, da) << 1), + SkAlphaMulAlpha(ida, SkGetPackedR32(src)) + + SkAlphaMulAlpha(isa, SkGetPackedR32(dst)), + SkAlphaMulAlpha(ida, SkGetPackedG32(src)) + + SkAlphaMulAlpha(isa, SkGetPackedG32(dst)), + SkAlphaMulAlpha(ida, SkGetPackedB32(src)) + + SkAlphaMulAlpha(isa, SkGetPackedB32(dst))); +} + + +// kDarken_Mode, [Sa + Da - Sa·Da, Sc·(1 - Da) + Dc·(1 - Sa) + min(Sc, Dc)] + +static inline unsigned darken_p(unsigned src, unsigned dst, + unsigned src_mul, unsigned dst_mul) { + return ((dst_mul * src + src_mul * dst) >> 8) + SkMin32(src, dst); +} + +static SkPMColor darken_modeproc(SkPMColor src, SkPMColor dst) { + unsigned sa = SkGetPackedA32(src); + unsigned da = SkGetPackedA32(dst); + unsigned src_scale = SkAlpha255To256(255 - sa); + unsigned dst_scale = SkAlpha255To256(255 - da); + + unsigned ra = sa + da - SkAlphaMulAlpha(sa, da); + unsigned rr = darken_p(SkGetPackedR32(src), SkGetPackedR32(dst), + src_scale, dst_scale); + unsigned rg = darken_p(SkGetPackedG32(src), SkGetPackedG32(dst), + src_scale, dst_scale); + unsigned rb = darken_p(SkGetPackedB32(src), SkGetPackedB32(dst), + src_scale, dst_scale); + + return SkPackARGB32(ra, SkFastMin32(rr, ra), + SkFastMin32(rg, ra), SkFastMin32(rb, ra)); +} + +// kLighten_Mode, [Sa + Da - Sa·Da, Sc·(1 - Da) + Dc·(1 - Sa) + max(Sc, Dc)] +static inline unsigned lighten_p(unsigned src, unsigned dst, + unsigned src_mul, unsigned dst_mul) { + return ((dst_mul * src + src_mul * dst) >> 8) + SkMax32(src, dst); +} + +static SkPMColor lighten_modeproc(SkPMColor src, SkPMColor dst) { + unsigned sa = SkGetPackedA32(src); + unsigned da = SkGetPackedA32(dst); + unsigned src_scale = SkAlpha255To256(255 - sa); + unsigned dst_scale = SkAlpha255To256(255 - da); + + unsigned ra = sa + da - SkAlphaMulAlpha(sa, da); + unsigned rr = lighten_p(SkGetPackedR32(src), SkGetPackedR32(dst), + src_scale, dst_scale); + unsigned rg = lighten_p(SkGetPackedG32(src), SkGetPackedG32(dst), + src_scale, dst_scale); + unsigned rb = lighten_p(SkGetPackedB32(src), SkGetPackedB32(dst), + src_scale, dst_scale); + + return SkPackARGB32(ra, SkFastMin32(rr, ra), + SkFastMin32(rg, ra), SkFastMin32(rb, ra)); +} + +static SkPMColor mult_modeproc(SkPMColor src, SkPMColor dst) { + int a = SkAlphaMulAlpha(SkGetPackedA32(src), SkGetPackedA32(dst)); + int r = SkAlphaMulAlpha(SkGetPackedR32(src), SkGetPackedR32(dst)); + int g = SkAlphaMulAlpha(SkGetPackedG32(src), SkGetPackedG32(dst)); + int b = SkAlphaMulAlpha(SkGetPackedB32(src), SkGetPackedB32(dst)); + return SkPackARGB32(a, r, g, b); +} + +static inline int screen_byte(int a, int b) { + return a + b - SkAlphaMulAlpha(a, b); +} + +static SkPMColor screen_modeproc(SkPMColor src, SkPMColor dst) { + int a = screen_byte(SkGetPackedA32(src), SkGetPackedA32(dst)); + int r = screen_byte(SkGetPackedR32(src), SkGetPackedR32(dst)); + int g = screen_byte(SkGetPackedG32(src), SkGetPackedG32(dst)); + int b = screen_byte(SkGetPackedB32(src), SkGetPackedB32(dst)); + return SkPackARGB32(a, r, g, b); +} + +/////////////////////////////////////////////////////////////////////////////// + +class SkClearXfermode : public SkProcCoeffXfermode { +public: + SkClearXfermode() : SkProcCoeffXfermode(clear_modeproc, + kZero_Coeff, kZero_Coeff) {} + + virtual void xfer32(SK_RESTRICT SkPMColor dst[], + const SK_RESTRICT SkPMColor[], int count, + const SK_RESTRICT SkAlpha aa[]) { + SkASSERT(dst && count >= 0); + + if (NULL == aa) { + memset(dst, 0, count << 2); + } else { + for (int i = count - 1; i >= 0; --i) { + unsigned a = aa[i]; + if (0xFF == a) { + dst[i] = 0; + } else if (a != 0) { + dst[i] = SkAlphaMulQ(dst[i], SkAlpha255To256(255 - a)); + } + } + } + } + virtual void xferA8(SK_RESTRICT SkAlpha dst[], + const SK_RESTRICT SkPMColor[], int count, + const SK_RESTRICT SkAlpha aa[]) { + SkASSERT(dst && count >= 0); + + if (NULL == aa) { + memset(dst, 0, count); + } else { + for (int i = count - 1; i >= 0; --i) { + unsigned a = aa[i]; + if (0xFF == a) { + dst[i] = 0; + } else if (0 != a) { + dst[i] = SkAlphaMulAlpha(dst[i], 255 - a); + } + } + } + } + + virtual Factory getFactory() { return CreateProc; } + +private: + SkClearXfermode(SkFlattenableReadBuffer& buffer) + : SkProcCoeffXfermode(buffer) {} + + static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) { + return SkNEW_ARGS(SkClearXfermode, (buffer)); + } +}; + +/////////////////////////////////////////////////////////////////////////////// + +class SkSrcXfermode : public SkProcCoeffXfermode { +public: + SkSrcXfermode() : SkProcCoeffXfermode(src_modeproc, + kOne_Coeff, kZero_Coeff) {} + + virtual void xfer32(SK_RESTRICT SkPMColor dst[], + const SK_RESTRICT SkPMColor src[], int count, + const SK_RESTRICT SkAlpha aa[]) { + SkASSERT(dst && src && count >= 0); + + if (NULL == aa) { + memcpy(dst, src, count << 2); + } else { + for (int i = count - 1; i >= 0; --i) { + unsigned a = aa[i]; + if (a == 0xFF) { + dst[i] = src[i]; + } else if (a != 0) { + dst[i] = SkFourByteInterp(src[i], dst[i], a); + } + } + } + } + + virtual void xferA8(SK_RESTRICT SkAlpha dst[], + const SK_RESTRICT SkPMColor src[], int count, + const SK_RESTRICT SkAlpha aa[]) { + SkASSERT(dst && src && count >= 0); + + if (NULL == aa) { + for (int i = count - 1; i >= 0; --i) { + dst[i] = SkToU8(SkGetPackedA32(src[i])); + } + } else { + for (int i = count - 1; i >= 0; --i) { + unsigned a = aa[i]; + if (0 != a) { + unsigned srcA = SkGetPackedA32(src[i]); + if (a == 0xFF) { + dst[i] = SkToU8(srcA); + } else { + dst[i] = SkToU8(SkAlphaBlend(srcA, dst[i], a)); + } + } + } + } + } + + virtual Factory getFactory() { return CreateProc; } + +private: + SkSrcXfermode(SkFlattenableReadBuffer& buffer) + : SkProcCoeffXfermode(buffer) {} + + static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) { + return SkNEW_ARGS(SkSrcXfermode, (buffer)); + } +}; + +class SkDstInXfermode : public SkProcCoeffXfermode { +public: + SkDstInXfermode() : SkProcCoeffXfermode(dstin_modeproc, + kZero_Coeff, kSA_Coeff) {} + + virtual void xfer32(SK_RESTRICT SkPMColor dst[], + const SK_RESTRICT SkPMColor src[], int count, + const SK_RESTRICT SkAlpha aa[]) { + SkASSERT(dst && src); + + if (count <= 0) { + return; + } + if (NULL != aa) { + return this->INHERITED::xfer32(dst, src, count, aa); + } + + do { + unsigned a = SkGetPackedA32(*src); + *dst = SkAlphaMulQ(*dst, SkAlpha255To256(a)); + dst++; + src++; + } while (--count != 0); + } + + virtual Factory getFactory() { return CreateProc; } + +private: + SkDstInXfermode(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {} + + static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) { + return SkNEW_ARGS(SkDstInXfermode, (buffer)); + } + + typedef SkProcCoeffXfermode INHERITED; +}; + +class SkDstOutXfermode : public SkProcCoeffXfermode { +public: + SkDstOutXfermode() : SkProcCoeffXfermode(dstout_modeproc, + kZero_Coeff, kISA_Coeff) {} + + virtual void xfer32(SK_RESTRICT SkPMColor dst[], + const SK_RESTRICT SkPMColor src[], int count, + const SK_RESTRICT SkAlpha aa[]) { + SkASSERT(dst && src); + + if (count <= 0) { + return; + } + if (NULL != aa) { + return this->INHERITED::xfer32(dst, src, count, aa); + } + + do { + unsigned a = SkGetPackedA32(*src); + *dst = SkAlphaMulQ(*dst, SkAlpha255To256(255 - a)); + dst++; + src++; + } while (--count != 0); + } + + virtual Factory getFactory() { return CreateProc; } + +private: + SkDstOutXfermode(SkFlattenableReadBuffer& buffer) + : INHERITED(buffer) {} + + static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) { + return SkNEW_ARGS(SkDstOutXfermode, (buffer)); + } + + typedef SkProcCoeffXfermode INHERITED; +}; + +/////////////////////////////////////////////////////////////////////////////// + +#include "SkPorterDuff.h" + +struct ProcCoeff { + SkXfermodeProc fProc; + SkXfermode::Coeff fSC; + SkXfermode::Coeff fDC; +}; + +static const ProcCoeff gProcCoeffs[] = { + { clear_modeproc, SkXfermode::kZero_Coeff, SkXfermode::kZero_Coeff }, + { src_modeproc, SkXfermode::kOne_Coeff, SkXfermode::kZero_Coeff }, + { dst_modeproc, SkXfermode::kZero_Coeff, SkXfermode::kOne_Coeff }, + { srcover_modeproc, SkXfermode::kOne_Coeff, SkXfermode::kISA_Coeff }, + { dstover_modeproc, SkXfermode::kIDA_Coeff, SkXfermode::kOne_Coeff }, + { srcin_modeproc, SkXfermode::kDA_Coeff, SkXfermode::kZero_Coeff }, + { dstin_modeproc, SkXfermode::kZero_Coeff, SkXfermode::kSA_Coeff }, + { srcout_modeproc, SkXfermode::kIDA_Coeff, SkXfermode::kZero_Coeff }, + { dstout_modeproc, SkXfermode::kZero_Coeff, SkXfermode::kISA_Coeff }, + { srcatop_modeproc, SkXfermode::kDA_Coeff, SkXfermode::kISA_Coeff }, + { dstatop_modeproc, SkXfermode::kIDA_Coeff, SkXfermode::kSA_Coeff }, + { xor_modeproc, SkXfermode::kIDA_Coeff, SkXfermode::kISA_Coeff }, + // these two can't be represented as coefficients + { darken_modeproc, SkXfermode::Coeff(-1), SkXfermode::Coeff(-1) }, + { lighten_modeproc, SkXfermode::Coeff(-1), SkXfermode::Coeff(-1) }, + // these can use coefficients + { mult_modeproc, SkXfermode::kZero_Coeff, SkXfermode::kSC_Coeff }, + { screen_modeproc, SkXfermode::kOne_Coeff, SkXfermode::kISC_Coeff } +}; + +SkXfermode* SkPorterDuff::CreateXfermode(SkPorterDuff::Mode mode) { + SkASSERT(SK_ARRAY_COUNT(gProcCoeffs) == SkPorterDuff::kModeCount); + SkASSERT((unsigned)mode < SkPorterDuff::kModeCount); + + switch (mode) { + case kClear_Mode: + return SkNEW(SkClearXfermode); + case kSrc_Mode: + return SkNEW(SkSrcXfermode); + case kSrcOver_Mode: + return NULL; + case kDstIn_Mode: + return SkNEW(SkDstInXfermode); + case kDstOut_Mode: + return SkNEW(SkDstOutXfermode); + // these two can't be represented with Coeff + case kDarken_Mode: + return SkNEW_ARGS(SkProcXfermode, (darken_modeproc)); + case kLighten_Mode: + return SkNEW_ARGS(SkProcXfermode, (lighten_modeproc)); + // use the table + default: { + const ProcCoeff& rec = gProcCoeffs[mode]; + SkASSERT((unsigned)rec.fSC < SkXfermode::kCoeffCount); + SkASSERT((unsigned)rec.fDC < SkXfermode::kCoeffCount); + return SkNEW_ARGS(SkProcCoeffXfermode, (rec.fProc, + rec.fSC, rec.fDC)); + } + } +} + +bool SkPorterDuff::IsMode(SkXfermode* xfer, Mode* mode) { + if (NULL == xfer) { + if (mode) { + *mode = kSrcOver_Mode; + } + return true; + } + + SkXfermode::Coeff sc, dc; + if (xfer->asCoeff(&sc, &dc)) { + SkASSERT((unsigned)sc < (unsigned)SkXfermode::kCoeffCount); + SkASSERT((unsigned)dc < (unsigned)SkXfermode::kCoeffCount); + + const ProcCoeff* rec = gProcCoeffs; + for (size_t i = 0; i < SK_ARRAY_COUNT(gProcCoeffs); i++) { + if (rec[i].fSC == sc && rec[i].fDC == dc) { + if (mode) { + *mode = SkPorterDuff::Mode(i); + } + return true; + } + } + } + + // no coefficients, or not found in our table + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_DEBUG +static void unit_test() { + for (unsigned a = 0; a <= 255; a++) { + for (unsigned c = 0; c <= a; c++) { + SkPMColor pm = SkPackARGB32(a, c, c, c); + for (unsigned aa = 0; aa <= 255; aa++) { + for (unsigned cc = 0; cc <= aa; cc++) { + SkPMColor pm2 = SkPackARGB32(aa, cc, cc, cc); + + const size_t N = SK_ARRAY_COUNT(gProcCoeffs); + for (size_t i = 0; i < N; i++) { + gProcCoeffs[i].fProc(pm, pm2); + } + } + } + } + } +} +#endif + +SkXfermodeProc SkPorterDuff::GetXfermodeProc(Mode mode) { +#ifdef SK_DEBUGx + static bool gUnitTest; + if (!gUnitTest) { + gUnitTest = true; + unit_test(); + } +#endif + + SkXfermodeProc proc = NULL; + + if ((unsigned)mode < SkPorterDuff::kModeCount) { + proc = gProcCoeffs[mode].fProc; + } + return proc; +} + +/////////////////////////////////////////////////////////////////////////////// +//////////// 16bit xfermode procs + +#ifdef SK_DEBUG +static bool require_255(SkPMColor src) { return SkGetPackedA32(src) == 0xFF; } +static bool require_0(SkPMColor src) { return SkGetPackedA32(src) == 0; } +#endif + +static uint16_t src_modeproc16_255(SkPMColor src, uint16_t dst) { + SkASSERT(require_255(src)); + return SkPixel32ToPixel16(src); +} + +static uint16_t dst_modeproc16(SkPMColor src, uint16_t dst) { + return dst; +} + +static uint16_t srcover_modeproc16_0(SkPMColor src, uint16_t dst) { + SkASSERT(require_0(src)); + return dst; +} + +static uint16_t srcover_modeproc16_255(SkPMColor src, uint16_t dst) { + SkASSERT(require_255(src)); + return SkPixel32ToPixel16(src); +} + +static uint16_t dstover_modeproc16_0(SkPMColor src, uint16_t dst) { + SkASSERT(require_0(src)); + return dst; +} + +static uint16_t dstover_modeproc16_255(SkPMColor src, uint16_t dst) { + SkASSERT(require_255(src)); + return dst; +} + +static uint16_t srcin_modeproc16_255(SkPMColor src, uint16_t dst) { + SkASSERT(require_255(src)); + return SkPixel32ToPixel16(src); +} + +static uint16_t dstin_modeproc16_255(SkPMColor src, uint16_t dst) { + SkASSERT(require_255(src)); + return dst; +} + +static uint16_t dstout_modeproc16_0(SkPMColor src, uint16_t dst) { + SkASSERT(require_0(src)); + return dst; +} + +static uint16_t srcatop_modeproc16(SkPMColor src, uint16_t dst) { + unsigned isa = 255 - SkGetPackedA32(src); + + return SkPackRGB16( + SkPacked32ToR16(src) + SkAlphaMulAlpha(SkGetPackedR16(dst), isa), + SkPacked32ToG16(src) + SkAlphaMulAlpha(SkGetPackedG16(dst), isa), + SkPacked32ToB16(src) + SkAlphaMulAlpha(SkGetPackedB16(dst), isa)); +} + +static uint16_t srcatop_modeproc16_0(SkPMColor src, uint16_t dst) { + SkASSERT(require_0(src)); + return dst; +} + +static uint16_t srcatop_modeproc16_255(SkPMColor src, uint16_t dst) { + SkASSERT(require_255(src)); + return SkPixel32ToPixel16(src); +} + +static uint16_t dstatop_modeproc16_255(SkPMColor src, uint16_t dst) { + SkASSERT(require_255(src)); + return dst; +} + +/********* + darken and lighten boil down to this. + + darken = (1 - Sa) * Dc + min(Sc, Dc) + lighten = (1 - Sa) * Dc + max(Sc, Dc) + + if (Sa == 0) these become + darken = Dc + min(0, Dc) = 0 + lighten = Dc + max(0, Dc) = Dc + + if (Sa == 1) these become + darken = min(Sc, Dc) + lighten = max(Sc, Dc) +*/ + +static uint16_t darken_modeproc16_0(SkPMColor src, uint16_t dst) { + SkASSERT(require_0(src)); + return 0; +} + +static uint16_t darken_modeproc16_255(SkPMColor src, uint16_t dst) { + SkASSERT(require_255(src)); + unsigned r = SkFastMin32(SkPacked32ToR16(src), SkGetPackedR16(dst)); + unsigned g = SkFastMin32(SkPacked32ToG16(src), SkGetPackedG16(dst)); + unsigned b = SkFastMin32(SkPacked32ToB16(src), SkGetPackedB16(dst)); + return SkPackRGB16(r, g, b); +} + +static uint16_t lighten_modeproc16_0(SkPMColor src, uint16_t dst) { + SkASSERT(require_0(src)); + return dst; +} + +static uint16_t lighten_modeproc16_255(SkPMColor src, uint16_t dst) { + SkASSERT(require_255(src)); + unsigned r = SkMax32(SkPacked32ToR16(src), SkGetPackedR16(dst)); + unsigned g = SkMax32(SkPacked32ToG16(src), SkGetPackedG16(dst)); + unsigned b = SkMax32(SkPacked32ToB16(src), SkGetPackedB16(dst)); + return SkPackRGB16(r, g, b); +} + +struct Proc16Rec { + SkXfermodeProc16 fProc16_0; + SkXfermodeProc16 fProc16_255; + SkXfermodeProc16 fProc16_General; +}; + +static const Proc16Rec gPorterDuffModeProcs16[] = { + { NULL, NULL, NULL }, // CLEAR + { NULL, src_modeproc16_255, NULL }, + { dst_modeproc16, dst_modeproc16, dst_modeproc16 }, + { srcover_modeproc16_0, srcover_modeproc16_255, NULL }, + { dstover_modeproc16_0, dstover_modeproc16_255, NULL }, + { NULL, srcin_modeproc16_255, NULL }, + { NULL, dstin_modeproc16_255, NULL }, + { NULL, NULL, NULL },// SRC_OUT + { dstout_modeproc16_0, NULL, NULL }, + { srcatop_modeproc16_0, srcatop_modeproc16_255, srcatop_modeproc16 }, + { NULL, dstatop_modeproc16_255, NULL }, + { NULL, NULL, NULL }, // XOR + { darken_modeproc16_0, darken_modeproc16_255, NULL }, + { lighten_modeproc16_0, lighten_modeproc16_255, NULL }, + { NULL, NULL, NULL },//multiply + { NULL, NULL, NULL }// screen +}; + +SkXfermodeProc16 SkPorterDuff::GetXfermodeProc16(Mode mode, SkColor srcColor) { + SkXfermodeProc16 proc16 = NULL; + + if ((unsigned)mode < SkPorterDuff::kModeCount) { + const Proc16Rec& rec = gPorterDuffModeProcs16[mode]; + + unsigned a = SkColorGetA(srcColor); + + if (0 == a) { + proc16 = rec.fProc16_0; + } else if (255 == a) { + proc16 = rec.fProc16_255; + } else { + proc16 = rec.fProc16_General; + } + } + return proc16; +} + diff --git a/skia/skia.vcproj b/skia/skia.vcproj new file mode 100644 index 0000000..82993e4 --- /dev/null +++ b/skia/skia.vcproj @@ -0,0 +1,1332 @@ +<?xml version="1.0" encoding="Windows-1252"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="8.00" + Name="skia" + ProjectGUID="{CD9CA56E-4E94-444C-87D4-58CA1E6F300D}" + RootNamespace="skia" + Keyword="Win32Proj" + > + <Platforms> + <Platform + Name="Win32" + /> + </Platforms> + <ToolFiles> + </ToolFiles> + <Configurations> + <Configuration + Name="Debug|Win32" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="4" + InheritedPropertySheets="$(SolutionDir)..\build\debug.vsprops;.\skia.vsprops;.\precompiled.vsprops" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLibrarianTool" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCPostBuildEventTool" + /> + </Configuration> + <Configuration + Name="Release|Win32" + ConfigurationType="4" + InheritedPropertySheets="$(SolutionDir)..\build\release.vsprops;.\skia.vsprops" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLibrarianTool" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCPostBuildEventTool" + /> + </Configuration> + </Configurations> + <References> + </References> + <Files> + <Filter + Name="Header files" + > + <File + RelativePath=".\include\Sk1DPathEffect.h" + > + </File> + <File + RelativePath=".\include\Sk2DPathEffect.h" + > + </File> + <File + RelativePath=".\include\corecg\Sk64.h" + > + </File> + <File + RelativePath=".\include\SkAnimator.h" + > + </File> + <File + RelativePath=".\include\SkAnimatorView.h" + > + </File> + <File + RelativePath=".\include\SkApplication.h" + > + </File> + <File + RelativePath=".\include\SkAvoidXfermode.h" + > + </File> + <File + RelativePath=".\include\SkBGViewArtist.h" + > + </File> + <File + RelativePath=".\include\SkBitmap.h" + > + </File> + <File + RelativePath=".\include\SkBlurDrawLooper.h" + > + </File> + <File + RelativePath=".\include\SkBlurMaskFilter.h" + > + </File> + <File + RelativePath=".\include\SkBML_WXMLParser.h" + > + </File> + <File + RelativePath=".\include\SkBML_XMLParser.h" + > + </File> + <File + RelativePath=".\include\SkBorderView.h" + > + </File> + <File + RelativePath=".\include\SkBounder.h" + > + </File> + <File + RelativePath=".\include\corecg\SkBuffer.h" + > + </File> + <File + RelativePath=".\include\SkCamera.h" + > + </File> + <File + RelativePath=".\include\SkCanvas.h" + > + </File> + <File + RelativePath=".\include\corecg\SkChunkAlloc.h" + > + </File> + <File + RelativePath=".\include\SkColor.h" + > + </File> + <File + RelativePath=".\include\SkColorFilter.h" + > + </File> + <File + RelativePath=".\include\SkColorMatrix.h" + > + </File> + <File + RelativePath=".\include\SkColorPriv.h" + > + </File> + <File + RelativePath=".\include\SkColorShader.h" + > + </File> + <File + RelativePath=".\include\SkCornerPathEffect.h" + > + </File> + <File + RelativePath=".\include\SkCullPoints.h" + > + </File> + <File + RelativePath=".\include\SkDashPathEffect.h" + > + </File> + <File + RelativePath=".\include\SkDeque.h" + > + </File> + <File + RelativePath=".\include\SkDescriptor.h" + > + </File> + <File + RelativePath=".\include\SkDevice.h" + > + </File> + <File + RelativePath=".\include\SkDiscretePathEffect.h" + > + </File> + <File + RelativePath=".\include\SkDither.h" + > + </File> + <File + RelativePath=".\include\SkDOM.h" + > + </File> + <File + RelativePath=".\include\SkDraw.h" + > + </File> + <File + RelativePath=".\include\SkDrawExtraPathEffect.h" + > + </File> + <File + RelativePath=".\include\SkDrawFilter.h" + > + </File> + <File + RelativePath=".\include\SkDrawLooper.h" + > + </File> + <File + RelativePath=".\include\SkEmbossMaskFilter.h" + > + </File> + <File + RelativePath=".\include\corecg\SkEndian.h" + > + </File> + <File + RelativePath=".\include\SkEvent.h" + > + </File> + <File + RelativePath=".\include\SkEventSink.h" + > + </File> + <File + RelativePath=".\include\corecg\SkFDot6.h" + > + </File> + <File + RelativePath=".\include\corecg\SkFixed.h" + > + </File> + <File + RelativePath=".\include\SkFlattenable.h" + > + </File> + <File + RelativePath=".\include\corecg\SkFloatingPoint.h" + > + </File> + <File + RelativePath=".\include\SkFontCodec.h" + > + </File> + <File + RelativePath=".\include\SkFontHost.h" + > + </File> + <File + RelativePath=".\include\SkGlobals.h" + > + </File> + <File + RelativePath=".\include\SkGradientShader.h" + > + </File> + <File + RelativePath=".\include\SkGraphics.h" + > + </File> + <File + RelativePath=".\include\SkImageDecoder.h" + > + </File> + <File + RelativePath=".\include\SkImageRef.h" + > + </File> + <File + RelativePath=".\include\SkImageView.h" + > + </File> + <File + RelativePath=".\include\corecg\SkInterpolator.h" + > + </File> + <File + RelativePath=".\include\SkJS.h" + > + </File> + <File + RelativePath=".\include\SkKernel33MaskFilter.h" + > + </File> + <File + RelativePath=".\include\SkKey.h" + > + </File> + <File + RelativePath=".\include\SkLayerRasterizer.h" + > + </File> + <File + RelativePath=".\include\SkMask.h" + > + </File> + <File + RelativePath=".\include\SkMaskFilter.h" + > + </File> + <File + RelativePath=".\include\corecg\SkMath.h" + > + </File> + <File + RelativePath=".\include\corecg\SkMatrix.h" + > + </File> + <File + RelativePath=".\include\SkMetaData.h" + > + </File> + <File + RelativePath=".\include\SkMMapStream.h" + > + </File> + <File + RelativePath=".\include\SkMovie.h" + > + </File> + <File + RelativePath=".\include\SkNinePatch.h" + > + </File> + <File + RelativePath=".\include\SkOSFile.h" + > + </File> + <File + RelativePath=".\include\SkOSMenu.h" + > + </File> + <File + RelativePath=".\include\SkOSSound.h" + > + </File> + <File + RelativePath=".\include\SkOSWindow_Mac.h" + > + </File> + <File + RelativePath=".\include\SkOSWindow_Unix.h" + > + </File> + <File + RelativePath=".\include\SkOSWindow_Win.h" + > + </File> + <File + RelativePath=".\include\SkOSWindow_wxwidgets.h" + > + </File> + <File + RelativePath=".\include\SkPackBits.h" + > + </File> + <File + RelativePath=".\include\SkPaint.h" + > + </File> + <File + RelativePath=".\include\SkPaintFlagsDrawFilter.h" + > + </File> + <File + RelativePath=".\include\SkParse.h" + > + </File> + <File + RelativePath=".\include\SkParsePaint.h" + > + </File> + <File + RelativePath=".\include\SkPath.h" + > + </File> + <File + RelativePath=".\include\SkPathEffect.h" + > + </File> + <File + RelativePath=".\include\SkPathMeasure.h" + > + </File> + <File + RelativePath=".\include\corecg\SkPerspIter.h" + > + </File> + <File + RelativePath=".\include\SkPicture.h" + > + </File> + <File + RelativePath=".\include\SkPixelRef.h" + > + </File> + <File + RelativePath=".\include\SkPixelXorXfermode.h" + > + </File> + <File + RelativePath=".\include\corecg\SkPoint.h" + > + </File> + <File + RelativePath=".\include\SkPorterDuff.h" + > + </File> + <File + RelativePath=".\include\corecg\SkPostConfig.h" + > + </File> + <File + RelativePath=".\include\corecg\SkPreConfig.h" + > + </File> + <File + RelativePath=".\include\SkProgressBarView.h" + > + </File> + <File + RelativePath=".\include\SkPtrRecorder.h" + > + </File> + <File + RelativePath=".\include\corecg\SkRandom.h" + > + </File> + <File + RelativePath=".\include\SkRasterizer.h" + > + </File> + <File + RelativePath=".\include\SkReader32.h" + > + </File> + <File + RelativePath=".\include\corecg\SkRect.h" + > + </File> + <File + RelativePath=".\include\SkRefCnt.h" + > + </File> + <File + RelativePath=".\include\corecg\SkRegion.h" + > + </File> + <File + RelativePath=".\include\corecg\SkScalar.h" + > + </File> + <File + RelativePath=".\include\SkScalerContext.h" + > + </File> + <File + RelativePath=".\include\SkScrollBarView.h" + > + </File> + <File + RelativePath=".\include\SkShader.h" + > + </File> + <File + RelativePath=".\include\SkShaderExtras.h" + > + </File> + <File + RelativePath=".\include\SkStackViewLayout.h" + > + </File> + <File + RelativePath=".\include\SkStream.h" + > + </File> + <File + RelativePath=".\include\SkStream_Win.h" + > + </File> + <File + RelativePath=".\include\SkString.h" + > + </File> + <File + RelativePath=".\include\SkStroke.h" + > + </File> + <File + RelativePath=".\include\SkSVGAttribute.h" + > + </File> + <File + RelativePath=".\include\SkSVGBase.h" + > + </File> + <File + RelativePath=".\include\SkSVGPaintState.h" + > + </File> + <File + RelativePath=".\include\SkSVGParser.h" + > + </File> + <File + RelativePath=".\include\SkSVGTypes.h" + > + </File> + <File + RelativePath=".\include\SkSystemEventTypes.h" + > + </File> + <File + RelativePath=".\include\SkTDArray.h" + > + </File> + <File + RelativePath=".\include\SkTDict.h" + > + </File> + <File + RelativePath=".\include\SkTDStack.h" + > + </File> + <File + RelativePath=".\include\corecg\SkTemplates.h" + > + </File> + <File + RelativePath=".\include\SkTextBox.h" + > + </File> + <File + RelativePath=".\include\corecg\SkThread.h" + > + </File> + <File + RelativePath=".\include\corecg\SkThread_platform.h" + > + </File> + <File + RelativePath=".\include\SkTime.h" + > + </File> + <File + RelativePath=".\include\SkTransparentShader.h" + > + </File> + <File + RelativePath=".\include\corecg\SkTSearch.h" + > + </File> + <File + RelativePath=".\include\SkTypeface.h" + > + </File> + <File + RelativePath=".\include\corecg\SkTypes.h" + > + </File> + <File + RelativePath=".\include\SkUnitMapper.h" + > + </File> + <File + RelativePath=".\include\SkUnitMappers.h" + > + </File> + <File + RelativePath=".\include\SkUnPreMultiply.h" + > + </File> + <File + RelativePath=".\include\corecg\SkUserConfig.h" + > + </File> + <File + RelativePath=".\include\SkUtils.h" + > + </File> + <File + RelativePath=".\include\SkView.h" + > + </File> + <File + RelativePath=".\include\SkViewInflate.h" + > + </File> + <File + RelativePath=".\include\SkWidget.h" + > + </File> + <File + RelativePath=".\include\SkWidgetViews.h" + > + </File> + <File + RelativePath=".\include\SkWindow.h" + > + </File> + <File + RelativePath=".\include\SkWriter32.h" + > + </File> + <File + RelativePath=".\include\SkXfermode.h" + > + </File> + <File + RelativePath=".\include\SkXMLParser.h" + > + </File> + <File + RelativePath=".\include\SkXMLWriter.h" + > + </File> + </Filter> + <Filter + Name="corecg" + > + <File + RelativePath=".\corecg\Sk64.cpp" + > + </File> + <File + RelativePath=".\corecg\SkBuffer.cpp" + > + </File> + <File + RelativePath=".\corecg\SkChunkAlloc.cpp" + > + </File> + <File + RelativePath=".\corecg\SkCordic.cpp" + > + </File> + <File + RelativePath=".\corecg\SkCordic.h" + > + </File> + <File + RelativePath=".\corecg\SkDebug.cpp" + > + </File> + <File + RelativePath=".\corecg\SkDebug_stdio.cpp" + > + </File> + <File + RelativePath=".\corecg\SkFloat.cpp" + > + </File> + <File + RelativePath=".\corecg\SkFloat.h" + > + </File> + <File + RelativePath=".\corecg\SkInterpolator.cpp" + > + </File> + <File + RelativePath=".\corecg\SkMath.cpp" + > + </File> + <File + RelativePath=".\corecg\SkMatrix.cpp" + > + </File> + <File + RelativePath=".\corecg\SkMemory_stdlib.cpp" + > + </File> + <File + RelativePath=".\corecg\SkPoint.cpp" + > + </File> + <File + RelativePath=".\corecg\SkRect.cpp" + > + </File> + <File + RelativePath=".\corecg\SkRegion.cpp" + > + </File> + <File + RelativePath=".\corecg\SkRegionPriv.h" + > + </File> + <File + RelativePath=".\corecg\SkSinTable.h" + > + </File> + <File + RelativePath=".\corecg\SkTSort.h" + > + </File> + </Filter> + <Filter + Name="sgl" + > + <File + RelativePath=".\sgl\SkAlphaRuns.cpp" + > + </File> + <File + RelativePath=".\sgl\SkAntiRun.h" + > + </File> + <File + RelativePath=".\sgl\SkAutoKern.h" + > + </File> + <File + RelativePath=".\sgl\SkBitmap.cpp" + > + </File> + <File + RelativePath=".\sgl\SkBitmapProcShader.cpp" + > + </File> + <File + RelativePath=".\sgl\SkBitmapProcShader.h" + > + </File> + <File + RelativePath=".\sgl\SkBitmapProcState.cpp" + > + </File> + <File + RelativePath=".\sgl\SkBitmapProcState.h" + > + </File> + <File + RelativePath=".\sgl\SkBitmapProcState_matrix.h" + > + </File> + <File + RelativePath=".\sgl\SkBitmapProcState_matrixProcs.cpp" + > + </File> + <File + RelativePath=".\sgl\SkBitmapSampler.cpp" + > + </File> + <File + RelativePath=".\sgl\SkBitmapSampler.h" + > + </File> + <File + RelativePath=".\sgl\SkBitmapSamplerTemplate.h" + > + </File> + <File + RelativePath=".\sgl\SkBitmapShader.cpp" + > + </File> + <File + RelativePath=".\sgl\SkBitmapShader.h" + > + </File> + <File + RelativePath=".\sgl\SkBitmapShader16BilerpTemplate.h" + > + </File> + <File + RelativePath=".\sgl\SkBitmapShaderTemplate.h" + > + </File> + <File + RelativePath=".\sgl\SkBlitBWMaskTemplate.h" + > + </File> + <File + RelativePath=".\sgl\SkBlitRow.h" + > + </File> + <File + RelativePath=".\sgl\SkBlitRow_D16.cpp" + > + </File> + <File + RelativePath=".\sgl\SkBlitRow_D4444.cpp" + > + </File> + <File + RelativePath=".\sgl\SkBlitter.cpp" + > + </File> + <File + RelativePath=".\sgl\SkBlitter.h" + > + </File> + <File + RelativePath=".\sgl\SkBlitter_4444.cpp" + > + </File> + <File + RelativePath=".\sgl\SkBlitter_A1.cpp" + > + </File> + <File + RelativePath=".\sgl\SkBlitter_A8.cpp" + > + </File> + <File + RelativePath=".\sgl\SkBlitter_ARGB32.cpp" + > + </File> + <File + RelativePath=".\sgl\SkBlitter_RGB16.cpp" + > + </File> + <File + RelativePath=".\sgl\SkBlitter_Sprite.cpp" + > + </File> + <File + RelativePath=".\sgl\SkCanvas.cpp" + > + </File> + <File + RelativePath=".\sgl\SkColor.cpp" + > + </File> + <File + RelativePath=".\sgl\SkColorFilter.cpp" + > + </File> + <File + RelativePath=".\sgl\SkColorTable.cpp" + > + </File> + <File + RelativePath=".\sgl\SkCoreBlitters.h" + > + </File> + <File + RelativePath=".\sgl\SkDeque.cpp" + > + </File> + <File + RelativePath=".\sgl\SkDevice.cpp" + > + </File> + <File + RelativePath=".\sgl\SkDither.cpp" + > + </File> + <File + RelativePath=".\sgl\SkDraw.cpp" + > + </File> + <File + RelativePath=".\sgl\SkDraw.h" + > + </File> + <File + RelativePath=".\sgl\SkEdge.cpp" + > + </File> + <File + RelativePath=".\sgl\SkEdge.h" + > + </File> + <File + RelativePath=".\sgl\SkFilterProc.cpp" + > + </File> + <File + RelativePath=".\sgl\SkFilterProc.h" + > + </File> + <File + RelativePath=".\sgl\SkFlattenable.cpp" + > + </File> + <File + RelativePath=".\sgl\SkFP.h" + > + </File> + <File + RelativePath=".\sgl\SkGeometry.cpp" + > + </File> + <File + RelativePath=".\sgl\SkGeometry.h" + > + </File> + <File + RelativePath=".\sgl\SkGlobals.cpp" + > + </File> + <File + RelativePath=".\sgl\SkGlyphCache.cpp" + > + </File> + <File + RelativePath=".\sgl\SkGlyphCache.h" + > + </File> + <File + RelativePath=".\sgl\SkGraphics.cpp" + > + </File> + <File + RelativePath=".\sgl\SkMask.cpp" + > + </File> + <File + RelativePath=".\sgl\SkMaskFilter.cpp" + > + </File> + <File + RelativePath=".\sgl\SkPackBits.cpp" + > + </File> + <File + RelativePath=".\sgl\SkPaint.cpp" + > + </File> + <File + RelativePath=".\sgl\SkPath.cpp" + > + </File> + <File + RelativePath=".\sgl\SkPathEffect.cpp" + > + </File> + <File + RelativePath=".\sgl\SkPathMeasure.cpp" + > + </File> + <File + RelativePath=".\sgl\SkPicture.cpp" + > + </File> + <File + RelativePath=".\sgl\SkPixelRef.cpp" + > + </File> + <File + RelativePath=".\sgl\SkProcSpriteBlitter.cpp" + > + </File> + <File + RelativePath=".\sgl\SkPtrRecorder.cpp" + > + </File> + <File + RelativePath=".\sgl\SkRasterizer.cpp" + > + </File> + <File + RelativePath=".\sgl\SkRefCnt.cpp" + > + </File> + <File + RelativePath=".\sgl\SkRegion_path.cpp" + > + </File> + <File + RelativePath=".\sgl\SkScalerContext.cpp" + > + </File> + <File + RelativePath=".\sgl\SkScan.cpp" + > + </File> + <File + RelativePath=".\sgl\SkScan.h" + > + </File> + <File + RelativePath=".\sgl\SkScan_Antihair.cpp" + > + </File> + <File + RelativePath=".\sgl\SkScan_AntiPath.cpp" + > + </File> + <File + RelativePath=".\sgl\SkScan_Hairline.cpp" + > + </File> + <File + RelativePath=".\sgl\SkScan_Path.cpp" + > + </File> + <File + RelativePath=".\sgl\SkScanPriv.h" + > + </File> + <File + RelativePath=".\sgl\SkShader.cpp" + > + </File> + <File + RelativePath=".\sgl\SkSpriteBlitter.h" + > + </File> + <File + RelativePath=".\sgl\SkSpriteBlitter_ARGB32.cpp" + > + </File> + <File + RelativePath=".\sgl\SkSpriteBlitter_RGB16.cpp" + > + </File> + <File + RelativePath=".\sgl\SkSpriteBlitterTemplate.h" + > + </File> + <File + RelativePath=".\sgl\SkString.cpp" + > + </File> + <File + RelativePath=".\sgl\SkStroke.cpp" + > + </File> + <File + RelativePath=".\sgl\SkStrokerPriv.cpp" + > + </File> + <File + RelativePath=".\sgl\SkStrokerPriv.h" + > + </File> + <File + RelativePath=".\sgl\SkTemplatesPriv.h" + > + </File> + <File + RelativePath=".\sgl\SkTSearch.cpp" + > + </File> + <File + RelativePath=".\sgl\SkTSort.h" + > + </File> + <File + RelativePath=".\sgl\SkTypeface_fake.cpp" + > + </File> + <File + RelativePath=".\sgl\SkUtils.cpp" + > + </File> + <File + RelativePath=".\sgl\SkWriter32.cpp" + > + </File> + <File + RelativePath=".\sgl\SkXfermode.cpp" + > + </File> + </Filter> + <Filter + Name="ports" + > + <File + RelativePath=".\ports\SkFontHost_none.cpp" + > + </File> + <File + RelativePath=".\ports\SkGlobals_global.cpp" + > + </File> + <File + RelativePath=".\ports\SkImageDecoder_Factory.cpp" + > + </File> + <File + RelativePath=".\ports\SkOSFile_stdio.cpp" + > + </File> + <File + RelativePath=".\ports\SkThread_win.cpp" + > + </File> + </Filter> + <Filter + Name="images" + > + <File + RelativePath=".\images\SkImageDecoder.cpp" + > + </File> + <File + RelativePath=".\images\SkImageRef.cpp" + > + </File> + <File + RelativePath=".\images\SkScaledBitmapSampler.h" + > + </File> + <File + RelativePath=".\images\SkStream.cpp" + > + </File> + </Filter> + <Filter + Name="effects" + > + <File + RelativePath=".\effects\Sk1DPathEffect.cpp" + > + </File> + <File + RelativePath=".\effects\Sk2DPathEffect.cpp" + > + </File> + <File + RelativePath=".\effects\SkAvoidXfermode.cpp" + > + </File> + <File + RelativePath=".\effects\SkBlurDrawLooper.cpp" + > + </File> + <File + RelativePath=".\effects\SkBlurMask.cpp" + > + </File> + <File + RelativePath=".\effects\SkBlurMaskFilter.cpp" + > + </File> + <File + RelativePath=".\effects\SkCamera.cpp" + > + </File> + <File + RelativePath=".\effects\SkColorFilters.cpp" + > + </File> + <File + RelativePath=".\effects\SkColorMatrix.cpp" + > + </File> + <File + RelativePath=".\effects\SkColorMatrixFilter.cpp" + > + </File> + <File + RelativePath=".\effects\SkCornerPathEffect.cpp" + > + </File> + <File + RelativePath=".\effects\SkCullPoints.cpp" + > + </File> + <File + RelativePath=".\effects\SkDashPathEffect.cpp" + > + </File> + <File + RelativePath=".\effects\SkDiscretePathEffect.cpp" + > + </File> + <File + RelativePath=".\effects\SkEmbossMask.cpp" + > + </File> + <File + RelativePath=".\effects\SkEmbossMaskFilter.cpp" + > + </File> + <File + RelativePath=".\effects\SkGradientShader.cpp" + > + </File> + <File + RelativePath=".\effects\SkKernel33MaskFilter.cpp" + > + </File> + <File + RelativePath=".\effects\SkLayerRasterizer.cpp" + > + </File> + <File + RelativePath=".\effects\SkPaintFlagsDrawFilter.cpp" + > + </File> + <File + RelativePath=".\effects\SkPixelXorXfermode.cpp" + > + </File> + <File + RelativePath=".\effects\SkRadialGradient_Table.h" + > + </File> + <File + RelativePath=".\effects\SkShaderExtras.cpp" + > + </File> + <File + RelativePath=".\effects\SkTransparentShader.cpp" + > + </File> + <File + RelativePath=".\effects\SkUnitMappers.cpp" + > + </File> + </Filter> + <Filter + Name="animator" + > + <File + RelativePath=".\animator\SkTime.cpp" + > + </File> + </Filter> + <Filter + Name="picture" + > + <File + RelativePath=".\picture\SkPictureFlat.cpp" + > + </File> + <File + RelativePath=".\picture\SkPictureFlat.h" + > + </File> + <File + RelativePath=".\picture\SkPicturePlayback.cpp" + > + </File> + <File + RelativePath=".\picture\SkPicturePlayback.h" + > + </File> + <File + RelativePath=".\picture\SkPictureRecord.cpp" + > + </File> + <File + RelativePath=".\picture\SkPictureRecord.h" + > + </File> + </Filter> + <File + RelativePath=".\precompiled.cc" + > + <FileConfiguration + Name="Debug|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="1" + /> + </FileConfiguration> + <FileConfiguration + Name="Release|Win32" + ExcludedFromBuild="true" + > + <Tool + Name="VCCLCompilerTool" + /> + </FileConfiguration> + </File> + </Files> + <Globals> + </Globals> +</VisualStudioProject> diff --git a/skia/skia.vsprops b/skia/skia.vsprops new file mode 100644 index 0000000..bfc11b1 --- /dev/null +++ b/skia/skia.vsprops @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="Windows-1252"?> +<VisualStudioPropertySheet + ProjectType="Visual C++" + Version="8.00" + Name="skia" + InheritedPropertySheets="$(SolutionDir)..\build\common.vsprops;$(SolutionDir)..\build\external_code.vsprops" + > + <Tool + Name="VCCLCompilerTool" + AdditionalIncludeDirectories="include;include\corecg;corecg;sgl;picture" + PreprocessorDefinitions="SKIA_DISABLE_SUPPORT_FOR_DECODERS" + DisableSpecificWarnings="4244; 4267; 4345; 4390; 4554; 4800" + /> +</VisualStudioPropertySheet> diff --git a/skia/svg/SkSVG.cpp b/skia/svg/SkSVG.cpp new file mode 100644 index 0000000..7c39044 --- /dev/null +++ b/skia/svg/SkSVG.cpp @@ -0,0 +1,36 @@ +/* libs/graphics/svg/SkSVG.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkSVG.h" +#include 'SkSVGParser.h" + +SkSVG::SkSVG() { +} + +SkSVG::~SkSVG() { +} + +bool SkSVG::decodeStream(SkStream* stream); +{ + size_t size = stream->read(nil, 0); + SkAutoMalloc storage(size); + char* data = (char*)storage.get(); + size_t actual = stream->read(data, size); + SkASSERT(size == actual); + SkSVGParser parser(*fMaker); + return parser.parse(data, actual, &fErrorCode, &fErrorLineNumber); +} diff --git a/skia/svg/SkSVGCircle.cpp b/skia/svg/SkSVGCircle.cpp new file mode 100644 index 0000000..b72d46c --- /dev/null +++ b/skia/svg/SkSVGCircle.cpp @@ -0,0 +1,53 @@ +/* libs/graphics/svg/SkSVGCircle.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkSVGCircle.h" +#include "SkSVGParser.h" +#include "SkParse.h" +#include <stdio.h> + +const SkSVGAttribute SkSVGCircle::gAttributes[] = { + SVG_ATTRIBUTE(cx), + SVG_ATTRIBUTE(cy), + SVG_ATTRIBUTE(r) +}; + +DEFINE_SVG_INFO(Circle) + +void SkSVGCircle::translate(SkSVGParser& parser, bool defState) { + parser._startElement("oval"); + INHERITED::translate(parser, defState); + SkScalar cx, cy, r; + SkParse::FindScalar(f_cx.c_str(), &cx); + SkParse::FindScalar(f_cy.c_str(), &cy); + SkParse::FindScalar(f_r.c_str(), &r); + SkScalar left, top, right, bottom; + left = cx - r; + top = cy - r; + right = cx + r; + bottom = cy + r; + char scratch[16]; + sprintf(scratch, "%g", left); + parser._addAttribute("left", scratch); + sprintf(scratch, "%g", top); + parser._addAttribute("top", scratch); + sprintf(scratch, "%g", right); + parser._addAttribute("right", scratch); + sprintf(scratch, "%g", bottom); + parser._addAttribute("bottom", scratch); + parser._endElement(); +} diff --git a/skia/svg/SkSVGCircle.h b/skia/svg/SkSVGCircle.h new file mode 100644 index 0000000..9124c1c --- /dev/null +++ b/skia/svg/SkSVGCircle.h @@ -0,0 +1,32 @@ +/* libs/graphics/svg/SkSVGCircle.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkSVGCircle_DEFINED +#define SkSVGCircle_DEFINED + +#include "SkSVGElements.h" + +class SkSVGCircle : public SkSVGElement { + DECLARE_SVG_INFO(Circle); +private: + SkString f_cx; + SkString f_cy; + SkString f_r; + typedef SkSVGElement INHERITED; +}; + +#endif // SkSVGCircle_DEFINED diff --git a/skia/svg/SkSVGClipPath.cpp b/skia/svg/SkSVGClipPath.cpp new file mode 100644 index 0000000..586571a --- /dev/null +++ b/skia/svg/SkSVGClipPath.cpp @@ -0,0 +1,48 @@ +/* libs/graphics/svg/SkSVGClipPath.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkSVGClipPath.h" +#include "SkSVGParser.h" +#include "SkSVGUse.h" + +DEFINE_SVG_NO_INFO(ClipPath) + +bool SkSVGClipPath::isDef() { + return true; +} + +bool SkSVGClipPath::isNotDef() { + return false; +} + +void SkSVGClipPath::translate(SkSVGParser& parser, bool defState) { + parser._startElement("clip"); + INHERITED::translate(parser, defState); + SkASSERT(fChildren.count() == 1); + SkSVGElement* child = *fChildren.begin(); + SkASSERT(child->getType() == SkSVGType_Use); + SkSVGUse* use = (SkSVGUse*) child; + SkSVGElement* ref; + const char* refStr = &use->f_xlink_href.c_str()[1]; + SkASSERT(parser.getIDs().find(refStr, &ref)); + SkASSERT(ref); + if (ref->getType() == SkSVGType_Rect) + parser._addAttribute("rectangle", refStr); + else + parser._addAttribute("path", refStr); + parser._endElement(); +} diff --git a/skia/svg/SkSVGClipPath.h b/skia/svg/SkSVGClipPath.h new file mode 100644 index 0000000..4fc7da6 --- /dev/null +++ b/skia/svg/SkSVGClipPath.h @@ -0,0 +1,31 @@ +/* libs/graphics/svg/SkSVGClipPath.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkSVGClipPath_DEFINED +#define SkSVGClipPath_DEFINED + +#include "SkSVGElements.h" + +class SkSVGClipPath : public SkSVGElement { + DECLARE_SVG_INFO(ClipPath); + virtual bool isDef(); + virtual bool isNotDef(); +private: + typedef SkSVGElement INHERITED; +}; + +#endif // SkSVGClipPath_DEFINED diff --git a/skia/svg/SkSVGDefs.cpp b/skia/svg/SkSVGDefs.cpp new file mode 100644 index 0000000..ae1da54 --- /dev/null +++ b/skia/svg/SkSVGDefs.cpp @@ -0,0 +1,32 @@ +/* libs/graphics/svg/SkSVGDefs.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkSVGDefs.h" + +DEFINE_SVG_NO_INFO(Defs) + +bool SkSVGDefs::isDef() { + return true; +} + +bool SkSVGDefs::isNotDef() { + return false; +} + +void SkSVGDefs::translate(SkSVGParser& parser, bool defState) { + INHERITED::translate(parser, defState); +} diff --git a/skia/svg/SkSVGDefs.h b/skia/svg/SkSVGDefs.h new file mode 100644 index 0000000..b4f024c --- /dev/null +++ b/skia/svg/SkSVGDefs.h @@ -0,0 +1,31 @@ +/* libs/graphics/svg/SkSVGDefs.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkSVGDefs_DEFINED +#define SkSVGDefs_DEFINED + +#include "SkSVGGroup.h" + +class SkSVGDefs : public SkSVGGroup { + DECLARE_SVG_INFO(Defs); + virtual bool isDef(); + virtual bool isNotDef(); +private: + typedef SkSVGGroup INHERITED; +}; + +#endif // SkSVGDefs_DEFINED diff --git a/skia/svg/SkSVGElements.cpp b/skia/svg/SkSVGElements.cpp new file mode 100644 index 0000000..52a2dc1 --- /dev/null +++ b/skia/svg/SkSVGElements.cpp @@ -0,0 +1,96 @@ +/* libs/graphics/svg/SkSVGElements.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkSVGElements.h" +#include "SkSVGParser.h" + +SkSVGBase::~SkSVGBase() { +} + +void SkSVGBase::addAttribute(SkSVGParser& parser, int attrIndex, + const char* attrValue, size_t attrLength) { + SkString* first = (SkString*) ((char*) this + sizeof(SkSVGElement)); + first += attrIndex; + first->set(attrValue, attrLength); +} + + +SkSVGElement::SkSVGElement() : fParent(NULL), fIsDef(false), fIsNotDef(true) { +} + +SkSVGElement::~SkSVGElement() { +} + +SkSVGElement* SkSVGElement::getGradient() { + return NULL; +} + +bool SkSVGElement::isGroupParent() { + SkSVGElement* parent = fParent; + while (parent) { + if (parent->getType() != SkSVGType_G) + return false; + parent = parent->fParent; + } + return true; +} + +bool SkSVGElement::isDef() { + return isGroupParent() == false ? fParent->isDef() : fIsDef; +} + +bool SkSVGElement::isFlushable() { + return true; +} + +bool SkSVGElement::isGroup() { + return false; +} + +bool SkSVGElement::isNotDef() { + return isGroupParent() == false ? fParent->isNotDef() : fIsNotDef; +} + +bool SkSVGElement::onEndElement(SkSVGParser& parser) { + if (f_id.size() > 0) + parser.getIDs().set(f_id.c_str(), f_id.size(), this); + return false; +} + +bool SkSVGElement::onStartElement(SkSVGElement* child) { + *fChildren.append() = child; + return false; +} + +void SkSVGElement::translate(SkSVGParser& parser, bool) { + if (f_id.size() > 0) + SVG_ADD_ATTRIBUTE(id); +} + +void SkSVGElement::setIsDef() { + fIsDef = isDef(); +} + +//void SkSVGElement::setIsNotDef() { +// fIsNotDef = isNotDef(); +//} + +void SkSVGElement::write(SkSVGParser& , SkString& ) { + SkASSERT(0); +} + + diff --git a/skia/svg/SkSVGElements.h b/skia/svg/SkSVGElements.h new file mode 100644 index 0000000..743d07f --- /dev/null +++ b/skia/svg/SkSVGElements.h @@ -0,0 +1,81 @@ +/* libs/graphics/svg/SkSVGElements.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkSVGElements_DEFINED +#define SkSVGElements_DEFINED + +#include "SkSVGPaintState.h" +#include "SkSVGTypes.h" +#include "SkTDArray.h" + +class SkSVGParser; + +#define DECLARE_SVG_INFO(_type) \ +public: \ + virtual ~SkSVG##_type(); \ + static const SkSVGAttribute gAttributes[]; \ + virtual int getAttributes(const SkSVGAttribute** attrPtr); \ + virtual SkSVGTypes getType() const; \ + virtual void translate(SkSVGParser& parser, bool defState); \ + typedef SkSVG##_type BASE_CLASS + +#define DEFINE_SVG_INFO(_type) \ + SkSVG##_type::~SkSVG##_type() {} \ + int SkSVG##_type::getAttributes(const SkSVGAttribute** attrPtr) { \ + *attrPtr = gAttributes; \ + return SK_ARRAY_COUNT(gAttributes); \ + } \ + SkSVGTypes SkSVG##_type::getType() const { return SkSVGType_##_type; } + +#define DEFINE_SVG_NO_INFO(_type) \ + SkSVG##_type::~SkSVG##_type() {} \ + int SkSVG##_type::getAttributes(const SkSVGAttribute** ) { return 0; } \ + SkSVGTypes SkSVG##_type::getType() const { return SkSVGType_##_type; } + + +struct SkSVGTypeName { + const char* fName; + SkSVGTypes fType; +}; + +class SkSVGElement : public SkSVGBase { +public: + SkSVGElement(); + virtual ~SkSVGElement(); + virtual SkSVGElement* getGradient(); + virtual SkSVGTypes getType() const = 0; + virtual bool isDef(); + virtual bool isFlushable(); + virtual bool isGroup(); + virtual bool isNotDef(); + virtual bool onEndElement(SkSVGParser& parser); + virtual bool onStartElement(SkSVGElement* child); + void setIsDef(); +// void setIsNotDef(); + virtual void translate(SkSVGParser& parser, bool defState); + virtual void write(SkSVGParser& , SkString& color); + SkString f_id; + SkSVGPaint fPaintState; + SkTDArray<SkSVGElement*> fChildren; + SkSVGElement* fParent; + bool fIsDef; + bool fIsNotDef; +private: + bool isGroupParent(); +}; + +#endif // SkSVGElements_DEFINED diff --git a/skia/svg/SkSVGEllipse.cpp b/skia/svg/SkSVGEllipse.cpp new file mode 100644 index 0000000..f795d21 --- /dev/null +++ b/skia/svg/SkSVGEllipse.cpp @@ -0,0 +1,55 @@ +/* libs/graphics/svg/SkSVGEllipse.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkSVGEllipse.h" +#include "SkSVGParser.h" +#include "SkParse.h" +#include <stdio.h> + +const SkSVGAttribute SkSVGEllipse::gAttributes[] = { + SVG_ATTRIBUTE(cx), + SVG_ATTRIBUTE(cy), + SVG_ATTRIBUTE(rx), + SVG_ATTRIBUTE(ry) +}; + +DEFINE_SVG_INFO(Ellipse) + +void SkSVGEllipse::translate(SkSVGParser& parser, bool defState) { + parser._startElement("oval"); + INHERITED::translate(parser, defState); + SkScalar cx, cy, rx, ry; + SkParse::FindScalar(f_cx.c_str(), &cx); + SkParse::FindScalar(f_cy.c_str(), &cy); + SkParse::FindScalar(f_rx.c_str(), &rx); + SkParse::FindScalar(f_ry.c_str(), &ry); + SkScalar left, top, right, bottom; + left = cx - rx; + top = cy - ry; + right = cx + rx; + bottom = cy + ry; + char scratch[16]; + sprintf(scratch, "%g", left); + parser._addAttribute("left", scratch); + sprintf(scratch, "%g", top); + parser._addAttribute("top", scratch); + sprintf(scratch, "%g", right); + parser._addAttribute("right", scratch); + sprintf(scratch, "%g", bottom); + parser._addAttribute("bottom", scratch); + parser._endElement(); +} diff --git a/skia/svg/SkSVGEllipse.h b/skia/svg/SkSVGEllipse.h new file mode 100644 index 0000000..ea5d291 --- /dev/null +++ b/skia/svg/SkSVGEllipse.h @@ -0,0 +1,33 @@ +/* libs/graphics/svg/SkSVGEllipse.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkSVGEllipse_DEFINED +#define SkSVGEllipse_DEFINED + +#include "SkSVGElements.h" + +class SkSVGEllipse : public SkSVGElement { + DECLARE_SVG_INFO(Ellipse); +private: + SkString f_cx; + SkString f_cy; + SkString f_rx; + SkString f_ry; + typedef SkSVGElement INHERITED; +}; + +#endif // SkSVGEllipse_DEFINED diff --git a/skia/svg/SkSVGFeColorMatrix.cpp b/skia/svg/SkSVGFeColorMatrix.cpp new file mode 100644 index 0000000..f55985a --- /dev/null +++ b/skia/svg/SkSVGFeColorMatrix.cpp @@ -0,0 +1,32 @@ +/* libs/graphics/svg/SkSVGFeColorMatrix.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkSVGFeColorMatrix.h" +#include "SkSVGParser.h" + +const SkSVGAttribute SkSVGFeColorMatrix::gAttributes[] = { + SVG_LITERAL_ATTRIBUTE(color-interpolation-filters, f_color_interpolation_filters), + SVG_ATTRIBUTE(result), + SVG_ATTRIBUTE(type), + SVG_ATTRIBUTE(values) +}; + +DEFINE_SVG_INFO(FeColorMatrix) + +void SkSVGFeColorMatrix::translate(SkSVGParser& parser, bool defState) { + INHERITED::translate(parser, defState); +} diff --git a/skia/svg/SkSVGFeColorMatrix.h b/skia/svg/SkSVGFeColorMatrix.h new file mode 100644 index 0000000..bb79ed9 --- /dev/null +++ b/skia/svg/SkSVGFeColorMatrix.h @@ -0,0 +1,34 @@ +/* libs/graphics/svg/SkSVGFeColorMatrix.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkSVGFeColorMatrix_DEFINED +#define SkSVGFeColorMatrix_DEFINED + +#include "SkSVGElements.h" + +class SkSVGFeColorMatrix : public SkSVGElement { + DECLARE_SVG_INFO(FeColorMatrix); +protected: + SkString f_color_interpolation_filters; + SkString f_result; + SkString f_type; + SkString f_values; +private: + typedef SkSVGElement INHERITED; +}; + +#endif // SkSVGFeColorMatrix_DEFINED diff --git a/skia/svg/SkSVGFilter.cpp b/skia/svg/SkSVGFilter.cpp new file mode 100644 index 0000000..9d97f69 --- /dev/null +++ b/skia/svg/SkSVGFilter.cpp @@ -0,0 +1,33 @@ +/* libs/graphics/svg/SkSVGFilter.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkSVGFilter.h" +#include "SkSVGParser.h" + +const SkSVGAttribute SkSVGFilter::gAttributes[] = { + SVG_ATTRIBUTE(filterUnits), + SVG_ATTRIBUTE(height), + SVG_ATTRIBUTE(width), + SVG_ATTRIBUTE(x), + SVG_ATTRIBUTE(y) +}; + +DEFINE_SVG_INFO(Filter) + +void SkSVGFilter::translate(SkSVGParser& parser, bool defState) { +// INHERITED::translate(parser, defState); +} diff --git a/skia/svg/SkSVGFilter.h b/skia/svg/SkSVGFilter.h new file mode 100644 index 0000000..5323e1b --- /dev/null +++ b/skia/svg/SkSVGFilter.h @@ -0,0 +1,36 @@ +/* libs/graphics/svg/SkSVGFilter.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkSVGFilter_DEFINED +#define SkSVGFilter_DEFINED + +#include "SkSVGElements.h" + +class SkSVGFilter : public SkSVGElement { + DECLARE_SVG_INFO(Filter); +protected: + SkString f_filterUnits; + SkString f_height; + SkString f_width; + SkString f_x; + SkString f_y; +private: + typedef SkSVGElement INHERITED; +}; + +#endif // SkSVGFilter_DEFINEDRITED; + diff --git a/skia/svg/SkSVGG.cpp b/skia/svg/SkSVGG.cpp new file mode 100644 index 0000000..2e2c876 --- /dev/null +++ b/skia/svg/SkSVGG.cpp @@ -0,0 +1,24 @@ +/* libs/graphics/svg/SkSVGG.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkSVGG.h" + +DEFINE_SVG_NO_INFO(G) + +void SkSVGG::translate(SkSVGParser& parser, bool defState) { + INHERITED::translate(parser, defState); +} diff --git a/skia/svg/SkSVGG.h b/skia/svg/SkSVGG.h new file mode 100644 index 0000000..4200c2c --- /dev/null +++ b/skia/svg/SkSVGG.h @@ -0,0 +1,29 @@ +/* libs/graphics/svg/SkSVGG.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkSVGG_DEFINED +#define SkSVGG_DEFINED + +#include "SkSVGGroup.h" + +class SkSVGG : public SkSVGGroup { + DECLARE_SVG_INFO(G); +private: + typedef SkSVGGroup INHERITED; +}; + +#endif // SkSVGG_DEFINED diff --git a/skia/svg/SkSVGGradient.cpp b/skia/svg/SkSVGGradient.cpp new file mode 100644 index 0000000..d1fdc22 --- /dev/null +++ b/skia/svg/SkSVGGradient.cpp @@ -0,0 +1,123 @@ +/* libs/graphics/svg/SkSVGGradient.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkSVGGradient.h" +#include "SkSVGParser.h" +#include "SkSVGStop.h" + +SkSVGGradient::SkSVGGradient() { +} + +SkSVGElement* SkSVGGradient::getGradient() { + return this; +} + +bool SkSVGGradient::isDef() { + return true; +} + +bool SkSVGGradient::isNotDef() { + return false; +} + +void SkSVGGradient::translate(SkSVGParser& parser, bool defState) { + INHERITED::translate(parser, defState); + // !!! no support for 'objectBoundingBox' yet + bool first = true; + bool addedFirst = false; + bool addedLast = false; + SkString offsets("["); + SkString* lastOffset = NULL; + for (SkSVGElement** ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) { + SkASSERT((*ptr)->getType() == SkSVGType_Stop); + SkSVGStop* stop = (SkSVGStop*) *ptr; + if (first && stop->f_offset.equals("0") == false) { + addedFirst = true; + offsets.append("0,"); + } + SkString* thisOffset = &stop->f_offset; + if (lastOffset && thisOffset->equals(*lastOffset)) { + if (thisOffset->equals("1")) { + offsets.remove(offsets.size() - 2, 2); + offsets.append(".999,"); + } else { + SkASSERT(0); // !!! need to write this case + } + } + offsets.append(*thisOffset); + if (ptr == fChildren.end() - 1) { // last + if (stop->f_offset.equals("1") == false) { + offsets.append(",1"); + addedLast = true; + } + } else + offsets.appendUnichar(','); + first = false; + lastOffset = thisOffset; + } + offsets.appendUnichar(']'); + parser._addAttribute("offsets", offsets); + if (addedFirst) + parser.translate(*fChildren.begin(), defState); + for (SkSVGElement** ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) + parser.translate(*ptr, defState); + if (addedLast) + parser.translate(*(fChildren.end() - 1), defState); +} + +void SkSVGGradient::translateGradientUnits(SkString& units) { + // !!! no support for 'objectBoundingBox' yet + SkASSERT(strcmp(units.c_str(), "userSpaceOnUse") == 0); +} + +void SkSVGGradient::write(SkSVGParser& parser, SkString& baseColor) { + if (baseColor.c_str()[0] != '#') + return; + SkSVGPaint* saveHead = parser.fHead; + parser.fHead = &fPaintState; + parser.fSuppressPaint = true; + SkString originalID(f_id); + f_id.set("mask"); // write out gradient named given name + color (less initial #) + f_id.append(baseColor.c_str() + 1); + SkString originalColors; + for (SkSVGElement** ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) { + SkSVGStop* colorElement = (SkSVGStop*) *ptr; + SkString& color = colorElement->fPaintState.f_stopColor; + originalColors.append(color); + originalColors.appendUnichar(','); + SkASSERT(color.c_str()[0] == '#'); + SkString replacement; + replacement.set("0x"); + replacement.append(color.c_str() + 1, 2); // add stop colors using given color, turning existing stop color into alpha + SkASSERT(baseColor.c_str()[0] == '#'); + SkASSERT(baseColor.size() == 7); + replacement.append(baseColor.c_str() + 1); + color.set(replacement); + } + translate(parser, true); + const char* originalPtr = originalColors.c_str(); // restore original gradient values + for (SkSVGElement** ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) { + SkSVGStop* color = (SkSVGStop*) *ptr; + const char* originalEnd = strchr(originalPtr, ','); + color->fPaintState.f_stopColor.set(originalPtr, originalEnd - originalPtr); + originalPtr = originalEnd + 1; + } + f_id.set(originalID); + parser.fSuppressPaint = false; + parser.fHead = saveHead; +} + diff --git a/skia/svg/SkSVGGradient.h b/skia/svg/SkSVGGradient.h new file mode 100644 index 0000000..1f91b1c --- /dev/null +++ b/skia/svg/SkSVGGradient.h @@ -0,0 +1,37 @@ +/* libs/graphics/svg/SkSVGGradient.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkSVGGradient_DEFINED +#define SkSVGGradient_DEFINED + +#include "SkSVGElements.h" + +class SkSVGGradient : public SkSVGElement { +public: + SkSVGGradient(); + virtual SkSVGElement* getGradient(); + virtual bool isDef(); + virtual bool isNotDef(); + virtual void write(SkSVGParser& , SkString& color); +protected: + void translate(SkSVGParser& , bool defState); + void translateGradientUnits(SkString& units); +private: + typedef SkSVGElement INHERITED; +}; + +#endif // SkSVGGradient_DEFINED diff --git a/skia/svg/SkSVGGroup.cpp b/skia/svg/SkSVGGroup.cpp new file mode 100644 index 0000000..6d7e438 --- /dev/null +++ b/skia/svg/SkSVGGroup.cpp @@ -0,0 +1,53 @@ +/* libs/graphics/svg/SkSVGGroup.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkSVGGroup.h" +#include "SkSVGParser.h" + +SkSVGGroup::SkSVGGroup() { + fIsNotDef = false; +} + +SkSVGElement* SkSVGGroup::getGradient() { + for (SkSVGElement** ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) { + SkSVGElement* result = (*ptr)->getGradient(); + if (result != NULL) + return result; + } + return NULL; +} + +bool SkSVGGroup::isDef() { + return fParent ? fParent->isDef() : false; +} + +bool SkSVGGroup::isFlushable() { + return false; +} + +bool SkSVGGroup::isGroup() { + return true; +} + +bool SkSVGGroup::isNotDef() { + return fParent ? fParent->isNotDef() : false; +} + +void SkSVGGroup::translate(SkSVGParser& parser, bool defState) { + for (SkSVGElement** ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) + parser.translate(*ptr, defState); +} diff --git a/skia/svg/SkSVGGroup.h b/skia/svg/SkSVGGroup.h new file mode 100644 index 0000000..31fff46 --- /dev/null +++ b/skia/svg/SkSVGGroup.h @@ -0,0 +1,36 @@ +/* libs/graphics/svg/SkSVGGroup.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkSVGGroup_DEFINED +#define SkSVGGroup_DEFINED + +#include "SkSVGElements.h" + +class SkSVGGroup : public SkSVGElement { +public: + SkSVGGroup(); + virtual SkSVGElement* getGradient(); + virtual bool isDef(); + virtual bool isFlushable(); + virtual bool isGroup(); + virtual bool isNotDef(); + void translate(SkSVGParser& , bool defState); +private: + typedef SkSVGElement INHERITED; +}; + +#endif // SkSVGGroup_DEFINED diff --git a/skia/svg/SkSVGImage.cpp b/skia/svg/SkSVGImage.cpp new file mode 100644 index 0000000..17df84d --- /dev/null +++ b/skia/svg/SkSVGImage.cpp @@ -0,0 +1,52 @@ +/* libs/graphics/svg/SkSVGImage.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkSVGImage.h" +#include "SkSVGParser.h" + +const SkSVGAttribute SkSVGImage::gAttributes[] = { + SVG_ATTRIBUTE(height), + SVG_ATTRIBUTE(width), + SVG_ATTRIBUTE(x), + SVG_LITERAL_ATTRIBUTE(xlink:href, f_xlink_href), + SVG_ATTRIBUTE(y) +}; + +DEFINE_SVG_INFO(Image) + +void SkSVGImage::translate(SkSVGParser& parser, bool defState) { + parser._startElement("image"); + INHERITED::translate(parser, defState); + SVG_ADD_ATTRIBUTE(x); + SVG_ADD_ATTRIBUTE(y); +// SVG_ADD_ATTRIBUTE(width); +// SVG_ADD_ATTRIBUTE(height); + translateImage(parser); + parser._endElement(); +} + +void SkSVGImage::translateImage(SkSVGParser& parser) { + SkASSERT(f_xlink_href.size() > 0); + const char* data = f_xlink_href.c_str(); + SkASSERT(strncmp(data, "data:image/", 11) == 0); + data += 11; + SkASSERT(strncmp(data, "png;", 4) == 0 || strncmp(data, "jpeg;", 5) == 0); + data = strchr(data, ';'); + SkASSERT(strncmp(data, ";base64,", 8) == 0); + data += 8; + parser._addAttribute("base64", data); +} diff --git a/skia/svg/SkSVGImage.h b/skia/svg/SkSVGImage.h new file mode 100644 index 0000000..9b21f69 --- /dev/null +++ b/skia/svg/SkSVGImage.h @@ -0,0 +1,36 @@ +/* libs/graphics/svg/SkSVGImage.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkSVGImage_DEFINED +#define SkSVGImage_DEFINED + +#include "SkSVGElements.h" + +class SkSVGImage : public SkSVGElement { +public: + DECLARE_SVG_INFO(Image); +private: + void translateImage(SkSVGParser& parser); + SkString f_height; + SkString f_width; + SkString f_x; + SkString f_xlink_href; + SkString f_y; + typedef SkSVGElement INHERITED; +}; + +#endif // SkSVGImage_DEFINED diff --git a/skia/svg/SkSVGLine.cpp b/skia/svg/SkSVGLine.cpp new file mode 100644 index 0000000..23cc46f --- /dev/null +++ b/skia/svg/SkSVGLine.cpp @@ -0,0 +1,38 @@ +/* libs/graphics/svg/SkSVGLine.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkSVGLine.h" +#include "SkSVGParser.h" + +const SkSVGAttribute SkSVGLine::gAttributes[] = { + SVG_ATTRIBUTE(x1), + SVG_ATTRIBUTE(x2), + SVG_ATTRIBUTE(y1), + SVG_ATTRIBUTE(y2) +}; + +DEFINE_SVG_INFO(Line) + +void SkSVGLine::translate(SkSVGParser& parser, bool defState) { + parser._startElement("line"); + INHERITED::translate(parser, defState); + SVG_ADD_ATTRIBUTE(x1); + SVG_ADD_ATTRIBUTE(y1); + SVG_ADD_ATTRIBUTE(x2); + SVG_ADD_ATTRIBUTE(y2); + parser._endElement(); +} diff --git a/skia/svg/SkSVGLine.h b/skia/svg/SkSVGLine.h new file mode 100644 index 0000000..6d8b733 --- /dev/null +++ b/skia/svg/SkSVGLine.h @@ -0,0 +1,33 @@ +/* libs/graphics/svg/SkSVGLine.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkSVGLine_DEFINED +#define SkSVGLine_DEFINED + +#include "SkSVGElements.h" + +class SkSVGLine : public SkSVGElement { + DECLARE_SVG_INFO(Line); +private: + SkString f_x1; + SkString f_x2; + SkString f_y1; + SkString f_y2; + typedef SkSVGElement INHERITED; +}; + +#endif // SkSVGLine_DEFINED diff --git a/skia/svg/SkSVGLinearGradient.cpp b/skia/svg/SkSVGLinearGradient.cpp new file mode 100644 index 0000000..d950cae --- /dev/null +++ b/skia/svg/SkSVGLinearGradient.cpp @@ -0,0 +1,52 @@ +/* libs/graphics/svg/SkSVGLinearGradient.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkSVGLinearGradient.h" +#include "SkSVGParser.h" + +const SkSVGAttribute SkSVGLinearGradient::gAttributes[] = { + SVG_ATTRIBUTE(gradientTransform), + SVG_ATTRIBUTE(gradientUnits), + SVG_ATTRIBUTE(x1), + SVG_ATTRIBUTE(x2), + SVG_ATTRIBUTE(y1), + SVG_ATTRIBUTE(y2) +}; + +DEFINE_SVG_INFO(LinearGradient) + +void SkSVGLinearGradient::translate(SkSVGParser& parser, bool defState) { + if (fMatrixID.size() == 0) + parser.translateMatrix(f_gradientTransform, &fMatrixID); + parser._startElement("linearGradient"); + if (fMatrixID.size() > 0) + parser._addAttribute("matrix", fMatrixID); + INHERITED::translateGradientUnits(f_gradientUnits); + SkString points; + points.appendUnichar('['); + points.append(f_x1); + points.appendUnichar(','); + points.append(f_y1); + points.appendUnichar(','); + points.append(f_x2); + points.appendUnichar(','); + points.append(f_y2); + points.appendUnichar(']'); + parser._addAttribute("points", points.c_str()); + INHERITED::translate(parser, defState); + parser._endElement(); +} diff --git a/skia/svg/SkSVGLinearGradient.h b/skia/svg/SkSVGLinearGradient.h new file mode 100644 index 0000000..96301d2 --- /dev/null +++ b/skia/svg/SkSVGLinearGradient.h @@ -0,0 +1,36 @@ +/* libs/graphics/svg/SkSVGLinearGradient.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkSVGLinearGradient_DEFINED +#define SkSVGLinearGradient_DEFINED + +#include "SkSVGGradient.h" + +class SkSVGLinearGradient : public SkSVGGradient { + DECLARE_SVG_INFO(LinearGradient); +private: + SkString f_gradientTransform; + SkString f_gradientUnits; + SkString f_x1; + SkString f_x2; + SkString f_y1; + SkString f_y2; + SkString fMatrixID; + typedef SkSVGGradient INHERITED; +}; + +#endif // SkSVGLinearGradient_DEFINED diff --git a/skia/svg/SkSVGMask.cpp b/skia/svg/SkSVGMask.cpp new file mode 100644 index 0000000..dfc9ebf --- /dev/null +++ b/skia/svg/SkSVGMask.cpp @@ -0,0 +1,41 @@ +/* libs/graphics/svg/SkSVGMask.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkSVGMask.h" +#include "SkSVGParser.h" + +const SkSVGAttribute SkSVGMask::gAttributes[] = { + SVG_ATTRIBUTE(height), + SVG_ATTRIBUTE(maskUnits), + SVG_ATTRIBUTE(width), + SVG_ATTRIBUTE(x), + SVG_ATTRIBUTE(y) +}; + +DEFINE_SVG_INFO(Mask) + +bool SkSVGMask::isDef() { + return false; +} + +bool SkSVGMask::isNotDef() { + return false; +} + +void SkSVGMask::translate(SkSVGParser& parser, bool defState) { + INHERITED::translate(parser, defState); +} diff --git a/skia/svg/SkSVGMask.h b/skia/svg/SkSVGMask.h new file mode 100644 index 0000000..6a2f3c3 --- /dev/null +++ b/skia/svg/SkSVGMask.h @@ -0,0 +1,37 @@ +/* libs/graphics/svg/SkSVGMask.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkSVGMask_DEFINED +#define SkSVGMask_DEFINED + +#include "SkSVGGroup.h" + +class SkSVGMask : public SkSVGGroup { + DECLARE_SVG_INFO(Mask); + virtual bool isDef(); + virtual bool isNotDef(); +protected: + SkString f_height; + SkString f_maskUnits; + SkString f_width; + SkString f_x; + SkString f_y; +private: + typedef SkSVGGroup INHERITED; +}; + +#endif // SkSVGMask_DEFINED diff --git a/skia/svg/SkSVGMetadata.cpp b/skia/svg/SkSVGMetadata.cpp new file mode 100644 index 0000000..9c305e4 --- /dev/null +++ b/skia/svg/SkSVGMetadata.cpp @@ -0,0 +1,32 @@ +/* libs/graphics/svg/SkSVGMetadata.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkSVGMetadata.h" +#include "SkSVGParser.h" + +DEFINE_SVG_NO_INFO(Metadata) + +bool SkSVGMetadata::isDef() { + return false; +} + +bool SkSVGMetadata::isNotDef() { + return false; +} + +void SkSVGMetadata::translate(SkSVGParser& parser, bool defState) { +} diff --git a/skia/svg/SkSVGMetadata.h b/skia/svg/SkSVGMetadata.h new file mode 100644 index 0000000..8fb42a8 --- /dev/null +++ b/skia/svg/SkSVGMetadata.h @@ -0,0 +1,31 @@ +/* libs/graphics/svg/SkSVGMetadata.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkSVGMetadata_DEFINED +#define SkSVGMetadata_DEFINED + +#include "SkSVGElements.h" + +class SkSVGMetadata : public SkSVGElement { + DECLARE_SVG_INFO(Metadata); + virtual bool isDef(); + virtual bool isNotDef(); +private: + typedef SkSVGElement INHERITED; +}; + +#endif // SkSVGMetadata_DEFINED diff --git a/skia/svg/SkSVGPaintState.cpp b/skia/svg/SkSVGPaintState.cpp new file mode 100644 index 0000000..20fb5db --- /dev/null +++ b/skia/svg/SkSVGPaintState.cpp @@ -0,0 +1,463 @@ +/* libs/graphics/svg/SkSVGPaintState.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkSVGPaintState.h" +#include "SkSVGElements.h" +#include "SkSVGParser.h" +#include "SkParse.h" + +SkSVGAttribute SkSVGPaint::gAttributes[] = { + SVG_LITERAL_ATTRIBUTE(clip-path, f_clipPath), + SVG_LITERAL_ATTRIBUTE(clip-rule, f_clipRule), + SVG_LITERAL_ATTRIBUTE(enable-background, f_enableBackground), + SVG_ATTRIBUTE(fill), + SVG_LITERAL_ATTRIBUTE(fill-rule, f_fillRule), + SVG_ATTRIBUTE(filter), + SVG_LITERAL_ATTRIBUTE(font-family, f_fontFamily), + SVG_LITERAL_ATTRIBUTE(font-size, f_fontSize), + SVG_LITERAL_ATTRIBUTE(letter-spacing, f_letterSpacing), + SVG_ATTRIBUTE(mask), + SVG_ATTRIBUTE(opacity), + SVG_LITERAL_ATTRIBUTE(stop-color, f_stopColor), + SVG_LITERAL_ATTRIBUTE(stop-opacity, f_stopOpacity), + SVG_ATTRIBUTE(stroke), + SVG_LITERAL_ATTRIBUTE(stroke-dasharray, f_strokeDasharray), + SVG_LITERAL_ATTRIBUTE(stroke-linecap, f_strokeLinecap), + SVG_LITERAL_ATTRIBUTE(stroke-linejoin, f_strokeLinejoin), + SVG_LITERAL_ATTRIBUTE(stroke-miterlimit, f_strokeMiterlimit), + SVG_LITERAL_ATTRIBUTE(stroke-width, f_strokeWidth), + SVG_ATTRIBUTE(style), + SVG_ATTRIBUTE(transform) +}; + +const int SkSVGPaint::kAttributesSize = SK_ARRAY_COUNT(SkSVGPaint::gAttributes); + +SkSVGPaint::SkSVGPaint() : fNext(NULL) { +} + +SkString* SkSVGPaint::operator[](int index) { + SkASSERT(index >= 0); + SkASSERT(index < &fTerminal - &fInitial); + SkASSERT(&fTerminal - &fInitial == kTerminal - kInitial); + SkString* result = &fInitial + index + 1; + return result; +} + +void SkSVGPaint::addAttribute(SkSVGParser& parser, int attrIndex, + const char* attrValue, size_t attrLength) { + SkString* attr = (*this)[attrIndex]; + switch(attrIndex) { + case kClipPath: + case kClipRule: + case kEnableBackground: + case kFill: + case kFillRule: + case kFilter: + case kFontFamily: + case kFontSize: + case kLetterSpacing: + case kMask: + case kOpacity: + case kStopColor: + case kStopOpacity: + case kStroke: + case kStroke_Dasharray: + case kStroke_Linecap: + case kStroke_Linejoin: + case kStroke_Miterlimit: + case kStroke_Width: + case kTransform: + attr->set(attrValue, attrLength); + return; + case kStyle: { + // iterate through colon / semi-colon delimited pairs + int pairs = SkParse::Count(attrValue, ';'); + const char* attrEnd = attrValue + attrLength; + do { + const char* end = strchr(attrValue, ';'); + if (end == NULL) + end = attrEnd; + const char* delimiter = strchr(attrValue, ':'); + SkASSERT(delimiter != 0 && delimiter < end); + int index = parser.findAttribute(this, attrValue, (int) (delimiter - attrValue), true); + SkASSERT(index >= 0); + delimiter++; + addAttribute(parser, index, delimiter, (int) (end - delimiter)); + attrValue = end + 1; + } while (--pairs); + return; + } + default: + SkASSERT(0); + } +} + +bool SkSVGPaint::flush(SkSVGParser& parser, bool isFlushable, bool isDef) { + SkSVGPaint current; + SkSVGPaint* walking = parser.fHead; + int index; + while (walking != NULL) { + for (index = kInitial + 1; index < kTerminal; index++) { + SkString* lastAttr = (*walking)[index]; + if (lastAttr->size() == 0) + continue; + if (current[index]->size() > 0) + continue; + current[index]->set(*lastAttr); + } + walking = walking->fNext; + } + bool paintChanged = false; + SkSVGPaint& lastState = parser.fLastFlush; + if (isFlushable == false) { + if (isDef == true) { + if (current.f_mask.size() > 0 && current.f_mask.equals(lastState.f_mask) == false) { + SkSVGElement* found; + const char* idStart = strchr(current.f_mask.c_str(), '#'); + SkASSERT(idStart); + SkString id(idStart + 1, strlen(idStart) - 2); + bool itsFound = parser.fIDs.find(id.c_str(), &found); + SkASSERT(itsFound); + SkSVGElement* gradient = found->getGradient(); + if (gradient) { + gradient->write(parser, current.f_fill); + gradient->write(parser, current.f_stroke); + } + } + } + goto setLast; + } + { + bool changed[kTerminal]; + memset(changed, 0, sizeof(changed)); + for (index = kInitial + 1; index < kTerminal; index++) { + if (index == kTransform || index == kClipPath || index == kStopColor || index == kStopOpacity || + index == kClipRule || index == kFillRule) + continue; + SkString* lastAttr = lastState[index]; + SkString* currentAttr = current[index]; + paintChanged |= changed[index] = lastAttr->equals(*currentAttr) == false; + } + if (paintChanged) { + if (current.f_mask.size() > 0) { + if (current.f_fill.equals("none") == false && strncmp(current.f_fill.c_str(), "url(#", 5) != 0) { + SkASSERT(current.f_fill.c_str()[0] == '#'); + SkString replacement("url(#mask"); + replacement.append(current.f_fill.c_str() + 1); + replacement.appendUnichar(')'); + current.f_fill.set(replacement); + } + if (current.f_stroke.equals("none") == false && strncmp(current.f_stroke.c_str(), "url(#", 5) != 0) { + SkASSERT(current.f_stroke.c_str()[0] == '#'); + SkString replacement("url(#mask"); + replacement.append(current.f_stroke.c_str() + 1); + replacement.appendUnichar(')'); + current.f_stroke.set(replacement); + } + } + if (current.f_fill.equals("none") && current.f_stroke.equals("none")) + current.f_opacity.set("0"); + if (parser.fSuppressPaint == false) { + parser._startElement("paint"); + bool success = writeChangedAttributes(parser, current, changed); + if (success == false) + return paintChanged; + success = writeChangedElements(parser, current, changed); + if (success == false) + return paintChanged; + parser._endElement(); // paint + } + } + } +setLast: + for (index = kInitial + 1; index < kTerminal; index++) { + SkString* lastAttr = lastState[index]; + SkString* currentAttr = current[index]; + lastAttr->set(*currentAttr); + } + return paintChanged; +} + +int SkSVGPaint::getAttributes(const SkSVGAttribute** attrPtr) { + *attrPtr = gAttributes; + return kAttributesSize; +} + +void SkSVGPaint::setSave(SkSVGParser& parser) { + SkTDArray<SkString*> clips; + SkSVGPaint* walking = parser.fHead; + int index; + SkMatrix sum; + sum.reset(); + while (walking != NULL) { + for (index = kInitial + 1; index < kTerminal; index++) { + SkString* lastAttr = (*walking)[index]; + if (lastAttr->size() == 0) + continue; + if (index == kTransform) { + const char* str = lastAttr->c_str(); + SkASSERT(strncmp(str, "matrix(", 7) == 0); + str += 6; + const char* strEnd = strrchr(str, ')'); + SkASSERT(strEnd != NULL); + SkString mat(str, strEnd - str); + SkSVGParser::ConvertToArray(mat); + SkScalar values[6]; + SkParse::FindScalars(mat.c_str() + 1, values, 6); + SkMatrix matrix; + matrix.reset(); + matrix.setScaleX(values[0]); + matrix.setSkewY(values[1]); + matrix.setSkewX(values[2]); + matrix.setScaleY(values[3]); + matrix.setTranslateX(values[4]); + matrix.setTranslateY(values[5]); + sum.setConcat(matrix, sum); + continue; + } + if ( index == kClipPath) + *clips.insert(0) = lastAttr; + } + walking = walking->fNext; + } + if ((sum == parser.fLastTransform) == false) { + SkMatrix inverse; + bool success = parser.fLastTransform.invert(&inverse); + SkASSERT(success == true); + SkMatrix output; + output.setConcat(inverse, sum); + parser.fLastTransform = sum; + SkString outputStr; + outputStr.appendUnichar('['); + outputStr.appendScalar(output.getScaleX()); + outputStr.appendUnichar(','); + outputStr.appendScalar(output.getSkewX()); + outputStr.appendUnichar(','); + outputStr.appendScalar(output.getTranslateX()); + outputStr.appendUnichar(','); + outputStr.appendScalar(output.getSkewY()); + outputStr.appendUnichar(','); + outputStr.appendScalar(output.getScaleY()); + outputStr.appendUnichar(','); + outputStr.appendScalar(output.getTranslateY()); + outputStr.appendUnichar(','); + outputStr.appendScalar(output.getPerspX()); + outputStr.appendUnichar(','); + outputStr.appendScalar(output.getPerspY()); + outputStr.append(",1]"); + parser._startElement("matrix"); + parser._addAttributeLen("matrix", outputStr.c_str(), outputStr.size()); + parser._endElement(); + } +#if 0 // incomplete + if (parser.fTransformClips.size() > 0) { + // need to reset the clip when the 'g' scope is ended + parser._startElement("add"); + const char* start = strchr(current->f_clipPath.c_str(), '#') + 1; + SkASSERT(start); + parser._addAttributeLen("use", start, strlen(start) - 1); + parser._endElement(); // clip + } +#endif +} + +bool SkSVGPaint::writeChangedAttributes(SkSVGParser& parser, + SkSVGPaint& current, bool* changed) { + SkSVGPaint& lastState = parser.fLastFlush; + for (int index = kInitial + 1; index < kTerminal; index++) { + if (changed[index] == false) + continue; + SkString* topAttr = current[index]; + size_t attrLength = topAttr->size(); + if (attrLength == 0) + continue; + const char* attrValue = topAttr->c_str(); + SkString* lastAttr = lastState[index]; + switch(index) { + case kClipPath: + case kClipRule: + case kEnableBackground: + break; + case kFill: + if (topAttr->equals("none") == false && lastAttr->equals("none") == true) + parser._addAttribute("stroke", "false"); + goto fillStrokeAttrCommon; + case kFillRule: + case kFilter: + case kFontFamily: + break; + case kFontSize: + parser._addAttributeLen("textSize", attrValue, attrLength); + break; + case kLetterSpacing: + parser._addAttributeLen("textTracking", attrValue, attrLength); + break; + case kMask: + break; + case kOpacity: + break; + case kStopColor: + break; + case kStopOpacity: + break; + case kStroke: + if (topAttr->equals("none") == false && lastAttr->equals("none") == true) + parser._addAttribute("stroke", "true"); +fillStrokeAttrCommon: + if (strncmp(attrValue, "url(", 4) == 0) { + SkASSERT(attrValue[4] == '#'); + const char* idStart = attrValue + 5; + char* idEnd = strrchr(attrValue, ')'); + SkASSERT(idStart < idEnd); + SkString id(idStart, idEnd - idStart); + SkSVGElement* found; + if (strncmp(id.c_str(), "mask", 4) != 0) { + bool itsFound = parser.fIDs.find(id.c_str(), &found); + SkASSERT(itsFound); + SkASSERT(found->getType() == SkSVGType_LinearGradient || + found->getType() == SkSVGType_RadialGradient); + } + parser._addAttribute("shader", id.c_str()); + } + break; + case kStroke_Dasharray: + break; + case kStroke_Linecap: + parser._addAttributeLen("strokeCap", attrValue, attrLength); + break; + case kStroke_Linejoin: + parser._addAttributeLen("strokeJoin", attrValue, attrLength); + break; + case kStroke_Miterlimit: + parser._addAttributeLen("strokeMiter", attrValue, attrLength); + break; + case kStroke_Width: + parser._addAttributeLen("strokeWidth", attrValue, attrLength); + case kStyle: + case kTransform: + break; + default: + SkASSERT(0); + return false; + } + } + return true; +} + +bool SkSVGPaint::writeChangedElements(SkSVGParser& parser, + SkSVGPaint& current, bool* changed) { + SkSVGPaint& lastState = parser.fLastFlush; + for (int index = kInitial + 1; index < kTerminal; index++) { + SkString* topAttr = current[index]; + size_t attrLength = topAttr->size(); + if (attrLength == 0) + continue; + const char* attrValue = topAttr->c_str(); + SkString* lastAttr = lastState[index]; + switch(index) { + case kClipPath: + case kClipRule: + // !!! need to add this outside of paint + break; + case kEnableBackground: + // !!! don't know what to do with this + break; + case kFill: + goto addColor; + case kFillRule: + case kFilter: + break; + case kFontFamily: + parser._startElement("typeface"); + parser._addAttributeLen("fontName", attrValue, attrLength); + parser._endElement(); // typeface + break; + case kFontSize: + case kLetterSpacing: + break; + case kMask: + case kOpacity: + if (changed[kStroke] == false && changed[kFill] == false) { + parser._startElement("color"); + SkString& opacity = current.f_opacity; + parser._addAttributeLen("color", parser.fLastColor.c_str(), parser.fLastColor.size()); + parser._addAttributeLen("alpha", opacity.c_str(), opacity.size()); + parser._endElement(); // color + } + break; + case kStopColor: + break; + case kStopOpacity: + break; + case kStroke: +addColor: + if (strncmp(lastAttr->c_str(), "url(", 4) == 0 && strncmp(attrValue, "url(", 4) != 0) { + parser._startElement("shader"); + parser._endElement(); + } + if (topAttr->equals(*lastAttr)) + continue; + { + bool urlRef = strncmp(attrValue, "url(", 4) == 0; + bool colorNone = strcmp(attrValue, "none") == 0; + bool lastEqual = parser.fLastColor.equals(attrValue, attrLength); + bool newColor = urlRef == false && colorNone == false && lastEqual == false; + if (newColor || changed[kOpacity]) { + parser._startElement("color"); + if (newColor || changed[kOpacity]) { + parser._addAttributeLen("color", attrValue, attrLength); + parser.fLastColor.set(attrValue, attrLength); + } + if (changed[kOpacity]) { + SkString& opacity = current.f_opacity; + parser._addAttributeLen("alpha", opacity.c_str(), opacity.size()); + } + parser._endElement(); // color + } + } + break; + case kStroke_Dasharray: + parser._startElement("dash"); + SkSVGParser::ConvertToArray(*topAttr); + parser._addAttribute("intervals", topAttr->c_str()); + parser._endElement(); // dash + break; + case kStroke_Linecap: + case kStroke_Linejoin: + case kStroke_Miterlimit: + case kStroke_Width: + case kStyle: + case kTransform: + break; + default: + SkASSERT(0); + return false; + } + } + return true; +} + +void SkSVGPaint::Push(SkSVGPaint** head, SkSVGPaint* newRecord) { + newRecord->fNext = *head; + *head = newRecord; +} + +void SkSVGPaint::Pop(SkSVGPaint** head) { + SkSVGPaint* next = (*head)->fNext; + *head = next; +} + diff --git a/skia/svg/SkSVGParser.cpp b/skia/svg/SkSVGParser.cpp new file mode 100644 index 0000000..869a55a --- /dev/null +++ b/skia/svg/SkSVGParser.cpp @@ -0,0 +1,443 @@ +/* libs/graphics/svg/SkSVGParser.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkSVGParser.h" +#include "SkSVGCircle.h" +#include "SkSVGClipPath.h" +#include "SkSVGDefs.h" +#include "SkSVGEllipse.h" +#include "SkSVGFeColorMatrix.h" +#include "SkSVGFilter.h" +#include "SkSVGG.h" +#include "SkSVGImage.h" +#include "SkSVGLine.h" +#include "SkSVGLinearGradient.h" +#include "SkSVGMask.h" +#include "SkSVGMetadata.h" +#include "SkSVGPath.h" +#include "SkSVGPolygon.h" +#include "SkSVGPolyline.h" +#include "SkSVGRadialGradient.h" +#include "SkSVGRect.h" +#include "SkSVGSVG.h" +#include "SkSVGStop.h" +#include "SkSVGSymbol.h" +#include "SkSVGText.h" +#include "SkSVGUse.h" +#include "SkTSearch.h" +#include <stdio.h> + +static int gGeneratedMatrixID = 0; + +SkSVGParser::SkSVGParser() : fHead(&fEmptyPaint), fIDs(256), + fXMLWriter(&fStream), fCurrElement(NULL), fInSVG(false), fSuppressPaint(false) { + fLastTransform.reset(); + fEmptyPaint.f_fill.set("black"); + fEmptyPaint.f_stroke.set("none"); + fEmptyPaint.f_strokeMiterlimit.set("4"); + fEmptyPaint.f_fillRule.set("winding"); + fEmptyPaint.f_opacity.set("1"); + fEmptyPaint.fNext = NULL; + for (int index = SkSVGPaint::kInitial + 1; index < SkSVGPaint::kTerminal; index++) { + SkString* initial = fEmptyPaint[index]; + if (initial->size() == 0) + continue; + fLastFlush[index]->set(*initial); + } +} + +SkSVGParser::~SkSVGParser() { +} + +void SkSVGParser::Delete(SkTDArray<SkSVGElement*>& fChildren) { + SkSVGElement** ptr; + for (ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) { + Delete((*ptr)->fChildren); + delete *ptr; + } +} + +int SkSVGParser::findAttribute(SkSVGBase* element, const char* attrValue, + size_t len, bool isPaint) { + const SkSVGAttribute* attributes; + int count = element->getAttributes(&attributes); + int result = 0; + while (result < count) { + if (strncmp(attributes->fName, attrValue, len) == 0 && strlen(attributes->fName) == len) { + SkASSERT(result == (attributes->fOffset - + (isPaint ? sizeof(SkString) : sizeof(SkSVGElement))) / sizeof(SkString)); + return result; + } + attributes++; + result++; + } + return -1; +} + +const char* SkSVGParser::getFinal() { + _startElement("screenplay"); + // generate defs + SkSVGElement** ptr; + for (ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) { + SkSVGElement* element = *ptr; + translate(element, true); + } + // generate onLoad + _startElement("event"); + _addAttribute("kind", "onLoad"); + _startElement("paint"); + _addAttribute("antiAlias", "true"); + _endElement(); + for (ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) { + SkSVGElement* element = *ptr; + translate(element, false); + } + _endElement(); // event + _endElement(); // screenplay + Delete(fChildren); + fStream.write("", 1); + return fStream.getStream(); +} + +SkString& SkSVGParser::getPaintLast(SkSVGPaint::Field field) { + SkSVGPaint* state = fHead; + do { + SkString* attr = (*state)[field]; + SkASSERT(attr); + if (attr->size() > 0) + return *attr; + state = state->fNext; + } while (state); + SkASSERT(0); + SkASSERT(fEmptyPaint[field]); + return *fEmptyPaint[field]; +} + +bool SkSVGParser::isStrokeAndFill( SkSVGPaint** strokeState, SkSVGPaint** fillState) { + SkSVGPaint* walking = fHead; + bool stroke = false; + bool fill = false; + bool strokeSet = false; + bool fillSet = false; + while (walking != NULL) { + if (strokeSet == false && walking->f_stroke.size() > 0) { + stroke = walking->f_stroke.equals("none") == false; + *strokeState = walking; + strokeSet = true; + } + if (fillSet == false && walking->f_fill.size() > 0) { + fill = walking->f_fill.equals("none") == false; + *fillState = walking; + fillSet = true; + } + walking = walking->fNext; + } + return stroke && fill; +} + +bool SkSVGParser::onAddAttribute(const char name[], const char value[]) { + return onAddAttributeLen(name, value, strlen(value)); +} + +bool SkSVGParser::onAddAttributeLen(const char name[], const char value[], size_t len) { + if (fCurrElement == NULL) // this signals we should ignore attributes for this element + return true; + if (fCurrElement->fIsDef == false && fCurrElement->fIsNotDef == false) + return true; // also an ignored element + size_t nameLen = strlen(name); + int attrIndex = findAttribute(fCurrElement, name, nameLen, false); + if (attrIndex == -1) { + attrIndex = findAttribute(&fCurrElement->fPaintState, name, nameLen, true); + if (attrIndex >= 0) { + fCurrElement->fPaintState.addAttribute(*this, attrIndex, value, len); + return false; + } + if (nameLen == 2 && strncmp("id", name, nameLen) == 0) { + fCurrElement->f_id.set(value, len); + return false; + } + if (strchr(name, ':') != 0) // part of a different namespace + return false; + } + SkASSERT(attrIndex >= 0); + fCurrElement->addAttribute(*this, attrIndex, value, len); + return false; +} + +bool SkSVGParser::onEndElement(const char elem[]) { + int parentIndex = fParents.count() - 1; + if (parentIndex >= 0) { + SkSVGElement* element = fParents[parentIndex]; + element->onEndElement(*this); + fParents.remove(parentIndex); + } + return false; +} + +bool SkSVGParser::onStartElement(const char name[]) { + return onStartElementLen(name, strlen(name)); +} + +bool SkSVGParser::onStartElementLen(const char name[], size_t len) { + if (strncmp(name, "svg", len) == 0) { + fInSVG = true; + } else if (fInSVG == false) + return false; + const char* nextColon = strchr(name, ':'); + if (nextColon && nextColon - name < len) + return false; + SkSVGTypes type = GetType(name, len); + SkASSERT(type >= 0); + if (type < 0) + return true; + SkSVGElement* parent = fParents.count() > 0 ? fParents.top() : NULL; + SkSVGElement* element = CreateElement(type, parent); + bool result = false; + if (parent) { + element->fParent = parent; + result = fParents.top()->onStartElement(element); + } else + *fChildren.append() = element; + if (strncmp(name, "svg", len) != 0) + *fParents.append() = element; + fCurrElement = element; + return result; +} + +bool SkSVGParser::onText(const char text[], int len) { + if (fInSVG == false) + return false; + SkSVGTypes type = fCurrElement->getType(); + if (type != SkSVGType_Text && type != SkSVGType_Tspan) + return false; + SkSVGText* textElement = (SkSVGText*) fCurrElement; + textElement->f_text.set(text, len); + return false; +} + +static int32_t strokeFillID = 0; + +void SkSVGParser::translate(SkSVGElement* element, bool isDef) { + SkSVGPaint::Push(&fHead, &element->fPaintState); + bool isFlushable = element->isFlushable(); + if ((element->fIsDef == false && element->fIsNotDef == false) || + (element->fIsDef && isDef == false && element->fIsNotDef == false) || + (element->fIsDef == false && isDef && element->fIsNotDef)) { + isFlushable = false; + } + SkSVGPaint* strokeState = NULL, * fillState = NULL; + if (isFlushable) + element->fPaintState.setSave(*this); + if (isFlushable && isStrokeAndFill(&strokeState, &fillState)) { + SkString& elementID = element->f_id; + if (elementID.size() == 0) { + elementID.set("sf"); + elementID.appendS32(++strokeFillID); + } + SkString saveStroke(strokeState->f_stroke); + SkString saveFill(fillState->f_fill); + strokeState->f_stroke.set("none"); + element->fPaintState.flush(*this, isFlushable, isDef); + element->translate(*this, isDef); + strokeState->f_stroke.set(saveStroke); + fillState->f_fill.set("none"); + if (element->fPaintState.flush(*this, isFlushable, isDef)) { + _startElement("add"); + _addAttributeLen("use", elementID.c_str(), elementID.size()); + _endElement(); // add + } + fillState->f_fill.set(saveFill); + } else { + element->fPaintState.flush(*this, isFlushable, isDef); + if (isFlushable || element->isGroup()) + element->translate(*this, isDef); + } + SkSVGPaint::Pop(&fHead); +} + +void SkSVGParser::translateMatrix(SkString& string, SkString* stringID) { + if (string.size() == 0) + return; + if (stringID->size() > 0) { + _startElement("add"); + _addAttribute("use", stringID->c_str()); + _endElement(); // add + return; + } + SkASSERT(strncmp(string.c_str(), "matrix", 6) == 0); + ++gGeneratedMatrixID; + _startElement("matrix"); + char idStr[24]; + strcpy(idStr, "sk_matrix"); + sprintf(idStr + strlen(idStr), "%d", gGeneratedMatrixID); + _addAttribute("id", idStr); + stringID->set(idStr); + const char* str = string.c_str(); + SkASSERT(strncmp(str, "matrix(", 7) == 0); + str += 6; + const char* strEnd = strrchr(str, ')'); + SkASSERT(strEnd != NULL); + SkString mat(str, strEnd - str); + ConvertToArray(mat); + const char* elems[6]; + static const int order[] = {0, 3, 1, 4, 2, 5}; + const int* orderPtr = order; + str = mat.c_str(); + strEnd = str + mat.size(); + while (str < strEnd) { + elems[*orderPtr++] = str; + while (str < strEnd && *str != ',' ) + str++; + str++; + } + string.reset(); + for (int index = 0; index < 6; index++) { + const char* end = strchr(elems[index], ','); + if (end == NULL) + end= strchr(elems[index], ']'); + string.append(elems[index], end - elems[index] + 1); + } + string.remove(string.size() - 1, 1); + string.append(",0,0,1]"); + _addAttribute("matrix", string); + _endElement(); // matrix +} + +static bool is_whitespace(char ch) { + return ch > 0 && ch <= ' '; +} + +void SkSVGParser::ConvertToArray(SkString& vals) { + vals.appendUnichar(']'); + char* valCh = (char*) vals.c_str(); + valCh[0] = '['; + int index = 1; + while (valCh[index] != ']') { + while (is_whitespace(valCh[index])) + index++; + bool foundComma = false; + char next; + do { + next = valCh[index++]; + if (next == ',') { + foundComma = true; + continue; + } + if (next == ']') { + index--; + goto undoLastComma; + } + if (next == ' ') + break; + foundComma = false; + } while (is_whitespace(next) == false); + if (foundComma == false) + valCh[index - 1] = ','; + } +undoLastComma: + while (is_whitespace(valCh[--index])) + ; + if (valCh[index] == ',') + valCh[index] = ' '; +} + +#define CASE_NEW(type) case SkSVGType_##type : created = new SkSVG##type(); break + +SkSVGElement* SkSVGParser::CreateElement(SkSVGTypes type, SkSVGElement* parent) { + SkSVGElement* created = NULL; + switch (type) { + CASE_NEW(Circle); + CASE_NEW(ClipPath); + CASE_NEW(Defs); + CASE_NEW(Ellipse); + CASE_NEW(FeColorMatrix); + CASE_NEW(Filter); + CASE_NEW(G); + CASE_NEW(Image); + CASE_NEW(Line); + CASE_NEW(LinearGradient); + CASE_NEW(Mask); + CASE_NEW(Metadata); + CASE_NEW(Path); + CASE_NEW(Polygon); + CASE_NEW(Polyline); + CASE_NEW(RadialGradient); + CASE_NEW(Rect); + CASE_NEW(Stop); + CASE_NEW(SVG); + CASE_NEW(Symbol); + CASE_NEW(Text); + CASE_NEW(Tspan); + CASE_NEW(Use); + default: + SkASSERT(0); + return NULL; + } + created->fParent = parent; + bool isDef = created->fIsDef = created->isDef(); + bool isNotDef = created->fIsNotDef = created->isNotDef(); + if (isDef) { + SkSVGElement* up = parent; + while (up && up->fIsDef == false) { + up->fIsDef = true; + up = up->fParent; + } + } + if (isNotDef) { + SkSVGElement* up = parent; + while (up && up->fIsNotDef == false) { + up->fIsNotDef = true; + up = up->fParent; + } + } + return created; +} + +const SkSVGTypeName gSVGTypeNames[] = { + {"circle", SkSVGType_Circle}, + {"clipPath", SkSVGType_ClipPath}, + {"defs", SkSVGType_Defs}, + {"ellipse", SkSVGType_Ellipse}, + {"feColorMatrix", SkSVGType_FeColorMatrix}, + {"filter", SkSVGType_Filter}, + {"g", SkSVGType_G}, + {"image", SkSVGType_Image}, + {"line", SkSVGType_Line}, + {"linearGradient", SkSVGType_LinearGradient}, + {"mask", SkSVGType_Mask}, + {"metadata", SkSVGType_Metadata}, + {"path", SkSVGType_Path}, + {"polygon", SkSVGType_Polygon}, + {"polyline", SkSVGType_Polyline}, + {"radialGradient", SkSVGType_RadialGradient}, + {"rect", SkSVGType_Rect}, + {"stop", SkSVGType_Stop}, + {"svg", SkSVGType_SVG}, + {"symbol", SkSVGType_Symbol}, + {"text", SkSVGType_Text}, + {"tspan", SkSVGType_Tspan}, + {"use", SkSVGType_Use} +}; + +const int kSVGTypeNamesSize = SK_ARRAY_COUNT(gSVGTypeNames); + +SkSVGTypes SkSVGParser::GetType(const char match[], size_t len ) { + int index = SkStrSearch(&gSVGTypeNames[0].fName, kSVGTypeNamesSize, match, + len, sizeof(gSVGTypeNames[0])); + return index >= 0 && index < kSVGTypeNamesSize ? gSVGTypeNames[index].fType : + (SkSVGTypes) -1; +} diff --git a/skia/svg/SkSVGPath.cpp b/skia/svg/SkSVGPath.cpp new file mode 100644 index 0000000..bff8f24 --- /dev/null +++ b/skia/svg/SkSVGPath.cpp @@ -0,0 +1,45 @@ +/* libs/graphics/svg/SkSVGPath.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkSVGPath.h" +#include "SkSVGParser.h" + +const SkSVGAttribute SkSVGPath::gAttributes[] = { + SVG_ATTRIBUTE(d) +}; + +DEFINE_SVG_INFO(Path) + +void SkSVGPath::translate(SkSVGParser& parser, bool defState) { + parser._startElement("path"); + INHERITED::translate(parser, defState); + bool hasMultiplePaths = false; + const char* firstZ = strchr(f_d.c_str(), 'z'); + if (firstZ != NULL) { + firstZ++; // skip over 'z' + while (*firstZ == ' ') + firstZ++; + hasMultiplePaths = *firstZ != '\0'; + } + if (hasMultiplePaths) { + SkString& fillRule = parser.getPaintLast(SkSVGPaint::kFillRule); + if (fillRule.size() > 0) + parser._addAttribute("fillType", fillRule.equals("evenodd") ? "evenOdd" : "winding"); + } + SVG_ADD_ATTRIBUTE(d); + parser._endElement(); +} diff --git a/skia/svg/SkSVGPath.h b/skia/svg/SkSVGPath.h new file mode 100644 index 0000000..f3be03b --- /dev/null +++ b/skia/svg/SkSVGPath.h @@ -0,0 +1,30 @@ +/* libs/graphics/svg/SkSVGPath.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkSVGPath_DEFINED +#define SkSVGPath_DEFINED + +#include "SkSVGElements.h" + +class SkSVGPath : public SkSVGElement { + DECLARE_SVG_INFO(Path); +private: + SkString f_d; + typedef SkSVGElement INHERITED; +}; + +#endif // SkSVGPath_DEFINED diff --git a/skia/svg/SkSVGPolygon.cpp b/skia/svg/SkSVGPolygon.cpp new file mode 100644 index 0000000..f1ed9ce --- /dev/null +++ b/skia/svg/SkSVGPolygon.cpp @@ -0,0 +1,41 @@ +/* libs/graphics/svg/SkSVGPolygon.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkSVGPolygon.h" +#include "SkSVGParser.h" + +const SkSVGAttribute SkSVGPolygon::gAttributes[] = { + SVG_LITERAL_ATTRIBUTE(clip-rule, f_clipRule), + SVG_LITERAL_ATTRIBUTE(fill-rule, f_fillRule), + SVG_ATTRIBUTE(points) +}; + +DEFINE_SVG_INFO(Polygon) + +void SkSVGPolygon::addAttribute(SkSVGParser& parser, int attrIndex, + const char* attrValue, size_t attrLength) { + INHERITED::addAttribute(parser, attrIndex, attrValue, attrLength); +} + +void SkSVGPolygon::translate(SkSVGParser& parser, bool defState) { + parser._startElement("polygon"); + SkSVGElement::translate(parser, defState); + SVG_ADD_ATTRIBUTE(points); + if (f_fillRule.size() > 0) + parser._addAttribute("fillType", f_fillRule.equals("evenodd") ? "evenOdd" : "winding"); + parser._endElement(); +} diff --git a/skia/svg/SkSVGPolygon.h b/skia/svg/SkSVGPolygon.h new file mode 100644 index 0000000..a01d389 --- /dev/null +++ b/skia/svg/SkSVGPolygon.h @@ -0,0 +1,31 @@ +/* libs/graphics/svg/SkSVGPolygon.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkSVGPolygon_DEFINED +#define SkSVGPolygon_DEFINED + +#include "SkSVGPolyline.h" + +class SkSVGPolygon : public SkSVGPolyline { + DECLARE_SVG_INFO(Polygon); + virtual void addAttribute(SkSVGParser& , int attrIndex, + const char* attrValue, size_t attrLength); +private: + typedef SkSVGPolyline INHERITED; +}; + +#endif // SkSVGPolygon_DEFINED diff --git a/skia/svg/SkSVGPolyline.cpp b/skia/svg/SkSVGPolyline.cpp new file mode 100644 index 0000000..7e7b575 --- /dev/null +++ b/skia/svg/SkSVGPolyline.cpp @@ -0,0 +1,51 @@ +/* libs/graphics/svg/SkSVGPolyline.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkSVGPolyline.h" +#include "SkSVGParser.h" + +enum { + kCliipRule, + kFillRule, + kPoints +}; + +const SkSVGAttribute SkSVGPolyline::gAttributes[] = { + SVG_LITERAL_ATTRIBUTE(clip-rule, f_clipRule), + SVG_LITERAL_ATTRIBUTE(fill-rule, f_fillRule), + SVG_ATTRIBUTE(points) +}; + +DEFINE_SVG_INFO(Polyline) + +void SkSVGPolyline::addAttribute(SkSVGParser& , int attrIndex, + const char* attrValue, size_t attrLength) { + if (attrIndex != kPoints) + return; + f_points.set("["); + f_points.append(attrValue, attrLength); + SkSVGParser::ConvertToArray(f_points); +} + +void SkSVGPolyline::translate(SkSVGParser& parser, bool defState) { + parser._startElement("polyline"); + INHERITED::translate(parser, defState); + SVG_ADD_ATTRIBUTE(points); + if (f_fillRule.size() > 0) + parser._addAttribute("fillType", f_fillRule.equals("evenodd") ? "evenOdd" : "winding"); + parser._endElement(); +} diff --git a/skia/svg/SkSVGPolyline.h b/skia/svg/SkSVGPolyline.h new file mode 100644 index 0000000..68527e7 --- /dev/null +++ b/skia/svg/SkSVGPolyline.h @@ -0,0 +1,35 @@ +/* libs/graphics/svg/SkSVGPolyline.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkSVGPolyline_DEFINED +#define SkSVGPolyline_DEFINED + +#include "SkSVGElements.h" +#include "SkString.h" + +class SkSVGPolyline : public SkSVGElement { + DECLARE_SVG_INFO(Polyline); + virtual void addAttribute(SkSVGParser& , int attrIndex, + const char* attrValue, size_t attrLength); +protected: + SkString f_clipRule; + SkString f_fillRule; + SkString f_points; + typedef SkSVGElement INHERITED; +}; + +#endif // SkSVGPolyline_DEFINED diff --git a/skia/svg/SkSVGRadialGradient.cpp b/skia/svg/SkSVGRadialGradient.cpp new file mode 100644 index 0000000..31bfde6 --- /dev/null +++ b/skia/svg/SkSVGRadialGradient.cpp @@ -0,0 +1,50 @@ +/* libs/graphics/svg/SkSVGRadialGradient.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkSVGRadialGradient.h" +#include "SkSVGParser.h" + +const SkSVGAttribute SkSVGRadialGradient::gAttributes[] = { + SVG_ATTRIBUTE(cx), + SVG_ATTRIBUTE(cy), + SVG_ATTRIBUTE(fx), + SVG_ATTRIBUTE(fy), + SVG_ATTRIBUTE(gradientTransform), + SVG_ATTRIBUTE(gradientUnits), + SVG_ATTRIBUTE(r) +}; + +DEFINE_SVG_INFO(RadialGradient) + +void SkSVGRadialGradient::translate(SkSVGParser& parser, bool defState) { + if (fMatrixID.size() == 0) + parser.translateMatrix(f_gradientTransform, &fMatrixID); + parser._startElement("radialGradient"); + if (fMatrixID.size() > 0) + parser._addAttribute("matrix", fMatrixID); + INHERITED::translateGradientUnits(f_gradientUnits); + SkString center; + center.appendUnichar('['); + center.append(f_cx); + center.appendUnichar(','); + center.append(f_cy); + center.appendUnichar(']'); + parser._addAttribute("center", center); + parser._addAttribute("radius", f_r); + INHERITED::translate(parser, defState); + parser._endElement(); +} diff --git a/skia/svg/SkSVGRadialGradient.h b/skia/svg/SkSVGRadialGradient.h new file mode 100644 index 0000000..cdf2c10 --- /dev/null +++ b/skia/svg/SkSVGRadialGradient.h @@ -0,0 +1,38 @@ +/* libs/graphics/svg/SkSVGRadialGradient.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkSVGRadialGradient_DEFINED +#define SkSVGRadialGradient_DEFINED + +#include "SkSVGGradient.h" + +class SkSVGRadialGradient : public SkSVGGradient { + DECLARE_SVG_INFO(RadialGradient); +protected: + SkString f_cx; + SkString f_cy; + SkString f_fx; + SkString f_fy; + SkString f_gradientTransform; + SkString f_gradientUnits; + SkString f_r; + SkString fMatrixID; +private: + typedef SkSVGGradient INHERITED; +}; + +#endif // SkSVGRadialGradient_DEFINED diff --git a/skia/svg/SkSVGRect.cpp b/skia/svg/SkSVGRect.cpp new file mode 100644 index 0000000..9f1da07 --- /dev/null +++ b/skia/svg/SkSVGRect.cpp @@ -0,0 +1,43 @@ +/* libs/graphics/svg/SkSVGRect.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkSVGRect.h" +#include "SkSVGParser.h" + +const SkSVGAttribute SkSVGRect::gAttributes[] = { + SVG_ATTRIBUTE(height), + SVG_ATTRIBUTE(width), + SVG_ATTRIBUTE(x), + SVG_ATTRIBUTE(y) +}; + +DEFINE_SVG_INFO(Rect) + +SkSVGRect::SkSVGRect() { + f_x.set("0"); + f_y.set("0"); +} + +void SkSVGRect::translate(SkSVGParser& parser, bool defState) { + parser._startElement("rectangle"); + INHERITED::translate(parser, defState); + SVG_ADD_ATTRIBUTE_ALIAS(left, x); + SVG_ADD_ATTRIBUTE_ALIAS(top, y); + SVG_ADD_ATTRIBUTE(width); + SVG_ADD_ATTRIBUTE(height); + parser._endElement(); +} diff --git a/skia/svg/SkSVGRect.h b/skia/svg/SkSVGRect.h new file mode 100644 index 0000000..ac48d8e --- /dev/null +++ b/skia/svg/SkSVGRect.h @@ -0,0 +1,34 @@ +/* libs/graphics/svg/SkSVGRect.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkSVGRect_DEFINED +#define SkSVGRect_DEFINED + +#include "SkSVGElements.h" + +class SkSVGRect : public SkSVGElement { + DECLARE_SVG_INFO(Rect); + SkSVGRect(); +private: + SkString f_height; + SkString f_width; + SkString f_x; + SkString f_y; + typedef SkSVGElement INHERITED; +}; + +#endif // SkSVGRect_DEFINED diff --git a/skia/svg/SkSVGSVG.cpp b/skia/svg/SkSVGSVG.cpp new file mode 100644 index 0000000..6b0aed3 --- /dev/null +++ b/skia/svg/SkSVGSVG.cpp @@ -0,0 +1,82 @@ +/* libs/graphics/svg/SkSVGSVG.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkSVGSVG.h" +#include "SkParse.h" +#include "SkRect.h" +#include "SkSVGParser.h" + +const SkSVGAttribute SkSVGSVG::gAttributes[] = { + SVG_LITERAL_ATTRIBUTE(enable-background, f_enable_background), + SVG_ATTRIBUTE(height), + SVG_ATTRIBUTE(overflow), + SVG_ATTRIBUTE(width), + SVG_ATTRIBUTE(version), + SVG_ATTRIBUTE(viewBox), + SVG_LITERAL_ATTRIBUTE(xml:space, f_xml_space), + SVG_ATTRIBUTE(xmlns), + SVG_LITERAL_ATTRIBUTE(xmlns:xlink, f_xml_xlink) +}; + +DEFINE_SVG_INFO(SVG) + + +bool SkSVGSVG::isFlushable() { + return false; +} + +void SkSVGSVG::translate(SkSVGParser& parser, bool defState) { + SkScalar height, width; + SkScalar viewBox[4]; + const char* hSuffix = SkParse::FindScalar(f_height.c_str(), &height); + if (strcmp(hSuffix, "pt") == 0) + height = SkScalarMulDiv(height, SK_Scalar1 * 72, SK_Scalar1 * 96); + const char* wSuffix = SkParse::FindScalar(f_width.c_str(), &width); + if (strcmp(wSuffix, "pt") == 0) + width = SkScalarMulDiv(width, SK_Scalar1 * 72, SK_Scalar1 * 96); + SkParse::FindScalars(f_viewBox.c_str(), viewBox, 4); + SkRect box; + box.fLeft = SkScalarDiv(viewBox[0], width); + box.fTop = SkScalarDiv(viewBox[1], height); + box.fRight = SkScalarDiv(viewBox[2], width); + box.fBottom = SkScalarDiv(viewBox[3], height); + if (box.fLeft == 0 && box.fTop == 0 && + box.fRight == SK_Scalar1 && box.fBottom == SK_Scalar1) + return; + parser._startElement("matrix"); + if (box.fLeft != 0) { + SkString x; + x.appendScalar(box.fLeft); + parser._addAttributeLen("translateX", x.c_str(), x.size()); + } + if (box.fTop != 0) { + SkString y; + y.appendScalar(box.fTop); + parser._addAttributeLen("translateY", y.c_str(), y.size()); + } + if (box.fRight != SK_Scalar1) { + SkString x; + x.appendScalar(box.fRight); + parser._addAttributeLen("scaleX", x.c_str(), x.size()); + } + if (box.fBottom != SK_Scalar1) { + SkString y; + y.appendScalar(box.fBottom); + parser._addAttributeLen("scaleY", y.c_str(), y.size()); + } + parser._endElement(); +} diff --git a/skia/svg/SkSVGSVG.h b/skia/svg/SkSVGSVG.h new file mode 100644 index 0000000..c8eec08 --- /dev/null +++ b/skia/svg/SkSVGSVG.h @@ -0,0 +1,39 @@ +/* libs/graphics/svg/SkSVGSVG.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkSVGSVG_DEFINED +#define SkSVGSVG_DEFINED + +#include "SkSVGElements.h" + +class SkSVGSVG : public SkSVGElement { + DECLARE_SVG_INFO(SVG); + virtual bool isFlushable(); +private: + SkString f_enable_background; + SkString f_height; + SkString f_overflow; + SkString f_width; + SkString f_version; + SkString f_viewBox; + SkString f_xml_space; + SkString f_xmlns; + SkString f_xml_xlink; + typedef SkSVGElement INHERITED; +}; + +#endif // SkSVGSVG_DEFINED diff --git a/skia/svg/SkSVGStop.cpp b/skia/svg/SkSVGStop.cpp new file mode 100644 index 0000000..10472d2 --- /dev/null +++ b/skia/svg/SkSVGStop.cpp @@ -0,0 +1,32 @@ +/* libs/graphics/svg/SkSVGStop.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkSVGStop.h" +#include "SkSVGParser.h" + +const SkSVGAttribute SkSVGStop::gAttributes[] = { + SVG_ATTRIBUTE(offset) +}; + +DEFINE_SVG_INFO(Stop) + +void SkSVGStop::translate(SkSVGParser& parser, bool defState) { + parser._startElement("color"); + INHERITED::translate(parser, defState); + parser._addAttribute("color", parser.getPaintLast(SkSVGPaint::kStopColor)); + parser._endElement(); +} diff --git a/skia/svg/SkSVGStop.h b/skia/svg/SkSVGStop.h new file mode 100644 index 0000000..50be491 --- /dev/null +++ b/skia/svg/SkSVGStop.h @@ -0,0 +1,31 @@ +/* libs/graphics/svg/SkSVGStop.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkSVGStop_DEFINED +#define SkSVGStop_DEFINED + +#include "SkSVGElements.h" + +class SkSVGStop : public SkSVGElement { + DECLARE_SVG_INFO(Stop); +private: + SkString f_offset; + friend class SkSVGGradient; + typedef SkSVGElement INHERITED; +}; + +#endif // SkSVGStop_DEFINED diff --git a/skia/svg/SkSVGSymbol.cpp b/skia/svg/SkSVGSymbol.cpp new file mode 100644 index 0000000..a559397 --- /dev/null +++ b/skia/svg/SkSVGSymbol.cpp @@ -0,0 +1,30 @@ +/* libs/graphics/svg/SkSVGSymbol.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkSVGSymbol.h" +#include "SkSVGParser.h" + +const SkSVGAttribute SkSVGSymbol::gAttributes[] = { + SVG_ATTRIBUTE(viewBox) +}; + +DEFINE_SVG_INFO(Symbol) + +void SkSVGSymbol::translate(SkSVGParser& parser, bool defState) { + INHERITED::translate(parser, defState); + // !!! children need to be written into document +} diff --git a/skia/svg/SkSVGSymbol.h b/skia/svg/SkSVGSymbol.h new file mode 100644 index 0000000..08285eb --- /dev/null +++ b/skia/svg/SkSVGSymbol.h @@ -0,0 +1,30 @@ +/* libs/graphics/svg/SkSVGSymbol.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkSVGSymbol_DEFINED +#define SkSVGSymbol_DEFINED + +#include "SkSVGElements.h" + +class SkSVGSymbol : public SkSVGElement { + DECLARE_SVG_INFO(Symbol); +private: + SkString f_viewBox; + typedef SkSVGElement INHERITED; +}; + +#endif // SkSVGSymbol_DEFINED diff --git a/skia/svg/SkSVGText.cpp b/skia/svg/SkSVGText.cpp new file mode 100644 index 0000000..930055c --- /dev/null +++ b/skia/svg/SkSVGText.cpp @@ -0,0 +1,47 @@ +/* libs/graphics/svg/SkSVGText.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkSVGText.h" +#include "SkSVGParser.h" + +const SkSVGAttribute SkSVGText::gAttributes[] = { + SVG_ATTRIBUTE(x), + SVG_ATTRIBUTE(y) +}; + +DEFINE_SVG_INFO(Text) + +void SkSVGText::translate(SkSVGParser& parser, bool defState) { + parser._startElement("text"); + INHERITED::translate(parser, defState); + SVG_ADD_ATTRIBUTE(x); + SVG_ADD_ATTRIBUTE(y); + SVG_ADD_ATTRIBUTE(text); + parser._endElement(); +} + + +const SkSVGAttribute SkSVGTspan::gAttributes[] = { + SVG_ATTRIBUTE(x), + SVG_ATTRIBUTE(y) +}; + +DEFINE_SVG_INFO(Tspan) + +void SkSVGTspan::translate(SkSVGParser& parser, bool defState) { + INHERITED::translate(parser, defState); +} diff --git a/skia/svg/SkSVGText.h b/skia/svg/SkSVGText.h new file mode 100644 index 0000000..82f0242 --- /dev/null +++ b/skia/svg/SkSVGText.h @@ -0,0 +1,40 @@ +/* libs/graphics/svg/SkSVGText.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkSVGText_DEFINED +#define SkSVGText_DEFINED + +#include "SkSVGElements.h" + +class SkSVGText : public SkSVGElement { + DECLARE_SVG_INFO(Text); +protected: + SkString f_x; + SkString f_y; + SkString f_text; // not an attribute +private: + typedef SkSVGElement INHERITED; + friend class SkSVGParser; +}; + +class SkSVGTspan : public SkSVGText { + DECLARE_SVG_INFO(Tspan); +private: + typedef SkSVGText INHERITED; +}; + +#endif // SkSVGText_DEFINED diff --git a/skia/svg/SkSVGUse.cpp b/skia/svg/SkSVGUse.cpp new file mode 100644 index 0000000..9c419e0 --- /dev/null +++ b/skia/svg/SkSVGUse.cpp @@ -0,0 +1,38 @@ +/* libs/graphics/svg/SkSVGUse.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkSVGUse.h" +#include "SkSVGParser.h" + +const SkSVGAttribute SkSVGUse::gAttributes[] = { + SVG_ATTRIBUTE(height), + SVG_ATTRIBUTE(width), + SVG_ATTRIBUTE(x), + SVG_LITERAL_ATTRIBUTE(xlink:href, f_xlink_href), + SVG_ATTRIBUTE(y) +}; + +DEFINE_SVG_INFO(Use) + +void SkSVGUse::translate(SkSVGParser& parser, bool defState) { + INHERITED::translate(parser, defState); + parser._startElement("add"); + const char* start = strchr(f_xlink_href.c_str(), '#') + 1; + SkASSERT(start); + parser._addAttributeLen("use", start, strlen(start) - 1); + parser._endElement(); // clip +} diff --git a/skia/svg/SkSVGUse.h b/skia/svg/SkSVGUse.h new file mode 100644 index 0000000..1c024d5 --- /dev/null +++ b/skia/svg/SkSVGUse.h @@ -0,0 +1,36 @@ +/* libs/graphics/svg/SkSVGUse.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkSVGUse_DEFINED +#define SkSVGUse_DEFINED + +#include "SkSVGElements.h" + +class SkSVGUse : public SkSVGElement { + DECLARE_SVG_INFO(Use); +protected: + SkString f_height; + SkString f_width; + SkString f_x; + SkString f_xlink_href; + SkString f_y; +private: + typedef SkSVGElement INHERITED; + friend class SkSVGClipPath; +}; + +#endif // SkSVGUse_DEFINED diff --git a/skia/text/ATextEntry.h b/skia/text/ATextEntry.h new file mode 100644 index 0000000..8d7b60d --- /dev/null +++ b/skia/text/ATextEntry.h @@ -0,0 +1,37 @@ +/* libs/graphics/text/ATextEntry.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef ATextEntry_DEFINED +#define ATextEntry_DEFINED + +#include "SkTypes.h" + +class SkCanavs; + +class ATextEntry { +public: + ATextEntry(); + ~ATextEntry(); + + void setUtf16(const U16 text[], size_t count); + void setSize(SkScalar width, SkScalar height); + void setSelection(int start, int stop); + void draw(SkCanvas*); + void handleKey(int key); +}; + +#endif diff --git a/skia/tile_patch.diff b/skia/tile_patch.diff new file mode 100644 index 0000000..7811841 --- /dev/null +++ b/skia/tile_patch.diff @@ -0,0 +1,284 @@ +Index: sgl/SkBitmapProcState.h
+===================================================================
+--- sgl/SkBitmapProcState.h (revision 42716)
++++ sgl/SkBitmapProcState.h (working copy)
+@@ -39,8 +39,9 @@
+ int count, + uint16_t colors[]); + +- typedef U16CPU (*FixedTileProc)(SkFixed); // returns 0..0xFFFF +- ++ typedef SkFixed (*FixedTileProc)(SkFixed, int); ++ typedef int (*IntTileProc)(int, int); ++ + MatrixProc fMatrixProc; // chooseProcs + SampleProc32 fSampleProc32; // chooseProcs + SampleProc16 fSampleProc16; // chooseProcs +@@ -48,6 +49,8 @@
+ SkMatrix fUnitInvMatrix; // chooseProcs + FixedTileProc fTileProcX; // chooseProcs + FixedTileProc fTileProcY; // chooseProcs ++ IntTileProc iTileProcX; // chooseProcs ++ IntTileProc iTileProcY; // chooseProcs + SkFixed fFilterOneX; + SkFixed fFilterOneY; + +Index: sgl/SkBitmapProcState.cpp
+===================================================================
+--- sgl/SkBitmapProcState.cpp (revision 42716)
++++ sgl/SkBitmapProcState.cpp (working copy)
+@@ -296,8 +296,9 @@
+ } + const SkMatrix* m; + +- if (SkShader::kClamp_TileMode == fTileModeX && +- SkShader::kClamp_TileMode == fTileModeY) { ++ if (inv.getType() <= SkMatrix::kTranslate_Mask || ++ (SkShader::kClamp_TileMode == fTileModeX && ++ SkShader::kClamp_TileMode == fTileModeY)) { + m = &inv; + } else { + fUnitInvMatrix = inv; +@@ -330,6 +331,16 @@
+ fInvMatrix = m; + fInvProc = m->getMapXYProc(); + fInvType = m->getType(); ++ if (fInvType <= SkMatrix::kTranslate_Mask && ++ inv.getType() > SkMatrix::kTranslate_Mask) { ++ SkASSERT(inv.getType() <= ++ (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)); ++ // It is possible that by the calculation of fUnitInvMatrix, we have ++ // eliminated the scale transformation of the matrix (e.g., if inv^(-1) ++ // scales fOrigBitmap into an 1X1 rect). We add the scale flag back so ++ // that we don't make wrong choice in chooseMatrixProc(). ++ fInvType |= SkMatrix::kScale_Mask; ++ } + fInvSx = SkScalarToFixed(m->getScaleX()); + fInvSy = SkScalarToFixed(m->getScaleY()); + fInvKy = SkScalarToFixed(m->getSkewY()); +Index: sgl/SkBitmapProcState_matrix.h
+===================================================================
+--- sgl/SkBitmapProcState_matrix.h (revision 42716)
++++ sgl/SkBitmapProcState_matrix.h (working copy)
+@@ -1,4 +1,5 @@
+ ++#define TRANSLATE_NOFILTER_NAME MAKENAME(_nofilter_translate) + #define SCALE_NOFILTER_NAME MAKENAME(_nofilter_scale) + #define SCALE_FILTER_NAME MAKENAME(_filter_scale) + #define AFFINE_NOFILTER_NAME MAKENAME(_nofilter_affine) +@@ -17,6 +18,38 @@
+ #define PREAMBLE_ARG_Y + #endif + ++#ifndef PREAMBLE_TRANS ++ #define PREAMBLE_TRANS(state) ++#endif ++ ++static void TRANSLATE_NOFILTER_NAME(const SkBitmapProcState& s, ++ uint32_t xy[], int count, int x, int y) ++{ ++ SkASSERT((s.fInvType & ~SkMatrix::kTranslate_Mask) == 0); ++ ++ PREAMBLE_TRANS(s); ++ ++ x += SkScalarFloor(s.fInvMatrix->getTranslateX()); ++ y += SkScalarFloor(s.fInvMatrix->getTranslateY()); ++ ++ *xy++ = (uint32_t)TILEY_TRANS(y, (s.fBitmap->height() - 1)); ++ ++ int maxX = s.fBitmap->width() - 1; ++ int i; ++ uint16_t* xx = (uint16_t*)xy; ++ for (i = (count >> 2); i > 0; --i) ++ { ++ *xx++ = (uint16_t)TILEX_TRANS(x, maxX); x++; ++ *xx++ = (uint16_t)TILEX_TRANS(x, maxX); x++; ++ *xx++ = (uint16_t)TILEX_TRANS(x, maxX); x++; ++ *xx++ = (uint16_t)TILEX_TRANS(x, maxX); x++; ++ } ++ for (i = (count & 3); i > 0; --i) ++ { ++ *xx++ = (uint16_t)TILEX_TRANS(x, maxX); x++; ++ } ++} ++ + static void SCALE_NOFILTER_NAME(const SkBitmapProcState& s, + uint32_t xy[], int count, int x, int y) { + SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask | +@@ -206,9 +239,9 @@
+ unsigned maxY = s.fBitmap->height() - 1; + + do { +- *xy++ = PACK_FILTER_Y_NAME(fy, maxY, oneX PREAMBLE_ARG_Y); ++ *xy++ = PACK_FILTER_Y_NAME(fy, maxY, oneY PREAMBLE_ARG_Y); + fy += dy; +- *xy++ = PACK_FILTER_X_NAME(fx, maxX, oneY PREAMBLE_ARG_X); ++ *xy++ = PACK_FILTER_X_NAME(fx, maxX, oneX PREAMBLE_ARG_X); + fx += dx; + } while (--count != 0); + } +@@ -241,6 +274,9 @@
+ } + + static SkBitmapProcState::MatrixProc MAKENAME(_Procs)[] = { ++ TRANSLATE_NOFILTER_NAME, ++ TRANSLATE_NOFILTER_NAME, // No need to do filtering if the matrix is no ++ // more complex than identity/translate. + SCALE_NOFILTER_NAME, + SCALE_FILTER_NAME, + AFFINE_NOFILTER_NAME, +@@ -255,7 +291,10 @@
+ #ifdef CHECK_FOR_DECAL + #undef CHECK_FOR_DECAL + #endif +- ++#undef TILEX_TRANS ++#undef TILEY_TRANS ++ ++#undef TRANSLATE_NOFILTER_NAME + #undef SCALE_NOFILTER_NAME + #undef SCALE_FILTER_NAME + #undef AFFINE_NOFILTER_NAME +@@ -268,6 +307,7 @@
+ #undef PREAMBLE_PARAM_Y + #undef PREAMBLE_ARG_X + #undef PREAMBLE_ARG_Y ++#undef PREAMBLE_TRANS + + #undef TILEX_LOW_BITS + #undef TILEY_LOW_BITS +Index: sgl/SkBitmapProcState_matrixProcs.cpp
+===================================================================
+--- sgl/SkBitmapProcState_matrixProcs.cpp (revision 42716)
++++ sgl/SkBitmapProcState_matrixProcs.cpp (working copy)
+@@ -28,6 +28,8 @@
+ #define TILEX_LOW_BITS(fx, max) (((fx) >> 12) & 0xF) + #define TILEY_LOW_BITS(fy, max) (((fy) >> 12) & 0xF) + #define CHECK_FOR_DECAL ++#define TILEX_TRANS(x, max) SkClampMax(x, max) ++#define TILEY_TRANS(y, max) SkClampMax(y, max) + #include "SkBitmapProcState_matrix.h" + + #define MAKENAME(suffix) RepeatX_RepeatY ## suffix +@@ -35,6 +37,9 @@
+ #define TILEY_PROCF(fy, max) (((fy) & 0xFFFF) * ((max) + 1) >> 16) + #define TILEX_LOW_BITS(fx, max) ((((fx) & 0xFFFF) * ((max) + 1) >> 12) & 0xF) + #define TILEY_LOW_BITS(fy, max) ((((fy) & 0xFFFF) * ((max) + 1) >> 12) & 0xF) ++#define REAL_MOD(val, modulus) (((val)%(modulus)) + (modulus)*( (val)<0 )) ++#define TILEX_TRANS(x, max) (REAL_MOD((x), ((max) + 1))) ++#define TILEY_TRANS(y, max) (REAL_MOD((y), ((max) + 1))) + #include "SkBitmapProcState_matrix.h" + + #define MAKENAME(suffix) GeneralXY ## suffix +@@ -44,13 +49,17 @@
+ #define PREAMBLE_PARAM_Y , SkBitmapProcState::FixedTileProc tileProcY + #define PREAMBLE_ARG_X , tileProcX + #define PREAMBLE_ARG_Y , tileProcY +-#define TILEX_PROCF(fx, max) (tileProcX(fx) * ((max) + 1) >> 16) +-#define TILEY_PROCF(fy, max) (tileProcY(fy) * ((max) + 1) >> 16) +-#define TILEX_LOW_BITS(fx, max) ((tileProcX(fx) * ((max) + 1) >> 12) & 0xF) +-#define TILEY_LOW_BITS(fy, max) ((tileProcY(fy) * ((max) + 1) >> 12) & 0xF) ++#define TILEX_PROCF(fx, max) (tileProcX(fx, max) >> 16) ++#define TILEY_PROCF(fy, max) (tileProcY(fy, max) >> 16) ++#define TILEX_LOW_BITS(fx, max) ((tileProcX(fx, max) >> 14) & 0x3) ++#define TILEY_LOW_BITS(fy, max) ((tileProcY(fy, max) >> 14) & 0x3) ++#define PREAMBLE_TRANS(state) SkBitmapProcState::IntTileProc tileProcX = (state).iTileProcX; \ ++ SkBitmapProcState::IntTileProc tileProcY = (state).iTileProcY ++#define TILEX_TRANS(x, max) tileProcX(x, max) ++#define TILEY_TRANS(y, max) tileProcY(y, max) + #include "SkBitmapProcState_matrix.h" + +-static inline U16CPU fixed_clamp(SkFixed x) ++static inline SkFixed fixed_clamp(SkFixed x, int max) + { + #ifdef SK_CPU_HAS_CONDITIONAL_INSTR + if (x >> 16) +@@ -66,19 +75,20 @@
+ x = 0xFFFF; + } + #endif +- return x; ++ return x * (max + 1); + } + +-static inline U16CPU fixed_repeat(SkFixed x) ++static inline SkFixed fixed_repeat(SkFixed x, int max) + { +- return x & 0xFFFF; ++ return (x & 0xFFFF) * (max + 1); + } + +-static inline U16CPU fixed_mirror(SkFixed x) ++static inline SkFixed fixed_mirror(SkFixed x, int max) + { + SkFixed s = x << 15 >> 31; + // s is FFFFFFFF if we're on an odd interval, or 0 if an even interval +- return (x ^ s) & 0xFFFF; ++ x = ((x ^ s) & 0xFFFF) * (max + 1); ++ return s ? (x ^ 0xFFFF) : x; + } + + static SkBitmapProcState::FixedTileProc choose_tile_proc(unsigned m) +@@ -90,15 +100,52 @@
+ SkASSERT(SkShader::kMirror_TileMode == m); + return fixed_mirror; + } ++ ++static inline int int_clamp(int x, int max) ++{ ++ SkASSERT(max >= 0); ++ ++ return SkClampMax(x, max); ++} + ++static inline int int_repeat(int x, int max) ++{ ++ SkASSERT(max >= 0); ++ ++ return x % (max + 1); ++} ++ ++static inline int int_mirror(int x, int max) ++{ ++ SkASSERT(max >= 0); ++ ++ int dx = x % (max + 1); ++ if (dx < 0) ++ dx = -dx - 1; ++ ++ return (x / (max + 1) % 2) ? max - dx : dx; ++} ++ ++static SkBitmapProcState::IntTileProc choose_int_tile_proc(unsigned m) ++{ ++ if (SkShader::kClamp_TileMode == m) ++ return int_clamp; ++ if (SkShader::kRepeat_TileMode == m) ++ return int_repeat; ++ SkASSERT(SkShader::kMirror_TileMode == m); ++ return int_mirror; ++} ++ + SkBitmapProcState::MatrixProc SkBitmapProcState::chooseMatrixProc() + { + int index = 0; + if (fDoFilter) + index = 1; + if (fInvType & SkMatrix::kPerspective_Mask) ++ index |= 6; ++ else if (fInvType & SkMatrix::kAffine_Mask) + index |= 4; +- else if (fInvType & SkMatrix::kAffine_Mask) ++ else if (fInvType & SkMatrix::kScale_Mask) + index |= 2; + + if (SkShader::kClamp_TileMode == fTileModeX && +@@ -123,6 +170,8 @@
+ // only general needs these procs + fTileProcX = choose_tile_proc(fTileModeX); + fTileProcY = choose_tile_proc(fTileModeY); ++ iTileProcX = choose_int_tile_proc(fTileModeX); ++ iTileProcY = choose_int_tile_proc(fTileModeY); + return GeneralXY_Procs[index]; + } + diff --git a/skia/using_skia.vsprops b/skia/using_skia.vsprops new file mode 100644 index 0000000..faf27e2 --- /dev/null +++ b/skia/using_skia.vsprops @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="Windows-1252"?> +<VisualStudioPropertySheet + ProjectType="Visual C++" + Version="8.00" + Name="using_skia" + > + <Tool + Name="VCCLCompilerTool" + AdditionalIncludeDirectories=""$(SolutionDir)..\skia\include";"$(SolutionDir)..\skia\include\corecg";"$(SolutionDir)..\skia\platform"" + /> +</VisualStudioPropertySheet> diff --git a/skia/views/SkEvent.cpp b/skia/views/SkEvent.cpp new file mode 100644 index 0000000..7d00862 --- /dev/null +++ b/skia/views/SkEvent.cpp @@ -0,0 +1,565 @@ +/* libs/graphics/views/SkEvent.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkEvent.h" + +void SkEvent::initialize(const char* type, size_t typeLen) { + fType = NULL; + setType(type, typeLen); + f32 = 0; +#ifdef SK_DEBUG + fTargetID = 0; + fTime = 0; + fNextEvent = NULL; +#endif + SkDEBUGCODE(fDebugTrace = false;) +} + +SkEvent::SkEvent() +{ + initialize("", 0); +} + +SkEvent::SkEvent(const SkEvent& src) +{ + *this = src; + if (((size_t) fType & 1) == 0) + setType(src.fType); +} + +SkEvent::SkEvent(const SkString& type) +{ + initialize(type.c_str(), type.size()); +} + +SkEvent::SkEvent(const char type[]) +{ + SkASSERT(type); + initialize(type, strlen(type)); +} + +SkEvent::~SkEvent() +{ + if (((size_t) fType & 1) == 0) + sk_free((void*) fType); +} + +static size_t makeCharArray(char* buffer, size_t compact) +{ + size_t bits = (size_t) compact >> 1; + memcpy(buffer, &bits, sizeof(compact)); + buffer[sizeof(compact)] = 0; + return strlen(buffer); +} + +#if 0 +const char* SkEvent::getType() const +{ + if ((size_t) fType & 1) { // not a pointer + char chars[sizeof(size_t) + 1]; + size_t len = makeCharArray(chars, (size_t) fType); + fType = (char*) sk_malloc_throw(len); + SkASSERT(((size_t) fType & 1) == 0); + memcpy(fType, chars, len); + } + return fType; +} +#endif + +void SkEvent::getType(SkString* str) const +{ + if (str) + { + if ((size_t) fType & 1) // not a pointer + { + char chars[sizeof(size_t) + 1]; + size_t len = makeCharArray(chars, (size_t) fType); + str->set(chars, len); + } + else + str->set(fType); + } +} + +bool SkEvent::isType(const SkString& str) const +{ + return this->isType(str.c_str(), str.size()); +} + +bool SkEvent::isType(const char type[], size_t typeLen) const +{ + if (typeLen == 0) + typeLen = strlen(type); + if ((size_t) fType & 1) { // not a pointer + char chars[sizeof(size_t) + 1]; + size_t len = makeCharArray(chars, (size_t) fType); + return len == typeLen && strncmp(chars, type, typeLen) == 0; + } + return strncmp(fType, type, typeLen) == 0 && fType[typeLen] == 0; +} + +void SkEvent::setType(const char type[], size_t typeLen) +{ + if (typeLen == 0) + typeLen = strlen(type); + if (typeLen <= sizeof(fType)) { + size_t slot = 0; + memcpy(&slot, type, typeLen); + if (slot << 1 >> 1 != slot) + goto useCharStar; + slot <<= 1; + slot |= 1; + fType = (char*) slot; + } else { +useCharStar: + fType = (char*) sk_malloc_throw(typeLen + 1); + SkASSERT(((size_t) fType & 1) == 0); + memcpy(fType, type, typeLen); + fType[typeLen] = 0; + } +} + +void SkEvent::setType(const SkString& type) +{ + setType(type.c_str()); +} + +//////////////////////////////////////////////////////////////////////////// + +#include "SkParse.h" + +void SkEvent::inflate(const SkDOM& dom, const SkDOM::Node* node) +{ + const char* name = dom.findAttr(node, "type"); + if (name) + this->setType(name); + + const char* value; + if ((value = dom.findAttr(node, "fast32")) != NULL) + { + int32_t n; + if (SkParse::FindS32(value, &n)) + this->setFast32(n); + } + + for (node = dom.getFirstChild(node); node; node = dom.getNextSibling(node)) + { + if (strcmp(dom.getName(node), "data")) + { + SkDEBUGCODE(SkDebugf("SkEvent::inflate unrecognized subelement <%s>\n", dom.getName(node));) + continue; + } + + name = dom.findAttr(node, "name"); + if (name == NULL) + { + SkDEBUGCODE(SkDebugf("SkEvent::inflate missing required \"name\" attribute in <data> subelement\n");) + continue; + } + + if ((value = dom.findAttr(node, "s32")) != NULL) + { + int32_t n; + if (SkParse::FindS32(value, &n)) + this->setS32(name, n); + } + else if ((value = dom.findAttr(node, "scalar")) != NULL) + { + SkScalar x; + if (SkParse::FindScalar(value, &x)) + this->setScalar(name, x); + } + else if ((value = dom.findAttr(node, "string")) != NULL) + this->setString(name, value); +#ifdef SK_DEBUG + else + { + SkDebugf("SkEvent::inflate <data name=\"%s\"> subelement missing required type attribute [S32 | scalar | string]\n", name); + } +#endif + } +} + +#ifdef SK_DEBUG + + #ifndef SkScalarToFloat + #define SkScalarToFloat(x) ((x) / 65536.f) + #endif + + void SkEvent::dump(const char title[]) + { + if (title) + SkDebugf("%s ", title); + + SkString etype; + this->getType(&etype); + SkDebugf("event<%s> fast32=%d", etype.c_str(), this->getFast32()); + + const SkMetaData& md = this->getMetaData(); + SkMetaData::Iter iter(md); + SkMetaData::Type mtype; + int count; + const char* name; + + while ((name = iter.next(&mtype, &count)) != NULL) + { + SkASSERT(count > 0); + + SkDebugf(" <%s>=", name); + switch (mtype) { + case SkMetaData::kS32_Type: // vector version??? + { + int32_t value; + md.findS32(name, &value); + SkDebugf("%d ", value); + } + break; + case SkMetaData::kScalar_Type: + { + const SkScalar* values = md.findScalars(name, &count, NULL); + SkDebugf("%f", SkScalarToFloat(values[0])); + for (int i = 1; i < count; i++) + SkDebugf(", %f", SkScalarToFloat(values[i])); + SkDebugf(" "); + } + break; + case SkMetaData::kString_Type: + { + const char* value = md.findString(name); + SkASSERT(value); + SkDebugf("<%s> ", value); + } + break; + case SkMetaData::kPtr_Type: // vector version??? + { + void* value; + md.findPtr(name, &value); + SkDebugf("%p ", value); + } + break; + case SkMetaData::kBool_Type: // vector version??? + { + bool value; + md.findBool(name, &value); + SkDebugf("%s ", value ? "true" : "false"); + } + break; + default: + SkASSERT(!"unknown metadata type returned from iterator"); + break; + } + } + SkDebugf("\n"); + } +#endif + +/////////////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_DEBUG +// #define SK_TRACE_EVENTSx +#endif + +#ifdef SK_TRACE_EVENTS + static void event_log(const char s[]) + { + SkDEBUGF(("%s\n", s)); + } + + #define EVENT_LOG(s) event_log(s) + #define EVENT_LOGN(s, n) do { SkString str(s); str.append(" "); str.appendS32(n); event_log(str.c_str()); } while (0) +#else + #define EVENT_LOG(s) + #define EVENT_LOGN(s, n) +#endif + +#include "SkGlobals.h" +#include "SkThread.h" +#include "SkTime.h" + +#define SK_Event_GlobalsTag SkSetFourByteTag('e', 'v', 'n', 't') + +class SkEvent_Globals : public SkGlobals::Rec { +public: + SkMutex fEventMutex; + SkEvent* fEventQHead, *fEventQTail; + SkEvent* fDelayQHead; + SkDEBUGCODE(int fEventCounter;) +}; + +static SkGlobals::Rec* create_globals() +{ + SkEvent_Globals* rec = new SkEvent_Globals; + rec->fEventQHead = NULL; + rec->fEventQTail = NULL; + rec->fDelayQHead = NULL; + SkDEBUGCODE(rec->fEventCounter = 0;) + return rec; +} + +bool SkEvent::Post(SkEvent* evt, SkEventSinkID sinkID, SkMSec delay) +{ + if (delay) + return SkEvent::PostTime(evt, sinkID, SkTime::GetMSecs() + delay); + + SkEvent_Globals& globals = *(SkEvent_Globals*)SkGlobals::Find(SK_Event_GlobalsTag, create_globals); + + evt->fTargetID = sinkID; + +#ifdef SK_TRACE_EVENTS + { + SkString str("SkEvent::Post("); + str.append(evt->getType()); + str.append(", 0x"); + str.appendHex(sinkID); + str.append(", "); + str.appendS32(delay); + str.append(")"); + event_log(str.c_str()); + } +#endif + + globals.fEventMutex.acquire(); + bool wasEmpty = SkEvent::Enqueue(evt); + globals.fEventMutex.release(); + + // call outside of us holding the mutex + if (wasEmpty) + SkEvent::SignalNonEmptyQueue(); + return true; +} + +#if defined(SK_SIMULATE_FAILED_MALLOC) && defined(SK_FIND_MEMORY_LEAKS) +SkMSec gMaxDrawTime; +#endif + +bool SkEvent::PostTime(SkEvent* evt, SkEventSinkID sinkID, SkMSec time) +{ +#if defined(SK_SIMULATE_FAILED_MALLOC) && defined(SK_FIND_MEMORY_LEAKS) + gMaxDrawTime = time; +#endif + SkEvent_Globals& globals = *(SkEvent_Globals*)SkGlobals::Find(SK_Event_GlobalsTag, create_globals); + + evt->fTargetID = sinkID; + +#ifdef SK_TRACE_EVENTS + { + SkString str("SkEvent::Post("); + str.append(evt->getType()); + str.append(", 0x"); + str.appendHex(sinkID); + str.append(", "); + str.appendS32(time); + str.append(")"); + event_log(str.c_str()); + } +#endif + + globals.fEventMutex.acquire(); + SkMSec queueDelay = SkEvent::EnqueueTime(evt, time); + globals.fEventMutex.release(); + + // call outside of us holding the mutex + if ((int32_t)queueDelay != ~0) + SkEvent::SignalQueueTimer(queueDelay); + return true; +} + +bool SkEvent::Enqueue(SkEvent* evt) +{ + SkEvent_Globals& globals = *(SkEvent_Globals*)SkGlobals::Find(SK_Event_GlobalsTag, create_globals); + // gEventMutex acquired by caller + + SkASSERT(evt); + + bool wasEmpty = globals.fEventQHead == NULL; + + if (globals.fEventQTail) + globals.fEventQTail->fNextEvent = evt; + globals.fEventQTail = evt; + if (globals.fEventQHead == NULL) + globals.fEventQHead = evt; + evt->fNextEvent = NULL; + + SkDEBUGCODE(++globals.fEventCounter); +// SkDebugf("Enqueue: count=%d\n", gEventCounter); + + return wasEmpty; +} + +SkEvent* SkEvent::Dequeue(SkEventSinkID* sinkID) +{ + SkEvent_Globals& globals = *(SkEvent_Globals*)SkGlobals::Find(SK_Event_GlobalsTag, create_globals); + globals.fEventMutex.acquire(); + + SkEvent* evt = globals.fEventQHead; + if (evt) + { + SkDEBUGCODE(--globals.fEventCounter); + + if (sinkID) + *sinkID = evt->fTargetID; + + globals.fEventQHead = evt->fNextEvent; + if (globals.fEventQHead == NULL) + globals.fEventQTail = NULL; + } + globals.fEventMutex.release(); + +// SkDebugf("Dequeue: count=%d\n", gEventCounter); + + return evt; +} + +bool SkEvent::QHasEvents() +{ + SkEvent_Globals& globals = *(SkEvent_Globals*)SkGlobals::Find(SK_Event_GlobalsTag, create_globals); + + // this is not thread accurate, need a semaphore for that + return globals.fEventQHead != NULL; +} + +#ifdef SK_TRACE_EVENTS + static int gDelayDepth; +#endif + +SkMSec SkEvent::EnqueueTime(SkEvent* evt, SkMSec time) +{ +#ifdef SK_TRACE_EVENTS + SkDebugf("enqueue-delay %s %d (%d)", evt->getType(), time, gDelayDepth); + const char* idStr = evt->findString("id"); + if (idStr) + SkDebugf(" (%s)", idStr); + SkDebugf("\n"); + ++gDelayDepth; +#endif + + SkEvent_Globals& globals = *(SkEvent_Globals*)SkGlobals::Find(SK_Event_GlobalsTag, create_globals); + // gEventMutex acquired by caller + + SkEvent* curr = globals.fDelayQHead; + SkEvent* prev = NULL; + + while (curr) + { + if (SkMSec_LT(time, curr->fTime)) + break; + prev = curr; + curr = curr->fNextEvent; + } + + evt->fTime = time; + evt->fNextEvent = curr; + if (prev == NULL) + globals.fDelayQHead = evt; + else + prev->fNextEvent = evt; + + SkMSec delay = globals.fDelayQHead->fTime - SkTime::GetMSecs(); + if ((int32_t)delay <= 0) + delay = 1; + return delay; +} + +////////////////////////////////////////////////////////////////////////////// + +#include "SkEventSink.h" + +bool SkEvent::ProcessEvent() +{ + SkEventSinkID sinkID; + SkEvent* evt = SkEvent::Dequeue(&sinkID); + SkAutoTDelete<SkEvent> autoDelete(evt); + bool again = false; + + EVENT_LOGN("ProcessEvent", (int32_t)evt); + + if (evt) + { + (void)SkEventSink::DoEvent(*evt, sinkID); + again = SkEvent::QHasEvents(); + } + return again; +} + +void SkEvent::ServiceQueueTimer() +{ + SkEvent_Globals& globals = *(SkEvent_Globals*)SkGlobals::Find(SK_Event_GlobalsTag, create_globals); + + globals.fEventMutex.acquire(); + + bool wasEmpty = false; + SkMSec now = SkTime::GetMSecs(); + SkEvent* evt = globals.fDelayQHead; + + while (evt) + { + if (SkMSec_LT(now, evt->fTime)) + break; + +#ifdef SK_TRACE_EVENTS + --gDelayDepth; + SkDebugf("dequeue-delay %s (%d)", evt->getType(), gDelayDepth); + const char* idStr = evt->findString("id"); + if (idStr) + SkDebugf(" (%s)", idStr); + SkDebugf("\n"); +#endif + + SkEvent* next = evt->fNextEvent; + if (SkEvent::Enqueue(evt)) + wasEmpty = true; + evt = next; + } + globals.fDelayQHead = evt; + + SkMSec time = evt ? evt->fTime - now : 0; + + globals.fEventMutex.release(); + + if (wasEmpty) + SkEvent::SignalNonEmptyQueue(); + + SkEvent::SignalQueueTimer(time); +} + +//////////////////////////////////////////////////////////////// + +void SkEvent::Init() +{ +} + +void SkEvent::Term() +{ + SkEvent_Globals& globals = *(SkEvent_Globals*)SkGlobals::Find(SK_Event_GlobalsTag, create_globals); + + SkEvent* evt = globals.fEventQHead; + while (evt) + { + SkEvent* next = evt->fNextEvent; + delete evt; + evt = next; + } + + evt = globals.fDelayQHead; + while (evt) + { + SkEvent* next = evt->fNextEvent; + delete evt; + evt = next; + } +} + diff --git a/skia/views/SkEventSink.cpp b/skia/views/SkEventSink.cpp new file mode 100644 index 0000000..8c9d73c --- /dev/null +++ b/skia/views/SkEventSink.cpp @@ -0,0 +1,345 @@ +/* libs/graphics/views/SkEventSink.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkEventSink.h" +#include "SkTagList.h" +#include "SkThread.h" + +#include "SkGlobals.h" +#include "SkThread.h" +#include "SkTime.h" + +#define SK_EventSink_GlobalsTag SkSetFourByteTag('e', 'v', 's', 'k') + +class SkEventSink_Globals : public SkGlobals::Rec { +public: + SkMutex fSinkMutex; + SkEventSinkID fNextSinkID; + SkEventSink* fSinkHead; +}; + +static SkGlobals::Rec* create_globals() +{ + SkEventSink_Globals* rec = new SkEventSink_Globals; + rec->fNextSinkID = 0; + rec->fSinkHead = NULL; + return rec; +} + +SkEventSink::SkEventSink() : fTagHead(NULL) +{ + SkEventSink_Globals& globals = *(SkEventSink_Globals*)SkGlobals::Find(SK_EventSink_GlobalsTag, create_globals); + + globals.fSinkMutex.acquire(); + + fID = ++globals.fNextSinkID; + fNextSink = globals.fSinkHead; + globals.fSinkHead = this; + + globals.fSinkMutex.release(); +} + +SkEventSink::~SkEventSink() +{ + SkEventSink_Globals& globals = *(SkEventSink_Globals*)SkGlobals::Find(SK_EventSink_GlobalsTag, create_globals); + + if (fTagHead) + SkTagList::DeleteAll(fTagHead); + + globals.fSinkMutex.acquire(); + + SkEventSink* sink = globals.fSinkHead; + SkEventSink* prev = NULL; + + for (;;) + { + SkEventSink* next = sink->fNextSink; + if (sink == this) + { + if (prev) + prev->fNextSink = next; + else + globals.fSinkHead = next; + break; + } + prev = sink; + sink = next; + } + globals.fSinkMutex.release(); +} + +bool SkEventSink::doEvent(const SkEvent& evt) +{ + return this->onEvent(evt); +} + +bool SkEventSink::doQuery(SkEvent* evt) +{ + SkASSERT(evt); + return this->onQuery(evt); +} + +bool SkEventSink::onEvent(const SkEvent&) +{ + return false; +} + +bool SkEventSink::onQuery(SkEvent*) +{ + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +SkTagList* SkEventSink::findTagList(U8CPU tag) const +{ + return fTagHead ? SkTagList::Find(fTagHead, tag) : NULL; +} + +void SkEventSink::addTagList(SkTagList* rec) +{ + SkASSERT(rec); + SkASSERT(fTagHead == NULL || SkTagList::Find(fTagHead, rec->fTag) == NULL); + + rec->fNext = fTagHead; + fTagHead = rec; +} + +void SkEventSink::removeTagList(U8CPU tag) +{ + if (fTagHead) + SkTagList::DeleteTag(&fTagHead, tag); +} + +/////////////////////////////////////////////////////////////////////////////// + +struct SkListenersTagList : SkTagList { + SkListenersTagList(U16CPU count) : SkTagList(kListeners_SkTagList) + { + fExtra16 = SkToU16(count); + fIDs = (SkEventSinkID*)sk_malloc_throw(count * sizeof(SkEventSinkID)); + } + virtual ~SkListenersTagList() + { + sk_free(fIDs); + } + + int countListners() const { return fExtra16; } + + int find(SkEventSinkID id) const + { + const SkEventSinkID* idptr = fIDs; + for (int i = fExtra16 - 1; i >= 0; --i) + if (idptr[i] == id) + return i; + return -1; + } + + SkEventSinkID* fIDs; +}; + +void SkEventSink::addListenerID(SkEventSinkID id) +{ + if (id == 0) + return; + + SkListenersTagList* prev = (SkListenersTagList*)this->findTagList(kListeners_SkTagList); + int count = 0; + + if (prev) + { + if (prev->find(id) >= 0) + return; + count = prev->countListners(); + } + + SkListenersTagList* next = SkNEW_ARGS(SkListenersTagList, (count + 1)); + + if (prev) + { + memcpy(next->fIDs, prev->fIDs, count * sizeof(SkEventSinkID)); + this->removeTagList(kListeners_SkTagList); + } + next->fIDs[count] = id; + this->addTagList(next); +} + +void SkEventSink::copyListeners(const SkEventSink& sink) +{ + SkListenersTagList* sinkList = (SkListenersTagList*)sink.findTagList(kListeners_SkTagList); + if (sinkList == NULL) + return; + SkASSERT(sinkList->countListners() > 0); + const SkEventSinkID* iter = sinkList->fIDs; + const SkEventSinkID* stop = iter + sinkList->countListners(); + while (iter < stop) + addListenerID(*iter++); +} + +void SkEventSink::removeListenerID(SkEventSinkID id) +{ + if (id == 0) + return; + + SkListenersTagList* list = (SkListenersTagList*)this->findTagList(kListeners_SkTagList); + + if (list == NULL) + return; + + int index = list->find(id); + if (index >= 0) + { + int count = list->countListners(); + SkASSERT(count > 0); + if (count == 1) + this->removeTagList(kListeners_SkTagList); + else + { + // overwrite without resize/reallocating our struct (for speed) + list->fIDs[index] = list->fIDs[count - 1]; + list->fExtra16 = SkToU16(count - 1); + } + } +} + +bool SkEventSink::hasListeners() const +{ + return this->findTagList(kListeners_SkTagList) != NULL; +} + +void SkEventSink::postToListeners(const SkEvent& evt, SkMSec delay) +{ + SkListenersTagList* list = (SkListenersTagList*)this->findTagList(kListeners_SkTagList); + if (list) + { + SkASSERT(list->countListners() > 0); + const SkEventSinkID* iter = list->fIDs; + const SkEventSinkID* stop = iter + list->countListners(); + while (iter < stop) + (SkNEW_ARGS(SkEvent, (evt)))->post(*iter++, delay); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +SkEventSink::EventResult SkEventSink::DoEvent(const SkEvent& evt, SkEventSinkID sinkID) +{ + SkEventSink* sink = SkEventSink::FindSink(sinkID); + + if (sink) + { +#ifdef SK_DEBUG + if (evt.isDebugTrace()) + { + SkString etype; + evt.getType(&etype); + SkDebugf("SkEventTrace: dispatching event <%s> to 0x%x", etype.c_str(), sinkID); + const char* idStr = evt.findString("id"); + if (idStr) + SkDebugf(" (%s)", idStr); + SkDebugf("\n"); + } +#endif + return sink->doEvent(evt) ? kHandled_EventResult : kNotHandled_EventResult; + } + else + { +#ifdef SK_DEBUG + if (sinkID) + SkDebugf("DoEvent: Can't find sink for ID(%x)\n", sinkID); + else + SkDebugf("Event sent to 0 sinkID\n"); + + if (evt.isDebugTrace()) + { + SkString etype; + evt.getType(&etype); + SkDebugf("SkEventTrace: eventsink not found <%s> for 0x%x\n", etype.c_str(), sinkID); + } +#endif + return kSinkNotFound_EventResult; + } +} + +SkEventSink* SkEventSink::FindSink(SkEventSinkID sinkID) +{ + if (sinkID == 0) + return 0; + + SkEventSink_Globals& globals = *(SkEventSink_Globals*)SkGlobals::Find(SK_EventSink_GlobalsTag, create_globals); + SkAutoMutexAcquire ac(globals.fSinkMutex); + SkEventSink* sink = globals.fSinkHead; + + while (sink) + { + if (sink->getSinkID() == sinkID) + return sink; + sink = sink->fNextSink; + } + return NULL; +} + +//////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////// + +#if 0 // experimental, not tested + +#include "SkThread.h" +#include "SkTDict.h" + +#define kMinStringBufferSize 128 +static SkMutex gNamedSinkMutex; +static SkTDict<SkEventSinkID> gNamedSinkIDs(kMinStringBufferSize); + +/** Register a name/id pair with the system. If the name already exists, + replace its ID with the new id. This pair will persist until UnregisterNamedSink() + is called. +*/ +void SkEventSink::RegisterNamedSinkID(const char name[], SkEventSinkID id) +{ + if (id && name && *name) + { + SkAutoMutexAcquire ac(gNamedSinkMutex); + gNamedSinkIDs.set(name, id); + } +} + +/** Return the id that matches the specified name (from a previous call to + RegisterNamedSinkID(). If no match is found, return 0 +*/ +SkEventSinkID SkEventSink::FindNamedSinkID(const char name[]) +{ + SkEventSinkID id = 0; + + if (name && *name) + { + SkAutoMutexAcquire ac(gNamedSinkMutex); + (void)gNamedSinkIDs.find(name, &id); + } + return id; +} + +/** Remove all name/id pairs from the system. This is call internally + on shutdown, to ensure no memory leaks. It should not be called + before shutdown. +*/ +void SkEventSink::RemoveAllNamedSinkIDs() +{ + SkAutoMutexAcquire ac(gNamedSinkMutex); + (void)gNamedSinkIDs.reset(); +} +#endif diff --git a/skia/views/SkMetaData.cpp b/skia/views/SkMetaData.cpp new file mode 100644 index 0000000..1d423a4 --- /dev/null +++ b/skia/views/SkMetaData.cpp @@ -0,0 +1,405 @@ +/* libs/graphics/views/SkMetaData.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkMetaData.h" + +SkMetaData::SkMetaData() : fRec(NULL) +{ +} + +SkMetaData::SkMetaData(const SkMetaData& src) : fRec(NULL) +{ + *this = src; +} + +SkMetaData::~SkMetaData() +{ + this->reset(); +} + +void SkMetaData::reset() +{ + Rec* rec = fRec; + while (rec) + { + Rec* next = rec->fNext; + Rec::Free(rec); + rec = next; + } + fRec = NULL; +} + +SkMetaData& SkMetaData::operator=(const SkMetaData& src) +{ + this->reset(); + + const Rec* rec = src.fRec; + while (rec) + { + this->set(rec->name(), rec->data(), rec->fDataLen, (Type)rec->fType, rec->fDataCount); + rec = rec->fNext; + } + return *this; +} + +void SkMetaData::setS32(const char name[], int32_t value) +{ + (void)this->set(name, &value, sizeof(int32_t), kS32_Type, 1); +} + +void SkMetaData::setScalar(const char name[], SkScalar value) +{ + (void)this->set(name, &value, sizeof(SkScalar), kScalar_Type, 1); +} + +SkScalar* SkMetaData::setScalars(const char name[], int count, const SkScalar values[]) +{ + SkASSERT(count > 0); + if (count > 0) + return (SkScalar*)this->set(name, values, sizeof(SkScalar), kScalar_Type, count); + return NULL; +} + +void SkMetaData::setString(const char name[], const char value[]) +{ + (void)this->set(name, value, sizeof(char), kString_Type, strlen(value) + 1); +} + +void SkMetaData::setPtr(const char name[], void* ptr) +{ + (void)this->set(name, &ptr, sizeof(void*), kPtr_Type, 1); +} + +void SkMetaData::setBool(const char name[], bool value) +{ + (void)this->set(name, &value, sizeof(bool), kBool_Type, 1); +} + +void* SkMetaData::set(const char name[], const void* data, size_t dataSize, Type type, int count) +{ + SkASSERT(name); + SkASSERT(dataSize); + SkASSERT(count > 0); + + (void)this->remove(name, type); + + size_t len = strlen(name); + Rec* rec = Rec::Alloc(sizeof(Rec) + dataSize * count + len + 1); + +#ifndef SK_DEBUG + rec->fType = SkToU8(type); +#else + rec->fType = type; +#endif + rec->fDataLen = SkToU8(dataSize); + rec->fDataCount = SkToU16(count); + if (data) + memcpy(rec->data(), data, dataSize * count); + memcpy(rec->name(), name, len + 1); + +#ifdef SK_DEBUG + rec->fName = rec->name(); + switch (type) { + case kS32_Type: + rec->fData.fS32 = *(const int32_t*)rec->data(); + break; + case kScalar_Type: + rec->fData.fScalar = *(const SkScalar*)rec->data(); + break; + case kString_Type: + rec->fData.fString = (const char*)rec->data(); + break; + case kPtr_Type: + rec->fData.fPtr = *(void**)rec->data(); + break; + case kBool_Type: + rec->fData.fBool = *(const bool*)rec->data(); + break; + default: + SkASSERT(!"bad type"); + break; + } +#endif + + rec->fNext = fRec; + fRec = rec; + return rec->data(); +} + +bool SkMetaData::findS32(const char name[], int32_t* value) const +{ + const Rec* rec = this->find(name, kS32_Type); + if (rec) + { + SkASSERT(rec->fDataCount == 1); + if (value) + *value = *(const int32_t*)rec->data(); + return true; + } + return false; +} + +bool SkMetaData::findScalar(const char name[], SkScalar* value) const +{ + const Rec* rec = this->find(name, kScalar_Type); + if (rec) + { + SkASSERT(rec->fDataCount == 1); + if (value) + *value = *(const SkScalar*)rec->data(); + return true; + } + return false; +} + +const SkScalar* SkMetaData::findScalars(const char name[], int* count, SkScalar values[]) const +{ + const Rec* rec = this->find(name, kScalar_Type); + if (rec) + { + if (count) + *count = rec->fDataCount; + if (values) + memcpy(values, rec->data(), rec->fDataCount * rec->fDataLen); + return (const SkScalar*)rec->data(); + } + return NULL; +} + +bool SkMetaData::findPtr(const char name[], void** value) const +{ + const Rec* rec = this->find(name, kPtr_Type); + if (rec) + { + SkASSERT(rec->fDataCount == 1); + if (value) + *value = *(void**)rec->data(); + return true; + } + return false; +} + +const char* SkMetaData::findString(const char name[]) const +{ + const Rec* rec = this->find(name, kString_Type); + SkASSERT(rec == NULL || rec->fDataLen == sizeof(char)); + return rec ? (const char*)rec->data() : NULL; +} + +bool SkMetaData::findBool(const char name[], bool* value) const +{ + const Rec* rec = this->find(name, kBool_Type); + if (rec) + { + SkASSERT(rec->fDataCount == 1); + if (value) + *value = *(const bool*)rec->data(); + return true; + } + return false; +} + +const SkMetaData::Rec* SkMetaData::find(const char name[], Type type) const +{ + const Rec* rec = fRec; + while (rec) + { + if (rec->fType == type && !strcmp(rec->name(), name)) + return rec; + rec = rec->fNext; + } + return NULL; +} + +bool SkMetaData::remove(const char name[], Type type) +{ + Rec* rec = fRec; + Rec* prev = NULL; + while (rec) + { + Rec* next = rec->fNext; + if (rec->fType == type && !strcmp(rec->name(), name)) + { + if (prev) + prev->fNext = next; + else + fRec = next; + Rec::Free(rec); + return true; + } + prev = rec; + rec = next; + } + return false; +} + +bool SkMetaData::removeS32(const char name[]) +{ + return this->remove(name, kS32_Type); +} + +bool SkMetaData::removeScalar(const char name[]) +{ + return this->remove(name, kScalar_Type); +} + +bool SkMetaData::removeString(const char name[]) +{ + return this->remove(name, kString_Type); +} + +bool SkMetaData::removePtr(const char name[]) +{ + return this->remove(name, kPtr_Type); +} + +bool SkMetaData::removeBool(const char name[]) +{ + return this->remove(name, kBool_Type); +} + +/////////////////////////////////////////////////////////////////////////////////// + +SkMetaData::Iter::Iter(const SkMetaData& metadata) +{ + fRec = metadata.fRec; +} + +void SkMetaData::Iter::reset(const SkMetaData& metadata) +{ + fRec = metadata.fRec; +} + +const char* SkMetaData::Iter::next(SkMetaData::Type* t, int* count) +{ + const char* name = NULL; + + if (fRec) + { + if (t) + *t = (SkMetaData::Type)fRec->fType; + if (count) + *count = fRec->fDataCount; + name = fRec->name(); + + fRec = fRec->fNext; + } + return name; +} + +/////////////////////////////////////////////////////////////////////////////////// + +SkMetaData::Rec* SkMetaData::Rec::Alloc(size_t size) +{ + return (Rec*)sk_malloc_throw(size); +} + +void SkMetaData::Rec::Free(Rec* rec) +{ + sk_free(rec); +} + +/////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_DEBUG + +void SkMetaData::UnitTest() +{ +#ifdef SK_SUPPORT_UNITTEST + SkMetaData m1; + + SkASSERT(!m1.findS32("int")); + SkASSERT(!m1.findScalar("scalar")); + SkASSERT(!m1.findString("hello")); + SkASSERT(!m1.removeS32("int")); + SkASSERT(!m1.removeScalar("scalar")); + SkASSERT(!m1.removeString("hello")); + SkASSERT(!m1.removeString("true")); + SkASSERT(!m1.removeString("false")); + + m1.setS32("int", 12345); + m1.setScalar("scalar", SK_Scalar1 * 42); + m1.setString("hello", "world"); + m1.setPtr("ptr", &m1); + m1.setBool("true", true); + m1.setBool("false", false); + + int32_t n; + SkScalar s; + + m1.setScalar("scalar", SK_Scalar1/2); + + SkASSERT(m1.findS32("int", &n) && n == 12345); + SkASSERT(m1.findScalar("scalar", &s) && s == SK_Scalar1/2); + SkASSERT(!strcmp(m1.findString("hello"), "world")); + SkASSERT(m1.hasBool("true", true)); + SkASSERT(m1.hasBool("false", false)); + + Iter iter(m1); + const char* name; + + static const struct { + const char* fName; + SkMetaData::Type fType; + int fCount; + } gElems[] = { + { "int", SkMetaData::kS32_Type, 1 }, + { "scalar", SkMetaData::kScalar_Type, 1 }, + { "ptr", SkMetaData::kPtr_Type, 1 }, + { "hello", SkMetaData::kString_Type, sizeof("world") }, + { "true", SkMetaData::kBool_Type, 1 }, + { "false", SkMetaData::kBool_Type, 1 } + }; + + int loop = 0; + int count; + SkMetaData::Type t; + while ((name = iter.next(&t, &count)) != NULL) + { + int match = 0; + for (unsigned i = 0; i < SK_ARRAY_COUNT(gElems); i++) + { + if (!strcmp(name, gElems[i].fName)) + { + match += 1; + SkASSERT(gElems[i].fType == t); + SkASSERT(gElems[i].fCount == count); + } + } + SkASSERT(match == 1); + loop += 1; + } + SkASSERT(loop == SK_ARRAY_COUNT(gElems)); + + SkASSERT(m1.removeS32("int")); + SkASSERT(m1.removeScalar("scalar")); + SkASSERT(m1.removeString("hello")); + SkASSERT(m1.removeBool("true")); + SkASSERT(m1.removeBool("false")); + + SkASSERT(!m1.findS32("int")); + SkASSERT(!m1.findScalar("scalar")); + SkASSERT(!m1.findString("hello")); + SkASSERT(!m1.findBool("true")); + SkASSERT(!m1.findBool("false")); +#endif +} + +#endif + + diff --git a/skia/views/SkTagList.cpp b/skia/views/SkTagList.cpp new file mode 100644 index 0000000..4d73000 --- /dev/null +++ b/skia/views/SkTagList.cpp @@ -0,0 +1,71 @@ +/* libs/graphics/views/SkTagList.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkTagList.h" + +SkTagList::~SkTagList() +{ +} + +SkTagList* SkTagList::Find(SkTagList* rec, U8CPU tag) +{ + SkASSERT(tag < kSkTagListCount); + + while (rec != NULL) + { + if (rec->fTag == tag) + break; + rec = rec->fNext; + } + return rec; +} + +void SkTagList::DeleteTag(SkTagList** head, U8CPU tag) +{ + SkASSERT(tag < kSkTagListCount); + + SkTagList* rec = *head; + SkTagList* prev = NULL; + + while (rec != NULL) + { + SkTagList* next = rec->fNext; + + if (rec->fTag == tag) + { + if (prev) + prev->fNext = next; + else + *head = next; + delete rec; + break; + } + prev = rec; + rec = next; + } +} + +void SkTagList::DeleteAll(SkTagList* rec) +{ + while (rec) + { + SkTagList* next = rec->fNext; + delete rec; + rec = next; + } +} + diff --git a/skia/views/SkTagList.h b/skia/views/SkTagList.h new file mode 100644 index 0000000..c093fa0 --- /dev/null +++ b/skia/views/SkTagList.h @@ -0,0 +1,51 @@ +/* libs/graphics/views/SkTagList.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkTagList_DEFINED +#define SkTagList_DEFINED + +#include "SkTypes.h" + +enum SkTagListEnum { + kListeners_SkTagList, + kViewLayout_SkTagList, + kViewArtist_SkTagList, + + kSkTagListCount +}; + +struct SkTagList { + SkTagList* fNext; + uint16_t fExtra16; + uint8_t fExtra8; + uint8_t fTag; + + SkTagList(U8CPU tag) : fTag(SkToU8(tag)) + { + SkASSERT(tag < kSkTagListCount); + fNext = NULL; + fExtra16 = 0; + fExtra8 = 0; + } + virtual ~SkTagList(); + + static SkTagList* Find(SkTagList* head, U8CPU tag); + static void DeleteTag(SkTagList** headptr, U8CPU tag); + static void DeleteAll(SkTagList* head); +}; + +#endif diff --git a/skia/views/SkTextBox.cpp b/skia/views/SkTextBox.cpp new file mode 100644 index 0000000..08ec4fd --- /dev/null +++ b/skia/views/SkTextBox.cpp @@ -0,0 +1,216 @@ +/* libs/graphics/views/SkTextBox.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkTextBox.h" +#include "SkGlyphCache.h" +#include "SkUtils.h" +#include "SkAutoKern.h" + +static inline int is_ws(int c) +{ + return !((c - 1) >> 5); +} + +static size_t linebreak(const char text[], const char stop[], const SkPaint& paint, SkScalar margin) +{ + const char* start = text; + + SkAutoGlyphCache ac(paint, NULL); + SkGlyphCache* cache = ac.getCache(); + SkFixed w = 0; + SkFixed limit = SkScalarToFixed(margin); + SkAutoKern autokern; + + const char* word_start = text; + int prevWS = true; + + while (text < stop) + { + const char* prevText = text; + SkUnichar uni = SkUTF8_NextUnichar(&text); + int currWS = is_ws(uni); + const SkGlyph& glyph = cache->getUnicharMetrics(uni); + + if (!currWS && prevWS) + word_start = prevText; + prevWS = currWS; + + w += autokern.adjust(glyph) + glyph.fAdvanceX; + if (w > limit) + { + if (currWS) // eat the rest of the whitespace + { + while (text < stop && is_ws(SkUTF8_ToUnichar(text))) + text += SkUTF8_CountUTF8Bytes(text); + } + else // backup until a whitespace (or 1 char) + { + if (word_start == start) + { + if (prevText > start) + text = prevText; + } + else + text = word_start; + } + break; + } + } + return text - start; +} + +int SkTextLineBreaker::CountLines(const char text[], size_t len, const SkPaint& paint, SkScalar width) +{ + const char* stop = text + len; + int count = 0; + + if (width > 0) + { + do { + count += 1; + text += linebreak(text, stop, paint, width); + } while (text < stop); + } + return count; +} + +////////////////////////////////////////////////////////////////////////////// + +SkTextBox::SkTextBox() +{ + fBox.setEmpty(); + fSpacingMul = SK_Scalar1; + fSpacingAdd = 0; + fMode = kLineBreak_Mode; + fSpacingAlign = kStart_SpacingAlign; +} + +void SkTextBox::setMode(Mode mode) +{ + SkASSERT((unsigned)mode < kModeCount); + fMode = SkToU8(mode); +} + +void SkTextBox::setSpacingAlign(SpacingAlign align) +{ + SkASSERT((unsigned)align < kSpacingAlignCount); + fSpacingAlign = SkToU8(align); +} + +void SkTextBox::getBox(SkRect* box) const +{ + if (box) + *box = fBox; +} + +void SkTextBox::setBox(const SkRect& box) +{ + fBox = box; +} + +void SkTextBox::setBox(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) +{ + fBox.set(left, top, right, bottom); +} + +void SkTextBox::getSpacing(SkScalar* mul, SkScalar* add) const +{ + if (mul) + *mul = fSpacingMul; + if (add) + *add = fSpacingAdd; +} + +void SkTextBox::setSpacing(SkScalar mul, SkScalar add) +{ + fSpacingMul = mul; + fSpacingAdd = add; +} + +///////////////////////////////////////////////////////////////////////////////////////////// + +void SkTextBox::draw(SkCanvas* canvas, const char text[], size_t len, const SkPaint& paint) +{ + SkASSERT(canvas && &paint && (text || len == 0)); + + SkScalar marginWidth = fBox.width(); + + if (marginWidth <= 0 || len == 0) + return; + + const char* textStop = text + len; + + SkScalar x, y, scaledSpacing, height, fontHeight; + SkPaint::FontMetrics metrics; + + switch (paint.getTextAlign()) { + case SkPaint::kLeft_Align: + x = 0; + break; + case SkPaint::kCenter_Align: + x = SkScalarHalf(marginWidth); + break; + default: + x = marginWidth; + break; + } + x += fBox.fLeft; + + fontHeight = paint.getFontMetrics(&metrics); + scaledSpacing = SkScalarMul(fontHeight, fSpacingMul) + fSpacingAdd; + height = fBox.height(); + + // compute Y position for first line + { + SkScalar textHeight = fontHeight; + + if (fMode == kLineBreak_Mode && fSpacingAlign != kStart_SpacingAlign) + { + int count = SkTextLineBreaker::CountLines(text, textStop - text, paint, marginWidth); + SkASSERT(count > 0); + textHeight += scaledSpacing * (count - 1); + } + + switch (fSpacingAlign) { + case kStart_SpacingAlign: + y = 0; + break; + case kCenter_SpacingAlign: + y = SkScalarHalf(height - textHeight); + break; + default: + SkASSERT(fSpacingAlign == kEnd_SpacingAlign); + y = height - textHeight; + break; + } + y += fBox.fTop - metrics.fAscent; + } + + for (;;) + { + len = linebreak(text, textStop, paint, marginWidth); + if (y + metrics.fDescent + metrics.fLeading > 0) + canvas->drawText(text, len, x, y, paint); + text += len; + if (text >= textStop) + break; + y += scaledSpacing; + if (y + metrics.fAscent >= height) + break; + } +} + diff --git a/skia/xml/SkBML_Verbs.h b/skia/xml/SkBML_Verbs.h new file mode 100644 index 0000000..2d17d87 --- /dev/null +++ b/skia/xml/SkBML_Verbs.h @@ -0,0 +1,33 @@ +/* libs/graphics/xml/SkBML_Verbs.h +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkBML_Verbs_DEFINED +#define SkBML_Verbs_DEFINED + +enum Verbs { + kStartElem_Value_Verb, + kStartElem_Index_Verb, + kEndElem_Verb, + kAttr_Value_Value_Verb, + kAttr_Value_Index_Verb, + kAttr_Index_Value_Verb, + kAttr_Index_Index_Verb, + + kVerbCount +}; + +#endif // SkBML_Verbs_DEFINED diff --git a/skia/xml/SkBML_XMLParser.cpp b/skia/xml/SkBML_XMLParser.cpp new file mode 100644 index 0000000..34b86a1 --- /dev/null +++ b/skia/xml/SkBML_XMLParser.cpp @@ -0,0 +1,192 @@ +/* libs/graphics/xml/SkBML_XMLParser.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkBML_XMLParser.h" +#include "SkBML_Verbs.h" +#include "SkStream.h" +#include "SkXMLWriter.h" + +static uint8_t rbyte(SkStream& s) +{ + uint8_t b; + size_t size = s.read(&b, 1); + SkASSERT(size == 1); + return b; +} + +static int rdata(SkStream& s, int data) +{ + SkASSERT((data & ~31) == 0); + if (data == 31) + { + data = rbyte(s); + if (data == 0xFF) + { + data = rbyte(s); + data = (data << 8) | rbyte(s); + } + } + return data; +} + +static void set(char* array[256], int index, SkStream& s, int data) +{ + SkASSERT((unsigned)index <= 255); + + size_t size = rdata(s, data); + + if (array[index] == NULL) + array[index] = (char*)sk_malloc_throw(size + 1); + else + { + if (strlen(array[index]) < size) + array[index] = (char*)sk_realloc_throw(array[index], size + 1); + } + + s.read(array[index], size); + array[index][size] = 0; +} + +static void freeAll(char* array[256]) +{ + for (int i = 0; i < 256; i++) + sk_free(array[i]); +} + +struct BMLW { + char* fElems[256]; + char* fNames[256]; + char* fValues[256]; + + // important that these are uint8_t, so we get automatic wrap-around + uint8_t fNextElem, fNextName, fNextValue; + + BMLW() + { + memset(fElems, 0, sizeof(fElems)); + memset(fNames, 0, sizeof(fNames)); + memset(fValues, 0, sizeof(fValues)); + + fNextElem = fNextName = fNextValue = 0; + } + ~BMLW() + { + freeAll(fElems); + freeAll(fNames); + freeAll(fValues); + } +}; + +static void rattr(unsigned verb, SkStream& s, BMLW& rec, SkXMLWriter& writer) +{ + int data = verb & 31; + verb >>= 5; + + int nameIndex, valueIndex; + + switch (verb) { + case kAttr_Value_Value_Verb: + nameIndex = rec.fNextName; // record before the ++ + set(rec.fNames, rec.fNextName++, s, data); + valueIndex = rec.fNextValue; // record before the ++ + set(rec.fValues, rec.fNextValue++, s, 31); + break; + case kAttr_Value_Index_Verb: + nameIndex = rec.fNextName; // record before the ++ + set(rec.fNames, rec.fNextName++, s, data); + valueIndex = rbyte(s); + break; + case kAttr_Index_Value_Verb: + nameIndex = rdata(s, data); + valueIndex = rec.fNextValue; // record before the ++ + set(rec.fValues, rec.fNextValue++, s, 31); + break; + case kAttr_Index_Index_Verb: + nameIndex = rdata(s, data); + valueIndex = rbyte(s); + break; + default: + SkASSERT(!"bad verb"); + return; + } + writer.addAttribute(rec.fNames[nameIndex], rec.fValues[valueIndex]); +} + +static void relem(unsigned verb, SkStream& s, BMLW& rec, SkXMLWriter& writer) +{ + int data = verb & 31; + verb >>= 5; + + int elemIndex; + + if (verb == kStartElem_Value_Verb) + { + elemIndex = rec.fNextElem; // record before the ++ + set(rec.fElems, rec.fNextElem++, s, data); + } + else + { + SkASSERT(verb == kStartElem_Index_Verb); + elemIndex = rdata(s, data); + } + + writer.startElement(rec.fElems[elemIndex]); + + for (;;) + { + verb = rbyte(s); + switch (verb >> 5) { + case kAttr_Value_Value_Verb: + case kAttr_Value_Index_Verb: + case kAttr_Index_Value_Verb: + case kAttr_Index_Index_Verb: + rattr(verb, s, rec, writer); + break; + case kStartElem_Value_Verb: + case kStartElem_Index_Verb: + relem(verb, s, rec, writer); + break; + case kEndElem_Verb: + writer.endElement(); + return; + default: + SkASSERT(!"bad verb"); + } + } +} + +void BML_XMLParser::Read(SkStream& s, SkXMLWriter& writer) +{ + BMLW rec; + writer.writeHeader(); + relem(rbyte(s), s, rec, writer); +} + +void BML_XMLParser::Read(SkStream& s, SkWStream& output) +{ + SkXMLStreamWriter writer(&output); + Read(s, writer); +} + +void BML_XMLParser::Read(SkStream& s, SkXMLParser& output) +{ + SkXMLParserWriter writer(&output); + Read(s, writer); +} + + + diff --git a/skia/xml/SkDOM.cpp b/skia/xml/SkDOM.cpp new file mode 100644 index 0000000..c738be6 --- /dev/null +++ b/skia/xml/SkDOM.cpp @@ -0,0 +1,512 @@ +/* libs/graphics/xml/SkDOM.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkDOM.h" + +///////////////////////////////////////////////////////////////////////// + +#include "SkXMLParser.h" + +bool SkXMLParser::parse(const SkDOM& dom, const SkDOMNode* node) +{ + const char* elemName = dom.getName(node); + + if (this->startElement(elemName)) + return false; + + SkDOM::AttrIter iter(dom, node); + const char* name, *value; + + while ((name = iter.next(&value)) != NULL) + if (this->addAttribute(name, value)) + return false; + + if ((node = dom.getFirstChild(node)) != NULL) + do { + if (!this->parse(dom, node)) + return false; + } while ((node = dom.getNextSibling(node)) != NULL); + + return !this->endElement(elemName); +} + +///////////////////////////////////////////////////////////////////////// + +struct SkDOMAttr { + const char* fName; + const char* fValue; +}; + +struct SkDOMNode { + const char* fName; + SkDOMNode* fFirstChild; + SkDOMNode* fNextSibling; + uint16_t fAttrCount; + uint8_t fType; + uint8_t fPad; + + const SkDOMAttr* attrs() const + { + return (const SkDOMAttr*)(this + 1); + } + SkDOMAttr* attrs() + { + return (SkDOMAttr*)(this + 1); + } +}; + +///////////////////////////////////////////////////////////////////////// + +#define kMinChunkSize 512 + +SkDOM::SkDOM() : fAlloc(kMinChunkSize), fRoot(NULL) +{ +} + +SkDOM::~SkDOM() +{ +} + +const SkDOM::Node* SkDOM::getRootNode() const +{ + return fRoot; +} + +const SkDOM::Node* SkDOM::getFirstChild(const Node* node, const char name[]) const +{ + SkASSERT(node); + const Node* child = node->fFirstChild; + + if (name) + { + for (; child != NULL; child = child->fNextSibling) + if (!strcmp(name, child->fName)) + break; + } + return child; +} + +const SkDOM::Node* SkDOM::getNextSibling(const Node* node, const char name[]) const +{ + SkASSERT(node); + const Node* sibling = node->fNextSibling; + if (name) + { + for (; sibling != NULL; sibling = sibling->fNextSibling) + if (!strcmp(name, sibling->fName)) + break; + } + return sibling; +} + +SkDOM::Type SkDOM::getType(const Node* node) const +{ + SkASSERT(node); + return (Type)node->fType; +} + +const char* SkDOM::getName(const Node* node) const +{ + SkASSERT(node); + return node->fName; +} + +const char* SkDOM::findAttr(const Node* node, const char name[]) const +{ + SkASSERT(node); + const Attr* attr = node->attrs(); + const Attr* stop = attr + node->fAttrCount; + + while (attr < stop) + { + if (!strcmp(attr->fName, name)) + return attr->fValue; + attr += 1; + } + return NULL; +} + +///////////////////////////////////////////////////////////////////////////////////// + +const SkDOM::Attr* SkDOM::getFirstAttr(const Node* node) const +{ + return node->fAttrCount ? node->attrs() : NULL; +} + +const SkDOM::Attr* SkDOM::getNextAttr(const Node* node, const Attr* attr) const +{ + SkASSERT(node); + if (attr == NULL) + return NULL; + return (attr - node->attrs() + 1) < node->fAttrCount ? attr + 1 : NULL; +} + +const char* SkDOM::getAttrName(const Node* node, const Attr* attr) const +{ + SkASSERT(node); + SkASSERT(attr); + return attr->fName; +} + +const char* SkDOM::getAttrValue(const Node* node, const Attr* attr) const +{ + SkASSERT(node); + SkASSERT(attr); + return attr->fValue; +} + +///////////////////////////////////////////////////////////////////////////////////// + +SkDOM::AttrIter::AttrIter(const SkDOM&, const SkDOM::Node* node) +{ + SkASSERT(node); + fAttr = node->attrs(); + fStop = fAttr + node->fAttrCount; +} + +const char* SkDOM::AttrIter::next(const char** value) +{ + const char* name = NULL; + + if (fAttr < fStop) + { + name = fAttr->fName; + if (value) + *value = fAttr->fValue; + fAttr += 1; + } + return name; +} + +////////////////////////////////////////////////////////////////////////////// + +#include "SkXMLParser.h" +#include "SkTDArray.h" + +static char* dupstr(SkChunkAlloc* chunk, const char src[]) +{ + SkASSERT(chunk && src); + size_t len = strlen(src); + char* dst = (char*)chunk->alloc(len + 1, SkChunkAlloc::kThrow_AllocFailType); + memcpy(dst, src, len + 1); + return dst; +} + +class SkDOMParser : public SkXMLParser { + bool fNeedToFlush; +public: + SkDOMParser(SkChunkAlloc* chunk) : SkXMLParser(&fParserError), fAlloc(chunk) + { + fRoot = NULL; + fLevel = 0; + fNeedToFlush = true; + } + SkDOM::Node* getRoot() const { return fRoot; } + SkXMLParserError fParserError; +protected: + void flushAttributes() + { + int attrCount = fAttrs.count(); + + SkDOM::Node* node = (SkDOM::Node*)fAlloc->alloc(sizeof(SkDOM::Node) + attrCount * sizeof(SkDOM::Attr), + SkChunkAlloc::kThrow_AllocFailType); + + node->fName = fElemName; + node->fFirstChild = NULL; + node->fAttrCount = SkToU16(attrCount); + node->fType = SkDOM::kElement_Type; + + if (fRoot == NULL) + { + node->fNextSibling = NULL; + fRoot = node; + } + else // this adds siblings in reverse order. gets corrected in onEndElement() + { + SkDOM::Node* parent = fParentStack.top(); + SkASSERT(fRoot && parent); + node->fNextSibling = parent->fFirstChild; + parent->fFirstChild = node; + } + *fParentStack.push() = node; + + memcpy(node->attrs(), fAttrs.begin(), attrCount * sizeof(SkDOM::Attr)); + fAttrs.reset(); + + } + virtual bool onStartElement(const char elem[]) + { + if (fLevel > 0 && fNeedToFlush) + this->flushAttributes(); + fNeedToFlush = true; + fElemName = dupstr(fAlloc, elem); + ++fLevel; + return false; + } + virtual bool onAddAttribute(const char name[], const char value[]) + { + SkDOM::Attr* attr = fAttrs.append(); + attr->fName = dupstr(fAlloc, name); + attr->fValue = dupstr(fAlloc, value); + return false; + } + virtual bool onEndElement(const char elem[]) + { + --fLevel; + if (fNeedToFlush) + this->flushAttributes(); + fNeedToFlush = false; + + SkDOM::Node* parent; + + fParentStack.pop(&parent); + + SkDOM::Node* child = parent->fFirstChild; + SkDOM::Node* prev = NULL; + while (child) + { + SkDOM::Node* next = child->fNextSibling; + child->fNextSibling = prev; + prev = child; + child = next; + } + parent->fFirstChild = prev; + return false; + } +private: + SkTDArray<SkDOM::Node*> fParentStack; + SkChunkAlloc* fAlloc; + SkDOM::Node* fRoot; + + // state needed for flushAttributes() + SkTDArray<SkDOM::Attr> fAttrs; + char* fElemName; + int fLevel; +}; + +const SkDOM::Node* SkDOM::build(const char doc[], size_t len) +{ + fAlloc.reset(); + SkDOMParser parser(&fAlloc); + if (!parser.parse(doc, len)) + { + SkDEBUGCODE(SkDebugf("xml parse error, line %d\n", parser.fParserError.getLineNumber());) + fRoot = NULL; + fAlloc.reset(); + return NULL; + } + fRoot = parser.getRoot(); + return fRoot; +} + +/////////////////////////////////////////////////////////////////////////// + +static void walk_dom(const SkDOM& dom, const SkDOM::Node* node, SkXMLParser* parser) +{ + const char* elem = dom.getName(node); + + parser->startElement(elem); + + SkDOM::AttrIter iter(dom, node); + const char* name; + const char* value; + while ((name = iter.next(&value)) != NULL) + parser->addAttribute(name, value); + + node = dom.getFirstChild(node, NULL); + while (node) + { + walk_dom(dom, node, parser); + node = dom.getNextSibling(node, NULL); + } + + parser->endElement(elem); +} + +const SkDOM::Node* SkDOM::copy(const SkDOM& dom, const SkDOM::Node* node) +{ + fAlloc.reset(); + SkDOMParser parser(&fAlloc); + + walk_dom(dom, node, &parser); + + fRoot = parser.getRoot(); + return fRoot; +} + +////////////////////////////////////////////////////////////////////////// + +int SkDOM::countChildren(const Node* node, const char elem[]) const +{ + int count = 0; + + node = this->getFirstChild(node, elem); + while (node) + { + count += 1; + node = this->getNextSibling(node, elem); + } + return count; +} + +////////////////////////////////////////////////////////////////////////// + +#include "SkParse.h" + +bool SkDOM::findS32(const Node* node, const char name[], int32_t* value) const +{ + const char* vstr = this->findAttr(node, name); + return vstr && SkParse::FindS32(vstr, value); +} + +bool SkDOM::findScalars(const Node* node, const char name[], SkScalar value[], int count) const +{ + const char* vstr = this->findAttr(node, name); + return vstr && SkParse::FindScalars(vstr, value, count); +} + +bool SkDOM::findHex(const Node* node, const char name[], uint32_t* value) const +{ + const char* vstr = this->findAttr(node, name); + return vstr && SkParse::FindHex(vstr, value); +} + +bool SkDOM::findBool(const Node* node, const char name[], bool* value) const +{ + const char* vstr = this->findAttr(node, name); + return vstr && SkParse::FindBool(vstr, value); +} + +int SkDOM::findList(const Node* node, const char name[], const char list[]) const +{ + const char* vstr = this->findAttr(node, name); + return vstr ? SkParse::FindList(vstr, list) : -1; +} + +bool SkDOM::hasAttr(const Node* node, const char name[], const char value[]) const +{ + const char* vstr = this->findAttr(node, name); + return vstr && !strcmp(vstr, value); +} + +bool SkDOM::hasS32(const Node* node, const char name[], int32_t target) const +{ + const char* vstr = this->findAttr(node, name); + int32_t value; + return vstr && SkParse::FindS32(vstr, &value) && value == target; +} + +bool SkDOM::hasScalar(const Node* node, const char name[], SkScalar target) const +{ + const char* vstr = this->findAttr(node, name); + SkScalar value; + return vstr && SkParse::FindScalar(vstr, &value) && value == target; +} + +bool SkDOM::hasHex(const Node* node, const char name[], uint32_t target) const +{ + const char* vstr = this->findAttr(node, name); + uint32_t value; + return vstr && SkParse::FindHex(vstr, &value) && value == target; +} + +bool SkDOM::hasBool(const Node* node, const char name[], bool target) const +{ + const char* vstr = this->findAttr(node, name); + bool value; + return vstr && SkParse::FindBool(vstr, &value) && value == target; +} + +////////////////////////////////////////////////////////////////////////// + +#ifdef SK_DEBUG + +static void tab(int level) +{ + while (--level >= 0) + SkDebugf("\t"); +} + +void SkDOM::dump(const Node* node, int level) const +{ + if (node == NULL) + node = this->getRootNode(); + if (node) + { + tab(level); + SkDebugf("<%s", this->getName(node)); + + const Attr* attr = node->attrs(); + const Attr* stop = attr + node->fAttrCount; + for (; attr < stop; attr++) + SkDebugf(" %s=\"%s\"", attr->fName, attr->fValue); + + const Node* child = this->getFirstChild(node); + if (child) + { + SkDebugf(">\n"); + while (child) + { + this->dump(child, level+1); + child = this->getNextSibling(child); + } + tab(level); + SkDebugf("</%s>\n", node->fName); + } + else + SkDebugf("/>\n"); + } +} + +void SkDOM::UnitTest() +{ +#ifdef SK_SUPPORT_UNITTEST + static const char gDoc[] = + "<root a='1' b='2'>" + "<elem1 c='3' />" + "<elem2 d='4' />" + "<elem3 e='5'>" + "<subelem1/>" + "<subelem2 f='6' g='7'/>" + "</elem3>" + "<elem4 h='8'/>" + "</root>" + ; + + SkDOM dom; + + SkASSERT(dom.getRootNode() == NULL); + + const Node* root = dom.build(gDoc, sizeof(gDoc) - 1); + SkASSERT(root && dom.getRootNode() == root); + + const char* v = dom.findAttr(root, "a"); + SkASSERT(v && !strcmp(v, "1")); + v = dom.findAttr(root, "b"); + SkASSERT(v && !strcmp(v, "2")); + v = dom.findAttr(root, "c"); + SkASSERT(v == NULL); + + SkASSERT(dom.getFirstChild(root, "elem1")); + SkASSERT(!dom.getFirstChild(root, "subelem1")); + + dom.dump(); +#endif +} + +#endif + diff --git a/skia/xml/SkJS.cpp b/skia/xml/SkJS.cpp new file mode 100644 index 0000000..626bd6f --- /dev/null +++ b/skia/xml/SkJS.cpp @@ -0,0 +1,237 @@ +/* libs/graphics/xml/SkJS.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include <jsapi.h> + +#include "SkJS.h" +#include "SkString.h" + +#ifdef _WIN32_WCE +extern "C" { + void abort() { + SkASSERT(0); + } + + unsigned int _control87(unsigned int _new, unsigned int mask ) { + SkASSERT(0); + return 0; + } + + time_t mktime(struct tm *timeptr ) { + SkASSERT(0); + return 0; + } + +// int errno; + + char *strdup(const char *) { + SkASSERT(0); + return 0; + } + + char *strerror(int errnum) { + SkASSERT(0); + return 0; + } + + int isatty(void* fd) { + SkASSERT(0); + return 0; + } + + int putenv(const char *envstring) { + SkASSERT(0); + return 0; + } + + char *getenv(const char *varname) { + SkASSERT(0); + return 0; + } + + void GetSystemTimeAsFileTime(LPFILETIME lpSystemTimeAsFileTime) { + SkASSERT(0); + } + + struct tm * localtime(const time_t *timer) { + SkASSERT(0); + return 0; + } + + size_t strftime(char *strDest, size_t maxsize, const char *format, + const struct tm *timeptr ) { + SkASSERT(0); + return 0; + } + +} +#endif + +static JSBool +global_enumerate(JSContext *cx, JSObject *obj) +{ +#ifdef LAZY_STANDARD_CLASSES + return JS_EnumerateStandardClasses(cx, obj); +#else + return JS_TRUE; +#endif +} + +static JSBool +global_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, JSObject **objp) +{ +#ifdef LAZY_STANDARD_CLASSES + if ((flags & JSRESOLVE_ASSIGNING) == 0) { + JSBool resolved; + + if (!JS_ResolveStandardClass(cx, obj, id, &resolved)) + return JS_FALSE; + if (resolved) { + *objp = obj; + return JS_TRUE; + } + } +#endif + +#if defined(SHELL_HACK) && defined(DEBUG) && defined(XP_UNIX) + if ((flags & (JSRESOLVE_QUALIFIED | JSRESOLVE_ASSIGNING)) == 0) { + /* + * Do this expensive hack only for unoptimized Unix builds, which are + * not used for benchmarking. + */ + char *path, *comp, *full; + const char *name; + JSBool ok, found; + JSFunction *fun; + + if (!JSVAL_IS_STRING(id)) + return JS_TRUE; + path = getenv("PATH"); + if (!path) + return JS_TRUE; + path = JS_strdup(cx, path); + if (!path) + return JS_FALSE; + name = JS_GetStringBytes(JSVAL_TO_STRING(id)); + ok = JS_TRUE; + for (comp = strtok(path, ":"); comp; comp = strtok(NULL, ":")) { + if (*comp != '\0') { + full = JS_smprintf("%s/%s", comp, name); + if (!full) { + JS_ReportOutOfMemory(cx); + ok = JS_FALSE; + break; + } + } else { + full = (char *)name; + } + found = (access(full, X_OK) == 0); + if (*comp != '\0') + free(full); + if (found) { + fun = JS_DefineFunction(cx, obj, name, Exec, 0, JSPROP_ENUMERATE); + ok = (fun != NULL); + if (ok) + *objp = obj; + break; + } + } + JS_free(cx, path); + return ok; + } +#else + return JS_TRUE; +#endif +} + +JSClass global_class = { + "global", JSCLASS_NEW_RESOLVE, + JS_PropertyStub, JS_PropertyStub, + JS_PropertyStub, JS_PropertyStub, + global_enumerate, (JSResolveOp) global_resolve, + JS_ConvertStub, JS_FinalizeStub +}; + +SkJS::SkJS(void* hwnd) : SkOSWindow(hwnd) { + if ((fRuntime = JS_NewRuntime(0x100000)) == NULL) { + SkASSERT(0); + return; + } + if ((fContext = JS_NewContext(fRuntime, 0x1000)) == NULL) { + SkASSERT(0); + return; + } + ; + if ((fGlobal = JS_NewObject(fContext, &global_class, NULL, NULL)) == NULL) { + SkASSERT(0); + return; + } + if (JS_InitStandardClasses(fContext, fGlobal) == NULL) { + SkASSERT(0); + return; + } + setConfig(SkBitmap::kARGB32_Config); + updateSize(); + setVisibleP(true); + InitializeDisplayables(getBitmap(), fContext, fGlobal, NULL); +} + +SkJS::~SkJS() { + DisposeDisplayables(); + JS_DestroyContext(fContext); + JS_DestroyRuntime(fRuntime); + JS_ShutDown(); +} + +SkBool SkJS::EvaluateScript(const char* script, jsval* rVal) { + return JS_EvaluateScript(fContext, fGlobal, script, strlen(script), + "memory" /* no file name */, 0 /* no line number */, rVal); +} + +SkBool SkJS::ValueToString(jsval value, SkString* string) { + JSString* str = JS_ValueToString(fContext, value); + if (str == NULL) + return false; + string->set(JS_GetStringBytes(str)); + return true; +} + +#ifdef SK_DEBUG +void SkJS::Test(void* hwnd) { + SkJS js(hwnd); + jsval val; + SkBool success = js.EvaluateScript("22/7", &val); + SkASSERT(success); + SkString string; + success = js.ValueToString(val, &string); + SkASSERT(success); + SkASSERT(strcmp(string.c_str(), "3.142857142857143") == 0); + success = js.EvaluateScript( + "var rect = new rectangle();" + "rect.left = 4;" + "rect.top = 10;" + "rect.right = 20;" + "rect.bottom = 30;" + "rect.width = rect.height + 20;" + "rect.draw();" + , &val); + SkASSERT(success); + success = js.ValueToString(val, &string); + SkASSERT(success); +} +#endifASSERT(success); + diff --git a/skia/xml/SkJSDisplayable.cpp b/skia/xml/SkJSDisplayable.cpp new file mode 100644 index 0000000..87269f3 --- /dev/null +++ b/skia/xml/SkJSDisplayable.cpp @@ -0,0 +1,472 @@ +/* libs/graphics/xml/SkJSDisplayable.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include <jsapi.h> +#include "SkJS.h" +#include "SkDisplayType.h" +//#include "SkAnimateColor.h" +#include "SkAnimateMaker.h" +#include "SkAnimateSet.h" +//#include "SkAnimateTransform.h" +#include "SkCanvas.h" +//#include "SkDimensions.h" +#include "SkDisplayAdd.h" +#include "SkDisplayApply.h" +//#include "SkDisplayBefore.h" +#include "SkDisplayEvent.h" +//#include "SkDisplayFocus.h" +#include "SkDisplayInclude.h" +#include "SkDisplayPost.h" +#include "SkDisplayRandom.h" +#include "SkDraw3D.h" +#include "SkDrawBitmap.h" +#include "SkDrawClip.h" +#include "SkDrawDash.h" +#include "SkDrawDiscrete.h" +#include "SkDrawEmboss.h" +//#include "SkDrawFont.h" +#include "SkDrawFull.h" +#include "SkDrawGradient.h" +#include "SkDrawLine.h" +//#include "SkDrawMaskFilter.h" +#include "SkDrawMatrix.h" +#include "SkDrawOval.h" +#include "SkDrawPaint.h" +#include "SkDrawPath.h" +#include "SkDrawPoint.h" +// #include "SkDrawStroke.h" +#include "SkDrawText.h" +#include "SkDrawTo.h" +//#include "SkDrawTransferMode.h" +#include "SkDrawTransparentShader.h" +//#include "SkDrawUse.h" +#include "SkMatrixParts.h" +#include "SkPathParts.h" +#include "SkPostParts.h" +#include "SkScript.h" +#include "SkSnapshot.h" +#include "SkTextOnPath.h" +#include "SkTextToPath.h" + + +class SkJSDisplayable { +public: + SkJSDisplayable() : fDisplayable(NULL) {} + ~SkJSDisplayable() { delete fDisplayable; } + static void Destructor(JSContext *cx, JSObject *obj); + static JSBool GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp); + static JSBool SetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp); + static SkCanvas* gCanvas; + static SkPaint* gPaint; + static JSBool Draw(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); + SkDisplayable* fDisplayable; +}; + +SkCanvas* SkJSDisplayable::gCanvas; +SkPaint* SkJSDisplayable::gPaint; + +JSBool SkJSDisplayable::Draw(JSContext *cx, JSObject *obj, uintN argc, + jsval *argv, jsval *rval) +{ + SkJSDisplayable *p = (SkJSDisplayable*) JS_GetPrivate(cx, obj); + SkASSERT(p->fDisplayable->isDrawable()); + SkDrawable* drawable = (SkDrawable*) p->fDisplayable; + SkAnimateMaker maker(NULL, gCanvas, gPaint); + drawable->draw(maker); + return JS_TRUE; +} + + +JSFunctionSpec SkJSDisplayable_methods[] = +{ + { "draw", SkJSDisplayable::Draw, 1, 0, 0 }, + { 0 } +}; + +static JSPropertySpec* gDisplayableProperties[kNumberOfTypes]; +static JSClass gDisplayableClasses[kNumberOfTypes]; + +#define JS_INIT(_prefix, _class) \ +static JSBool _class##Constructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { \ + SkJSDisplayable* jsDisplayable = new SkJSDisplayable(); \ + jsDisplayable->fDisplayable = new _prefix##_class(); \ + JS_SetPrivate(cx, obj, (void*) jsDisplayable); \ + return JS_TRUE; \ +} \ + \ +static JSObject* _class##Init(JSContext *cx, JSObject *obj, JSObject *proto) { \ + JSObject *newProtoObj = JS_InitClass(cx, obj, proto, &gDisplayableClasses[SkType_##_class], \ + _class##Constructor, 0, \ + NULL, SkJSDisplayable_methods , \ + NULL, NULL); \ + JS_DefineProperties(cx, newProtoObj, gDisplayableProperties[SkType_##_class]); \ + return newProtoObj; \ +} + +JS_INIT(Sk, Add) +JS_INIT(Sk, AddCircle) +JS_INIT(Sk, AddOval) +JS_INIT(Sk, AddPath) +JS_INIT(Sk, AddRectangle) +JS_INIT(Sk, AddRoundRect) +//JS_INIT(Sk, After) +JS_INIT(Sk, Apply) +// JS_INIT(Sk, Animate) +//JS_INIT(Sk, AnimateColor) +JS_INIT(Sk, AnimateField) +//JS_INIT(Sk, AnimateRotate) +//JS_INIT(Sk, AnimateScale) +//JS_INIT(Sk, AnimateTranslate) +JS_INIT(SkDraw, Bitmap) +JS_INIT(Sk, BaseBitmap) +//JS_INIT(Sk, Before) +JS_INIT(SkDraw, BitmapShader) +JS_INIT(SkDraw, Blur) +JS_INIT(SkDraw, Clip) +JS_INIT(SkDraw, Color) +JS_INIT(Sk, CubicTo) +JS_INIT(Sk, Dash) +JS_INIT(Sk, Data) +//JS_INIT(Sk, Dimensions) +JS_INIT(Sk, Discrete) +JS_INIT(Sk, DrawTo) +JS_INIT(SkDraw, Emboss) +JS_INIT(SkDisplay, Event) +// JS_INIT(SkDraw, Font) +// JS_INIT(Sk, Focus) +JS_INIT(Sk, Image) +JS_INIT(Sk, Include) +// JS_INIT(Sk, Input) +JS_INIT(Sk, Line) +JS_INIT(Sk, LinearGradient) +JS_INIT(Sk, LineTo) +JS_INIT(SkDraw, Matrix) +JS_INIT(Sk, Move) +JS_INIT(Sk, MoveTo) +JS_INIT(Sk, Oval) +JS_INIT(SkDraw, Path) +JS_INIT(SkDraw, Paint) +JS_INIT(Sk, DrawPoint) +JS_INIT(Sk, PolyToPoly) +JS_INIT(Sk, Polygon) +JS_INIT(Sk, Polyline) +JS_INIT(Sk, Post) +JS_INIT(Sk, QuadTo) +JS_INIT(Sk, RadialGradient) +JS_INIT(SkDisplay, Random) +JS_INIT(Sk, RectToRect) +JS_INIT(Sk, Rectangle) +JS_INIT(Sk, Remove) +JS_INIT(Sk, Replace) +JS_INIT(Sk, Rotate) +JS_INIT(Sk, RoundRect) +JS_INIT(Sk, Scale) +JS_INIT(Sk, Set) +JS_INIT(Sk, Skew) +// JS_INIT(Sk, 3D_Camera) +// JS_INIT(Sk, 3D_Patch) +#ifdef SK_SUPPORT_IMAGE_ENCODE +JS_INIT(Sk, Snapshot) +#endif +// JS_INIT(SkDraw, Stroke) +JS_INIT(Sk, Text) +JS_INIT(Sk, TextOnPath) +JS_INIT(Sk, TextToPath) +JS_INIT(Sk, Translate) +//JS_INIT(Sk, Use) + +#if SK_USE_CONDENSED_INFO == 0 +static void GenerateTables() { + for (int index = 0; index < kTypeNamesSize; index++) { + int infoCount; + SkDisplayTypes type = gTypeNames[index].fType; + const SkMemberInfo* info = SkDisplayType::GetMembers(NULL /* fMaker */, type, &infoCount); + if (info == NULL) + continue; + gDisplayableProperties[type] = new JSPropertySpec[infoCount + 1]; + JSPropertySpec* propertySpec = gDisplayableProperties[type]; + memset(propertySpec, 0, sizeof (JSPropertySpec) * (infoCount + 1)); + for (int inner = 0; inner < infoCount; inner++) { + if (info[inner].fType == SkType_BaseClassInfo) + continue; + propertySpec[inner].name = info[inner].fName; + propertySpec[inner].tinyid = inner; + propertySpec[inner].flags = JSPROP_ENUMERATE; + } + gDisplayableClasses[type].name = gTypeNames[index].fName; + gDisplayableClasses[type].flags = JSCLASS_HAS_PRIVATE; + gDisplayableClasses[type].addProperty = JS_PropertyStub; + gDisplayableClasses[type].delProperty = JS_PropertyStub; + gDisplayableClasses[type].getProperty = SkJSDisplayable::GetProperty; + gDisplayableClasses[type].setProperty = SkJSDisplayable::SetProperty; + gDisplayableClasses[type].enumerate = JS_EnumerateStub; + gDisplayableClasses[type].resolve = JS_ResolveStub; + gDisplayableClasses[type].convert = JS_ConvertStub; + gDisplayableClasses[type].finalize = SkJSDisplayable::Destructor; + } +} +#endif + +void SkJSDisplayable::Destructor(JSContext *cx, JSObject *obj) { + delete (SkJSDisplayable*) JS_GetPrivate(cx, obj); +} + +JSBool SkJSDisplayable::GetProperty(JSContext *cx, JSObject *obj, jsval id, + jsval *vp) +{ + if (JSVAL_IS_INT(id) == 0) + return JS_TRUE; + SkJSDisplayable *p = (SkJSDisplayable *) JS_GetPrivate(cx, obj); + SkDisplayable* displayable = p->fDisplayable; + SkDisplayTypes displayableType = displayable->getType(); + int members; + const SkMemberInfo* info = SkDisplayType::GetMembers(NULL /* fMaker */, displayableType, &members); + int idIndex = JSVAL_TO_INT(id); + SkASSERT(idIndex >= 0 && idIndex < members); + info = &info[idIndex]; + SkDisplayTypes infoType = (SkDisplayTypes) info->fType; + SkScalar scalar = 0; + S32 s32 = 0; + SkString* string= NULL; + JSString *str; + if (infoType == SkType_MemberProperty) { + infoType = info->propertyType(); + switch (infoType) { + case SkType_Scalar: { + SkScriptValue scriptValue; + bool success = displayable->getProperty(info->propertyIndex(), &scriptValue); + SkASSERT(scriptValue.fType == SkType_Scalar); + scalar = scriptValue.fOperand.fScalar; + } break; + default: + SkASSERT(0); // !!! unimplemented + } + } else { + SkASSERT(info->fCount == 1); + switch (infoType) { + case SkType_Boolean: + case SkType_Color: + case SkType_S32: + s32 = *(S32*) info->memberData(displayable); + break; + case SkType_String: + info->getString(displayable, &string); + break; + case SkType_Scalar: + SkOperand operand; + info->getValue(displayable, &operand, 1); + scalar = operand.fScalar; + break; + default: + SkASSERT(0); // !!! unimplemented + } + } + switch (infoType) { + case SkType_Boolean: + *vp = BOOLEAN_TO_JSVAL(s32); + break; + case SkType_Color: + case SkType_S32: + *vp = INT_TO_JSVAL(s32); + break; + case SkType_Scalar: + if (SkScalarFraction(scalar) == 0) + *vp = INT_TO_JSVAL(SkScalarFloor(scalar)); + else +#ifdef SK_SCALAR_IS_FLOAT + *vp = DOUBLE_TO_JSVAL(scalar); +#else + *vp = DOUBLE_TO_JSVAL(scalar / 65536.0f ); +#endif + break; + case SkType_String: + str = JS_NewStringCopyN(cx, string->c_str(), string->size()); + *vp = STRING_TO_JSVAL(str); + break; + default: + SkASSERT(0); // !!! unimplemented + } + return JS_TRUE; +} + +JSBool SkJSDisplayable::SetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { + if (JSVAL_IS_INT(id) == 0) + return JS_TRUE; + SkJSDisplayable *p = (SkJSDisplayable *) JS_GetPrivate(cx, obj); + SkDisplayable* displayable = p->fDisplayable; + SkDisplayTypes displayableType = displayable->getType(); + int members; + const SkMemberInfo* info = SkDisplayType::GetMembers(NULL /* fMaker */, displayableType, &members); + int idIndex = JSVAL_TO_INT(id); + SkASSERT(idIndex >= 0 && idIndex < members); + info = &info[idIndex]; + SkDisplayTypes infoType = info->getType(); + SkScalar scalar = 0; + S32 s32 = 0; + SkString string; + JSString* str; + jsval value = *vp; + switch (infoType) { + case SkType_Boolean: + s32 = JSVAL_TO_BOOLEAN(value); + break; + case SkType_Color: + case SkType_S32: + s32 = JSVAL_TO_INT(value); + break; + case SkType_Scalar: + if (JSVAL_IS_INT(value)) + scalar = SkIntToScalar(JSVAL_TO_INT(value)); + else { + SkASSERT(JSVAL_IS_DOUBLE(value)); +#ifdef SK_SCALAR_IS_FLOAT + scalar = (float) *(double*) JSVAL_TO_DOUBLE(value); +#else + scalar = (SkFixed) (*(double*)JSVAL_TO_DOUBLE(value) * 65536.0); +#endif + } + break; + case SkType_String: + str = JS_ValueToString(cx, value); + string.set(JS_GetStringBytes(str)); + break; + default: + SkASSERT(0); // !!! unimplemented + } + if (info->fType == SkType_MemberProperty) { + switch (infoType) { + case SkType_Scalar: { + SkScriptValue scriptValue; + scriptValue.fType = SkType_Scalar; + scriptValue.fOperand.fScalar = scalar; + displayable->setProperty(-1 - (int) info->fOffset, scriptValue); + } break; + default: + SkASSERT(0); // !!! unimplemented + } + } else { + SkASSERT(info->fCount == 1); + switch (infoType) { + case SkType_Boolean: + case SkType_Color: + case SkType_S32: + s32 = *(S32*) ((const char*) displayable + info->fOffset); + break; + case SkType_String: + info->setString(displayable, &string); + break; + case SkType_Scalar: + SkOperand operand; + operand.fScalar = scalar; + info->setValue(displayable, &operand, 1); + break; + default: + SkASSERT(0); // !!! unimplemented + } + } + return JS_TRUE; +} + +void SkJS::InitializeDisplayables(const SkBitmap& bitmap, JSContext *cx, JSObject *obj, JSObject *proto) { + SkJSDisplayable::gCanvas = new SkCanvas(bitmap); + SkJSDisplayable::gPaint = new SkPaint(); +#if SK_USE_CONDENSED_INFO == 0 + GenerateTables(); +#else + SkASSERT(0); // !!! compressed version hasn't been implemented +#endif + AddInit(cx, obj, proto); + AddCircleInit(cx, obj, proto); + AddOvalInit(cx, obj, proto); + AddPathInit(cx, obj, proto); + AddRectangleInit(cx, obj, proto); + AddRoundRectInit(cx, obj, proto); +// AfterInit(cx, obj, proto); + ApplyInit(cx, obj, proto); + // AnimateInit(cx, obj, proto); +// AnimateColorInit(cx, obj, proto); + AnimateFieldInit(cx, obj, proto); +// AnimateRotateInit(cx, obj, proto); +// AnimateScaleInit(cx, obj, proto); +// AnimateTranslateInit(cx, obj, proto); + BitmapInit(cx, obj, proto); +// BaseBitmapInit(cx, obj, proto); +// BeforeInit(cx, obj, proto); + BitmapShaderInit(cx, obj, proto); + BlurInit(cx, obj, proto); + ClipInit(cx, obj, proto); + ColorInit(cx, obj, proto); + CubicToInit(cx, obj, proto); + DashInit(cx, obj, proto); + DataInit(cx, obj, proto); +// DimensionsInit(cx, obj, proto); + DiscreteInit(cx, obj, proto); + DrawToInit(cx, obj, proto); + EmbossInit(cx, obj, proto); + EventInit(cx, obj, proto); +// FontInit(cx, obj, proto); +// FocusInit(cx, obj, proto); + ImageInit(cx, obj, proto); + IncludeInit(cx, obj, proto); +// InputInit(cx, obj, proto); + LineInit(cx, obj, proto); + LinearGradientInit(cx, obj, proto); + LineToInit(cx, obj, proto); + MatrixInit(cx, obj, proto); + MoveInit(cx, obj, proto); + MoveToInit(cx, obj, proto); + OvalInit(cx, obj, proto); + PathInit(cx, obj, proto); + PaintInit(cx, obj, proto); + DrawPointInit(cx, obj, proto); + PolyToPolyInit(cx, obj, proto); + PolygonInit(cx, obj, proto); + PolylineInit(cx, obj, proto); + PostInit(cx, obj, proto); + QuadToInit(cx, obj, proto); + RadialGradientInit(cx, obj, proto); + RandomInit(cx, obj, proto); + RectToRectInit(cx, obj, proto); + RectangleInit(cx, obj, proto); + RemoveInit(cx, obj, proto); + ReplaceInit(cx, obj, proto); + RotateInit(cx, obj, proto); + RoundRectInit(cx, obj, proto); + ScaleInit(cx, obj, proto); + SetInit(cx, obj, proto); + SkewInit(cx, obj, proto); + // 3D_CameraInit(cx, obj, proto); + // 3D_PatchInit(cx, obj, proto); + #ifdef SK_SUPPORT_IMAGE_ENCODE + SnapshotInit(cx, obj, proto); + #endif +// StrokeInit(cx, obj, proto); + TextInit(cx, obj, proto); + TextOnPathInit(cx, obj, proto); + TextToPathInit(cx, obj, proto); + TranslateInit(cx, obj, proto); +// UseInit(cx, obj, proto); +} + +void SkJS::DisposeDisplayables() { + delete SkJSDisplayable::gPaint; + delete SkJSDisplayable::gCanvas; + for (int index = 0; index < kTypeNamesSize; index++) { + SkDisplayTypes type = gTypeNames[index].fType; + delete[] gDisplayableProperties[type]; + } +} diff --git a/skia/xml/SkParse.cpp b/skia/xml/SkParse.cpp new file mode 100644 index 0000000..b2cf296 --- /dev/null +++ b/skia/xml/SkParse.cpp @@ -0,0 +1,336 @@ +/* libs/graphics/xml/SkParse.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkParse.h" + +static inline bool is_between(int c, int min, int max) +{ + return (unsigned)(c - min) <= (unsigned)(max - min); +} + +static inline bool is_ws(int c) +{ + return is_between(c, 1, 32); +} + +static inline bool is_digit(int c) +{ + return is_between(c, '0', '9'); +} + +static inline bool is_sep(int c) +{ + return is_ws(c) || c == ',' || c == ';'; +} + +static int to_hex(int c) +{ + if (is_digit(c)) + return c - '0'; + + c |= 0x20; // make us lower-case + if (is_between(c, 'a', 'f')) + return c + 10 - 'a'; + else + return -1; +} + +static inline bool is_hex(int c) +{ + return to_hex(c) >= 0; +} + +static const char* skip_ws(const char str[]) +{ + SkASSERT(str); + while (is_ws(*str)) + str++; + return str; +} + +static const char* skip_sep(const char str[]) +{ + SkASSERT(str); + while (is_sep(*str)) + str++; + return str; +} + +int SkParse::Count(const char str[]) +{ + char c; + int count = 0; + goto skipLeading; + do { + count++; + do { + if ((c = *str++) == '\0') + goto goHome; + } while (is_sep(c) == false); +skipLeading: + do { + if ((c = *str++) == '\0') + goto goHome; + } while (is_sep(c)); + } while (true); +goHome: + return count; +} + +int SkParse::Count(const char str[], char separator) +{ + char c; + int count = 0; + goto skipLeading; + do { + count++; + do { + if ((c = *str++) == '\0') + goto goHome; + } while (c != separator); +skipLeading: + do { + if ((c = *str++) == '\0') + goto goHome; + } while (c == separator); + } while (true); +goHome: + return count; +} + +const char* SkParse::FindHex(const char str[], uint32_t* value) +{ + SkASSERT(str); + str = skip_ws(str); + + if (!is_hex(*str)) + return NULL; + + uint32_t n = 0; + int max_digits = 8; + int digit; + + while ((digit = to_hex(*str)) >= 0) + { + if (--max_digits < 0) + return NULL; + n = (n << 4) | digit; + str += 1; + } + + if (*str == 0 || is_ws(*str)) + { + if (value) + *value = n; + return str; + } + return false; +} + +const char* SkParse::FindS32(const char str[], int32_t* value) +{ + SkASSERT(str); + str = skip_ws(str); + + int sign = 0; + if (*str == '-') + { + sign = -1; + str += 1; + } + + if (!is_digit(*str)) + return NULL; + + int n = 0; + while (is_digit(*str)) + { + n = 10*n + *str - '0'; + str += 1; + } + if (value) + *value = (n ^ sign) - sign; + return str; +} + +const char* SkParse::FindMSec(const char str[], SkMSec* value) +{ + SkASSERT(str); + str = skip_ws(str); + + int sign = 0; + if (*str == '-') + { + sign = -1; + str += 1; + } + + if (!is_digit(*str)) + return NULL; + + int n = 0; + while (is_digit(*str)) + { + n = 10*n + *str - '0'; + str += 1; + } + int remaining10s = 3; + if (*str == '.') { + str++; + while (is_digit(*str)) + { + n = 10*n + *str - '0'; + str += 1; + if (--remaining10s == 0) + break; + } + } + while (--remaining10s >= 0) + n *= 10; + if (value) + *value = (n ^ sign) - sign; + return str; +} + +const char* SkParse::FindScalar(const char str[], SkScalar* value) +{ + SkASSERT(str); + str = skip_ws(str); + + int sign = 0; + if (*str == '-') + { + sign = -1; + str += 1; + } + + if (!is_digit(*str) && *str != '.') + return NULL; + + int n = 0; + while (is_digit(*str)) + { + n = 10*n + *str - '0'; + if (n > 0x7FFF) + return NULL; + str += 1; + } + n <<= 16; + + if (*str == '.') + { + static const int gFractions[] = { (1 << 24) / 10, (1 << 24) / 100, (1 << 24) / 1000, + (1 << 24) / 10000, (1 << 24) / 100000 }; + str += 1; + int d = 0; + const int* fraction = gFractions; + const int* end = &fraction[SK_ARRAY_COUNT(gFractions)]; + while (is_digit(*str) && fraction < end) + d += (*str++ - '0') * *fraction++; + d += 0x80; // round + n += d >> 8; + } + while (is_digit(*str)) + str += 1; + if (value) + { + n = (n ^ sign) - sign; // apply the sign + *value = SkFixedToScalar(n); + } + return str; +} + +const char* SkParse::FindScalars(const char str[], SkScalar value[], int count) +{ + SkASSERT(count >= 0); + + if (count > 0) + { + for (;;) + { + str = SkParse::FindScalar(str, value); + if (--count == 0 || str == NULL) + break; + + // keep going + str = skip_sep(str); + if (value) + value += 1; + } + } + return str; +} + +static bool lookup_str(const char str[], const char** table, int count) +{ + while (--count >= 0) + if (!strcmp(str, table[count])) + return true; + return false; +} + +bool SkParse::FindBool(const char str[], bool* value) +{ + static const char* gYes[] = { "yes", "1", "true" }; + static const char* gNo[] = { "no", "0", "false" }; + + if (lookup_str(str, gYes, SK_ARRAY_COUNT(gYes))) + { + if (value) *value = true; + return true; + } + else if (lookup_str(str, gNo, SK_ARRAY_COUNT(gNo))) + { + if (value) *value = false; + return true; + } + return false; +} + +int SkParse::FindList(const char target[], const char list[]) +{ + size_t len = strlen(target); + int index = 0; + + for (;;) + { + const char* end = strchr(list, ','); + size_t entryLen; + + if (end == NULL) // last entry + entryLen = strlen(list); + else + entryLen = end - list; + + if (entryLen == len && memcmp(target, list, len) == 0) + return index; + if (end == NULL) + break; + + list = end + 1; // skip the ',' + index += 1; + } + return -1; +} + +#ifdef SK_SUPPORT_UNITTEST +void SkParse::UnitTest() +{ + // !!! additional parse tests go here + SkParse::TestColor(); +} +#endif diff --git a/skia/xml/SkParseColor.cpp b/skia/xml/SkParseColor.cpp new file mode 100644 index 0000000..43bd09e --- /dev/null +++ b/skia/xml/SkParseColor.cpp @@ -0,0 +1,546 @@ +/* libs/graphics/xml/SkParseColor.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkParse.h" + +#ifdef SK_DEBUG +#include "SkString.h" + + // compress names 6 chars per long (packed 5 bits/char ) + // note: little advantage to splitting chars across longs, since 3 longs at 2 unused bits each + // allow for one additional split char (vs. the 18 unsplit chars in the three longs) + // use extra two bits to represent: + // 00 : final 6 (or fewer) chars (if 'a' is 0x01, zero could have special meaning) + // 01 : not final 6 chars + // 10 : color + // 11 : unused, except as debugging sentinal? (could be -1 for easier test) + // !!! the bit to end the word (last) is at the low bit for binary search + // lookup first character in offset for quick start + // offset is 27-entry table of bytes(?) that trims linear search to at most 21 entries ('d') + // shift match into long; set bit 30 if it all doesn't fit + // while longs don't match, march forward + // if they do match, and bit 30 is set, advance match, clearing bit 30 if + // final chars, and advance to next test + // if they do match, and bit 30 is clear, get next long (color) and return it + // stop at lookup of first char + 1 +static const struct SkNameRGB { + const char* name; + int rgb; +} colorNames[] = { + { "aliceblue", 0xF0F8FF }, + { "antiquewhite", 0xFAEBD7 }, + { "aqua", 0x00FFFF }, + { "aquamarine", 0x7FFFD4 }, + { "azure", 0xF0FFFF }, + { "beige", 0xF5F5DC }, + { "bisque", 0xFFE4C4 }, + { "black", 0x000000 }, + { "blanchedalmond", 0xFFEBCD }, + { "blue", 0x0000FF }, + { "blueviolet", 0x8A2BE2 }, + { "brown", 0xA52A2A }, + { "burlywood", 0xDEB887 }, + { "cadetblue", 0x5F9EA0 }, + { "chartreuse", 0x7FFF00 }, + { "chocolate", 0xD2691E }, + { "coral", 0xFF7F50 }, + { "cornflowerblue", 0x6495ED }, + { "cornsilk", 0xFFF8DC }, + { "crimson", 0xDC143C }, + { "cyan", 0x00FFFF }, + { "darkblue", 0x00008B }, + { "darkcyan", 0x008B8B }, + { "darkgoldenrod", 0xB8860B }, + { "darkgray", 0xA9A9A9 }, + { "darkgreen", 0x006400 }, + { "darkkhaki", 0xBDB76B }, + { "darkmagenta", 0x8B008B }, + { "darkolivegreen", 0x556B2F }, + { "darkorange", 0xFF8C00 }, + { "darkorchid", 0x9932CC }, + { "darkred", 0x8B0000 }, + { "darksalmon", 0xE9967A }, + { "darkseagreen", 0x8FBC8F }, + { "darkslateblue", 0x483D8B }, + { "darkslategray", 0x2F4F4F }, + { "darkturquoise", 0x00CED1 }, + { "darkviolet", 0x9400D3 }, + { "deeppink", 0xFF1493 }, + { "deepskyblue", 0x00BFFF }, + { "dimgray", 0x696969 }, + { "dodgerblue", 0x1E90FF }, + { "firebrick", 0xB22222 }, + { "floralwhite", 0xFFFAF0 }, + { "forestgreen", 0x228B22 }, + { "fuchsia", 0xFF00FF }, + { "gainsboro", 0xDCDCDC }, + { "ghostwhite", 0xF8F8FF }, + { "gold", 0xFFD700 }, + { "goldenrod", 0xDAA520 }, + { "gray", 0x808080 }, + { "green", 0x008000 }, + { "greenyellow", 0xADFF2F }, + { "honeydew", 0xF0FFF0 }, + { "hotpink", 0xFF69B4 }, + { "indianred", 0xCD5C5C }, + { "indigo", 0x4B0082 }, + { "ivory", 0xFFFFF0 }, + { "khaki", 0xF0E68C }, + { "lavender", 0xE6E6FA }, + { "lavenderblush", 0xFFF0F5 }, + { "lawngreen", 0x7CFC00 }, + { "lemonchiffon", 0xFFFACD }, + { "lightblue", 0xADD8E6 }, + { "lightcoral", 0xF08080 }, + { "lightcyan", 0xE0FFFF }, + { "lightgoldenrodyellow", 0xFAFAD2 }, + { "lightgreen", 0x90EE90 }, + { "lightgrey", 0xD3D3D3 }, + { "lightpink", 0xFFB6C1 }, + { "lightsalmon", 0xFFA07A }, + { "lightseagreen", 0x20B2AA }, + { "lightskyblue", 0x87CEFA }, + { "lightslategray", 0x778899 }, + { "lightsteelblue", 0xB0C4DE }, + { "lightyellow", 0xFFFFE0 }, + { "lime", 0x00FF00 }, + { "limegreen", 0x32CD32 }, + { "linen", 0xFAF0E6 }, + { "magenta", 0xFF00FF }, + { "maroon", 0x800000 }, + { "mediumaquamarine", 0x66CDAA }, + { "mediumblue", 0x0000CD }, + { "mediumorchid", 0xBA55D3 }, + { "mediumpurple", 0x9370DB }, + { "mediumseagreen", 0x3CB371 }, + { "mediumslateblue", 0x7B68EE }, + { "mediumspringgreen", 0x00FA9A }, + { "mediumturquoise", 0x48D1CC }, + { "mediumvioletred", 0xC71585 }, + { "midnightblue", 0x191970 }, + { "mintcream", 0xF5FFFA }, + { "mistyrose", 0xFFE4E1 }, + { "moccasin", 0xFFE4B5 }, + { "navajowhite", 0xFFDEAD }, + { "navy", 0x000080 }, + { "oldlace", 0xFDF5E6 }, + { "olive", 0x808000 }, + { "olivedrab", 0x6B8E23 }, + { "orange", 0xFFA500 }, + { "orangered", 0xFF4500 }, + { "orchid", 0xDA70D6 }, + { "palegoldenrod", 0xEEE8AA }, + { "palegreen", 0x98FB98 }, + { "paleturquoise", 0xAFEEEE }, + { "palevioletred", 0xDB7093 }, + { "papayawhip", 0xFFEFD5 }, + { "peachpuff", 0xFFDAB9 }, + { "peru", 0xCD853F }, + { "pink", 0xFFC0CB }, + { "plum", 0xDDA0DD }, + { "powderblue", 0xB0E0E6 }, + { "purple", 0x800080 }, + { "red", 0xFF0000 }, + { "rosybrown", 0xBC8F8F }, + { "royalblue", 0x4169E1 }, + { "saddlebrown", 0x8B4513 }, + { "salmon", 0xFA8072 }, + { "sandybrown", 0xF4A460 }, + { "seagreen", 0x2E8B57 }, + { "seashell", 0xFFF5EE }, + { "sienna", 0xA0522D }, + { "silver", 0xC0C0C0 }, + { "skyblue", 0x87CEEB }, + { "slateblue", 0x6A5ACD }, + { "slategray", 0x708090 }, + { "snow", 0xFFFAFA }, + { "springgreen", 0x00FF7F }, + { "steelblue", 0x4682B4 }, + { "tan", 0xD2B48C }, + { "teal", 0x008080 }, + { "thistle", 0xD8BFD8 }, + { "tomato", 0xFF6347 }, + { "turquoise", 0x40E0D0 }, + { "violet", 0xEE82EE }, + { "wheat", 0xF5DEB3 }, + { "white", 0xFFFFFF }, + { "whitesmoke", 0xF5F5F5 }, + { "yellow", 0xFFFF00 }, + { "yellowgreen", 0x9ACD32 } +}; + +int colorNamesSize = sizeof(colorNames) / sizeof(colorNames[0]); + +static void CreateTable() { + SkString comment; + size_t originalSize = 0; + int replacement = 0; + for (int index = 0; index < colorNamesSize; index++) { + SkNameRGB nameRGB = colorNames[index]; + const char* name = nameRGB.name; + size_t len = strlen(name); + originalSize += len + 9; + bool first = true; + bool last = false; + do { + int compressed = 0; + const char* start = name; + for (int chIndex = 0; chIndex < 6; chIndex++) { + compressed <<= 5; + compressed |= *name ? *name++ - 'a' + 1 : 0 ; + } + replacement += sizeof(int); + compressed <<= 1; + compressed |= 1; + if (first) { + compressed |= 0x80000000; + first = false; + } + if (len <= 6) { // last + compressed &= ~1; + last = true; + } + len -= 6; + SkDebugf("0x%08x, ", compressed); + comment.append(start, name - start); + } while (last == false); + replacement += sizeof(int); + SkDebugf("0x%08x, ", nameRGB.rgb); + SkDebugf("// %s\n", comment.c_str()); + comment.reset(); + } + SkDebugf("// original = %d : replacement = %d\n", originalSize, replacement); + SkASSERT(0); // always stop after creating table +} + +#endif + +static const unsigned int gColorNames[] = { +0x85891945, 0x32a50000, 0x00f0f8ff, // aliceblue +0x85d44c6b, 0x16e84d0a, 0x00faebd7, // antiquewhite +0x86350800, 0x0000ffff, // aqua +0x86350b43, 0x492e2800, 0x007fffd4, // aquamarine +0x87559140, 0x00f0ffff, // azure +0x88a93940, 0x00f5f5dc, // beige +0x89338d4a, 0x00ffe4c4, // bisque +0x89811ac0, 0x00000000, // black +0x898170d1, 0x1481635f, 0x38800000, 0x00ffebcd, // blanchedalmond +0x89952800, 0x000000ff, // blue +0x89952d93, 0x3d85a000, 0x008a2be2, // blueviolet +0x8a4fbb80, 0x00a52a2a, // brown +0x8ab2666f, 0x3de40000, 0x00deb887, // burlywood +0x8c242d05, 0x32a50000, 0x005f9ea0, // cadetblue +0x8d019525, 0x16b32800, 0x007fff00, // chartreuse +0x8d0f1bd9, 0x06850000, 0x00d2691e, // chocolate +0x8df20b00, 0x00ff7f50, // coral +0x8df27199, 0x3ee59099, 0x54a00000, 0x006495ed, // cornflowerblue +0x8df274d3, 0x31600000, 0x00fff8dc, // cornsilk +0x8e496cdf, 0x38000000, 0x00dc143c, // crimson +0x8f217000, 0x0000ffff, // cyan +0x90325899, 0x54a00000, 0x0000008b, // darkblue +0x903258f3, 0x05c00000, 0x00008b8b, // darkcyan +0x903259df, 0x3085749f, 0x10000000, 0x00b8860b, // darkgoldenrod +0x903259e5, 0x07200000, 0x00a9a9a9, // darkgray +0x903259e5, 0x14ae0000, 0x00006400, // darkgreen +0x90325ad1, 0x05690000, 0x00bdb76b, // darkkhaki +0x90325b43, 0x1caea040, 0x008b008b, // darkmagenta +0x90325bd9, 0x26c53c8b, 0x15c00000, 0x00556b2f, // darkolivegreen +0x90325be5, 0x05c72800, 0x00ff8c00, // darkorange +0x90325be5, 0x0d092000, 0x009932cc, // darkorchid +0x90325c8b, 0x10000000, 0x008b0000, // darkred +0x90325cc3, 0x31af7000, 0x00e9967a, // darksalmon +0x90325ccb, 0x04f2295c, 0x008fbc8f, // darkseagreen +0x90325cd9, 0x0685132b, 0x14000000, 0x00483d8b, // darkslateblue +0x90325cd9, 0x06853c83, 0x64000000, 0x002f4f4f, // darkslategray +0x90325d2b, 0x4a357a67, 0x14000000, 0x0000ced1, // darkturquoise +0x90325d93, 0x3d85a000, 0x009400d3, // darkviolet +0x90a58413, 0x39600000, 0x00ff1493, // deeppink +0x90a584d7, 0x644ca940, 0x0000bfff, // deepskyblue +0x912d3c83, 0x64000000, 0x00696969, // dimgray +0x91e43965, 0x09952800, 0x001e90ff, // dodgerblue +0x993228a5, 0x246b0000, 0x00b22222, // firebrick +0x998f9059, 0x5d09a140, 0x00fffaf0, // floralwhite +0x99f22ce9, 0x1e452b80, 0x00228b22, // forestgreen +0x9aa344d3, 0x04000000, 0x00ff00ff, // fuchsia +0x9c2974c5, 0x3e4f0000, 0x00dcdcdc, // gainsboro +0x9d0f9d2f, 0x21342800, 0x00f8f8ff, // ghostwhite +0x9dec2000, 0x00ffd700, // gold +0x9dec215d, 0x49e40000, 0x00daa520, // goldenrod +0x9e41c800, 0x00808080, // gray +0x9e452b80, 0x00008000, // green +0x9e452bb3, 0x158c7dc0, 0x00adff2f, // greenyellow +0xa1ee2e49, 0x16e00000, 0x00f0fff0, // honeydew +0xa1f4825d, 0x2c000000, 0x00ff69b4, // hotpink +0xa5c4485d, 0x48a40000, 0x00cd5c5c, // indianred +0xa5c449de, 0x004b0082, // indigo +0xa6cf9640, 0x00fffff0, // ivory +0xad015a40, 0x00f0e68c, // khaki +0xb0362b89, 0x16400000, 0x00e6e6fa, // lavender +0xb0362b89, 0x16426567, 0x20000000, 0x00fff0f5, // lavenderblush +0xb03771e5, 0x14ae0000, 0x007cfc00, // lawngreen +0xb0ad7b87, 0x212633dc, 0x00fffacd, // lemonchiffon +0xb1274505, 0x32a50000, 0x00add8e6, // lightblue +0xb1274507, 0x3e416000, 0x00f08080, // lightcoral +0xb1274507, 0x642e0000, 0x00e0ffff, // lightcyan +0xb127450f, 0x3d842ba5, 0x3c992b19, 0x3ee00000, 0x00fafad2, // lightgoldenrodyellow +0xb127450f, 0x48a57000, 0x0090ee90, // lightgreen +0xb127450f, 0x48b90000, 0x00d3d3d3, // lightgrey +0xb1274521, 0x25cb0000, 0x00ffb6c1, // lightpink +0xb1274527, 0x058d7b80, 0x00ffa07a, // lightsalmon +0xb1274527, 0x1427914b, 0x38000000, 0x0020b2aa, // lightseagreen +0xb1274527, 0x2f22654a, 0x0087cefa, // lightskyblue +0xb1274527, 0x303429e5, 0x07200000, 0x00778899, // lightslategray +0xb1274527, 0x50a56099, 0x54a00000, 0x00b0c4de, // lightsteelblue +0xb1274533, 0x158c7dc0, 0x00ffffe0, // lightyellow +0xb12d2800, 0x0000ff00, // lime +0xb12d29e5, 0x14ae0000, 0x0032cd32, // limegreen +0xb12e2b80, 0x00faf0e6, // linen +0xb4272ba9, 0x04000000, 0x00ff00ff, // magenta +0xb4327bdc, 0x00800000, // maroon +0xb4a44d5b, 0x06350b43, 0x492e2800, 0x0066cdaa, // mediumaquamarine +0xb4a44d5b, 0x09952800, 0x000000cd, // mediumblue +0xb4a44d5b, 0x3e434248, 0x00ba55d3, // mediumorchid +0xb4a44d5b, 0x42b2830a, 0x009370db, // mediumpurple +0xb4a44d5b, 0x4ca13c8b, 0x15c00000, 0x003cb371, // mediumseagreen +0xb4a44d5b, 0x4d81a145, 0x32a50000, 0x007b68ee, // mediumslateblue +0xb4a44d5b, 0x4e124b8f, 0x1e452b80, 0x0000fa9a, // mediumspringgreen +0xb4a44d5b, 0x52b28d5f, 0x26650000, 0x0048d1cc, // mediumturquoise +0xb4a44d5b, 0x592f6169, 0x48a40000, 0x00c71585, // mediumvioletred +0xb524724f, 0x2282654a, 0x00191970, // midnightblue +0xb52ea0e5, 0x142d0000, 0x00f5fffa, // mintcream +0xb533a665, 0x3e650000, 0x00ffe4e1, // mistyrose +0xb5e31867, 0x25c00000, 0x00ffe4b5, // moccasin +0xb8360a9f, 0x5d09a140, 0x00ffdead, // navajowhite +0xb836c800, 0x00000080, // navy +0xbd846047, 0x14000000, 0x00fdf5e6, // oldlace +0xbd89b140, 0x00808000, // olive +0xbd89b149, 0x48220000, 0x006b8e23, // olivedrab +0xbe4171ca, 0x00ffa500, // orange +0xbe4171cb, 0x48a40000, 0x00ff4500, // orangered +0xbe434248, 0x00da70d6, // orchid +0xc02c29df, 0x3085749f, 0x10000000, 0x00eee8aa, // palegoldenrod +0xc02c29e5, 0x14ae0000, 0x0098fb98, // palegreen +0xc02c2d2b, 0x4a357a67, 0x14000000, 0x00afeeee, // paleturquoise +0xc02c2d93, 0x3d85a48b, 0x10000000, 0x00db7093, // palevioletred +0xc0300e43, 0x5d098000, 0x00ffefd5, // papayawhip +0xc0a11a21, 0x54c60000, 0x00ffdab9, // peachpuff +0xc0b2a800, 0x00cd853f, // peru +0xc12e5800, 0x00ffc0cb, // pink +0xc1956800, 0x00dda0dd, // plum +0xc1f72165, 0x09952800, 0x00b0e0e6, // powderblue +0xc2b2830a, 0x00800080, // purple +0xc8a40000, 0x00ff0000, // red +0xc9f3c8a5, 0x3eee0000, 0x00bc8f8f, // rosybrown +0xc9f90b05, 0x32a50000, 0x004169e1, // royalblue +0xcc24230b, 0x0a4fbb80, 0x008b4513, // saddlebrown +0xcc2c6bdc, 0x00fa8072, // salmon +0xcc2e2645, 0x49f77000, 0x00f4a460, // sandybrown +0xcca13c8b, 0x15c00000, 0x002e8b57, // seagreen +0xcca19a0b, 0x31800000, 0x00fff5ee, // seashell +0xcd257382, 0x00a0522d, // sienna +0xcd2cb164, 0x00c0c0c0, // silver +0xcd79132b, 0x14000000, 0x0087ceeb, // skyblue +0xcd81a145, 0x32a50000, 0x006a5acd, // slateblue +0xcd81a14f, 0x48390000, 0x00708090, // slategray +0xcdcfb800, 0x00fffafa, // snow +0xce124b8f, 0x1e452b80, 0x0000ff7f, // springgreen +0xce852b05, 0x32a50000, 0x004682b4, // steelblue +0xd02e0000, 0x00d2b48c, // tan +0xd0a16000, 0x00008080, // teal +0xd1099d19, 0x14000000, 0x00d8bfd8, // thistle +0xd1ed0d1e, 0x00ff6347, // tomato +0xd2b28d5f, 0x26650000, 0x0040e0d0, // turquoise +0xd92f6168, 0x00ee82ee, // violet +0xdd050d00, 0x00f5deb3, // wheat +0xdd09a140, 0x00ffffff, // white +0xdd09a167, 0x35eb2800, 0x00f5f5f5, // whitesmoke +0xe4ac63ee, 0x00ffff00, // yellow +0xe4ac63ef, 0x1e452b80, 0x009acd32 // yellowgreen +}; // original = 2505 : replacement = 1616 + + +const char* SkParse::FindNamedColor(const char* name, size_t len, SkColor* color) { + const char* namePtr = name; + unsigned int sixMatches[4]; + unsigned int* sixMatchPtr = sixMatches; + bool first = true; + bool last = false; + char ch; + do { + unsigned int sixMatch = 0; + for (int chIndex = 0; chIndex < 6; chIndex++) { + sixMatch <<= 5; + ch = *namePtr | 0x20; + if (ch < 'a' || ch > 'z') + ch = 0; + else { + ch = ch - 'a' + 1; + namePtr++; + } + sixMatch |= ch ; // turn 'A' (0x41) into 'a' (0x61); + } + sixMatch <<= 1; + sixMatch |= 1; + if (first) { + sixMatch |= 0x80000000; + first = false; + } + ch = *namePtr | 0x20; + last = ch < 'a' || ch > 'z'; + if (last) + sixMatch &= ~1; + len -= 6; + *sixMatchPtr++ = sixMatch; + } while (last == false && len > 0); + const int colorNameSize = sizeof(gColorNames) / sizeof(unsigned int); + int lo = 0; + int hi = colorNameSize - 3; // back off to beginning of yellowgreen + while (lo <= hi) { + int mid = (hi + lo) >> 1; + while ((int) gColorNames[mid] >= 0) + --mid; + sixMatchPtr = sixMatches; + while (gColorNames[mid] == *sixMatchPtr) { + ++mid; + if ((*sixMatchPtr & 1) == 0) { // last + *color = gColorNames[mid] | 0xFF000000; + return namePtr; + } + ++sixMatchPtr; + } + int sixMask = *sixMatchPtr & ~0x80000000; + int midMask = gColorNames[mid] & ~0x80000000; + if (sixMask > midMask) { + lo = mid + 2; // skip color + while ((int) gColorNames[lo] >= 0) + ++lo; + } else if (hi == mid) + return NULL; + else + hi = mid; + } + return NULL; +} + +// !!! move to char utilities +//static int count_separators(const char* str, const char* sep) { +// char c; +// int separators = 0; +// while ((c = *str++) != '\0') { +// if (strchr(sep, c) == NULL) +// continue; +// do { +// if ((c = *str++) == '\0') +// goto goHome; +// } while (strchr(sep, c) != NULL); +// separators++; +// } +//goHome: +// return separators; +//} + +static inline unsigned nib2byte(unsigned n) +{ + SkASSERT((n & ~0xF) == 0); + return (n << 4) | n; +} + +const char* SkParse::FindColor(const char* value, SkColor* colorPtr) { + unsigned int oldAlpha = SkColorGetA(*colorPtr); + if (value[0] == '#') { + uint32_t hex; + const char* end = SkParse::FindHex(value + 1, &hex); +// SkASSERT(end); + if (end == NULL) + return end; + size_t len = end - value - 1; + if (len == 3 || len == 4) { + unsigned a = len == 4 ? nib2byte(hex >> 12) : oldAlpha; + unsigned r = nib2byte((hex >> 8) & 0xF); + unsigned g = nib2byte((hex >> 4) & 0xF); + unsigned b = nib2byte(hex & 0xF); + *colorPtr = SkColorSetARGB(a, r, g, b); + return end; + } else if (len == 6 || len == 8) { + if (len == 6) + hex |= oldAlpha << 24; + *colorPtr = hex; + return end; + } else { +// SkASSERT(0); + return NULL; + } +// } else if (strchr(value, ',')) { +// SkScalar array[4]; +// int count = count_separators(value, ",") + 1; // !!! count commas, add 1 +// SkASSERT(count == 3 || count == 4); +// array[0] = SK_Scalar1 * 255; +// const char* end = SkParse::FindScalars(value, &array[4 - count], count); +// if (end == NULL) +// return NULL; + // !!! range check for errors? +// *colorPtr = SkColorSetARGB(SkScalarRound(array[0]), SkScalarRound(array[1]), +// SkScalarRound(array[2]), SkScalarRound(array[3])); +// return end; + } else + return FindNamedColor(value, strlen(value), colorPtr); +} + +#ifdef SK_DEBUG +void SkParse::TestColor() { + if (false) + CreateTable(); // regenerates data table in the output window + SkColor result; + int index; + for (index = 0; index < colorNamesSize; index++) { + result = SK_ColorBLACK; + SkNameRGB nameRGB = colorNames[index]; + SkASSERT(FindColor(nameRGB.name, &result) != NULL); + SkASSERT(result == (SkColor) (nameRGB.rgb | 0xFF000000)); + } + for (index = 0; index < colorNamesSize; index++) { + result = SK_ColorBLACK; + SkNameRGB nameRGB = colorNames[index]; + char bad[24]; + size_t len = strlen(nameRGB.name); + memcpy(bad, nameRGB.name, len); + bad[len - 1] -= 1; + SkASSERT(FindColor(bad, &result) == false); + bad[len - 1] += 2; + SkASSERT(FindColor(bad, &result) == false); + } + result = SK_ColorBLACK; + SkASSERT(FindColor("lightGrey", &result)); + SkASSERT(result == 0xffd3d3d3); +// SkASSERT(FindColor("12,34,56,78", &result)); +// SkASSERT(result == ((12 << 24) | (34 << 16) | (56 << 8) | (78 << 0))); + result = SK_ColorBLACK; + SkASSERT(FindColor("#ABCdef", &result)); + SkASSERT(result == 0XFFABCdef); + SkASSERT(FindColor("#12ABCdef", &result)); + SkASSERT(result == 0X12ABCdef); + result = SK_ColorBLACK; + SkASSERT(FindColor("#123", &result)); + SkASSERT(result == 0Xff112233); + SkASSERT(FindColor("#abcd", &result)); + SkASSERT(result == 0Xaabbccdd); + result = SK_ColorBLACK; +// SkASSERT(FindColor("71,162,253", &result)); +// SkASSERT(result == ((0xFF << 24) | (71 << 16) | (162 << 8) | (253 << 0))); +} +#endif + diff --git a/skia/xml/SkXMLParser.cpp b/skia/xml/SkXMLParser.cpp new file mode 100644 index 0000000..0a88bfb --- /dev/null +++ b/skia/xml/SkXMLParser.cpp @@ -0,0 +1,95 @@ +/* libs/graphics/xml/SkXMLParser.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkXMLParser.h" + +static char const* const gErrorStrings[] = { + "empty or missing file ", + "unknown element ", + "unknown attribute name ", + "error in attribute value ", + "duplicate ID ", + "unknown error " +}; + +SkXMLParserError::SkXMLParserError() : fCode(kNoError), fLineNumber(-1), + fNativeCode(-1) +{ + reset(); +} + +SkXMLParserError::~SkXMLParserError() +{ + // need a virtual destructor for our subclasses +} + +void SkXMLParserError::getErrorString(SkString* str) const +{ + SkASSERT(str); + SkString temp; + if (fCode != kNoError) { + if ((unsigned)fCode < SK_ARRAY_COUNT(gErrorStrings)) + temp.set(gErrorStrings[fCode - 1]); + temp.append(fNoun); + } else + SkXMLParser::GetNativeErrorString(fNativeCode, &temp); + str->append(temp); +} + +void SkXMLParserError::reset() { + fCode = kNoError; + fLineNumber = -1; + fNativeCode = -1; +} + + +//////////////// + +SkXMLParser::SkXMLParser(SkXMLParserError* parserError) : fParser(NULL), fError(parserError) +{ +} + +SkXMLParser::~SkXMLParser() +{ +} + +bool SkXMLParser::startElement(const char elem[]) +{ + return this->onStartElement(elem); +} + +bool SkXMLParser::addAttribute(const char name[], const char value[]) +{ + return this->onAddAttribute(name, value); +} + +bool SkXMLParser::endElement(const char elem[]) +{ + return this->onEndElement(elem); +} + +bool SkXMLParser::text(const char text[], int len) +{ + return this->onText(text, len); +} + +//////////////////////////////////////////////////////////////////////////////// + +bool SkXMLParser::onStartElement(const char elem[]) {return false; } +bool SkXMLParser::onAddAttribute(const char name[], const char value[]) {return false; } +bool SkXMLParser::onEndElement(const char elem[]) { return false; } +bool SkXMLParser::onText(const char text[], int len) {return false; } diff --git a/skia/xml/SkXMLPullParser.cpp b/skia/xml/SkXMLPullParser.cpp new file mode 100644 index 0000000..5df75b7 --- /dev/null +++ b/skia/xml/SkXMLPullParser.cpp @@ -0,0 +1,132 @@ +#include "SkXMLParser.h" +#include "SkStream.h" + +static void reset(SkXMLPullParser::Curr* curr) +{ + curr->fEventType = SkXMLPullParser::ERROR; + curr->fName = ""; + curr->fAttrInfoCount = 0; + curr->fIsWhitespace = false; +} + +SkXMLPullParser::SkXMLPullParser() : fStream(NULL) +{ + fCurr.fEventType = ERROR; + fDepth = -1; +} + +SkXMLPullParser::SkXMLPullParser(SkStream* stream) : fStream(NULL) +{ + fCurr.fEventType = ERROR; + fDepth = 0; + + this->setStream(stream); +} + +SkXMLPullParser::~SkXMLPullParser() +{ + this->setStream(NULL); +} + +SkStream* SkXMLPullParser::setStream(SkStream* stream) +{ + if (fStream && !stream) + this->onExit(); + + SkRefCnt_SafeAssign(fStream, stream); + + if (fStream) + { + fCurr.fEventType = START_DOCUMENT; + this->onInit(); + } + else + { + fCurr.fEventType = ERROR; + } + fDepth = 0; + + return fStream; +} + +SkXMLPullParser::EventType SkXMLPullParser::nextToken() +{ + switch (fCurr.fEventType) { + case ERROR: + case END_DOCUMENT: + break; + case END_TAG: + fDepth -= 1; + // fall through + default: + reset(&fCurr); + fCurr.fEventType = this->onNextToken(); + break; + } + + switch (fCurr.fEventType) { + case START_TAG: + fDepth += 1; + break; + default: + break; + } + + return fCurr.fEventType; +} + +const char* SkXMLPullParser::getName() +{ + switch (fCurr.fEventType) { + case START_TAG: + case END_TAG: + return fCurr.fName; + default: + return NULL; + } +} + +const char* SkXMLPullParser::getText() +{ + switch (fCurr.fEventType) { + case TEXT: + case IGNORABLE_WHITESPACE: + return fCurr.fName; + default: + return NULL; + } +} + +bool SkXMLPullParser::isWhitespace() +{ + switch (fCurr.fEventType) { + case IGNORABLE_WHITESPACE: + return true; + case TEXT: + case CDSECT: + return fCurr.fIsWhitespace; + default: + return false; // unknown/illegal + } +} + +int SkXMLPullParser::getAttributeCount() +{ + return fCurr.fAttrInfoCount; +} + +void SkXMLPullParser::getAttributeInfo(int index, AttrInfo* info) +{ + SkASSERT((unsigned)index < (unsigned)fCurr.fAttrInfoCount); + + if (info) + *info = fCurr.fAttrInfos[index]; +} + +bool SkXMLPullParser::onEntityReplacement(const char name[], + SkString* replacement) +{ + // TODO: std 5 entities here + return false; +} + diff --git a/skia/xml/SkXMLWriter.cpp b/skia/xml/SkXMLWriter.cpp new file mode 100644 index 0000000..e8df912 --- /dev/null +++ b/skia/xml/SkXMLWriter.cpp @@ -0,0 +1,342 @@ +/* libs/graphics/xml/SkXMLWriter.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkXMLWriter.h" +#include "SkStream.h" + +SkXMLWriter::SkXMLWriter(bool doEscapeMarkup) : fDoEscapeMarkup(doEscapeMarkup) +{ +} + +SkXMLWriter::~SkXMLWriter() +{ + SkASSERT(fElems.count() == 0); +} + +void SkXMLWriter::flush() +{ + while (fElems.count()) + this->endElement(); +} + +void SkXMLWriter::addAttribute(const char name[], const char value[]) +{ + this->addAttributeLen(name, value, strlen(value)); +} + +void SkXMLWriter::addS32Attribute(const char name[], int32_t value) +{ + SkString tmp; + tmp.appendS32(value); + this->addAttribute(name, tmp.c_str()); +} + +void SkXMLWriter::addHexAttribute(const char name[], uint32_t value, int minDigits) +{ + SkString tmp("0x"); + tmp.appendHex(value, minDigits); + this->addAttribute(name, tmp.c_str()); +} + +void SkXMLWriter::addScalarAttribute(const char name[], SkScalar value) +{ + SkString tmp; + tmp.appendScalar(value); + this->addAttribute(name, tmp.c_str()); +} + +void SkXMLWriter::doEnd(Elem* elem) +{ + delete elem; +} + +bool SkXMLWriter::doStart(const char name[], size_t length) +{ + int level = fElems.count(); + bool firstChild = level > 0 && !fElems[level-1]->fHasChildren; + if (firstChild) + fElems[level-1]->fHasChildren = true; + Elem** elem = fElems.push(); + *elem = new Elem; + (*elem)->fName.set(name, length); + (*elem)->fHasChildren = 0; + return firstChild; +} + +SkXMLWriter::Elem* SkXMLWriter::getEnd() +{ + Elem* elem; + fElems.pop(&elem); + return elem; +} + +const char* SkXMLWriter::getHeader() +{ + static const char gHeader[] = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>"; + return gHeader; +} + +void SkXMLWriter::startElement(const char name[]) +{ + this->startElementLen(name, strlen(name)); +} + +static const char* escape_char(char c, char storage[2]) +{ + static const char* gEscapeChars[] = { + "<<", + ">>", + //"\""", + //"''", + "&&" + }; + + const char** array = gEscapeChars; + for (unsigned i = 0; i < SK_ARRAY_COUNT(gEscapeChars); i++) + { + if (array[i][0] == c) + return &array[i][1]; + } + storage[0] = c; + storage[1] = 0; + return storage; +} + +static size_t escape_markup(char dst[], const char src[], size_t length) +{ + size_t extra = 0; + const char* stop = src + length; + + while (src < stop) + { + char orig[2]; + const char* seq = escape_char(*src, orig); + size_t seqSize = strlen(seq); + + if (dst) + { + memcpy(dst, seq, seqSize); + dst += seqSize; + } + + // now record the extra size needed + extra += seqSize - 1; // minus one to subtract the original char + + // bump to the next src char + src += 1; + } + return extra; +} + +void SkXMLWriter::addAttributeLen(const char name[], const char value[], size_t length) +{ + SkString valueStr; + + if (fDoEscapeMarkup) + { + size_t extra = escape_markup(NULL, value, length); + if (extra) + { + valueStr.resize(length + extra); + (void)escape_markup(valueStr.writable_str(), value, length); + value = valueStr.c_str(); + length += extra; + } + } + this->onAddAttributeLen(name, value, length); +} + +void SkXMLWriter::startElementLen(const char elem[], size_t length) +{ + this->onStartElementLen(elem, length); +} + +//////////////////////////////////////////////////////////////////////////////////////// + +static void write_dom(const SkDOM& dom, const SkDOM::Node* node, SkXMLWriter* w, bool skipRoot) +{ + if (!skipRoot) + { + w->startElement(dom.getName(node)); + + SkDOM::AttrIter iter(dom, node); + const char* name; + const char* value; + while ((name = iter.next(&value)) != NULL) + w->addAttribute(name, value); + } + + node = dom.getFirstChild(node, NULL); + while (node) + { + write_dom(dom, node, w, false); + node = dom.getNextSibling(node, NULL); + } + + if (!skipRoot) + w->endElement(); +} + +void SkXMLWriter::writeDOM(const SkDOM& dom, const SkDOM::Node* node, bool skipRoot) +{ + if (node) + write_dom(dom, node, this, skipRoot); +} + +void SkXMLWriter::writeHeader() +{ +} + +// SkXMLStreamWriter + +static void tab(SkWStream& stream, int level) +{ + for (int i = 0; i < level; i++) + stream.writeText("\t"); +} + +SkXMLStreamWriter::SkXMLStreamWriter(SkWStream* stream) : fStream(*stream) +{ +} + +SkXMLStreamWriter::~SkXMLStreamWriter() +{ + this->flush(); +} + +void SkXMLStreamWriter::onAddAttributeLen(const char name[], const char value[], size_t length) +{ + SkASSERT(!fElems.top()->fHasChildren); + fStream.writeText(" "); + fStream.writeText(name); + fStream.writeText("=\""); + fStream.write(value, length); + fStream.writeText("\""); +} + +void SkXMLStreamWriter::onEndElement() +{ + Elem* elem = getEnd(); + if (elem->fHasChildren) + { + tab(fStream, fElems.count()); + fStream.writeText("</"); + fStream.writeText(elem->fName.c_str()); + fStream.writeText(">"); + } + else + fStream.writeText("/>"); + fStream.newline(); + doEnd(elem); +} + +void SkXMLStreamWriter::onStartElementLen(const char name[], size_t length) +{ + int level = fElems.count(); + if (this->doStart(name, length)) + { + // the first child, need to close with > + fStream.writeText(">"); + fStream.newline(); + } + + tab(fStream, level); + fStream.writeText("<"); + fStream.write(name, length); +} + +void SkXMLStreamWriter::writeHeader() +{ + const char* header = getHeader(); + fStream.write(header, strlen(header)); + fStream.newline(); +} + +//////////////////////////////////////////////////////////////////////////////////////////////// + +#include "SkXMLParser.h" + +SkXMLParserWriter::SkXMLParserWriter(SkXMLParser* parser) + : SkXMLWriter(false), fParser(*parser) +{ +} + +SkXMLParserWriter::~SkXMLParserWriter() +{ + this->flush(); +} + +void SkXMLParserWriter::onAddAttributeLen(const char name[], const char value[], size_t length) +{ + SkASSERT(fElems.count() == 0 || !fElems.top()->fHasChildren); + SkString str(value, length); + fParser.addAttribute(name, str.c_str()); +} + +void SkXMLParserWriter::onEndElement() +{ + Elem* elem = this->getEnd(); + fParser.endElement(elem->fName.c_str()); + this->doEnd(elem); +} + +void SkXMLParserWriter::onStartElementLen(const char name[], size_t length) +{ + (void)this->doStart(name, length); + SkString str(name, length); + fParser.startElement(str.c_str()); +} + + +//////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_DEBUG + +void SkXMLStreamWriter::UnitTest() +{ +#ifdef SK_SUPPORT_UNITTEST + SkDebugWStream s; + SkXMLStreamWriter w(&s); + + w.startElement("elem0"); + w.addAttribute("hello", "world"); + w.addS32Attribute("dec", 42); + w.addHexAttribute("hex", 0x42, 3); +#ifdef SK_SCALAR_IS_FLOAT + w.addScalarAttribute("scalar", -4.2f); +#endif + w.startElement("elem1"); + w.endElement(); + w.startElement("elem1"); + w.addAttribute("name", "value"); + w.endElement(); + w.startElement("elem1"); + w.startElement("elem2"); + w.startElement("elem3"); + w.addAttribute("name", "value"); + w.endElement(); + w.endElement(); + w.startElement("elem2"); + w.endElement(); + w.endElement(); + w.endElement(); +#endif +} + +#endif + |