grab from latest android
git-svn-id: http://skia.googlecode.com/svn/trunk@27 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/animator/SkAnimate.h b/src/animator/SkAnimate.h
new file mode 100644
index 0000000..a4281d3
--- /dev/null
+++ b/src/animator/SkAnimate.h
@@ -0,0 +1,43 @@
+/* libs/graphics/animator/SkAnimate.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkAnimate3DSchema.xsd b/src/animator/SkAnimate3DSchema.xsd
new file mode 100644
index 0000000..5063b75
--- /dev/null
+++ b/src/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/src/animator/SkAnimate3DSchema.xsx b/src/animator/SkAnimate3DSchema.xsx
new file mode 100644
index 0000000..ceb7d89
--- /dev/null
+++ b/src/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/src/animator/SkAnimateActive.cpp b/src/animator/SkAnimateActive.cpp
new file mode 100644
index 0000000..4ee7ded
--- /dev/null
+++ b/src/animator/SkAnimateActive.cpp
@@ -0,0 +1,509 @@
+/* libs/graphics/animator/SkAnimateActive.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkAnimateActive.h b/src/animator/SkAnimateActive.h
new file mode 100644
index 0000000..b0c4483
--- /dev/null
+++ b/src/animator/SkAnimateActive.h
@@ -0,0 +1,87 @@
+/* libs/graphics/animator/SkAnimateActive.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkAnimateBase.cpp b/src/animator/SkAnimateBase.cpp
new file mode 100644
index 0000000..10a3b5b
--- /dev/null
+++ b/src/animator/SkAnimateBase.cpp
@@ -0,0 +1,247 @@
+/* libs/graphics/animator/SkAnimateBase.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkAnimateBase.h b/src/animator/SkAnimateBase.h
new file mode 100644
index 0000000..64b9722
--- /dev/null
+++ b/src/animator/SkAnimateBase.h
@@ -0,0 +1,91 @@
+/* libs/graphics/animator/SkAnimateBase.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkAnimateField.cpp b/src/animator/SkAnimateField.cpp
new file mode 100644
index 0000000..f1439a2
--- /dev/null
+++ b/src/animator/SkAnimateField.cpp
@@ -0,0 +1,130 @@
+/* libs/graphics/animator/SkAnimateField.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkAnimateMaker.cpp b/src/animator/SkAnimateMaker.cpp
new file mode 100644
index 0000000..8a78678
--- /dev/null
+++ b/src/animator/SkAnimateMaker.cpp
@@ -0,0 +1,376 @@
+/* libs/graphics/animator/SkAnimateMaker.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkAnimateMaker.h b/src/animator/SkAnimateMaker.h
new file mode 100644
index 0000000..dae8caf
--- /dev/null
+++ b/src/animator/SkAnimateMaker.h
@@ -0,0 +1,169 @@
+/* libs/graphics/animator/SkAnimateMaker.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkAnimateProperties.h b/src/animator/SkAnimateProperties.h
new file mode 100644
index 0000000..9e3da9a
--- /dev/null
+++ b/src/animator/SkAnimateProperties.h
@@ -0,0 +1,29 @@
+/* libs/graphics/animator/SkAnimateProperties.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkAnimateSchema.xsd b/src/animator/SkAnimateSchema.xsd
new file mode 100644
index 0000000..2c75eb4
--- /dev/null
+++ b/src/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/src/animator/SkAnimateSchema.xsx b/src/animator/SkAnimateSchema.xsx
new file mode 100644
index 0000000..ceb7d89
--- /dev/null
+++ b/src/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/src/animator/SkAnimateSet.cpp b/src/animator/SkAnimateSet.cpp
new file mode 100644
index 0000000..dd636da
--- /dev/null
+++ b/src/animator/SkAnimateSet.cpp
@@ -0,0 +1,98 @@
+/* libs/graphics/animator/SkAnimateSet.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkAnimateSet.h b/src/animator/SkAnimateSet.h
new file mode 100644
index 0000000..9f9ecfb
--- /dev/null
+++ b/src/animator/SkAnimateSet.h
@@ -0,0 +1,36 @@
+/* libs/graphics/animator/SkAnimateSet.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkAnimator.cpp b/src/animator/SkAnimator.cpp
new file mode 100644
index 0000000..242398b
--- /dev/null
+++ b/src/animator/SkAnimator.cpp
@@ -0,0 +1,724 @@
+/* libs/graphics/animator/SkAnimator.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkAnimatorScript.cpp b/src/animator/SkAnimatorScript.cpp
new file mode 100644
index 0000000..eafe1db
--- /dev/null
+++ b/src/animator/SkAnimatorScript.cpp
@@ -0,0 +1,607 @@
+/* libs/graphics/animator/SkAnimatorScript.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkAnimatorScript.h b/src/animator/SkAnimatorScript.h
new file mode 100644
index 0000000..2fdb029
--- /dev/null
+++ b/src/animator/SkAnimatorScript.h
@@ -0,0 +1,84 @@
+/* libs/graphics/animator/SkAnimatorScript.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkAnimatorScript2.cpp b/src/animator/SkAnimatorScript2.cpp
new file mode 100644
index 0000000..08dbf16
--- /dev/null
+++ b/src/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/src/animator/SkAnimatorScript2.h b/src/animator/SkAnimatorScript2.h
new file mode 100644
index 0000000..61261e9
--- /dev/null
+++ b/src/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/src/animator/SkBase64.cpp b/src/animator/SkBase64.cpp
new file mode 100644
index 0000000..e192a52
--- /dev/null
+++ b/src/animator/SkBase64.cpp
@@ -0,0 +1,188 @@
+/* libs/graphics/animator/SkBase64.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkBase64.h b/src/animator/SkBase64.h
new file mode 100644
index 0000000..12ccf8c
--- /dev/null
+++ b/src/animator/SkBase64.h
@@ -0,0 +1,47 @@
+/* libs/graphics/animator/SkBase64.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkBoundable.cpp b/src/animator/SkBoundable.cpp
new file mode 100644
index 0000000..f2125ea
--- /dev/null
+++ b/src/animator/SkBoundable.cpp
@@ -0,0 +1,64 @@
+/* libs/graphics/animator/SkBoundable.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkBoundable.h b/src/animator/SkBoundable.h
new file mode 100644
index 0000000..67f5a12
--- /dev/null
+++ b/src/animator/SkBoundable.h
@@ -0,0 +1,50 @@
+/* libs/graphics/animator/SkBoundable.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkBuildCondensedInfo.cpp b/src/animator/SkBuildCondensedInfo.cpp
new file mode 100644
index 0000000..5e4eedc
--- /dev/null
+++ b/src/animator/SkBuildCondensedInfo.cpp
@@ -0,0 +1,292 @@
+/* libs/graphics/animator/SkBuildCondensedInfo.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkCondensedDebug.cpp b/src/animator/SkCondensedDebug.cpp
new file mode 100644
index 0000000..e7b7ff1
--- /dev/null
+++ b/src/animator/SkCondensedDebug.cpp
@@ -0,0 +1,1397 @@
+/* libs/graphics/animator/SkCondensedDebug.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkCondensedRelease.cpp b/src/animator/SkCondensedRelease.cpp
new file mode 100644
index 0000000..a493cb3
--- /dev/null
+++ b/src/animator/SkCondensedRelease.cpp
@@ -0,0 +1,1374 @@
+/* libs/graphics/animator/SkCondensedRelease.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkDisplayAdd.cpp b/src/animator/SkDisplayAdd.cpp
new file mode 100644
index 0000000..8a97a06
--- /dev/null
+++ b/src/animator/SkDisplayAdd.cpp
@@ -0,0 +1,254 @@
+/* libs/graphics/animator/SkDisplayAdd.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkDisplayAdd.h b/src/animator/SkDisplayAdd.h
new file mode 100644
index 0000000..0f3edc9
--- /dev/null
+++ b/src/animator/SkDisplayAdd.h
@@ -0,0 +1,81 @@
+/* libs/graphics/animator/SkDisplayAdd.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkDisplayApply.cpp b/src/animator/SkDisplayApply.cpp
new file mode 100644
index 0000000..b9e65f7
--- /dev/null
+++ b/src/animator/SkDisplayApply.cpp
@@ -0,0 +1,814 @@
+/* libs/graphics/animator/SkDisplayApply.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkDisplayApply.h b/src/animator/SkDisplayApply.h
new file mode 100644
index 0000000..d51e467
--- /dev/null
+++ b/src/animator/SkDisplayApply.h
@@ -0,0 +1,116 @@
+/* libs/graphics/animator/SkDisplayApply.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkDisplayBounds.cpp b/src/animator/SkDisplayBounds.cpp
new file mode 100644
index 0000000..d0499ce
--- /dev/null
+++ b/src/animator/SkDisplayBounds.cpp
@@ -0,0 +1,54 @@
+/* libs/graphics/animator/SkDisplayBounds.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkDisplayBounds.h b/src/animator/SkDisplayBounds.h
new file mode 100644
index 0000000..6fcd09c
--- /dev/null
+++ b/src/animator/SkDisplayBounds.h
@@ -0,0 +1,33 @@
+/* libs/graphics/animator/SkDisplayBounds.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkDisplayEvent.cpp b/src/animator/SkDisplayEvent.cpp
new file mode 100644
index 0000000..6253cdf
--- /dev/null
+++ b/src/animator/SkDisplayEvent.cpp
@@ -0,0 +1,339 @@
+/* libs/graphics/animator/SkDisplayEvent.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkDisplayEvent.h b/src/animator/SkDisplayEvent.h
new file mode 100644
index 0000000..837cfec
--- /dev/null
+++ b/src/animator/SkDisplayEvent.h
@@ -0,0 +1,75 @@
+/* libs/graphics/animator/SkDisplayEvent.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkDisplayEvents.cpp b/src/animator/SkDisplayEvents.cpp
new file mode 100644
index 0000000..e6a01ba
--- /dev/null
+++ b/src/animator/SkDisplayEvents.cpp
@@ -0,0 +1,121 @@
+/* libs/graphics/animator/SkDisplayEvents.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkDisplayEvents.h b/src/animator/SkDisplayEvents.h
new file mode 100644
index 0000000..73721d4
--- /dev/null
+++ b/src/animator/SkDisplayEvents.h
@@ -0,0 +1,51 @@
+/* libs/graphics/animator/SkDisplayEvents.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkDisplayInclude.cpp b/src/animator/SkDisplayInclude.cpp
new file mode 100644
index 0000000..26b1e69
--- /dev/null
+++ b/src/animator/SkDisplayInclude.cpp
@@ -0,0 +1,67 @@
+/* libs/graphics/animator/SkDisplayInclude.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkDisplayInclude.h b/src/animator/SkDisplayInclude.h
new file mode 100644
index 0000000..81ad76a
--- /dev/null
+++ b/src/animator/SkDisplayInclude.h
@@ -0,0 +1,34 @@
+/* libs/graphics/animator/SkDisplayInclude.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkDisplayInput.cpp b/src/animator/SkDisplayInput.cpp
new file mode 100644
index 0000000..3688930
--- /dev/null
+++ b/src/animator/SkDisplayInput.cpp
@@ -0,0 +1,63 @@
+/* libs/graphics/animator/SkDisplayInput.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkDisplayInput.h b/src/animator/SkDisplayInput.h
new file mode 100644
index 0000000..ac7c7c1
--- /dev/null
+++ b/src/animator/SkDisplayInput.h
@@ -0,0 +1,42 @@
+/* libs/graphics/animator/SkDisplayInput.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkDisplayList.cpp b/src/animator/SkDisplayList.cpp
new file mode 100644
index 0000000..57f29f7
--- /dev/null
+++ b/src/animator/SkDisplayList.cpp
@@ -0,0 +1,168 @@
+/* libs/graphics/animator/SkDisplayList.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkDisplayList.h b/src/animator/SkDisplayList.h
new file mode 100644
index 0000000..3743e35
--- /dev/null
+++ b/src/animator/SkDisplayList.h
@@ -0,0 +1,79 @@
+/* libs/graphics/animator/SkDisplayList.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkDisplayMath.cpp b/src/animator/SkDisplayMath.cpp
new file mode 100644
index 0000000..66bd1f8
--- /dev/null
+++ b/src/animator/SkDisplayMath.cpp
@@ -0,0 +1,248 @@
+/* libs/graphics/animator/SkDisplayMath.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkDisplayMath.h b/src/animator/SkDisplayMath.h
new file mode 100644
index 0000000..de26319
--- /dev/null
+++ b/src/animator/SkDisplayMath.h
@@ -0,0 +1,40 @@
+/* libs/graphics/animator/SkDisplayMath.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkDisplayMovie.cpp b/src/animator/SkDisplayMovie.cpp
new file mode 100644
index 0000000..ed74329
--- /dev/null
+++ b/src/animator/SkDisplayMovie.cpp
@@ -0,0 +1,138 @@
+/* libs/graphics/animator/SkDisplayMovie.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkDisplayMovie.h b/src/animator/SkDisplayMovie.h
new file mode 100644
index 0000000..2b8a893
--- /dev/null
+++ b/src/animator/SkDisplayMovie.h
@@ -0,0 +1,60 @@
+/* libs/graphics/animator/SkDisplayMovie.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkDisplayNumber.cpp b/src/animator/SkDisplayNumber.cpp
new file mode 100644
index 0000000..3bb96d7
--- /dev/null
+++ b/src/animator/SkDisplayNumber.cpp
@@ -0,0 +1,67 @@
+/* libs/graphics/animator/SkDisplayNumber.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkDisplayNumber.h b/src/animator/SkDisplayNumber.h
new file mode 100644
index 0000000..515c35b
--- /dev/null
+++ b/src/animator/SkDisplayNumber.h
@@ -0,0 +1,30 @@
+/* libs/graphics/animator/SkDisplayNumber.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkDisplayPost.cpp b/src/animator/SkDisplayPost.cpp
new file mode 100644
index 0000000..17e7c76
--- /dev/null
+++ b/src/animator/SkDisplayPost.cpp
@@ -0,0 +1,315 @@
+/* libs/graphics/animator/SkDisplayPost.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkDisplayPost.h b/src/animator/SkDisplayPost.h
new file mode 100644
index 0000000..4b75b07
--- /dev/null
+++ b/src/animator/SkDisplayPost.h
@@ -0,0 +1,67 @@
+/* libs/graphics/animator/SkDisplayPost.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkDisplayRandom.cpp b/src/animator/SkDisplayRandom.cpp
new file mode 100644
index 0000000..ab078cd
--- /dev/null
+++ b/src/animator/SkDisplayRandom.cpp
@@ -0,0 +1,80 @@
+/* libs/graphics/animator/SkDisplayRandom.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkDisplayRandom.h b/src/animator/SkDisplayRandom.h
new file mode 100644
index 0000000..b40907e
--- /dev/null
+++ b/src/animator/SkDisplayRandom.h
@@ -0,0 +1,49 @@
+/* libs/graphics/animator/SkDisplayRandom.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkDisplayScreenplay.cpp b/src/animator/SkDisplayScreenplay.cpp
new file mode 100644
index 0000000..58180e7
--- /dev/null
+++ b/src/animator/SkDisplayScreenplay.cpp
@@ -0,0 +1,30 @@
+/* libs/graphics/animator/SkDisplayScreenplay.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkDisplayScreenplay.h"
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDisplayScreenplay::fInfo[] = {
+ SK_MEMBER(time, MSec)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDisplayScreenplay);
+
+
diff --git a/src/animator/SkDisplayScreenplay.h b/src/animator/SkDisplayScreenplay.h
new file mode 100644
index 0000000..cb8103d
--- /dev/null
+++ b/src/animator/SkDisplayScreenplay.h
@@ -0,0 +1,29 @@
+/* libs/graphics/animator/SkDisplayScreenplay.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkDisplayType.cpp b/src/animator/SkDisplayType.cpp
new file mode 100644
index 0000000..90fcd89
--- /dev/null
+++ b/src/animator/SkDisplayType.cpp
@@ -0,0 +1,774 @@
+/* libs/graphics/animator/SkDisplayType.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkDisplayType.h b/src/animator/SkDisplayType.h
new file mode 100644
index 0000000..a29b285
--- /dev/null
+++ b/src/animator/SkDisplayType.h
@@ -0,0 +1,216 @@
+/* libs/graphics/animator/SkDisplayType.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkDisplayTypes.cpp b/src/animator/SkDisplayTypes.cpp
new file mode 100644
index 0000000..b5bc573
--- /dev/null
+++ b/src/animator/SkDisplayTypes.cpp
@@ -0,0 +1,229 @@
+/* libs/graphics/animator/SkDisplayTypes.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkDisplayTypes.h b/src/animator/SkDisplayTypes.h
new file mode 100644
index 0000000..0ac57ba
--- /dev/null
+++ b/src/animator/SkDisplayTypes.h
@@ -0,0 +1,115 @@
+/* libs/graphics/animator/SkDisplayTypes.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkDisplayXMLParser.cpp b/src/animator/SkDisplayXMLParser.cpp
new file mode 100644
index 0000000..d2775e8
--- /dev/null
+++ b/src/animator/SkDisplayXMLParser.cpp
@@ -0,0 +1,318 @@
+/* libs/graphics/animator/SkDisplayXMLParser.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkDisplayXMLParser.h b/src/animator/SkDisplayXMLParser.h
new file mode 100644
index 0000000..2c2bec1
--- /dev/null
+++ b/src/animator/SkDisplayXMLParser.h
@@ -0,0 +1,101 @@
+/* libs/graphics/animator/SkDisplayXMLParser.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkDisplayable.cpp b/src/animator/SkDisplayable.cpp
new file mode 100644
index 0000000..e50e1ab
--- /dev/null
+++ b/src/animator/SkDisplayable.cpp
@@ -0,0 +1,566 @@
+/* libs/graphics/animator/SkDisplayable.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkDisplayable.h b/src/animator/SkDisplayable.h
new file mode 100644
index 0000000..cbc96de
--- /dev/null
+++ b/src/animator/SkDisplayable.h
@@ -0,0 +1,120 @@
+/* libs/graphics/animator/SkDisplayable.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkDraw3D.cpp b/src/animator/SkDraw3D.cpp
new file mode 100644
index 0000000..137de9f
--- /dev/null
+++ b/src/animator/SkDraw3D.cpp
@@ -0,0 +1,117 @@
+/* libs/graphics/animator/SkDraw3D.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkDraw3D.h b/src/animator/SkDraw3D.h
new file mode 100644
index 0000000..5ab61cd
--- /dev/null
+++ b/src/animator/SkDraw3D.h
@@ -0,0 +1,59 @@
+/* libs/graphics/animator/SkDraw3D.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkDrawBitmap.cpp b/src/animator/SkDrawBitmap.cpp
new file mode 100644
index 0000000..25355be
--- /dev/null
+++ b/src/animator/SkDrawBitmap.cpp
@@ -0,0 +1,206 @@
+/* libs/graphics/animator/SkDrawBitmap.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkDrawBitmap.h b/src/animator/SkDrawBitmap.h
new file mode 100644
index 0000000..f846193
--- /dev/null
+++ b/src/animator/SkDrawBitmap.h
@@ -0,0 +1,82 @@
+/* libs/graphics/animator/SkDrawBitmap.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkDrawBlur.cpp b/src/animator/SkDrawBlur.cpp
new file mode 100644
index 0000000..0ebabce
--- /dev/null
+++ b/src/animator/SkDrawBlur.cpp
@@ -0,0 +1,40 @@
+/* libs/graphics/animator/SkDrawBlur.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkDrawBlur.h b/src/animator/SkDrawBlur.h
new file mode 100644
index 0000000..14ceba0
--- /dev/null
+++ b/src/animator/SkDrawBlur.h
@@ -0,0 +1,34 @@
+/* libs/graphics/animator/SkDrawBlur.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkDrawClip.cpp b/src/animator/SkDrawClip.cpp
new file mode 100644
index 0000000..1c01c6a
--- /dev/null
+++ b/src/animator/SkDrawClip.cpp
@@ -0,0 +1,48 @@
+/* libs/graphics/animator/SkDrawClip.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkDrawClip.h b/src/animator/SkDrawClip.h
new file mode 100644
index 0000000..2fed99a
--- /dev/null
+++ b/src/animator/SkDrawClip.h
@@ -0,0 +1,37 @@
+/* libs/graphics/animator/SkDrawClip.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkDrawColor.cpp b/src/animator/SkDrawColor.cpp
new file mode 100644
index 0000000..009ca10
--- /dev/null
+++ b/src/animator/SkDrawColor.cpp
@@ -0,0 +1,278 @@
+/* libs/graphics/animator/SkDrawColor.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkDrawColor.h b/src/animator/SkDrawColor.h
new file mode 100644
index 0000000..89b87cc
--- /dev/null
+++ b/src/animator/SkDrawColor.h
@@ -0,0 +1,50 @@
+/* libs/graphics/animator/SkDrawColor.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkDrawDash.cpp b/src/animator/SkDrawDash.cpp
new file mode 100644
index 0000000..bdac9cd
--- /dev/null
+++ b/src/animator/SkDrawDash.cpp
@@ -0,0 +1,44 @@
+/* libs/graphics/animator/SkDrawDash.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkDrawDash.h b/src/animator/SkDrawDash.h
new file mode 100644
index 0000000..c2108c1
--- /dev/null
+++ b/src/animator/SkDrawDash.h
@@ -0,0 +1,35 @@
+/* libs/graphics/animator/SkDrawDash.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkDrawDiscrete.cpp b/src/animator/SkDrawDiscrete.cpp
new file mode 100644
index 0000000..e1089c2
--- /dev/null
+++ b/src/animator/SkDrawDiscrete.cpp
@@ -0,0 +1,43 @@
+/* libs/graphics/animator/SkDrawDiscrete.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkDrawDiscrete.h b/src/animator/SkDrawDiscrete.h
new file mode 100644
index 0000000..78485ee
--- /dev/null
+++ b/src/animator/SkDrawDiscrete.h
@@ -0,0 +1,32 @@
+/* libs/graphics/animator/SkDrawDiscrete.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkDrawEmboss.cpp b/src/animator/SkDrawEmboss.cpp
new file mode 100644
index 0000000..77f6dfd
--- /dev/null
+++ b/src/animator/SkDrawEmboss.cpp
@@ -0,0 +1,42 @@
+/* libs/graphics/animator/SkDrawEmboss.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkDrawEmboss.h b/src/animator/SkDrawEmboss.h
new file mode 100644
index 0000000..fe6911b
--- /dev/null
+++ b/src/animator/SkDrawEmboss.h
@@ -0,0 +1,33 @@
+/* libs/graphics/animator/SkDrawEmboss.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkDrawExtraPathEffect.cpp b/src/animator/SkDrawExtraPathEffect.cpp
new file mode 100644
index 0000000..4cca738
--- /dev/null
+++ b/src/animator/SkDrawExtraPathEffect.cpp
@@ -0,0 +1,519 @@
+/* libs/graphics/animator/SkDrawExtraPathEffect.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkDrawFull.cpp b/src/animator/SkDrawFull.cpp
new file mode 100644
index 0000000..c6568db
--- /dev/null
+++ b/src/animator/SkDrawFull.cpp
@@ -0,0 +1,27 @@
+/* libs/graphics/animator/SkDrawFull.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkDrawFull.h b/src/animator/SkDrawFull.h
new file mode 100644
index 0000000..cfbb643
--- /dev/null
+++ b/src/animator/SkDrawFull.h
@@ -0,0 +1,30 @@
+/* libs/graphics/animator/SkDrawFull.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkDrawGradient.cpp b/src/animator/SkDrawGradient.cpp
new file mode 100644
index 0000000..e9061b5
--- /dev/null
+++ b/src/animator/SkDrawGradient.cpp
@@ -0,0 +1,235 @@
+/* libs/graphics/animator/SkDrawGradient.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkDrawGradient.h b/src/animator/SkDrawGradient.h
new file mode 100644
index 0000000..30a86df
--- /dev/null
+++ b/src/animator/SkDrawGradient.h
@@ -0,0 +1,76 @@
+/* libs/graphics/animator/SkDrawGradient.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkDrawGroup.cpp b/src/animator/SkDrawGroup.cpp
new file mode 100644
index 0000000..aa53564
--- /dev/null
+++ b/src/animator/SkDrawGroup.cpp
@@ -0,0 +1,331 @@
+/* libs/graphics/animator/SkDrawGroup.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkDrawGroup.h b/src/animator/SkDrawGroup.h
new file mode 100644
index 0000000..0009a4c
--- /dev/null
+++ b/src/animator/SkDrawGroup.h
@@ -0,0 +1,80 @@
+/* libs/graphics/animator/SkDrawGroup.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkDrawLine.cpp b/src/animator/SkDrawLine.cpp
new file mode 100644
index 0000000..166cbbc
--- /dev/null
+++ b/src/animator/SkDrawLine.cpp
@@ -0,0 +1,43 @@
+/* libs/graphics/animator/SkDrawLine.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkDrawLine.h b/src/animator/SkDrawLine.h
new file mode 100644
index 0000000..8f771cf
--- /dev/null
+++ b/src/animator/SkDrawLine.h
@@ -0,0 +1,37 @@
+/* libs/graphics/animator/SkDrawLine.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkDrawMatrix.cpp b/src/animator/SkDrawMatrix.cpp
new file mode 100644
index 0000000..dbdbf19
--- /dev/null
+++ b/src/animator/SkDrawMatrix.cpp
@@ -0,0 +1,290 @@
+/* libs/graphics/animator/SkDrawMatrix.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkDrawMatrix.h b/src/animator/SkDrawMatrix.h
new file mode 100644
index 0000000..8339d8c
--- /dev/null
+++ b/src/animator/SkDrawMatrix.h
@@ -0,0 +1,82 @@
+/* libs/graphics/animator/SkDrawMatrix.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkDrawOval.cpp b/src/animator/SkDrawOval.cpp
new file mode 100644
index 0000000..0e3a9ae
--- /dev/null
+++ b/src/animator/SkDrawOval.cpp
@@ -0,0 +1,37 @@
+/* libs/graphics/animator/SkDrawOval.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkDrawOval.h b/src/animator/SkDrawOval.h
new file mode 100644
index 0000000..0e7ae3f
--- /dev/null
+++ b/src/animator/SkDrawOval.h
@@ -0,0 +1,31 @@
+/* libs/graphics/animator/SkDrawOval.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkDrawPaint.cpp b/src/animator/SkDrawPaint.cpp
new file mode 100644
index 0000000..68caa5a
--- /dev/null
+++ b/src/animator/SkDrawPaint.cpp
@@ -0,0 +1,277 @@
+/* libs/graphics/animator/SkDrawPaint.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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
+ 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/src/animator/SkDrawPaint.h b/src/animator/SkDrawPaint.h
new file mode 100644
index 0000000..ea77acd
--- /dev/null
+++ b/src/animator/SkDrawPaint.h
@@ -0,0 +1,88 @@
+/* libs/graphics/animator/SkDrawPaint.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkDrawPath.cpp b/src/animator/SkDrawPath.cpp
new file mode 100644
index 0000000..895e597
--- /dev/null
+++ b/src/animator/SkDrawPath.cpp
@@ -0,0 +1,229 @@
+/* libs/graphics/animator/SkDrawPath.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkDrawPath.h b/src/animator/SkDrawPath.h
new file mode 100644
index 0000000..673051a
--- /dev/null
+++ b/src/animator/SkDrawPath.h
@@ -0,0 +1,77 @@
+/* libs/graphics/animator/SkDrawPath.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkDrawPoint.cpp b/src/animator/SkDrawPoint.cpp
new file mode 100644
index 0000000..383a599
--- /dev/null
+++ b/src/animator/SkDrawPoint.cpp
@@ -0,0 +1,54 @@
+/* libs/graphics/animator/SkDrawPoint.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkDrawPoint.h b/src/animator/SkDrawPoint.h
new file mode 100644
index 0000000..d06e5b7
--- /dev/null
+++ b/src/animator/SkDrawPoint.h
@@ -0,0 +1,41 @@
+/* libs/graphics/animator/SkDrawPoint.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkDrawRectangle.cpp b/src/animator/SkDrawRectangle.cpp
new file mode 100644
index 0000000..f2054bd
--- /dev/null
+++ b/src/animator/SkDrawRectangle.cpp
@@ -0,0 +1,153 @@
+/* libs/graphics/animator/SkDrawRectangle.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkDrawRectangle.h b/src/animator/SkDrawRectangle.h
new file mode 100644
index 0000000..9f8bc0a
--- /dev/null
+++ b/src/animator/SkDrawRectangle.h
@@ -0,0 +1,64 @@
+/* libs/graphics/animator/SkDrawRectangle.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkDrawSaveLayer.cpp b/src/animator/SkDrawSaveLayer.cpp
new file mode 100644
index 0000000..bc2288f
--- /dev/null
+++ b/src/animator/SkDrawSaveLayer.cpp
@@ -0,0 +1,86 @@
+/* libs/graphics/animator/SkDrawSaveLayer.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkDrawSaveLayer.h b/src/animator/SkDrawSaveLayer.h
new file mode 100644
index 0000000..52a36a4
--- /dev/null
+++ b/src/animator/SkDrawSaveLayer.h
@@ -0,0 +1,44 @@
+/* libs/graphics/animator/SkDrawSaveLayer.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkDrawShader.cpp b/src/animator/SkDrawShader.cpp
new file mode 100644
index 0000000..b38718e
--- /dev/null
+++ b/src/animator/SkDrawShader.cpp
@@ -0,0 +1,91 @@
+/* libs/graphics/animator/SkDrawShader.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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")
+ //
+ // oops, bitmapshader no longer takes filterBitmap, but deduces it at
+ // draw-time from the paint
+ 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/src/animator/SkDrawShader.h b/src/animator/SkDrawShader.h
new file mode 100644
index 0000000..35ca419
--- /dev/null
+++ b/src/animator/SkDrawShader.h
@@ -0,0 +1,38 @@
+/* libs/graphics/animator/SkDrawShader.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkDrawText.cpp b/src/animator/SkDrawText.cpp
new file mode 100644
index 0000000..dcc3a75
--- /dev/null
+++ b/src/animator/SkDrawText.cpp
@@ -0,0 +1,64 @@
+/* libs/graphics/animator/SkDrawText.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkDrawText.h b/src/animator/SkDrawText.h
new file mode 100644
index 0000000..cb1d1b9
--- /dev/null
+++ b/src/animator/SkDrawText.h
@@ -0,0 +1,44 @@
+/* libs/graphics/animator/SkDrawText.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkDrawTextBox.cpp b/src/animator/SkDrawTextBox.cpp
new file mode 100644
index 0000000..4c4ac76
--- /dev/null
+++ b/src/animator/SkDrawTextBox.cpp
@@ -0,0 +1,90 @@
+/* libs/graphics/animator/SkDrawTextBox.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkDrawTextBox.h b/src/animator/SkDrawTextBox.h
new file mode 100644
index 0000000..ee97c22
--- /dev/null
+++ b/src/animator/SkDrawTextBox.h
@@ -0,0 +1,47 @@
+/* libs/graphics/animator/SkDrawTextBox.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkDrawTo.cpp b/src/animator/SkDrawTo.cpp
new file mode 100644
index 0000000..342f7d4
--- /dev/null
+++ b/src/animator/SkDrawTo.cpp
@@ -0,0 +1,64 @@
+/* libs/graphics/animator/SkDrawTo.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkDrawTo.h b/src/animator/SkDrawTo.h
new file mode 100644
index 0000000..0ad78e8
--- /dev/null
+++ b/src/animator/SkDrawTo.h
@@ -0,0 +1,42 @@
+/* libs/graphics/animator/SkDrawTo.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkDrawTransparentShader.cpp b/src/animator/SkDrawTransparentShader.cpp
new file mode 100644
index 0000000..c1da687
--- /dev/null
+++ b/src/animator/SkDrawTransparentShader.cpp
@@ -0,0 +1,24 @@
+/* libs/graphics/animator/SkDrawTransparentShader.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkDrawTransparentShader.h"
+#include "SkTransparentShader.h"
+
+SkShader* SkDrawTransparentShader::getShader() {
+ return new SkTransparentShader();
+}
+
diff --git a/src/animator/SkDrawTransparentShader.h b/src/animator/SkDrawTransparentShader.h
new file mode 100644
index 0000000..e831883
--- /dev/null
+++ b/src/animator/SkDrawTransparentShader.h
@@ -0,0 +1,29 @@
+/* libs/graphics/animator/SkDrawTransparentShader.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkDrawable.cpp b/src/animator/SkDrawable.cpp
new file mode 100644
index 0000000..6968fe1
--- /dev/null
+++ b/src/animator/SkDrawable.cpp
@@ -0,0 +1,33 @@
+/* libs/graphics/animator/SkDrawable.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkDrawable.h b/src/animator/SkDrawable.h
new file mode 100644
index 0000000..7284a25
--- /dev/null
+++ b/src/animator/SkDrawable.h
@@ -0,0 +1,36 @@
+/* libs/graphics/animator/SkDrawable.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkDump.cpp b/src/animator/SkDump.cpp
new file mode 100644
index 0000000..eac956c
--- /dev/null
+++ b/src/animator/SkDump.cpp
@@ -0,0 +1,158 @@
+/* libs/graphics/animator/SkDump.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkDump.h b/src/animator/SkDump.h
new file mode 100644
index 0000000..73a6957
--- /dev/null
+++ b/src/animator/SkDump.h
@@ -0,0 +1,51 @@
+/* libs/graphics/animator/SkDump.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkExtraPathEffects.xsd b/src/animator/SkExtraPathEffects.xsd
new file mode 100644
index 0000000..9592443
--- /dev/null
+++ b/src/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/src/animator/SkExtras.h b/src/animator/SkExtras.h
new file mode 100644
index 0000000..88e9b2d
--- /dev/null
+++ b/src/animator/SkExtras.h
@@ -0,0 +1,42 @@
+/* libs/graphics/animator/SkExtras.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkGetCondensedInfo.cpp b/src/animator/SkGetCondensedInfo.cpp
new file mode 100644
index 0000000..ee91caa
--- /dev/null
+++ b/src/animator/SkGetCondensedInfo.cpp
@@ -0,0 +1,130 @@
+/* libs/graphics/animator/SkGetCondensedInfo.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkHitClear.cpp b/src/animator/SkHitClear.cpp
new file mode 100644
index 0000000..e7e301d
--- /dev/null
+++ b/src/animator/SkHitClear.cpp
@@ -0,0 +1,41 @@
+/* libs/graphics/animator/SkHitClear.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkHitClear.h b/src/animator/SkHitClear.h
new file mode 100644
index 0000000..f2b50c0
--- /dev/null
+++ b/src/animator/SkHitClear.h
@@ -0,0 +1,33 @@
+/* libs/graphics/animator/SkHitClear.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkHitTest.cpp b/src/animator/SkHitTest.cpp
new file mode 100644
index 0000000..585249a
--- /dev/null
+++ b/src/animator/SkHitTest.cpp
@@ -0,0 +1,83 @@
+/* libs/graphics/animator/SkHitTest.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkHitTest.h b/src/animator/SkHitTest.h
new file mode 100644
index 0000000..b3a9d85
--- /dev/null
+++ b/src/animator/SkHitTest.h
@@ -0,0 +1,38 @@
+/* libs/graphics/animator/SkHitTest.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkIntArray.h b/src/animator/SkIntArray.h
new file mode 100644
index 0000000..b2a49c1
--- /dev/null
+++ b/src/animator/SkIntArray.h
@@ -0,0 +1,66 @@
+/* libs/graphics/animator/SkIntArray.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkMatrixParts.cpp b/src/animator/SkMatrixParts.cpp
new file mode 100644
index 0000000..56bb2d0
--- /dev/null
+++ b/src/animator/SkMatrixParts.cpp
@@ -0,0 +1,302 @@
+/* libs/graphics/animator/SkMatrixParts.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkMatrixParts.h b/src/animator/SkMatrixParts.h
new file mode 100644
index 0000000..d97d9fb
--- /dev/null
+++ b/src/animator/SkMatrixParts.h
@@ -0,0 +1,127 @@
+/* libs/graphics/animator/SkMatrixParts.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkMemberInfo.cpp b/src/animator/SkMemberInfo.cpp
new file mode 100644
index 0000000..50536c2
--- /dev/null
+++ b/src/animator/SkMemberInfo.cpp
@@ -0,0 +1,568 @@
+/* libs/graphics/animator/SkMemberInfo.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkMemberInfo.h b/src/animator/SkMemberInfo.h
new file mode 100644
index 0000000..e45994e
--- /dev/null
+++ b/src/animator/SkMemberInfo.h
@@ -0,0 +1,283 @@
+/* libs/graphics/animator/SkMemberInfo.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkOpArray.cpp b/src/animator/SkOpArray.cpp
new file mode 100644
index 0000000..eb3fcec
--- /dev/null
+++ b/src/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/src/animator/SkOpArray.h b/src/animator/SkOpArray.h
new file mode 100644
index 0000000..5c511c0
--- /dev/null
+++ b/src/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/src/animator/SkOperand.h b/src/animator/SkOperand.h
new file mode 100644
index 0000000..2c7e93b
--- /dev/null
+++ b/src/animator/SkOperand.h
@@ -0,0 +1,54 @@
+/* libs/graphics/animator/SkOperand.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkOperand2.h b/src/animator/SkOperand2.h
new file mode 100644
index 0000000..f482e66
--- /dev/null
+++ b/src/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/src/animator/SkOperandInterpolator.h b/src/animator/SkOperandInterpolator.h
new file mode 100644
index 0000000..f155276
--- /dev/null
+++ b/src/animator/SkOperandInterpolator.h
@@ -0,0 +1,56 @@
+/* libs/graphics/animator/SkOperandInterpolator.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkOperandIterpolator.cpp b/src/animator/SkOperandIterpolator.cpp
new file mode 100644
index 0000000..2bddd34
--- /dev/null
+++ b/src/animator/SkOperandIterpolator.cpp
@@ -0,0 +1,159 @@
+/* libs/graphics/animator/SkOperandIterpolator.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkPaintParts.cpp b/src/animator/SkPaintParts.cpp
new file mode 100644
index 0000000..48799c6
--- /dev/null
+++ b/src/animator/SkPaintParts.cpp
@@ -0,0 +1,111 @@
+/* libs/graphics/animator/SkPaintParts.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkPaintParts.h b/src/animator/SkPaintParts.h
new file mode 100644
index 0000000..a8bb8bd
--- /dev/null
+++ b/src/animator/SkPaintParts.h
@@ -0,0 +1,83 @@
+/* libs/graphics/animator/SkPaintParts.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkPathParts.cpp b/src/animator/SkPathParts.cpp
new file mode 100644
index 0000000..46d2232
--- /dev/null
+++ b/src/animator/SkPathParts.cpp
@@ -0,0 +1,328 @@
+/* libs/graphics/animator/SkPathParts.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkPathParts.h b/src/animator/SkPathParts.h
new file mode 100644
index 0000000..72e4185
--- /dev/null
+++ b/src/animator/SkPathParts.h
@@ -0,0 +1,173 @@
+/* libs/graphics/animator/SkPathParts.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkPostParts.cpp b/src/animator/SkPostParts.cpp
new file mode 100644
index 0000000..254b17d
--- /dev/null
+++ b/src/animator/SkPostParts.cpp
@@ -0,0 +1,65 @@
+/* libs/graphics/animator/SkPostParts.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkPostParts.h b/src/animator/SkPostParts.h
new file mode 100644
index 0000000..ef41fe5
--- /dev/null
+++ b/src/animator/SkPostParts.h
@@ -0,0 +1,39 @@
+/* libs/graphics/animator/SkPostParts.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkSVGPath.cpp b/src/animator/SkSVGPath.cpp
new file mode 100644
index 0000000..86eeee8
--- /dev/null
+++ b/src/animator/SkSVGPath.cpp
@@ -0,0 +1,243 @@
+/* libs/graphics/animator/SkSVGPath.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include <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/src/animator/SkScript.cpp b/src/animator/SkScript.cpp
new file mode 100644
index 0000000..3b67d7b
--- /dev/null
+++ b/src/animator/SkScript.cpp
@@ -0,0 +1,1918 @@
+/* libs/graphics/animator/SkScript.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkScript.h b/src/animator/SkScript.h
new file mode 100644
index 0000000..bb7d978
--- /dev/null
+++ b/src/animator/SkScript.h
@@ -0,0 +1,274 @@
+/* libs/graphics/animator/SkScript.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkScript2.h b/src/animator/SkScript2.h
new file mode 100644
index 0000000..4d8bd8c
--- /dev/null
+++ b/src/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/src/animator/SkScriptCallBack.h b/src/animator/SkScriptCallBack.h
new file mode 100644
index 0000000..725e493
--- /dev/null
+++ b/src/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/src/animator/SkScriptDecompile.cpp b/src/animator/SkScriptDecompile.cpp
new file mode 100644
index 0000000..d582d33
--- /dev/null
+++ b/src/animator/SkScriptDecompile.cpp
@@ -0,0 +1,221 @@
+/* libs/graphics/animator/SkScriptDecompile.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkScriptRuntime.cpp b/src/animator/SkScriptRuntime.cpp
new file mode 100644
index 0000000..6d8c208
--- /dev/null
+++ b/src/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/src/animator/SkScriptRuntime.h b/src/animator/SkScriptRuntime.h
new file mode 100644
index 0000000..c864fe4
--- /dev/null
+++ b/src/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/src/animator/SkScriptTokenizer.cpp b/src/animator/SkScriptTokenizer.cpp
new file mode 100644
index 0000000..d75e68e
--- /dev/null
+++ b/src/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/src/animator/SkSnapshot.cpp b/src/animator/SkSnapshot.cpp
new file mode 100644
index 0000000..b65c517
--- /dev/null
+++ b/src/animator/SkSnapshot.cpp
@@ -0,0 +1,74 @@
+/* libs/graphics/animator/SkSnapshot.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkSnapshot.h b/src/animator/SkSnapshot.h
new file mode 100644
index 0000000..c459fbb
--- /dev/null
+++ b/src/animator/SkSnapshot.h
@@ -0,0 +1,42 @@
+/* libs/graphics/animator/SkSnapshot.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkTDArray_Experimental.h b/src/animator/SkTDArray_Experimental.h
new file mode 100644
index 0000000..94d7871
--- /dev/null
+++ b/src/animator/SkTDArray_Experimental.h
@@ -0,0 +1,150 @@
+/* libs/graphics/animator/SkTDArray_Experimental.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkTextOnPath.cpp b/src/animator/SkTextOnPath.cpp
new file mode 100644
index 0000000..1a06746
--- /dev/null
+++ b/src/animator/SkTextOnPath.cpp
@@ -0,0 +1,47 @@
+/* libs/graphics/animator/SkTextOnPath.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkTextOnPath.h b/src/animator/SkTextOnPath.h
new file mode 100644
index 0000000..3c78b5b
--- /dev/null
+++ b/src/animator/SkTextOnPath.h
@@ -0,0 +1,38 @@
+/* libs/graphics/animator/SkTextOnPath.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkTextToPath.cpp b/src/animator/SkTextToPath.cpp
new file mode 100644
index 0000000..44036a3
--- /dev/null
+++ b/src/animator/SkTextToPath.cpp
@@ -0,0 +1,56 @@
+/* libs/graphics/animator/SkTextToPath.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkTextToPath.h b/src/animator/SkTextToPath.h
new file mode 100644
index 0000000..6d93239
--- /dev/null
+++ b/src/animator/SkTextToPath.h
@@ -0,0 +1,40 @@
+/* libs/graphics/animator/SkTextToPath.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkTime.cpp b/src/animator/SkTime.cpp
new file mode 100644
index 0000000..a36a95a
--- /dev/null
+++ b/src/animator/SkTime.cpp
@@ -0,0 +1,89 @@
+/* libs/graphics/animator/SkTime.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkTypedArray.cpp b/src/animator/SkTypedArray.cpp
new file mode 100644
index 0000000..075c607
--- /dev/null
+++ b/src/animator/SkTypedArray.cpp
@@ -0,0 +1,187 @@
+/* libs/graphics/animator/SkTypedArray.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkTypedArray.h b/src/animator/SkTypedArray.h
new file mode 100644
index 0000000..a658f29
--- /dev/null
+++ b/src/animator/SkTypedArray.h
@@ -0,0 +1,39 @@
+/* libs/graphics/animator/SkTypedArray.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/SkXMLAnimatorWriter.cpp b/src/animator/SkXMLAnimatorWriter.cpp
new file mode 100644
index 0000000..b26bc73
--- /dev/null
+++ b/src/animator/SkXMLAnimatorWriter.cpp
@@ -0,0 +1,91 @@
+/* libs/graphics/animator/SkXMLAnimatorWriter.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/animator/SkXMLAnimatorWriter.h b/src/animator/SkXMLAnimatorWriter.h
new file mode 100644
index 0000000..c5830cd
--- /dev/null
+++ b/src/animator/SkXMLAnimatorWriter.h
@@ -0,0 +1,42 @@
+/* libs/graphics/animator/SkXMLAnimatorWriter.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/animator/thingstodo.txt b/src/animator/thingstodo.txt
new file mode 100644
index 0000000..8d0d47a
--- /dev/null
+++ b/src/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/src/core/ARGB32_Clamp_Bilinear_BitmapShader.h b/src/core/ARGB32_Clamp_Bilinear_BitmapShader.h
new file mode 100644
index 0000000..c7e23af
--- /dev/null
+++ b/src/core/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/src/core/Sk64.cpp b/src/core/Sk64.cpp
new file mode 100644
index 0000000..6013bd7
--- /dev/null
+++ b/src/core/Sk64.cpp
@@ -0,0 +1,575 @@
+/* libs/corecg/Sk64.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/core/SkAlphaRuns.cpp b/src/core/SkAlphaRuns.cpp
new file mode 100644
index 0000000..46b0206
--- /dev/null
+++ b/src/core/SkAlphaRuns.cpp
@@ -0,0 +1,185 @@
+/* libs/graphics/sgl/SkAlphaRuns.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/core/SkAntiRun.h b/src/core/SkAntiRun.h
new file mode 100644
index 0000000..12930e6
--- /dev/null
+++ b/src/core/SkAntiRun.h
@@ -0,0 +1,185 @@
+/* libs/graphics/sgl/SkAntiRun.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/core/SkAutoKern.h b/src/core/SkAutoKern.h
new file mode 100644
index 0000000..023cb6b
--- /dev/null
+++ b/src/core/SkAutoKern.h
@@ -0,0 +1,62 @@
+/* libs/graphics/sgl/SkAutoKern.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/core/SkBitmap.cpp b/src/core/SkBitmap.cpp
new file mode 100644
index 0000000..5ca3601
--- /dev/null
+++ b/src/core/SkBitmap.cpp
@@ -0,0 +1,1255 @@
+/*
+ * Copyright (C) 2006-2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SkBitmap.h"
+#include "SkColorPriv.h"
+#include "SkDither.h"
+#include "SkFlattenable.h"
+#include "SkMallocPixelRef.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 {
+ int32_t 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;
+}
+
+Sk64 SkBitmap::ComputeSize64(Config c, int width, int height) {
+ Sk64 size;
+ size.setMul(SkBitmap::ComputeRowBytes(c, width), height);
+ return size;
+}
+
+size_t SkBitmap::ComputeSize(Config c, int width, int height) {
+ Sk64 size = SkBitmap::ComputeSize64(c, width, height);
+ if (size.isNeg() || !size.is32()) {
+ return 0;
+ }
+ return size.get32();
+}
+
+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();
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+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 (!this->readyToDraw()) {
+ 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);
+ // don't call readyToDraw(), since we can operate w/o a colortable
+ // at this stage
+ 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 (!this->readyToDraw() || !tmp.readyToDraw()) {
+ // 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 0 // these asserts are not thread-correct, so disable for now
+ if (fPixelRef) {
+ if (fPixelLockCount > 0) {
+ SkASSERT(fPixelRef->getLockCount() > 0);
+ } else {
+ SkASSERT(NULL == fPixels);
+ SkASSERT(NULL == fColorTable);
+ }
+ }
+#endif
+}
+#endif
+
diff --git a/src/core/SkBitmapProcShader.cpp b/src/core/SkBitmapProcShader.cpp
new file mode 100644
index 0000000..6d7d581
--- /dev/null
+++ b/src/core/SkBitmapProcShader.cpp
@@ -0,0 +1,231 @@
+#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);
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const char* gTileModeName[] = {
+ "clamp", "repeat", "mirror"
+};
+
+bool SkBitmapProcShader::toDumpString(SkString* str) const {
+ str->printf("BitmapShader: [%d %d %d",
+ fRawBitmap.width(), fRawBitmap.height(),
+ fRawBitmap.bytesPerPixel());
+
+ // add the pixelref
+ SkPixelRef* pr = fRawBitmap.pixelRef();
+ if (pr) {
+ const char* uri = pr->getURI();
+ if (uri) {
+ str->appendf(" \"%s\"", uri);
+ }
+ }
+
+ // add the (optional) matrix
+ {
+ SkMatrix m;
+ if (this->getLocalMatrix(&m)) {
+ SkString info;
+ m.toDumpString(&info);
+ str->appendf(" %s", info.c_str());
+ }
+ }
+
+ str->appendf(" [%s %s]]",
+ gTileModeName[fState.fTileModeX],
+ gTileModeName[fState.fTileModeY]);
+ return true;
+}
+
diff --git a/src/core/SkBitmapProcShader.h b/src/core/SkBitmapProcShader.h
new file mode 100644
index 0000000..09d53af
--- /dev/null
+++ b/src/core/SkBitmapProcShader.h
@@ -0,0 +1,59 @@
+/* libs/graphics/sgl/SkBitmapShader.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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));
+ }
+
+ // override from flattenable
+ virtual bool toDumpString(SkString* str) const;
+
+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/src/core/SkBitmapProcState.cpp b/src/core/SkBitmapProcState.cpp
new file mode 100644
index 0000000..428921d
--- /dev/null
+++ b/src/core/SkBitmapProcState.cpp
@@ -0,0 +1,476 @@
+#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 (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();
+ 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/src/core/SkBitmapProcState.h b/src/core/SkBitmapProcState.h
new file mode 100644
index 0000000..1366d3b
--- /dev/null
+++ b/src/core/SkBitmapProcState.h
@@ -0,0 +1,78 @@
+/*
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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 U16CPU (*FixedTileProc)(SkFixed); // returns 0..0xFFFF
+
+ MatrixProc fMatrixProc; // chooseProcs
+ SampleProc32 fSampleProc32; // chooseProcs
+ SampleProc16 fSampleProc16; // chooseProcs
+
+ SkMatrix fUnitInvMatrix; // chooseProcs
+ FixedTileProc fTileProcX; // chooseProcs
+ FixedTileProc fTileProcY; // 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/src/core/SkBitmapProcState_matrix.h b/src/core/SkBitmapProcState_matrix.h
new file mode 100644
index 0000000..f54f8b1
--- /dev/null
+++ b/src/core/SkBitmapProcState_matrix.h
@@ -0,0 +1,271 @@
+
+#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
+
+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
+
+ const unsigned maxX = s.fBitmap->width() - 1;
+ const SkFixed dx = s.fInvSx;
+ SkFixed fx;
+ {
+ SkPoint pt;
+ s.fInvProc(*s.fInvMatrix, SkIntToScalar(x) + SK_ScalarHalf,
+ SkIntToScalar(y) + SK_ScalarHalf, &pt);
+ fx = SkScalarToFixed(pt.fY);
+ const unsigned maxY = s.fBitmap->height() - 1;
+ *xy++ = TILEY_PROCF(fx, maxY);
+ fx = SkScalarToFixed(pt.fX);
+ }
+
+#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;
+ 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;
+ }
+ }
+}
+
+// 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);
+
+ const unsigned maxX = s.fBitmap->width() - 1;
+ const SkFixed one = s.fFilterOneX;
+ const SkFixed dx = s.fInvSx;
+ SkFixed fx;
+
+ {
+ SkPoint pt;
+ s.fInvProc(*s.fInvMatrix, SkIntToScalar(x) + SK_ScalarHalf,
+ SkIntToScalar(y) + SK_ScalarHalf, &pt);
+ const SkFixed fy = SkScalarToFixed(pt.fY) - (s.fFilterOneY >> 1);
+ const unsigned maxY = s.fBitmap->height() - 1;
+ // compute our two Y values up front
+ *xy++ = PACK_FILTER_Y_NAME(fy, maxY, s.fFilterOneY PREAMBLE_ARG_Y);
+ // now initialize fx
+ fx = SkScalarToFixed(pt.fX) - (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)[] = {
+ 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 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 TILEX_LOW_BITS
+#undef TILEY_LOW_BITS
diff --git a/src/core/SkBitmapProcState_matrixProcs.cpp b/src/core/SkBitmapProcState_matrixProcs.cpp
new file mode 100644
index 0000000..beb21c8
--- /dev/null
+++ b/src/core/SkBitmapProcState_matrixProcs.cpp
@@ -0,0 +1,199 @@
+#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
+#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)
+#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) + 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)
+#include "SkBitmapProcState_matrix.h"
+
+static inline U16CPU 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 U16CPU fixed_repeat(SkFixed x)
+{
+ return x & 0xFFFF;
+}
+
+static inline U16CPU 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 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;
+}
+
+SkBitmapProcState::MatrixProc SkBitmapProcState::chooseMatrixProc()
+{
+ int index = 0;
+ if (fDoFilter)
+ index = 1;
+ if (fInvType & SkMatrix::kPerspective_Mask)
+ index |= 4;
+ else if (fInvType & SkMatrix::kAffine_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);
+ 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/src/core/SkBitmapProcState_sample.h b/src/core/SkBitmapProcState_sample.h
new file mode 100644
index 0000000..122ccf8
--- /dev/null
+++ b/src/core/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/src/core/SkBitmapSampler.cpp b/src/core/SkBitmapSampler.cpp
new file mode 100644
index 0000000..045efd1
--- /dev/null
+++ b/src/core/SkBitmapSampler.cpp
@@ -0,0 +1,423 @@
+/* libs/graphics/sgl/SkBitmapSampler.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/core/SkBitmapSampler.h b/src/core/SkBitmapSampler.h
new file mode 100644
index 0000000..eeef3b3
--- /dev/null
+++ b/src/core/SkBitmapSampler.h
@@ -0,0 +1,170 @@
+/* libs/graphics/sgl/SkBitmapSampler.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/core/SkBitmapSamplerTemplate.h b/src/core/SkBitmapSamplerTemplate.h
new file mode 100644
index 0000000..00df10c
--- /dev/null
+++ b/src/core/SkBitmapSamplerTemplate.h
@@ -0,0 +1,116 @@
+/* libs/graphics/sgl/SkBitmapSamplerTemplate.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+/* 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/src/core/SkBitmapShader.cpp b/src/core/SkBitmapShader.cpp
new file mode 100644
index 0000000..5d70dd3
--- /dev/null
+++ b/src/core/SkBitmapShader.cpp
@@ -0,0 +1,822 @@
+/* Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#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/src/core/SkBitmapShader.h b/src/core/SkBitmapShader.h
new file mode 100644
index 0000000..8d40a4b
--- /dev/null
+++ b/src/core/SkBitmapShader.h
@@ -0,0 +1,73 @@
+/* libs/graphics/sgl/SkBitmapShader.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/core/SkBitmapShader16BilerpTemplate.h b/src/core/SkBitmapShader16BilerpTemplate.h
new file mode 100644
index 0000000..b70801e
--- /dev/null
+++ b/src/core/SkBitmapShader16BilerpTemplate.h
@@ -0,0 +1,253 @@
+/* libs/graphics/sgl/SkBitmapShader16BilerpTemplate.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#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/src/core/SkBitmapShaderTemplate.h b/src/core/SkBitmapShaderTemplate.h
new file mode 100644
index 0000000..0174138
--- /dev/null
+++ b/src/core/SkBitmapShaderTemplate.h
@@ -0,0 +1,314 @@
+/* libs/graphics/sgl/SkBitmapShaderTemplate.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+
+#ifndef 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/src/core/SkBitmap_scroll.cpp b/src/core/SkBitmap_scroll.cpp
new file mode 100644
index 0000000..f9f197d
--- /dev/null
+++ b/src/core/SkBitmap_scroll.cpp
@@ -0,0 +1,107 @@
+#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)
+ // don't call readyToDraw(), since we don't require a colortable per se
+ 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/src/core/SkBlitBWMaskTemplate.h b/src/core/SkBlitBWMaskTemplate.h
new file mode 100644
index 0000000..e433d36
--- /dev/null
+++ b/src/core/SkBlitBWMaskTemplate.h
@@ -0,0 +1,137 @@
+/* libs/graphics/sgl/SkBlitBWMaskTemplate.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#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/src/core/SkBlitRow.h b/src/core/SkBlitRow.h
new file mode 100644
index 0000000..bb6a29b
--- /dev/null
+++ b/src/core/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/src/core/SkBlitRow_D16.cpp b/src/core/SkBlitRow_D16.cpp
new file mode 100644
index 0000000..f40df36
--- /dev/null
+++ b/src/core/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/src/core/SkBlitRow_D4444.cpp b/src/core/SkBlitRow_D4444.cpp
new file mode 100644
index 0000000..e60c721
--- /dev/null
+++ b/src/core/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/src/core/SkBlitter.cpp b/src/core/SkBlitter.cpp
new file mode 100644
index 0000000..9208429
--- /dev/null
+++ b/src/core/SkBlitter.cpp
@@ -0,0 +1,923 @@
+/* libs/graphics/sgl/SkBlitter.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/core/SkBlitter.h b/src/core/SkBlitter.h
new file mode 100644
index 0000000..11b84fd
--- /dev/null
+++ b/src/core/SkBlitter.h
@@ -0,0 +1,143 @@
+/* libs/graphics/sgl/SkBlitter.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/core/SkBlitter_4444.cpp b/src/core/SkBlitter_4444.cpp
new file mode 100644
index 0000000..cce94c5
--- /dev/null
+++ b/src/core/SkBlitter_4444.cpp
@@ -0,0 +1,501 @@
+/* libs/graphics/sgl/SkBlitter_ARGB32.cpp
+ **
+ ** Copyright 2006, The Android Open Source Project
+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ ** http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ */
+
+#include "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;
+ uint8_t* fAAExpand;
+public:
+SkARGB4444_Shader_Blitter(const SkBitmap& device, const SkPaint& paint)
+ : INHERITED(device, paint)
+{
+ const int width = device.width();
+ fBuffer = (SkPMColor*)sk_malloc_throw(width * sizeof(SkPMColor) + width);
+ fAAExpand = (uint8_t*)(fBuffer + width);
+
+ (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* SK_RESTRICT span = fBuffer;
+ uint8_t* SK_RESTRICT aaExpand = fAAExpand;
+ 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 {
+ const uint8_t* aaBuffer = antialias;
+ if (count > 1) {
+ memset(aaExpand, aa, count);
+ aaBuffer = aaExpand;
+ }
+ xfer->xfer4444(device, span, count, aaBuffer);
+ }
+ }
+ 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/src/core/SkBlitter_A1.cpp b/src/core/SkBlitter_A1.cpp
new file mode 100644
index 0000000..1a91a26
--- /dev/null
+++ b/src/core/SkBlitter_A1.cpp
@@ -0,0 +1,63 @@
+/* libs/graphics/sgl/SkBlitter_A1.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/core/SkBlitter_A8.cpp b/src/core/SkBlitter_A8.cpp
new file mode 100644
index 0000000..18b0881
--- /dev/null
+++ b/src/core/SkBlitter_A8.cpp
@@ -0,0 +1,387 @@
+/* libs/graphics/sgl/SkBlitter_A8.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/core/SkBlitter_ARGB32.cpp b/src/core/SkBlitter_ARGB32.cpp
new file mode 100644
index 0000000..ed2fc0e
--- /dev/null
+++ b/src/core/SkBlitter_ARGB32.cpp
@@ -0,0 +1,485 @@
+/* libs/graphics/sgl/SkBlitter_ARGB32.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/core/SkBlitter_RGB16.cpp b/src/core/SkBlitter_RGB16.cpp
new file mode 100644
index 0000000..b253662
--- /dev/null
+++ b/src/core/SkBlitter_RGB16.cpp
@@ -0,0 +1,782 @@
+/* libs/graphics/sgl/SkBlitter_RGB16.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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();
+
+ fSrcColor32 = SkPreMultiplyColor(color);
+ 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
+ SkPMColor src32 = fSrcColor32;
+ do {
+ *device = SkSrcOver32To16(src32, *device);
+ 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 {
+ SkPMColor src32 = fSrcColor32;
+ while (--height >= 0) {
+ for (int i = width - 1; i >= 0; --i) {
+ device[i] = SkSrcOver32To16(src32, device[i]);
+ }
+ 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/src/core/SkBlitter_Sprite.cpp b/src/core/SkBlitter_Sprite.cpp
new file mode 100644
index 0000000..f0da166
--- /dev/null
+++ b/src/core/SkBlitter_Sprite.cpp
@@ -0,0 +1,101 @@
+/* libs/graphics/sgl/SkBlitter_Sprite.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/core/SkBuffer.cpp b/src/core/SkBuffer.cpp
new file mode 100644
index 0000000..5768ca4
--- /dev/null
+++ b/src/core/SkBuffer.cpp
@@ -0,0 +1,137 @@
+/* libs/corecg/SkBuffer.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp
new file mode 100644
index 0000000..4088416
--- /dev/null
+++ b/src/core/SkCanvas.cpp
@@ -0,0 +1,1420 @@
+/*
+ * Copyright (C) 2006-2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SkCanvas.h"
+#include "SkBounder.h"
+#include "SkDevice.h"
+#include "SkDraw.h"
+#include "SkDrawFilter.h"
+#include "SkDrawLooper.h"
+#include "SkPicture.h"
+#include "SkScalarCompare.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
+
+///////////////////////////////////////////////////////////////////////////////
+// Helpers for computing fast bounds for quickReject tests
+
+static SkCanvas::EdgeType paint2EdgeType(const SkPaint* paint) {
+ return paint != NULL && paint->isAntiAlias() ?
+ SkCanvas::kAA_EdgeType : SkCanvas::kBW_EdgeType;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/* 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;
+ fPaint = rec->fPaint;
+ 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; }
+ const SkPaint* getPaint() const { return fPaint; }
+private:
+ SkCanvas* fCanvas;
+ const DeviceCM* fCurrLayer;
+ const SkPaint* fPaint; // May be null.
+ 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;
+ fLocalBoundsCompareTypeDirty = true;
+
+ 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;
+ fLocalBoundsCompareTypeDirty = 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;
+ fLocalBoundsCompareTypeDirty = true;
+ return fMCRec->fMatrix->preTranslate(dx, dy);
+}
+
+bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
+ fDeviceCMDirty = true;
+ fLocalBoundsCompareTypeDirty = true;
+ return fMCRec->fMatrix->preScale(sx, sy);
+}
+
+bool SkCanvas::rotate(SkScalar degrees) {
+ fDeviceCMDirty = true;
+ fLocalBoundsCompareTypeDirty = true;
+ return fMCRec->fMatrix->preRotate(degrees);
+}
+
+bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
+ fDeviceCMDirty = true;
+ fLocalBoundsCompareTypeDirty = true;
+ return fMCRec->fMatrix->preSkew(sx, sy);
+}
+
+bool SkCanvas::concat(const SkMatrix& matrix) {
+ fDeviceCMDirty = true;
+ fLocalBoundsCompareTypeDirty = true;
+ return fMCRec->fMatrix->preConcat(matrix);
+}
+
+void SkCanvas::setMatrix(const SkMatrix& matrix) {
+ fDeviceCMDirty = true;
+ fLocalBoundsCompareTypeDirty = 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;
+ fLocalBoundsCompareTypeDirty = 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;
+ fLocalBoundsCompareTypeDirty = 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;
+ fLocalBoundsCompareTypeDirty = true;
+
+ return fMCRec->fRegion->op(rgn, op);
+}
+
+void SkCanvas::computeLocalClipBoundsCompareType() const {
+ SkRect r;
+
+ if (!this->getClipBounds(&r, kAA_EdgeType)) {
+ fLocalBoundsCompareType.setEmpty();
+ } else {
+ fLocalBoundsCompareType.set(SkScalarToCompareType(r.fLeft),
+ SkScalarToCompareType(r.fTop),
+ SkScalarToCompareType(r.fRight),
+ SkScalarToCompareType(r.fBottom));
+ }
+}
+
+bool SkCanvas::quickReject(const SkRect& rect, EdgeType) const {
+ /* current impl ignores edgetype, and relies on
+ getLocalClipBoundsCompareType(), which always returns a value assuming
+ antialiasing (worst case)
+ */
+
+ if (fMCRec->fRegion->isEmpty()) {
+ return true;
+ }
+
+ // check for empty user rect (horizontal)
+ SkScalarCompareType userL = SkScalarToCompareType(rect.fLeft);
+ SkScalarCompareType userR = SkScalarToCompareType(rect.fRight);
+ if (userL >= userR) {
+ return true;
+ }
+
+ // check for empty user rect (vertical)
+ SkScalarCompareType userT = SkScalarToCompareType(rect.fTop);
+ SkScalarCompareType userB = SkScalarToCompareType(rect.fBottom);
+ if (userT >= userB) {
+ return true;
+ }
+
+ // check if we are completely outside of the local clip bounds
+ const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType();
+ return userL >= clipR.fRight || userT >= clipR.fBottom ||
+ userR <= clipR.fLeft || userB <= clipR.fTop;
+}
+
+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::kFast_BoundsType);
+ return this->quickReject(r, et);
+ }
+
+ SkPath dstPath;
+ SkRect r;
+ SkIRect ir;
+
+ path.transform(*fMCRec->fMatrix, &dstPath);
+ dstPath.computeBounds(&r, SkPath::kFast_BoundsType);
+ r.round(&ir);
+ if (kAA_EdgeType == et) {
+ ir.inset(-1, -1);
+ }
+ return fMCRec->fRegion->quickReject(ir);
+}
+
+bool SkCanvas::quickRejectY(SkScalar top, SkScalar bottom, EdgeType et) const {
+ /* current impl ignores edgetype, and relies on
+ getLocalClipBoundsCompareType(), which always returns a value assuming
+ antialiasing (worst case)
+ */
+
+ if (fMCRec->fRegion->isEmpty()) {
+ return true;
+ }
+
+ SkScalarCompareType userT = SkScalarAs2sCompliment(top);
+ SkScalarCompareType userB = SkScalarAs2sCompliment(bottom);
+
+ // check for invalid user Y coordinates (i.e. empty)
+ if (userT >= userB) {
+ return true;
+ }
+
+ // check if we are above or below the local clip bounds
+ const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType();
+ return userT >= clipR.fBottom || userB <= clipR.fTop;
+}
+
+bool SkCanvas::getClipBounds(SkRect* bounds, EdgeType et) const {
+ const SkRegion& clip = *fMCRec->fRegion;
+ if (clip.isEmpty()) {
+ if (bounds) {
+ bounds->setEmpty();
+ }
+ return false;
+ }
+
+ if (NULL != bounds) {
+ SkMatrix inverse;
+ SkRect r;
+
+ fMCRec->fMatrix->invert(&inverse);
+
+ // get the clip's bounds
+ const SkIRect& ibounds = clip.getBounds();
+ // adjust it outwards if we are antialiasing
+ int inset = (kAA_EdgeType == et);
+ r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
+ ibounds.fRight + inset, ibounds.fBottom + inset);
+
+ // invert into local coordinates
+ 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) {
+ if (paint.canComputeFastBounds()) {
+ SkRect storage;
+ if (this->quickReject(paint.computeFastBounds(r, &storage),
+ paint2EdgeType(&paint))) {
+ return;
+ }
+ }
+
+ 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) {
+ if (paint.canComputeFastBounds()) {
+ SkRect r;
+ path.computeBounds(&r, SkPath::kFast_BoundsType);
+ if (this->quickReject(paint.computeFastBounds(r, &r),
+ paint2EdgeType(&paint))) {
+ return;
+ }
+ }
+
+ 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();)
+
+ if (NULL == paint || (paint->getMaskFilter() == NULL)) {
+ SkRect fastBounds;
+ fastBounds.set(x, y,
+ x + SkIntToScalar(bitmap.width()),
+ y + SkIntToScalar(bitmap.height()));
+ if (this->quickReject(fastBounds, paint2EdgeType(paint))) {
+ return;
+ }
+ }
+
+ 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, paint2EdgeType(paint))) {
+ 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;
+ }
+
+ SkRect r;
+ r.set(cx - radius, cy - radius, cx + radius, cy + radius);
+
+ if (paint.canComputeFastBounds()) {
+ SkRect storage;
+ if (this->quickReject(paint.computeFastBounds(r, &storage),
+ paint2EdgeType(&paint))) {
+ return;
+ }
+ }
+
+ SkPath path;
+ 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) {
+ if (paint.canComputeFastBounds()) {
+ SkRect storage;
+ if (this->quickReject(paint.computeFastBounds(r, &storage),
+ paint2EdgeType(&paint))) {
+ return;
+ }
+ }
+
+ 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) {
+ if (paint.canComputeFastBounds()) {
+ SkRect storage;
+ if (this->quickReject(paint.computeFastBounds(oval, &storage),
+ paint2EdgeType(&paint))) {
+ return;
+ }
+ }
+
+ 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 SkPaint& SkCanvas::LayerIter::paint() const {
+ const SkPaint* paint = fImpl->getPaint();
+ if (NULL == paint) {
+ paint = &fDefaultPaint;
+ }
+ return *paint;
+}
+
+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/src/core/SkChunkAlloc.cpp b/src/core/SkChunkAlloc.cpp
new file mode 100644
index 0000000..ae37ec0
--- /dev/null
+++ b/src/core/SkChunkAlloc.cpp
@@ -0,0 +1,120 @@
+/* libs/corecg/SkChunkAlloc.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/core/SkColor.cpp b/src/core/SkColor.cpp
new file mode 100644
index 0000000..4256179
--- /dev/null
+++ b/src/core/SkColor.cpp
@@ -0,0 +1,138 @@
+/* libs/graphics/sgl/SkColor.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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] = 0;
+ hsv[1] = 0;
+ 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/src/core/SkColorFilter.cpp b/src/core/SkColorFilter.cpp
new file mode 100644
index 0000000..bb4be48
--- /dev/null
+++ b/src/core/SkColorFilter.cpp
@@ -0,0 +1,108 @@
+/* libs/graphics/sgl/SkColorFilter.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/core/SkColorTable.cpp b/src/core/SkColorTable.cpp
new file mode 100644
index 0000000..f991da3
--- /dev/null
+++ b/src/core/SkColorTable.cpp
@@ -0,0 +1,143 @@
+/* libs/graphics/sgl/SkColorTable.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/core/SkComposeShader.cpp b/src/core/SkComposeShader.cpp
new file mode 100644
index 0000000..1e5e202
--- /dev/null
+++ b/src/core/SkComposeShader.cpp
@@ -0,0 +1,174 @@
+/* libs/graphics/effects/SkShaderExtras.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkComposeShader.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/src/core/SkCordic.cpp b/src/core/SkCordic.cpp
new file mode 100644
index 0000000..539bc9b
--- /dev/null
+++ b/src/core/SkCordic.cpp
@@ -0,0 +1,301 @@
+/* libs/corecg/SkCordic.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/core/SkCordic.h b/src/core/SkCordic.h
new file mode 100644
index 0000000..9f45a81
--- /dev/null
+++ b/src/core/SkCordic.h
@@ -0,0 +1,37 @@
+/* libs/corecg/SkCordic.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/core/SkCoreBlitters.h b/src/core/SkCoreBlitters.h
new file mode 100644
index 0000000..5b3497e
--- /dev/null
+++ b/src/core/SkCoreBlitters.h
@@ -0,0 +1,259 @@
+/* libs/graphics/sgl/SkCoreBlitters.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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:
+ SkPMColor fSrcColor32;
+ 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/src/core/SkDebug.cpp b/src/core/SkDebug.cpp
new file mode 100644
index 0000000..64ea8b4
--- /dev/null
+++ b/src/core/SkDebug.cpp
@@ -0,0 +1,59 @@
+/* libs/corecg/SkDebug.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/core/SkDebug_stdio.cpp b/src/core/SkDebug_stdio.cpp
new file mode 100644
index 0000000..c8a0d81
--- /dev/null
+++ b/src/core/SkDebug_stdio.cpp
@@ -0,0 +1,61 @@
+/* libs/corecg/SkDebug_stdio.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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);
+}
+
+#else
+
+#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/src/core/SkDeque.cpp b/src/core/SkDeque.cpp
new file mode 100644
index 0000000..4f15051
--- /dev/null
+++ b/src/core/SkDeque.cpp
@@ -0,0 +1,252 @@
+/* libs/graphics/sgl/SkDeque.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/core/SkDevice.cpp b/src/core/SkDevice.cpp
new file mode 100644
index 0000000..139174d
--- /dev/null
+++ b/src/core/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/src/core/SkDither.cpp b/src/core/SkDither.cpp
new file mode 100644
index 0000000..53a8573
--- /dev/null
+++ b/src/core/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/src/core/SkDraw.cpp b/src/core/SkDraw.cpp
new file mode 100644
index 0000000..f85c5ae
--- /dev/null
+++ b/src/core/SkDraw.cpp
@@ -0,0 +1,2348 @@
+/* libs/graphics/sgl/SkDraw.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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
+
+class SkAutoRestoreBounder : SkNoncopyable {
+public:
+ // note: initializing fBounder is done only to fix a warning
+ SkAutoRestoreBounder() : fDraw(NULL), fBounder(NULL) {}
+ ~SkAutoRestoreBounder() {
+ if (fDraw) {
+ fDraw->fBounder = fBounder;
+ }
+ }
+
+ void clearBounder(const SkDraw* draw) {
+ fDraw = const_cast<SkDraw*>(draw);
+ fBounder = draw->fBounder;
+ fDraw->fBounder = NULL;
+ }
+
+private:
+ SkDraw* fDraw;
+ SkBounder* fBounder;
+};
+
+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?
+ */
+ 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;
+}
+
+static bool bounder_points(SkBounder* bounder, SkCanvas::PointMode mode,
+ size_t count, const SkPoint pts[],
+ const SkPaint& paint, const SkMatrix& matrix) {
+ SkIRect ibounds;
+ SkRect bounds;
+ SkScalar inset = paint.getStrokeWidth();
+
+ bounds.set(pts, count);
+ bounds.inset(-inset, -inset);
+ matrix.mapRect(&bounds);
+
+ bounds.roundOut(&ibounds);
+ return bounder->doIRect(ibounds);
+}
+
+// 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;
+ }
+
+ SkAutoRestoreBounder arb;
+
+ if (fBounder) {
+ if (!bounder_points(fBounder, mode, count, pts, paint, *fMatrix)) {
+ return;
+ }
+ // clear the bounder for the rest of this function, so we don't call it
+ // again later if we happen to call ourselves for drawRect, drawPath,
+ // etc.
+ arb.clearBounder(this);
+ }
+
+ 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???
+ 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 are valid
+ if (!bitmap.readyToDraw()) {
+ 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?
+ 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?
+ 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 "SkComposShader.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/src/core/SkDrawProcs.h b/src/core/SkDrawProcs.h
new file mode 100644
index 0000000..d64c088
--- /dev/null
+++ b/src/core/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/src/core/SkEdge.cpp b/src/core/SkEdge.cpp
new file mode 100644
index 0000000..6efe1ba
--- /dev/null
+++ b/src/core/SkEdge.cpp
@@ -0,0 +1,484 @@
+/* libs/graphics/sgl/SkEdge.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkEdge.h"
+#include "SkFDot6.h"
+
+/*
+ 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 = top;
+ fLastY = bot - 1;
+ 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 = top;
+ fLastY = 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;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/* We store 1<<shift in a (signed) byte, so its maximum value is 1<<6 == 64.
+ Note that this limits the number of lines we use to approximate a curve.
+ If we need to increase this, we need to store fCurveCount in something
+ larger than int8_t.
+*/
+#define MAX_COEFF_SHIFT 6
+
+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 = (dist + (1 << 4)) >> 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);
+ SkASSERT(shift >= 0);
+ }
+ // need at least 1 subdivision for our bias trick
+ if (shift == 0) {
+ shift = 1;
+ } else if (shift > MAX_COEFF_SHIFT) {
+ shift = MAX_COEFF_SHIFT;
+ }
+
+ fWinding = SkToS8(winding);
+ fCurveShift = SkToU8(shift);
+ //fCubicDShift only set for cubics
+ fCurveCount = SkToS8(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;
+}
+
+/////////////////////////////////////////////////////////////////////////
+
+static inline int SkFDot6UpShift(SkFDot6 x, int upShift) {
+ SkASSERT((x << upShift >> upShift) == x);
+ return x << upShift;
+}
+
+/* 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);
+ if (shift > MAX_COEFF_SHIFT) {
+ shift = MAX_COEFF_SHIFT;
+ }
+
+ /* Since our in coming data is initially shifted down by 10 (or 8 in
+ antialias). That means the most we can shift up is 8. However, we
+ compute coefficients with a 3*, so the safest upshift is really 6
+ */
+ int upShift = 6; // largest safe value
+ int downShift = shift + upShift - 10;
+ if (downShift < 0) {
+ downShift = 0;
+ upShift = 10 - shift;
+ }
+
+ fWinding = SkToS8(winding);
+ fCurveCount = SkToS8(-1 << shift);
+ fCurveShift = SkToU8(shift);
+ fCubicDShift = SkToU8(downShift);
+
+ SkFixed B = SkFDot6UpShift(3 * (x1 - x0), upShift);
+ SkFixed C = SkFDot6UpShift(3 * (x0 - x1 - x1 + x2), upShift);
+ SkFixed D = SkFDot6UpShift(x3 + 3 * (x1 - x2) - x0, upShift);
+
+ 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 = SkFDot6UpShift(3 * (y1 - y0), upShift);
+ C = SkFDot6UpShift(3 * (y0 - y1 - y1 + y2), upShift);
+ D = SkFDot6UpShift(y3 + 3 * (y1 - y2) - y0, upShift);
+
+ 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;
+ const int ddshift = fCurveShift;
+ const int dshift = fCubicDShift;
+
+ SkASSERT(count < 0);
+
+ do {
+ if (++count < 0)
+ {
+ newx = oldx + (fCDx >> dshift);
+ fCDx += fCDDx >> ddshift;
+ fCDDx += fCDDDx;
+
+ newy = oldy + (fCDy >> dshift);
+ fCDy += fCDDy >> ddshift;
+ 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/src/core/SkEdge.h b/src/core/SkEdge.h
new file mode 100644
index 0000000..5b0cc75
--- /dev/null
+++ b/src/core/SkEdge.h
@@ -0,0 +1,93 @@
+/* libs/graphics/sgl/SkEdge.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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;
+ int32_t fFirstY;
+ int32_t fLastY;
+ int8_t fCurveCount; // only used by kQuad(+) and kCubic(-)
+ uint8_t fCurveShift; // appled to all Dx/DDx/DDDx except for fCubicDShift exception
+ uint8_t fCubicDShift; // applied to fCDx and fCDy only in cubic
+ 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/src/core/SkFP.h b/src/core/SkFP.h
new file mode 100644
index 0000000..6c0c526
--- /dev/null
+++ b/src/core/SkFP.h
@@ -0,0 +1,87 @@
+/* libs/graphics/sgl/SkFP.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/core/SkFilterProc.cpp b/src/core/SkFilterProc.cpp
new file mode 100644
index 0000000..814bafe
--- /dev/null
+++ b/src/core/SkFilterProc.cpp
@@ -0,0 +1,303 @@
+/* libs/graphics/sgl/SkFilterProc.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/core/SkFilterProc.h b/src/core/SkFilterProc.h
new file mode 100644
index 0000000..9af4ed5
--- /dev/null
+++ b/src/core/SkFilterProc.h
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2006-2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef 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 SkFilter32Proc* 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 SkFilter32Proc* table,
+ unsigned y)
+{
+ SkASSERT(table);
+ // extract low 2 bits and shift up 2
+ return &table[y << 30 >> 28];
+}
+
+inline SkFilter32Proc SkGetFilter32Proc22RowProc(const SkFilter32Proc* 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/src/core/SkFlattenable.cpp b/src/core/SkFlattenable.cpp
new file mode 100644
index 0000000..3558519
--- /dev/null
+++ b/src/core/SkFlattenable.cpp
@@ -0,0 +1,263 @@
+#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;
+}
+
+bool SkFlattenable::toDumpString(SkString* str) const {
+ return false;
+}
+
diff --git a/src/core/SkFloat.cpp b/src/core/SkFloat.cpp
new file mode 100644
index 0000000..504c1d3
--- /dev/null
+++ b/src/core/SkFloat.cpp
@@ -0,0 +1,404 @@
+/*
+ * Copyright (C) 2006-2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "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/src/core/SkFloat.h b/src/core/SkFloat.h
new file mode 100644
index 0000000..31aaeed
--- /dev/null
+++ b/src/core/SkFloat.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2006-2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef 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/src/core/SkFloatBits.cpp b/src/core/SkFloatBits.cpp
new file mode 100644
index 0000000..1f15df2
--- /dev/null
+++ b/src/core/SkFloatBits.cpp
@@ -0,0 +1,205 @@
+#include "SkFloatBits.h"
+#include "SkMath.h"
+
+/******************************************************************************
+ SkFloatBits_toInt[Floor, Round, Ceil] are identical except for what they
+ do right before they return ... >> exp;
+ Floor - adds nothing
+ Round - adds 1 << (exp - 1)
+ Ceil - adds (1 << exp) - 1
+
+ Floor and Cast are very similar, but Cast applies its sign after all other
+ computations on value. Also, Cast does not need to check for negative zero,
+ as that value (0x80000000) "does the right thing" for Ceil. Note that it
+ doesn't for Floor/Round/Ceil, hence the explicit check.
+******************************************************************************/
+
+#define EXP_BIAS (127+23)
+#define MATISSA_MAGIC_BIG (1 << 23)
+
+static inline int unpack_exp(uint32_t packed) {
+ return (packed << 1 >> 24);
+}
+
+#if 0
+// the ARM compiler generates an extra BIC, so I use the dirty version instead
+static inline int unpack_matissa(uint32_t packed) {
+ // we could mask with 0x7FFFFF, but that is harder for ARM to encode
+ return (packed & ~0xFF000000) | MATISSA_MAGIC_BIG;
+}
+#endif
+
+// returns the low 24-bits, so we need to OR in the magic_bit afterwards
+static inline int unpack_matissa_dirty(uint32_t packed) {
+ return packed & ~0xFF000000;
+}
+
+// same as (int)float
+int32_t SkFloatBits_toIntCast(int32_t packed) {
+ int exp = unpack_exp(packed) - EXP_BIAS;
+ int value = unpack_matissa_dirty(packed) | MATISSA_MAGIC_BIG;
+
+ if (exp >= 0) {
+ if (exp > 7) { // overflow
+ value = SK_MaxS32;
+ } else {
+ value <<= exp;
+ }
+ } else {
+ exp = -exp;
+ if (exp > 25) { // underflow
+ exp = 25;
+ }
+ value >>= exp;
+ }
+ return SkApplySign(value, SkExtractSign(packed));
+}
+
+// same as (int)floor(float)
+int32_t SkFloatBits_toIntFloor(int32_t packed) {
+ // curse you negative 0
+ if ((packed << 1) == 0) {
+ return 0;
+ }
+
+ int exp = unpack_exp(packed) - EXP_BIAS;
+ int value = unpack_matissa_dirty(packed) | MATISSA_MAGIC_BIG;
+
+ if (exp >= 0) {
+ if (exp > 7) { // overflow
+ value = SK_MaxS32;
+ } else {
+ value <<= exp;
+ }
+ // apply the sign after we check for overflow
+ return SkApplySign(value, SkExtractSign(packed));
+ } else {
+ // apply the sign before we right-shift
+ value = SkApplySign(value, SkExtractSign(packed));
+ exp = -exp;
+ if (exp > 25) { // underflow
+ exp = 25;
+ }
+ // int add = 0;
+ return value >> exp;
+ }
+}
+
+// same as (int)floor(float + 0.5)
+int32_t SkFloatBits_toIntRound(int32_t packed) {
+ // curse you negative 0
+ if ((packed << 1) == 0) {
+ return 0;
+ }
+
+ int exp = unpack_exp(packed) - EXP_BIAS;
+ int value = unpack_matissa_dirty(packed) | MATISSA_MAGIC_BIG;
+
+ if (exp >= 0) {
+ if (exp > 7) { // overflow
+ value = SK_MaxS32;
+ } else {
+ value <<= exp;
+ }
+ // apply the sign after we check for overflow
+ return SkApplySign(value, SkExtractSign(packed));
+ } else {
+ // apply the sign before we right-shift
+ value = SkApplySign(value, SkExtractSign(packed));
+ exp = -exp;
+ if (exp > 25) { // underflow
+ exp = 25;
+ }
+ int add = 1 << (exp - 1);
+ return (value + add) >> exp;
+ }
+}
+
+// same as (int)ceil(float)
+int32_t SkFloatBits_toIntCeil(int32_t packed) {
+ // curse you negative 0
+ if ((packed << 1) == 0) {
+ return 0;
+ }
+
+ int exp = unpack_exp(packed) - EXP_BIAS;
+ int value = unpack_matissa_dirty(packed) | MATISSA_MAGIC_BIG;
+
+ if (exp >= 0) {
+ if (exp > 7) { // overflow
+ value = SK_MaxS32;
+ } else {
+ value <<= exp;
+ }
+ // apply the sign after we check for overflow
+ return SkApplySign(value, SkExtractSign(packed));
+ } else {
+ // apply the sign before we right-shift
+ value = SkApplySign(value, SkExtractSign(packed));
+ exp = -exp;
+ if (exp > 25) { // underflow
+ exp = 25;
+ }
+ int add = (1 << exp) - 1;
+ return (value + add) >> exp;
+ }
+}
+
+float SkFloatBits_intToFloatNative(int x);
+float SkFloatBits_intToFloatNative(int x) {
+ return x;
+}
+
+float SkIntToFloatCast(int32_t value) {
+ if (0 == value) {
+ return 0;
+ }
+
+ int shift = EXP_BIAS;
+
+ // 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);
+ SkDebugf("value = %d, bias = %d\n", value, bias);
+ SkASSERT(bias > 0 && bias < 8);
+ value >>= bias; // need to round?
+ 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);
+ SkASSERT(shift >= 0 && shift <= 255);
+
+ SkFloatIntUnion data;
+ data.fSignBitInt = (sign << 31) | (shift << 23) | (value & ~MATISSA_MAGIC_BIG);
+ return data.fFloat;
+}
+
+float SkIntToFloatCast_NoOverflowCheck(int32_t value) {
+ if (0 == value) {
+ return 0;
+ }
+
+ int shift = EXP_BIAS;
+
+ // record the sign and make value positive
+ int sign = SkExtractSign(value);
+ value = SkApplySign(value, sign);
+
+ int zeros = SkCLZ(value << 8);
+ value <<= zeros;
+ shift -= zeros;
+
+ SkFloatIntUnion data;
+ data.fSignBitInt = (sign << 31) | (shift << 23) | (value & ~MATISSA_MAGIC_BIG);
+ return data.fFloat;
+}
+
diff --git a/src/core/SkGeometry.cpp b/src/core/SkGeometry.cpp
new file mode 100644
index 0000000..4f22e92
--- /dev/null
+++ b/src/core/SkGeometry.cpp
@@ -0,0 +1,1072 @@
+/* libs/graphics/sgl/SkGeometry.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/core/SkGeometry.h b/src/core/SkGeometry.h
new file mode 100644
index 0000000..571159f
--- /dev/null
+++ b/src/core/SkGeometry.h
@@ -0,0 +1,163 @@
+/* libs/graphics/sgl/SkGeometry.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/core/SkGlobals.cpp b/src/core/SkGlobals.cpp
new file mode 100644
index 0000000..bc72b97
--- /dev/null
+++ b/src/core/SkGlobals.cpp
@@ -0,0 +1,92 @@
+/* libs/graphics/sgl/SkGlobals.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/core/SkGlyphCache.cpp b/src/core/SkGlyphCache.cpp
new file mode 100644
index 0000000..6b214df
--- /dev/null
+++ b/src/core/SkGlyphCache.cpp
@@ -0,0 +1,662 @@
+/* libs/graphics/sgl/SkGlyphCache.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkGlyphCache.h"
+#include "SkFontHost.h"
+#include "SkPaint.h"
+#include "SkTemplates.h"
+
+#define SPEW_PURGE_STATUS
+//#define USE_CACHE_HASH
+//#define RECORD_HASH_EFFICIENCY
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef RECORD_HASH_EFFICIENCY
+ static uint32_t gHashSuccess;
+ static uint32_t gHashCollision;
+
+ static void RecordHashSuccess() {
+ gHashSuccess += 1;
+ }
+
+ static void RecordHashCollisionIf(bool pred) {
+ if (pred) {
+ gHashCollision += 1;
+
+ uint32_t total = gHashSuccess + gHashCollision;
+ SkDebugf("Font Cache Hash success rate: %d%%\n",
+ 100 * gHashSuccess / total);
+ }
+ }
+#else
+ #define RecordHashSuccess() (void)0
+ #define RecordHashCollisionIf(pred) (void)0
+#endif
+#define RecordHashCollision() RecordHashCollisionIf(true)
+
+///////////////////////////////////////////////////////////////////////////////
+
+#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
+
+uint16_t SkGlyphCache::unicharToGlyph(SkUnichar charCode) {
+ VALIDATE();
+ uint32_t id = SkGlyph::MakeID(charCode);
+ const CharGlyphRec& rec = fCharToGlyphHash[ID2HashIndex(id)];
+
+ if (rec.fID == id) {
+ return rec.fGlyph->getGlyphID();
+ } else {
+ return fScalerContext->charToGlyphID(charCode);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+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) {
+ RecordHashCollisionIf(rec->fGlyph != NULL);
+ // 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 {
+ RecordHashSuccess();
+ 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) {
+ RecordHashCollisionIf(rec->fGlyph != NULL);
+ // 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 {
+ RecordHashSuccess();
+ 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) {
+ RecordHashCollisionIf(glyph != NULL);
+ glyph = this->lookupMetrics(glyphID, kFull_MetricsType);
+ fGlyphHash[index] = glyph;
+ } else {
+ RecordHashSuccess();
+ 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) {
+ RecordHashCollisionIf(glyph != NULL);
+ glyph = this->lookupMetrics(id, kFull_MetricsType);
+ fGlyphHash[index] = glyph;
+ } else {
+ RecordHashSuccess();
+ 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
+
+void SkGlyphCache::VisitAllCaches(bool (*proc)(SkGlyphCache*, void*),
+ void* context) {
+ SkGlyphCache_Globals& globals = FIND_GC_GLOBALS();
+ SkAutoMutexAcquire ac(globals.fMutex);
+ SkGlyphCache* cache;
+
+ globals.validate();
+
+ for (cache = globals.fHead; cache != NULL; cache = cache->fNext) {
+ if (proc(cache, context)) {
+ break;
+ }
+ }
+
+ globals.validate();
+}
+
+/* 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
+
+ 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/src/core/SkGlyphCache.h b/src/core/SkGlyphCache.h
new file mode 100644
index 0000000..2462ea5
--- /dev/null
+++ b/src/core/SkGlyphCache.h
@@ -0,0 +1,274 @@
+/* libs/graphics/sgl/SkGlyphCache.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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 glyphID for the specified Unichar. If the char has already
+ been seen, use the existing cache entry. If not, ask the scalercontext
+ to compute it for us.
+ */
+ uint16_t unicharToGlyph(SkUnichar);
+
+ /** 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*));
+
+ /** Call proc on all cache entries, stopping early if proc returns true.
+ The proc should not create or delete caches, since it could produce
+ deadlock.
+ */
+ static void VisitAllCaches(bool (*proc)(SkGlyphCache*, void*), void* ctx);
+
+ /** 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 = 8,
+ 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/src/core/SkGraphics.cpp b/src/core/SkGraphics.cpp
new file mode 100644
index 0000000..64fbab9
--- /dev/null
+++ b/src/core/SkGraphics.cpp
@@ -0,0 +1,526 @@
+/* libs/graphics/sgl/SkGraphics.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkGraphics.h"
+
+#include "Sk64.h"
+#include "SkBlitter.h"
+#include "SkCanvas.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
+
+#include "SkFloatBits.h"
+
+static inline float fast_inc(float x) {
+ SkFloatIntUnion data;
+ data.fFloat = x;
+ data.fSignBitInt += 1;
+ return data.fFloat;
+}
+
+extern float dummy();
+int time_math() {
+ SkMSec now;
+ int i;
+ int sum = 0;
+ const int repeat = 1000000;
+ float f;
+
+ f = dummy();
+ now = SkTime::GetMSecs();
+ for (i = repeat - 1; i >= 0; --i) {
+ sum += (int)f; f = fast_inc(f);
+ sum += (int)f; f = fast_inc(f);
+ sum += (int)f; f = fast_inc(f);
+ sum += (int)f; f = fast_inc(f);
+ }
+ SkDebugf("---- native cast %d\n", SkTime::GetMSecs() - now);
+
+ f = dummy();
+ now = SkTime::GetMSecs();
+ for (i = repeat - 1; i >= 0; --i) {
+ sum += SkFloatToIntCast(f); f = fast_inc(f);
+ sum += SkFloatToIntCast(f); f = fast_inc(f);
+ sum += SkFloatToIntCast(f); f = fast_inc(f);
+ sum += SkFloatToIntCast(f); f = fast_inc(f);
+ }
+ SkDebugf("---- hack cast %d\n", SkTime::GetMSecs() - now);
+
+ f = dummy();
+ now = SkTime::GetMSecs();
+ for (i = repeat - 1; i >= 0; --i) {
+ sum += (int)floorf(f + 0.5f); f = fast_inc(f);
+ sum += (int)floorf(f + 0.5f); f = fast_inc(f);
+ sum += (int)floorf(f + 0.5f); f = fast_inc(f);
+ sum += (int)floorf(f + 0.5f); f = fast_inc(f);
+ }
+ SkDebugf("---- native round %d\n", SkTime::GetMSecs() - now);
+
+ f = dummy();
+ now = SkTime::GetMSecs();
+ for (i = repeat - 1; i >= 0; --i) {
+ sum += SkFloatToIntRound(f); f = fast_inc(f);
+ sum += SkFloatToIntRound(f); f = fast_inc(f);
+ sum += SkFloatToIntRound(f); f = fast_inc(f);
+ sum += SkFloatToIntRound(f); f = fast_inc(f);
+ }
+ SkDebugf("---- hack round %d\n", SkTime::GetMSecs() - now);
+
+ f = dummy();
+ now = SkTime::GetMSecs();
+ for (i = repeat - 1; i >= 0; --i) {
+ sum += SkFloat2Bits(floorf(f)); f = fast_inc(f);
+ sum += SkFloat2Bits(floorf(f)); f = fast_inc(f);
+ sum += SkFloat2Bits(floorf(f)); f = fast_inc(f);
+ sum += SkFloat2Bits(floorf(f)); f = fast_inc(f);
+ }
+ SkDebugf("---- native floor %d\n", SkTime::GetMSecs() - now);
+
+ f = dummy();
+ now = SkTime::GetMSecs();
+ for (i = repeat - 1; i >= 0; --i) {
+ sum += SkFloatToIntFloor(f); f = fast_inc(f);
+ sum += SkFloatToIntFloor(f); f = fast_inc(f);
+ sum += SkFloatToIntFloor(f); f = fast_inc(f);
+ sum += SkFloatToIntFloor(f); f = fast_inc(f);
+ }
+ SkDebugf("---- hack floor %d\n", SkTime::GetMSecs() - now);
+
+ return sum;
+}
+
+static float time_intToFloat() {
+ const int repeat = 1000000;
+ int i, n;
+ SkMSec now;
+ float sum = 0;
+
+ n = (int)dummy();
+ now = SkTime::GetMSecs();
+ for (i = repeat - 1; i >= 0; --i) {
+ sum += (float)n; n += 1;
+ sum += (float)n; n += 1;
+ sum += (float)n; n += 1;
+ sum += (float)n; n += 1;
+ }
+ SkDebugf("---- native i2f %d\n", SkTime::GetMSecs() - now);
+
+ n = (int)dummy();
+ now = SkTime::GetMSecs();
+ for (i = repeat - 1; i >= 0; --i) {
+ sum += SkIntToFloatCast(n); n += 1;
+ sum += SkIntToFloatCast(n); n += 1;
+ sum += SkIntToFloatCast(n); n += 1;
+ sum += SkIntToFloatCast(n); n += 1;
+ }
+ SkDebugf("---- check i2f %d\n", SkTime::GetMSecs() - now);
+
+ n = (int)dummy();
+ now = SkTime::GetMSecs();
+ for (i = repeat - 1; i >= 0; --i) {
+ sum += SkIntToFloatCast_NoOverflowCheck(n); n += 1;
+ sum += SkIntToFloatCast_NoOverflowCheck(n); n += 1;
+ sum += SkIntToFloatCast_NoOverflowCheck(n); n += 1;
+ sum += SkIntToFloatCast_NoOverflowCheck(n); n += 1;
+ }
+ SkDebugf("---- nocheck i2f %d\n", SkTime::GetMSecs() - now);
+
+ return sum;
+}
+
+void SkGraphics::Init(bool runUnitTests)
+{
+ SkGlobals::Init();
+
+// time_math();
+// time_intToFloat();
+
+#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(SkMatrix),
+ unittestline(SkGeometry),
+ unittestline(SkPath),
+ unittestline(SkPathMeasure),
+ unittestline(SkStream),
+ unittestline(SkWStream),
+ };
+
+ 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"
+
+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);
+}
+
+float dummy() { return 1.25f; }
diff --git a/src/core/SkMask.cpp b/src/core/SkMask.cpp
new file mode 100644
index 0000000..b237639
--- /dev/null
+++ b/src/core/SkMask.cpp
@@ -0,0 +1,49 @@
+/* libs/graphics/sgl/SkMask.cpp
+**
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/core/SkMaskFilter.cpp b/src/core/SkMaskFilter.cpp
new file mode 100644
index 0000000..56fff97
--- /dev/null
+++ b/src/core/SkMaskFilter.cpp
@@ -0,0 +1,62 @@
+/* libs/graphics/sgl/SkMaskFilter.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/core/SkMath.cpp b/src/core/SkMath.cpp
new file mode 100644
index 0000000..c627d9b
--- /dev/null
+++ b/src/core/SkMath.cpp
@@ -0,0 +1,932 @@
+/*
+ * Copyright (C) 2006-2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SkMath.h"
+#include "SkCordic.h"
+#include "SkFloatBits.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 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
+
+#ifdef SK_CAN_USE_FLOAT
+
+static float nextFloat(SkRandom& rand) {
+ SkFloatIntUnion data;
+ data.fSignBitInt = rand.nextU();
+ return data.fFloat;
+}
+
+/* returns true if a == b as resulting from (int)x. Since it is undefined
+ what to do if the float exceeds 2^32-1, we check for that explicitly.
+*/
+static bool equal_float_native_skia(float x, uint32_t ni, uint32_t si) {
+ if (!(x == x)) { // NAN
+ return si == SK_MaxS32 || si == SK_MinS32;
+ }
+ // for out of range, C is undefined, but skia always should return NaN32
+ if (x > SK_MaxS32) {
+ return si == SK_MaxS32;
+ }
+ if (x < -SK_MaxS32) {
+ return si == SK_MinS32;
+ }
+ return si == ni;
+}
+
+static void assert_float_equal(const char op[], float x, uint32_t ni,
+ uint32_t si) {
+ if (!equal_float_native_skia(x, ni, si)) {
+ SkDebugf("-- %s float %g bits %x native %x skia %x\n", op, x, ni, si);
+ SkASSERT(!"oops");
+ }
+}
+
+static void test_float_cast(float x) {
+ int ix = (int)x;
+ int iix = SkFloatToIntCast(x);
+ assert_float_equal("cast", x, ix, iix);
+}
+
+static void test_float_floor(float x) {
+ int ix = (int)floor(x);
+ int iix = SkFloatToIntFloor(x);
+ assert_float_equal("floor", x, ix, iix);
+}
+
+static void test_float_round(float x) {
+ double xx = x + 0.5; // need intermediate double to avoid temp loss
+ int ix = (int)floor(xx);
+ int iix = SkFloatToIntRound(x);
+ assert_float_equal("round", x, ix, iix);
+}
+
+static void test_float_ceil(float x) {
+ int ix = (int)ceil(x);
+ int iix = SkFloatToIntCeil(x);
+ assert_float_equal("ceil", x, ix, iix);
+}
+
+static void test_float_conversions(float x) {
+ test_float_cast(x);
+ test_float_floor(x);
+ test_float_round(x);
+ test_float_ceil(x);
+}
+
+static void test_int2float(int ival) {
+ float x0 = (float)ival;
+ float x1 = SkIntToFloatCast(ival);
+ float x2 = SkIntToFloatCast_NoOverflowCheck(ival);
+ SkASSERT(x0 == x1);
+ SkASSERT(x0 == x2);
+}
+
+static void unittest_fastfloat() {
+ SkRandom rand;
+ size_t i;
+
+ static const float gFloats[] = {
+ 0.f, 1.f, 0.5f, 0.499999f, 0.5000001f, 1.f/3,
+ 0.000000001f, 1000000000.f, // doesn't overflow
+ 0.0000000001f, 10000000000.f // does overflow
+ };
+ for (i = 0; i < SK_ARRAY_COUNT(gFloats); i++) {
+// SkDebugf("---- test floats %g %d\n", gFloats[i], (int)gFloats[i]);
+ test_float_conversions(gFloats[i]);
+ test_float_conversions(-gFloats[i]);
+ }
+
+ for (int outer = 0; outer < 100; outer++) {
+ rand.setSeed(outer);
+ for (i = 0; i < 100000; i++) {
+ float x = nextFloat(rand);
+ test_float_conversions(x);
+ }
+
+ test_int2float(0);
+ test_int2float(1);
+ test_int2float(-1);
+ for (i = 0; i < 100000; i++) {
+ // for now only test ints that are 24bits or less, since we don't
+ // round (down) large ints the same as IEEE...
+ int ival = rand.nextU() & 0xFFFFFF;
+ test_int2float(ival);
+ test_int2float(-ival);
+ }
+ }
+}
+
+#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 SK_CAN_USE_FLOAT
+ unittest_fastfloat();
+#endif
+
+#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/src/core/SkMatrix.cpp b/src/core/SkMatrix.cpp
new file mode 100644
index 0000000..893aea1
--- /dev/null
+++ b/src/core/SkMatrix.cpp
@@ -0,0 +1,1690 @@
+/* libs/corecg/SkMatrix.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkMatrix.h"
+#include "Sk64.h"
+#include "SkFloatBits.h"
+#include "SkString.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 (SkScalarAs2sCompliment(fMat[kMPersp0]) |
+ SkScalarAs2sCompliment(fMat[kMPersp1]) |
+ (SkScalarAs2sCompliment(fMat[kMPersp2]) - kPersp1Int)) {
+ mask |= kPerspective_Mask;
+ }
+
+ if (SkScalarAs2sCompliment(fMat[kMTransX]) |
+ SkScalarAs2sCompliment(fMat[kMTransY])) {
+ mask |= kTranslate_Mask;
+ }
+
+ int m00 = SkScalarAs2sCompliment(fMat[SkMatrix::kMScaleX]);
+ int m01 = SkScalarAs2sCompliment(fMat[SkMatrix::kMSkewX]);
+ int m10 = SkScalarAs2sCompliment(fMat[SkMatrix::kMSkewY]);
+ int m11 = SkScalarAs2sCompliment(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 (SkScalarAs2sCompliment(dx) | SkScalarAs2sCompliment(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 (SkScalarAs2sCompliment(dx) | SkScalarAs2sCompliment(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 (SkScalarAs2sCompliment(dx) | SkScalarAs2sCompliment(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) {
+ // check for identity first, so we don't do a needless copy of ourselves
+ // to ourselves inside setConcat()
+ return mat.isIdentity() || this->setConcat(*this, mat);
+}
+
+bool SkMatrix::postConcat(const SkMatrix& mat) {
+ // check for identity first, so we don't do a needless copy of ourselves
+ // to ourselves inside setConcat()
+ return mat.isIdentity() || 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];
+ }
+
+ // Since the determinant is on the order of the square of the matrix members,
+ // compare to the square of the default nearly-zero constant
+ if (SkScalarNearlyZero((float)det, SK_ScalarNearlyZero * SK_ScalarNearlyZero)) {
+ 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 {
+ SkString str;
+ this->toDumpString(&str);
+ SkDebugf("%s\n", str.c_str());
+}
+
+void SkMatrix::toDumpString(SkString* str) const {
+#ifdef SK_CAN_USE_FLOAT
+ str->printf("[%8.4f %8.4f %8.4f][%8.4f %8.4f %8.4f][%8.4f %8.4f %8.4f]",
+#ifdef SK_SCALAR_IS_FLOAT
+ fMat[0], fMat[1], fMat[2], fMat[3], fMat[4], fMat[5],
+ fMat[6], fMat[7], fMat[8]);
+#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]));
+#endif
+#else // can't use float
+ str->printf("[%x %x %x][%x %x %x][%x %x %x]",
+ fMat[0], fMat[1], fMat[2], fMat[3], fMat[4], fMat[5],
+ fMat[6], fMat[7], fMat[8]);
+#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/src/core/SkMemory_stdlib.cpp b/src/core/SkMemory_stdlib.cpp
new file mode 100644
index 0000000..f1ac36b
--- /dev/null
+++ b/src/core/SkMemory_stdlib.cpp
@@ -0,0 +1,287 @@
+/* libs/corecg/SkMemory_stdlib.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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& get_block_mutex() {
+ static SkMutex* gBlockMutex;
+ if (NULL == gBlockMutex) {
+ gBlockMutex = new SkMutex;
+ }
+ return *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(get_block_mutex());
+ 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(get_block_mutex());
+ 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(get_block_mutex());
+ 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(get_block_mutex());
+ 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/src/core/SkPackBits.cpp b/src/core/SkPackBits.cpp
new file mode 100644
index 0000000..3e92841
--- /dev/null
+++ b/src/core/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/src/core/SkPaint.cpp b/src/core/SkPaint.cpp
new file mode 100644
index 0000000..bb6b31e
--- /dev/null
+++ b/src/core/SkPaint.cpp
@@ -0,0 +1,1571 @@
+/* libs/graphics/sgl/SkPaint.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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"
+
+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
+
+ // handle this encoding before the setup for the glyphcache
+ if (this->getTextEncoding() == kGlyphID_TextEncoding) {
+ // we want to ignore the low bit of byteLength
+ memcpy(glyphs, textData, byteLength >> 1 << 1);
+ return byteLength >> 1;
+ }
+
+ SkAutoGlyphCache autoCache(*this, NULL);
+ SkGlyphCache* cache = autoCache.getCache();
+
+ const char* text = (const char*)textData;
+ const char* stop = text + byteLength;
+ uint16_t* gptr = glyphs;
+
+ switch (this->getTextEncoding()) {
+ case SkPaint::kUTF8_TextEncoding:
+ while (text < stop) {
+ *gptr++ = cache->unicharToGlyph(SkUTF8_NextUnichar(&text));
+ }
+ break;
+ case SkPaint::kUTF16_TextEncoding: {
+ const uint16_t* text16 = (const uint16_t*)text;
+ const uint16_t* stop16 = (const uint16_t*)stop;
+ while (text16 < stop16) {
+ *gptr++ = cache->unicharToGlyph(SkUTF16_NextUnichar(&text16));
+ }
+ break;
+ }
+ default:
+ SkASSERT(!"unknown text encoding");
+ }
+ 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...
+ }
+ 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)
+}
+
+bool SkPaint::canComputeFastBounds() const {
+ // use bit-or since no need for early exit
+ return (reinterpret_cast<uintptr_t>(this->getMaskFilter()) |
+ reinterpret_cast<uintptr_t>(this->getLooper()) |
+ reinterpret_cast<uintptr_t>(this->getRasterizer()) |
+ reinterpret_cast<uintptr_t>(this->getPathEffect())) == 0;
+}
+
+const SkRect& SkPaint::computeFastBounds(const SkRect& src,
+ SkRect* storage) const {
+ SkASSERT(storage);
+
+ if (this->getStyle() != SkPaint::kFill_Style) {
+ // if we're stroked, outset the rect by the radius (and join type)
+ SkScalar radius = SkScalarHalf(this->getStrokeWidth());
+
+ if (0 == radius) { // hairline
+ radius = SK_Scalar1;
+ } else if (this->getStrokeJoin() == SkPaint::kMiter_Join) {
+ SkScalar scale = this->getStrokeMiter();
+ if (scale > SK_Scalar1) {
+ radius = SkScalarMul(radius, scale);
+ }
+ }
+ storage->set(src.fLeft - radius, src.fTop - radius,
+ src.fRight + radius, src.fBottom + radius);
+ return *storage;
+ }
+ // no adjustments needed, just return the original rect
+ return src;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+
+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/src/core/SkPath.cpp b/src/core/SkPath.cpp
new file mode 100644
index 0000000..82eb980
--- /dev/null
+++ b/src/core/SkPath.cpp
@@ -0,0 +1,1438 @@
+/* libs/graphics/sgl/SkPath.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkPath.h"
+#include "SkFlattenable.h"
+#include "SkMath.h"
+
+////////////////////////////////////////////////////////////////////////////
+
+/* This guy's constructor/destructor bracket a path editing operation. It is
+ used when we know the bounds of the amount we are going to add to the path
+ (usually a new contour, but not required).
+
+ It captures some state about the path up front (i.e. if it already has a
+ cached bounds), and the if it can, it updates the cache bounds explicitly,
+ avoiding the need to revisit all of the points in computeBounds().
+ */
+class SkAutoPathBoundsUpdate {
+public:
+ SkAutoPathBoundsUpdate(SkPath* path, const SkRect& r) : fRect(r) {
+ this->init(path);
+ }
+
+ SkAutoPathBoundsUpdate(SkPath* path, SkScalar left, SkScalar top,
+ SkScalar right, SkScalar bottom) {
+ fRect.set(left, top, right, bottom);
+ this->init(path);
+ }
+
+ ~SkAutoPathBoundsUpdate() {
+ if (fEmpty) {
+ fPath->fFastBounds = fRect;
+ fPath->fFastBoundsIsDirty = false;
+ } else if (!fDirty) {
+ fPath->fFastBounds.join(fRect);
+ fPath->fFastBoundsIsDirty = false;
+ }
+ }
+
+private:
+ const SkPath* fPath;
+ SkRect fRect;
+ bool fDirty;
+ bool fEmpty;
+
+ // returns true if we should proceed
+ void init(const SkPath* path) {
+ fPath = path;
+ fDirty = path->fFastBoundsIsDirty;
+ fEmpty = path->isEmpty();
+ }
+};
+
+static void compute_fast_bounds(SkRect* bounds, const SkTDArray<SkPoint>& pts) {
+ if (pts.count() <= 1) { // we ignore just 1 point (moveto)
+ bounds->set(0, 0, 0, 0);
+ } else {
+ bounds->set(pts.begin(), pts.count());
+// SkDebugf("------- compute bounds %p %d", &pts, pts.count());
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+/*
+ 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;
+ compute_fast_bounds(&fFastBounds, fPts);
+ }
+ *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) {
+ SkAutoPathBoundsUpdate apbu(this, left, top, right, bottom);
+
+ 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) {
+ SkAutoPathBoundsUpdate apbu(this, rect);
+
+ 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,
+ SkPath::Direction dir, bool forceMoveTo) {
+ 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 (SkPath::kCCW_Direction == dir) {
+ start += sweep;
+ sweep = -sweep;
+ }
+
+ path->arcTo(r, start, sweep, forceMoveTo);
+}
+
+void SkPath::addRoundRect(const SkRect& rect, const SkScalar rad[],
+ Direction dir) {
+ SkAutoPathBoundsUpdate apbu(this, rect);
+
+ if (kCW_Direction == dir) {
+ add_corner_arc(this, rect, rad[0], rad[1], 180, dir, true);
+ add_corner_arc(this, rect, rad[2], rad[3], 270, dir, false);
+ add_corner_arc(this, rect, rad[4], rad[5], 0, dir, false);
+ add_corner_arc(this, rect, rad[6], rad[7], 90, dir, false);
+ } else {
+ add_corner_arc(this, rect, rad[0], rad[1], 180, dir, true);
+ add_corner_arc(this, rect, rad[6], rad[7], 90, dir, false);
+ add_corner_arc(this, rect, rad[4], rad[5], 0, dir, false);
+ add_corner_arc(this, rect, rad[2], rad[3], 270, dir, false);
+ }
+ this->close();
+}
+
+void SkPath::addOval(const SkRect& oval, Direction dir) {
+ SkAutoPathBoundsUpdate apbu(this, oval);
+
+ SkScalar cx = oval.centerX();
+ SkScalar cy = oval.centerY();
+ SkScalar rx = SkScalarHalf(oval.width());
+ SkScalar ry = SkScalarHalf(oval.height());
+#if 0 // 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);
+
+ /*
+ To handle imprecision in computing the center and radii, we revert to
+ the provided bounds when we can (i.e. use oval.fLeft instead of cx-rx)
+ to ensure that we don't exceed the oval's bounds *ever*, since we want
+ to use oval for our fast-bounds, rather than have to recompute it.
+ */
+ const SkScalar L = oval.fLeft; // cx - rx
+ const SkScalar T = oval.fTop; // cy - ry
+ const SkScalar R = oval.fRight; // cx + rx
+ const SkScalar B = oval.fBottom; // cy + ry
+
+ this->incReserve(17); // 8 quads + close
+ this->moveTo(R, cy);
+ if (dir == kCCW_Direction) {
+ this->quadTo( R, cy - sy, cx + mx, cy - my);
+ this->quadTo(cx + sx, T, cx , T);
+ this->quadTo(cx - sx, T, cx - mx, cy - my);
+ this->quadTo( L, cy - sy, L, cy );
+ this->quadTo( L, cy + sy, cx - mx, cy + my);
+ this->quadTo(cx - sx, B, cx , B);
+ this->quadTo(cx + sx, B, cx + mx, cy + my);
+ this->quadTo( R, cy + sy, R, cy );
+ } else {
+ this->quadTo( R, cy + sy, cx + mx, cy + my);
+ this->quadTo(cx + sx, B, cx , B);
+ this->quadTo(cx - sx, B, cx - mx, cy + my);
+ this->quadTo( L, cy + sy, L, cy );
+ this->quadTo( L, cy - sy, cx - mx, cy - my);
+ this->quadTo(cx - sx, T, cx , T);
+ this->quadTo(cx + sx, T, cx + mx, cy - my);
+ this->quadTo( R, cy - sy, R, cy );
+ }
+#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 {
+ SkDEBUGCODE(this->validate();)
+ 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());
+ SkDEBUGCODE(dst->validate();)
+ }
+}
+
+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);
+ fPts.validate();
+ fVerbs.validate();
+
+ if (!fFastBoundsIsDirty) {
+ SkRect bounds;
+ compute_fast_bounds(&bounds, fPts);
+ // can't call contains(), since it returns false if the rect is empty
+ SkASSERT(fFastBounds.fLeft <= bounds.fLeft);
+ SkASSERT(fFastBounds.fTop <= bounds.fTop);
+ SkASSERT(fFastBounds.fRight >= bounds.fRight);
+ SkASSERT(fFastBounds.fBottom >= bounds.fBottom);
+ }
+}
+
+#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/src/core/SkPathEffect.cpp b/src/core/SkPathEffect.cpp
new file mode 100644
index 0000000..2905895
--- /dev/null
+++ b/src/core/SkPathEffect.cpp
@@ -0,0 +1,142 @@
+/* libs/graphics/sgl/SkPathEffect.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/core/SkPathHeap.cpp b/src/core/SkPathHeap.cpp
new file mode 100644
index 0000000..f6dae5d
--- /dev/null
+++ b/src/core/SkPathHeap.cpp
@@ -0,0 +1,55 @@
+#include "SkPathHeap.h"
+#include "SkPath.h"
+#include "SkStream.h"
+#include "SkFlattenable.h"
+#include <new>
+
+#define kPathCount 64
+
+SkPathHeap::SkPathHeap() : fHeap(kPathCount * sizeof(SkPath)) {
+}
+
+SkPathHeap::SkPathHeap(SkFlattenableReadBuffer& buffer)
+ : fHeap(kPathCount * sizeof(SkPath)) {
+ int count = buffer.readS32();
+
+ fPaths.setCount(count);
+ SkPath** ptr = fPaths.begin();
+ SkPath* p = (SkPath*)fHeap.allocThrow(count * sizeof(SkPath));
+
+ for (int i = 0; i < count; i++) {
+ new (p) SkPath;
+ p->unflatten(buffer);
+ *ptr++ = p; // record the pointer
+ p++; // move to the next storage location
+ }
+}
+
+SkPathHeap::~SkPathHeap() {
+ SkPath** iter = fPaths.begin();
+ SkPath** stop = fPaths.end();
+ while (iter < stop) {
+ (*iter)->~SkPath();
+ iter++;
+ }
+}
+
+int SkPathHeap::append(const SkPath& path) {
+ SkPath* p = (SkPath*)fHeap.allocThrow(sizeof(SkPath));
+ new (p) SkPath(path);
+ *fPaths.append() = p;
+ return fPaths.count();
+}
+
+void SkPathHeap::flatten(SkFlattenableWriteBuffer& buffer) const {
+ int count = fPaths.count();
+
+ buffer.write32(count);
+ SkPath** iter = fPaths.begin();
+ SkPath** stop = fPaths.end();
+ while (iter < stop) {
+ (*iter)->flatten(buffer);
+ iter++;
+ }
+}
+
diff --git a/src/core/SkPathHeap.h b/src/core/SkPathHeap.h
new file mode 100644
index 0000000..b8f3bd3
--- /dev/null
+++ b/src/core/SkPathHeap.h
@@ -0,0 +1,37 @@
+#ifndef SkPathHeap_DEFINED
+#define SkPathHeap_DEFINED
+
+#include "SkRefCnt.h"
+#include "SkChunkAlloc.h"
+#include "SkTDArray.h"
+
+class SkPath;
+class SkFlattenableReadBuffer;
+class SkFlattenableWriteBuffer;
+
+class SkPathHeap : public SkRefCnt {
+public:
+ SkPathHeap();
+ SkPathHeap(SkFlattenableReadBuffer&);
+ virtual ~SkPathHeap();
+
+ // called during picture-record
+ int append(const SkPath&);
+
+ // called during picture-playback
+ int count() const { return fPaths.count(); }
+ const SkPath& operator[](int index) const {
+ return *fPaths[index];
+ }
+
+ void flatten(SkFlattenableWriteBuffer&) const;
+
+private:
+ // we store the paths in the heap (placement new)
+ SkChunkAlloc fHeap;
+ // we just store ptrs into fHeap here
+ SkTDArray<SkPath*> fPaths;
+};
+
+#endif
+
diff --git a/src/core/SkPathMeasure.cpp b/src/core/SkPathMeasure.cpp
new file mode 100644
index 0000000..ec1510d
--- /dev/null
+++ b/src/core/SkPathMeasure.cpp
@@ -0,0 +1,598 @@
+/*
+ * Copyright (C) 2006-2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "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/src/core/SkPicture.cpp b/src/core/SkPicture.cpp
new file mode 100644
index 0000000..07cb0a6
--- /dev/null
+++ b/src/core/SkPicture.cpp
@@ -0,0 +1,246 @@
+/*
+**
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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);
+ }
+}
+
+void SkPicture::abortPlayback() {
+ if (NULL == fPlayback) {
+ return;
+ }
+ fPlayback->abort();
+}
+
+
diff --git a/src/core/SkPictureFlat.cpp b/src/core/SkPictureFlat.cpp
new file mode 100644
index 0000000..e221e55
--- /dev/null
+++ b/src/core/SkPictureFlat.cpp
@@ -0,0 +1,257 @@
+#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
+
+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/src/core/SkPictureFlat.h b/src/core/SkPictureFlat.h
new file mode 100644
index 0000000..9e7fc5e
--- /dev/null
+++ b/src/core/SkPictureFlat.h
@@ -0,0 +1,206 @@
+#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_POS_TEXT_H_TOP_BOTTOM, // fast variant of DRAW_POS_TEXT_H
+ DRAW_RECT,
+ DRAW_SPRITE,
+ DRAW_TEXT,
+ DRAW_TEXT_ON_PATH,
+ DRAW_TEXT_TOP_BOTTOM, // fast variant of DRAW_TEXT
+ 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 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/src/core/SkPicturePlayback.cpp b/src/core/SkPicturePlayback.cpp
new file mode 100644
index 0000000..64f8f1c
--- /dev/null
+++ b/src/core/SkPicturePlayback.cpp
@@ -0,0 +1,1338 @@
+#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);
+ }
+ }
+
+ fPathHeap = record.fPathHeap;
+ fPathHeap->safeRef();
+
+ 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];
+ }
+
+ fPathHeap = src.fPathHeap;
+ fPathHeap->safeRef();
+
+ 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;
+ fPathHeap = NULL;
+ fPictureRefs = NULL;
+ fRegions = NULL;
+ fBitmapCount = fMatrixCount = fPaintCount = fPictureCount =
+ fRegionCount = 0;
+
+ fFactoryPlayback = NULL;
+}
+
+SkPicturePlayback::~SkPicturePlayback() {
+ sk_free((void*) fReader.base());
+
+ SkDELETE_ARRAY(fBitmaps);
+ SkDELETE_ARRAY(fMatrices);
+ SkDELETE_ARRAY(fPaints);
+ SkDELETE_ARRAY(fRegions);
+
+ fPathHeap->safeUnref();
+
+ 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),
+ fPathHeap ? fPathHeap->count() : 0,
+ 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);
+ }
+
+ {
+ int count = fPathHeap ? fPathHeap->count() : 0;
+ writeTagSize(buffer, PICT_PATH_TAG, count);
+ if (count > 0) {
+ fPathHeap->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);
+ }
+
+ {
+ int count = readTagSize(buffer, PICT_PATH_TAG);
+ if (count > 0) {
+ fPathHeap = SkNEW_ARGS(SkPathHeap, (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();
+
+ 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
+ if (!canvas.clipPath(path, op) && false) {
+ //SkDebugf("---- skip clipPath for %d bytes\n", offsetToRestore - fReader.offset());
+ fReader.setOffset(offsetToRestore);
+ }
+ } 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);
+ }
+ } 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);
+ }
+ } break;
+ case CONCAT:
+ canvas.concat(*getMatrix());
+ 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();
+ canvas.drawPath(getPath(), 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 xCount = getInt();
+ const SkScalar constY = getScalar();
+ const SkScalar* xpos = (const SkScalar*)fReader.skip(xCount * sizeof(SkScalar));
+ canvas.drawPosTextH(text.text(), text.length(), xpos, constY,
+ paint);
+ } break;
+ case DRAW_POS_TEXT_H_TOP_BOTTOM: {
+ const SkPaint& paint = *getPaint();
+ getText(&text);
+ size_t xCount = getInt();
+ const SkScalar* xpos = (const SkScalar*)fReader.skip((3 + xCount) * sizeof(SkScalar));
+ const SkScalar top = *xpos++;
+ const SkScalar bottom = *xpos++;
+ const SkScalar constY = *xpos++;
+ if (!canvas.quickRejectY(top, bottom, SkCanvas::kAA_EdgeType)) {
+ canvas.drawPosTextH(text.text(), text.length(), xpos,
+ constY, paint);
+ }
+ } break;
+ case DRAW_RECT: {
+ const SkPaint& paint = *getPaint();
+ canvas.drawRect(*fReader.skipRect(), 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);
+ SkScalar x = getScalar();
+ SkScalar y = getScalar();
+ canvas.drawText(text.text(), text.length(), x, y, paint);
+ } break;
+ case DRAW_TEXT_TOP_BOTTOM: {
+ 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 (!canvas.quickRejectY(ptr[2], ptr[3],
+ SkCanvas::kAA_EdgeType)) {
+ 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();
+ break;
+ case ROTATE:
+ canvas.rotate(getScalar());
+ 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);
+ } break;
+ case SKEW: {
+ SkScalar sx = getScalar();
+ SkScalar sy = getScalar();
+ canvas.skew(sx, sy);
+ } break;
+ case TRANSLATE: {
+ SkScalar dx = getScalar();
+ SkScalar dy = getScalar();
+ canvas.translate(dx, dy);
+ } break;
+ default:
+ SkASSERT(0);
+ }
+ }
+
+// this->dumpSize();
+}
+
+void SkPicturePlayback::abort() {
+ fReader.skip(fReader.size() - fReader.offset());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#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: {
+ 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/src/core/SkPicturePlayback.h b/src/core/SkPicturePlayback.h
new file mode 100644
index 0000000..b4e69ca
--- /dev/null
+++ b/src/core/SkPicturePlayback.h
@@ -0,0 +1,168 @@
+#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 "SkPathHeap.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;
+
+ // Can be called in the middle of playback (the draw() call). WIll abort the
+ // drawing and return from draw() after the "current" op code is done
+ void abort();
+
+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() {
+ return (*fPathHeap)[getInt() - 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:
+ SkPathHeap* fPathHeap; // reference counted
+ SkBitmap* fBitmaps;
+ int fBitmapCount;
+ SkMatrix* fMatrices;
+ int fMatrixCount;
+ SkPaint* fPaints;
+ int fPaintCount;
+ SkRegion* fRegions;
+ int fRegionCount;
+ mutable SkFlattenableReadBuffer fReader;
+
+ SkPicture** fPictureRefs;
+ int fPictureCount;
+
+ SkRefCntPlayback fRCPlayback;
+ SkTypefacePlayback fTFPlayback;
+ SkFactoryPlayback* fFactoryPlayback;
+};
+
+#endif
diff --git a/src/core/SkPictureRecord.cpp b/src/core/SkPictureRecord.cpp
new file mode 100644
index 0000000..6e7c645
--- /dev/null
+++ b/src/core/SkPictureRecord.cpp
@@ -0,0 +1,698 @@
+#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 = fRegionIndex = 1;
+#ifdef SK_DEBUG_SIZE
+ fPointBytes = fRectBytes = fTextBytes = 0;
+ fPointWrites = fRectWrites = fTextWrites = 0;
+#endif
+
+ fRestoreOffsetStack.setReserve(32);
+ fRestoreOffsetStack.push(0);
+
+ fPathHeap = NULL; // lazy allocate
+}
+
+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();
+}
+
+void SkPictureRecord::drawRect(const SkRect& rect, const SkPaint& paint) {
+ addDraw(DRAW_RECT);
+ 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);
+ SkRect bounds;
+ // construct a rect so we can see any adjustments from the paint.
+ // we use 0,1 for left,right, just so the rect isn't empty
+ bounds.set(0, metrics.fTop + baselineY,
+ SK_Scalar1, metrics.fBottom + baselineY);
+ (void)paint.computeFastBounds(bounds, &bounds);
+ // now record the top and bottom
+ addScalar(bounds.fTop);
+ addScalar(bounds.fBottom);
+}
+
+void SkPictureRecord::drawText(const void* text, size_t byteLength, SkScalar x,
+ SkScalar y, const SkPaint& paint) {
+ bool fast = paint.canComputeFastBounds();
+
+ addDraw(fast ? DRAW_TEXT_TOP_BOTTOM : DRAW_TEXT);
+ addPaint(paint);
+ addText(text, byteLength);
+ addScalar(x);
+ addScalar(y);
+ if (fast) {
+ 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;
+ }
+ }
+ }
+
+ bool fast = canUseDrawH && paint.canComputeFastBounds();
+
+ if (fast) {
+ addDraw(DRAW_POS_TEXT_H_TOP_BOTTOM);
+ } else {
+ 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) {
+ if (fast) {
+ 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;
+
+ bool fast = paint.canComputeFastBounds();
+
+ addDraw(fast ? DRAW_POS_TEXT_H_TOP_BOTTOM : DRAW_POS_TEXT_H);
+ addPaint(paint);
+ addText(text, byteLength);
+ addInt(points);
+
+#ifdef SK_DEBUG_SIZE
+ size_t start = fWriter.size();
+#endif
+ if (fast) {
+ 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() {
+ fPathHeap->safeUnref();
+ fPathHeap = NULL;
+
+ fBitmaps.reset();
+ fMatrices.reset();
+ fPaints.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) {
+ if (NULL == fPathHeap) {
+ fPathHeap = SkNEW(SkPathHeap);
+ }
+ addInt(fPathHeap->append(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 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/src/core/SkPictureRecord.h b/src/core/SkPictureRecord.h
new file mode 100644
index 0000000..d25d23a
--- /dev/null
+++ b/src/core/SkPictureRecord.h
@@ -0,0 +1,178 @@
+#ifndef SkPictureRecord_DEFINED
+#define SkPictureRecord_DEFINED
+
+#include "SkCanvas.h"
+#include "SkFlattenable.h"
+#include "SkPathHeap.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<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 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 fRegionIndex;
+ SkTDArray<const SkFlatRegion* > fRegions;
+ SkPathHeap* fPathHeap; // reference counted
+ 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/src/core/SkPixelRef.cpp b/src/core/SkPixelRef.cpp
new file mode 100644
index 0000000..adfc3c0
--- /dev/null
+++ b/src/core/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/src/core/SkPoint.cpp b/src/core/SkPoint.cpp
new file mode 100644
index 0000000..704c2ba
--- /dev/null
+++ b/src/core/SkPoint.cpp
@@ -0,0 +1,334 @@
+/*
+ * Copyright (C) 2006-2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "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/src/core/SkProcSpriteBlitter.cpp b/src/core/SkProcSpriteBlitter.cpp
new file mode 100644
index 0000000..f727581
--- /dev/null
+++ b/src/core/SkProcSpriteBlitter.cpp
@@ -0,0 +1,55 @@
+/* libs/graphics/sgl/SkProcSpriteBlitter.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#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/src/core/SkPtrRecorder.cpp b/src/core/SkPtrRecorder.cpp
new file mode 100644
index 0000000..4f774ec
--- /dev/null
+++ b/src/core/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/src/core/SkRasterizer.cpp b/src/core/SkRasterizer.cpp
new file mode 100644
index 0000000..f1d087d
--- /dev/null
+++ b/src/core/SkRasterizer.cpp
@@ -0,0 +1,62 @@
+/* libs/graphics/sgl/SkRasterizer.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/core/SkRect.cpp b/src/core/SkRect.cpp
new file mode 100644
index 0000000..d602754
--- /dev/null
+++ b/src/core/SkRect.cpp
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "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) {
+ bzero(this, sizeof(SkRect));
+ } else {
+#ifdef SK_SCALAR_SLOW_COMPARES
+ int32_t l, t, r, b;
+
+ l = r = SkScalarAs2sCompliment(pts[0].fX);
+ t = b = SkScalarAs2sCompliment(pts[0].fY);
+
+ for (int i = 1; i < count; i++) {
+ int32_t x = SkScalarAs2sCompliment(pts[i].fX);
+ int32_t y = SkScalarAs2sCompliment(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(Sk2sComplimentAsScalar(l),
+ Sk2sComplimentAsScalar(t),
+ Sk2sComplimentAsScalar(r),
+ Sk2sComplimentAsScalar(b));
+#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);
+#endif
+ }
+}
+
+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/src/core/SkRefCnt.cpp b/src/core/SkRefCnt.cpp
new file mode 100644
index 0000000..fea1005
--- /dev/null
+++ b/src/core/SkRefCnt.cpp
@@ -0,0 +1,48 @@
+/* libs/graphics/sgl/SkRefCnt.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/core/SkRegion.cpp b/src/core/SkRegion.cpp
new file mode 100644
index 0000000..b48f3bb
--- /dev/null
+++ b/src/core/SkRegion.cpp
@@ -0,0 +1,1341 @@
+/* libs/corecg/SkRegion.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////
+
+bool SkRegion::intersects(const SkIRect& r) const {
+ if (this->isEmpty() || r.isEmpty()) {
+ return false;
+ }
+
+ if (!SkIRect::Intersects(fBounds, r)) {
+ return false;
+ }
+
+ if (this->isRect()) {
+ return true;
+ }
+
+ // we are complex
+ SkRegion tmp;
+ return tmp.op(*this, r, kIntersect_Op);
+}
+
+bool SkRegion::intersects(const SkRegion& rgn) const {
+ if (this->isEmpty() || rgn.isEmpty()) {
+ return false;
+ }
+
+ if (!SkIRect::Intersects(fBounds, rgn.fBounds)) {
+ return false;
+ }
+
+ if (this->isRect() && rgn.isRect()) {
+ return true;
+ }
+
+ // one or both of us is complex
+ // TODO: write a faster version that aborts as soon as we write the first
+ // non-empty span, to avoid build the entire result
+ SkRegion tmp;
+ return tmp.op(*this, rgn, kIntersect_Op);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////
+
+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();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/* Given count RunTypes in a complex region, return the worst case number of
+ logical intervals that represents (i.e. number of rects that would be
+ returned from the iterator).
+
+ We could just return count/2, since there must be at least 2 values per
+ interval, but we can first trim off the const overhead of the initial TOP
+ value, plus the final BOTTOM + 2 sentinels.
+ */
+static int count_to_intervals(int count) {
+ SkASSERT(count >= 6); // a single rect is 6 values
+ return (count - 4) >> 1;
+}
+
+/* Given a number of intervals, what is the worst case representation of that
+ many intervals?
+
+ Worst case (from a storage perspective), is a vertical stack of single
+ intervals: TOP + N * (BOTTOM LEFT RIGHT SENTINEL) + SENTINEL
+ */
+static int intervals_to_count(int intervals) {
+ return 1 + intervals * 4 + 1;
+}
+
+/* Given the counts of RunTypes in two regions, return the worst-case number
+ of RunTypes need to store the result after a region-op.
+ */
+static int compute_worst_case_count(int a_count, int b_count) {
+ int a_intervals = count_to_intervals(a_count);
+ int b_intervals = count_to_intervals(b_count);
+ // Our heuristic worst case is ai * (bi + 1) + bi * (ai + 1)
+ int intervals = 2 * a_intervals * b_intervals + a_intervals + b_intervals;
+ // convert back to number of RunType values
+ return intervals_to_count(intervals);
+}
+
+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 = compute_worst_case_count(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;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+
+bool SkRegion::debugSetRuns(const RunType runs[], int count) {
+ // we need to make a copy, since the real method may modify the array, and
+ // so it cannot be const.
+
+ SkAutoTArray<RunType> storage(count);
+ memcpy(storage.get(), runs, count * sizeof(RunType));
+ return this->setRuns(storage.get(), count);
+}
+
+#endif
+
+
diff --git a/src/core/SkRegionPriv.h b/src/core/SkRegionPriv.h
new file mode 100644
index 0000000..70f8828
--- /dev/null
+++ b/src/core/SkRegionPriv.h
@@ -0,0 +1,89 @@
+/* libs/corecg/SkRegionPriv.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/core/SkRegion_path.cpp b/src/core/SkRegion_path.cpp
new file mode 100644
index 0000000..d00baf9
--- /dev/null
+++ b/src/core/SkRegion_path.cpp
@@ -0,0 +1,480 @@
+/* libs/graphics/sgl/SkRegion_path.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkRegionPriv.h"
+#include "SkBlitter.h"
+#include "SkScan.h"
+#include "SkTDArray.h"
+#include "SkPath.h"
+
+class SkRgnBuilder : public SkBlitter {
+public:
+ virtual ~SkRgnBuilder();
+
+ // returns true if it could allocate the working storage needed
+ bool 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);
+}
+
+bool SkRgnBuilder::init(int maxHeight, int maxTransitions) {
+ if ((maxHeight | maxTransitions) < 0) {
+ return false;
+ }
+
+ Sk64 count, size;
+
+ // compute the count with +1 and +3 slop for the working buffer
+ count.setMul(maxHeight + 1, 3 + maxTransitions);
+ if (!count.is32() || count.isNeg()) {
+ return false;
+ }
+ fStorageCount = count.get32();
+
+ size.setMul(fStorageCount, sizeof(SkRegion::RunType));
+ if (!size.is32() || size.isNeg()) {
+ return false;
+ }
+
+ fStorage = (SkRegion::RunType*)sk_malloc_flags(size.get32(), 0);
+ if (NULL == fStorage) {
+ return false;
+ }
+
+ fCurrScanline = NULL; // signal empty collection
+ fPrevScanline = NULL; // signal first scanline
+ return true;
+}
+
+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;
+
+ if (!builder.init(bot - top, SkMax32(pathTransitions, clipTransitions))) {
+ // can't allocate working space, so return false
+ return this->setEmpty();
+ }
+
+ 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/src/core/SkScalerContext.cpp b/src/core/SkScalerContext.cpp
new file mode 100644
index 0000000..854c4de
--- /dev/null
+++ b/src/core/SkScalerContext.cpp
@@ -0,0 +1,541 @@
+/* libs/graphics/sgl/SkScalerContext.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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 (NULL == fMaskFilter &&
+ 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*)origGlyph.fImage;
+ unsigned rowBytes = origGlyph.rowBytes();
+
+ for (int y = origGlyph.fHeight - 1; y >= 0; --y)
+ {
+ for (int x = origGlyph.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/src/core/SkScan.cpp b/src/core/SkScan.cpp
new file mode 100644
index 0000000..013b0ea
--- /dev/null
+++ b/src/core/SkScan.cpp
@@ -0,0 +1,75 @@
+/* libs/graphics/sgl/SkScan.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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) {
+ SkIRect ir;
+
+ r.round(&ir);
+ SkScan::FillIRect(ir, clip, blitter);
+}
+
+#endif
+
diff --git a/src/core/SkScan.h b/src/core/SkScan.h
new file mode 100644
index 0000000..379c016
--- /dev/null
+++ b/src/core/SkScan.h
@@ -0,0 +1,123 @@
+/* libs/graphics/sgl/SkScan.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/core/SkScanPriv.h b/src/core/SkScanPriv.h
new file mode 100644
index 0000000..43dfdef
--- /dev/null
+++ b/src/core/SkScanPriv.h
@@ -0,0 +1,48 @@
+/* libs/graphics/sgl/SkScanPriv.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/core/SkScan_AntiPath.cpp b/src/core/SkScan_AntiPath.cpp
new file mode 100644
index 0000000..9cdeeaa
--- /dev/null
+++ b/src/core/SkScan_AntiPath.cpp
@@ -0,0 +1,406 @@
+/* libs/graphics/sgl/SkScan_AntiPath.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/core/SkScan_Antihair.cpp b/src/core/SkScan_Antihair.cpp
new file mode 100644
index 0000000..04e4690
--- /dev/null
+++ b/src/core/SkScan_Antihair.cpp
@@ -0,0 +1,645 @@
+/* libs/graphics/sgl/SkScan_Antihair.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkScan.h"
+#include "SkBlitter.h"
+#include "SkColorPriv.h"
+#include "SkRegion.h"
+#include "SkFDot6.h"
+
+#define HLINE_STACK_BUFFER 100
+
+static inline int SmallDot6Scale(int value, int dot6) {
+ SkASSERT((int16_t)value == value);
+ SkASSERT((unsigned)dot6 <= 64);
+ return SkMulS16(value, dot6) >> 6;
+}
+
+//#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, U8CPU 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 SkFixed hline(int x, int stopx, SkFixed fy, SkFixed /*slope*/, SkBlitter* blitter, int mod64)
+{
+ SkASSERT(x < stopx);
+ int count = stopx - x;
+ fy += SK_Fixed1/2;
+
+ int y = fy >> 16;
+ uint8_t a = (uint8_t)(fy >> 8);
+
+ // lower line
+ unsigned ma = SmallDot6Scale(a, mod64);
+ if (ma) {
+ call_hline_blitter(blitter, x, y, count, ma);
+ }
+
+ // upper line
+ ma = SmallDot6Scale(255 - a, mod64);
+ if (ma) {
+ call_hline_blitter(blitter, x, y - 1, count, ma);
+ }
+
+ return fy - SK_Fixed1/2;
+}
+
+static SkFixed horish(int x, int stopx, SkFixed fy, SkFixed dy, SkBlitter* blitter, int mod64)
+{
+ 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);
+ unsigned ma = SmallDot6Scale(a, mod64);
+ if (ma)
+ {
+ aa[0] = ApplyGamma(gamma, ma);
+ 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);
+ }
+ ma = SmallDot6Scale(255 - a, mod64);
+ if (ma)
+ {
+ aa[0] = ApplyGamma(gamma, ma);
+ 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);
+
+ return fy - SK_Fixed1/2;
+}
+
+static SkFixed vline(int y, int stopy, SkFixed fx, SkFixed /*slope*/, SkBlitter* blitter, int mod64)
+{
+ SkASSERT(y < stopy);
+ fx += SK_Fixed1/2;
+
+ int x = fx >> 16;
+ int a = (uint8_t)(fx >> 8);
+
+ unsigned ma = SmallDot6Scale(a, mod64);
+ if (ma)
+ blitter->blitV(x, y, stopy - y, ApplyGamma(gGammaTable, ma));
+ ma = SmallDot6Scale(255 - a, mod64);
+ if (ma)
+ blitter->blitV(x - 1, y, stopy - y, ApplyGamma(gGammaTable, ma));
+
+ return fx - SK_Fixed1/2;
+}
+
+static SkFixed vertish(int y, int stopy, SkFixed fx, SkFixed dx, SkBlitter* blitter, int mod64)
+{
+ 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, SmallDot6Scale(255 - a, mod64));
+ aa[1] = ApplyGamma(gamma, SmallDot6Scale(a, mod64));
+ // 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);
+
+ return fx - SK_Fixed1/2;
+}
+
+typedef SkFixed (*LineProc)(int istart, int istop, SkFixed fstart, SkFixed slope, SkBlitter*, int);
+
+static inline SkFixed fastfixdiv(SkFDot6 a, SkFDot6 b)
+{
+ SkASSERT((a << 16 >> 16) == a);
+ SkASSERT(b != 0);
+ return (a << 16) / b;
+}
+
+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 scaleStart, scaleStop;
+ 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 = SkFDot6Floor(x0);
+ istop = SkFDot6Ceil(x1);
+ fstart = SkFDot6ToFixed(y0);
+ if (y0 == y1) { // completely horizontal, take fast case
+ slope = 0;
+ proc = hline;
+ } else {
+ slope = fastfixdiv(y1 - y0, x1 - x0);
+ SkASSERT(slope >= -SK_Fixed1 && slope <= SK_Fixed1);
+ fstart += (slope * (32 - (x0 & 63)) + 32) >> 6;
+ proc = horish;
+ }
+
+ SkASSERT(istop > istart);
+ if (istop - istart == 1) {
+ scaleStart = x1 - x0;
+ SkASSERT(scaleStart >= 0 && scaleStart <= 64);
+ scaleStop = 0;
+ } else {
+ scaleStart = 64 - (x0 & 63);
+ scaleStop = x1 & 63;
+ }
+
+ if (clip)
+ {
+ if (istart >= clip->fRight || istop <= clip->fLeft)
+ return;
+ if (istart < clip->fLeft)
+ {
+ fstart += slope * (clip->fLeft - istart);
+ istart = clip->fLeft;
+ scaleStart = 64;
+ }
+ if (istop > clip->fRight) {
+ istop = clip->fRight;
+ scaleStop = 64;
+ }
+ 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 = SkFDot6Floor(y0);
+ istop = SkFDot6Ceil(y1);
+ fstart = SkFDot6ToFixed(x0);
+ if (x0 == x1)
+ {
+ if (y0 == y1) { // are we zero length?
+ return; // nothing to do
+ }
+ slope = 0;
+ proc = vline;
+ }
+ else
+ {
+ slope = fastfixdiv(x1 - x0, y1 - y0);
+ SkASSERT(slope <= SK_Fixed1 && slope >= -SK_Fixed1);
+ fstart += (slope * (32 - (y0 & 63)) + 32) >> 6;
+ proc = vertish;
+ }
+
+ SkASSERT(istop > istart);
+ if (istop - istart == 1) {
+ scaleStart = y1 - y0;
+ SkASSERT(scaleStart >= 0 && scaleStart <= 64);
+ scaleStop = 0;
+ } else {
+ scaleStart = 64 - (y0 & 63);
+ scaleStop = y1 & 63;
+ }
+
+ if (clip)
+ {
+ if (istart >= clip->fBottom || istop <= clip->fTop)
+ return;
+ if (istart < clip->fTop)
+ {
+ fstart += slope * (clip->fTop - istart);
+ istart = clip->fTop;
+ scaleStart = 64;
+ }
+ if (istop > clip->fBottom) {
+ istop = clip->fBottom;
+ scaleStop = 64;
+ }
+ 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;
+ }
+
+ fstart = proc(istart, istart + 1, fstart, slope, blitter, scaleStart);
+ istart += 1;
+ int fullSpans = istop - istart - 1;
+ if (fullSpans > 0) {
+ fstart = proc(istart, istart + fullSpans, fstart, slope, blitter, 64);
+ }
+ if (scaleStop > 0) {
+ proc(istop - 1, istop, fstart, slope, blitter, scaleStop);
+ }
+}
+
+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( SkFDot6Floor(left) - 1,
+ SkFDot6Floor(top) - 1,
+ SkFDot6Ceil(right) + 1,
+ SkFDot6Ceil(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
+
+/* This guy takes a float-rect, but with the key improvement that it has
+ already been clipped, so we know that it is safe to convert it into a
+ XRect (fixedpoint), as it won't overflow.
+*/
+static void antifillrect(const SkRect& r, SkBlitter* blitter) {
+ SkXRect xr;
+
+ XRect_set(&xr, r);
+ antifillrect(xr, blitter);
+}
+
+/* We repeat the clipping logic of AntiFillXRect because the float rect might
+ overflow if we blindly converted it to an XRect. This sucks that we have to
+ repeat the clipping logic, but I don't see how to share the code/logic.
+
+ We clip r (as needed) into one or more (smaller) float rects, and then pass
+ those to our version of antifillrect, which converts it into an XRect and
+ then calls the blit.
+*/
+void SkScan::AntiFillRect(const SkRect& r, const SkRegion* clip,
+ SkBlitter* blitter) {
+ if (clip) {
+ SkIRect outerBounds;
+ r.roundOut(&outerBounds);
+
+ if (clip->isRect()) {
+ const SkIRect& clipBounds = clip->getBounds();
+
+ if (clipBounds.contains(outerBounds)) {
+ antifillrect(r, blitter);
+ } else {
+ SkRect tmpR;
+ // this keeps our original edges fractional
+ tmpR.set(clipBounds);
+ if (tmpR.intersect(r)) {
+ antifillrect(tmpR, blitter);
+ }
+ }
+ } else {
+ SkRegion::Cliperator clipper(*clip, outerBounds);
+ const SkIRect& rr = clipper.rect();
+
+ while (!clipper.done()) {
+ SkRect tmpR;
+ // this keeps our original edges fractional
+ tmpR.set(rr);
+ if (tmpR.intersect(r)) {
+ antifillrect(tmpR, blitter);
+ }
+ clipper.next();
+ }
+ }
+ } else {
+ antifillrect(r, blitter);
+ }
+}
+
+#endif
+
+
diff --git a/src/core/SkScan_Hairline.cpp b/src/core/SkScan_Hairline.cpp
new file mode 100644
index 0000000..6a66754
--- /dev/null
+++ b/src/core/SkScan_Hairline.cpp
@@ -0,0 +1,345 @@
+/* libs/graphics/sgl/SkScan_Hairline.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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) + (slope * ((32 - x0) & 63) >> 6);
+
+ 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) + (slope * ((32 - y0) & 63) >> 6);
+
+ 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 int compute_int_quad_dist(const SkPoint pts[3]) {
+ // compute the vector between the control point ([1]) and the middle of the
+ // line connecting the start and end ([0] and [2])
+ SkScalar dx = SkScalarHalf(pts[0].fX + pts[2].fX) - pts[1].fX;
+ SkScalar dy = SkScalarHalf(pts[0].fY + pts[2].fY) - pts[1].fY;
+ // we want everyone to be positive
+ dx = SkScalarAbs(dx);
+ dy = SkScalarAbs(dy);
+ // convert to whole pixel values (use ceiling to be conservative)
+ int idx = SkScalarCeil(dx);
+ int idy = SkScalarCeil(dy);
+ // use the cheap approx for distance
+ if (idx > idy) {
+ return idx + (idy >> 1);
+ } else {
+ return idy + (idx >> 1);
+ }
+}
+
+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
+ int count = 1 << level;
+ const SkScalar dt = SkFixedToScalar(SK_Fixed1 >> level);
+ SkScalar t = dt;
+ SkPoint prevPt = pts[0];
+ for (int i = 1; i < count; i++) {
+ SkPoint nextPt;
+ SkEvalQuadAt(pts, t, &nextPt);
+ lineproc(prevPt, nextPt, clip, blitter);
+ t += dt;
+ prevPt = nextPt;
+ }
+ // draw the last line explicitly to 1.0, in case t didn't match that exactly
+ lineproc(prevPt, 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: {
+ int d = compute_int_quad_dist(pts);
+ /* quadratics approach the line connecting their start and end points
+ 4x closer with each subdivision, so we compute the number of
+ subdivisions to be the minimum need to get that distance to be less
+ than a pixel.
+ */
+ int level = (33 - SkCLZ(d)) >> 1;
+// SkDebugf("----- distance %d computedLevel %d\n", d, computedLevel);
+ // sanity check on level (from the previous version)
+ if (level > kMaxQuadSubdivideLevel) {
+ level = kMaxQuadSubdivideLevel;
+ }
+ hairquad(pts, clip, blitter, level, 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/src/core/SkScan_Path.cpp b/src/core/SkScan_Path.cpp
new file mode 100644
index 0000000..8b58991
--- /dev/null
+++ b/src/core/SkScan_Path.cpp
@@ -0,0 +1,671 @@
+/* libs/graphics/sgl/SkScan_Path.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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_MinS32
+#define kEDGE_TAIL_Y SK_MaxS32
+
+#ifdef SK_DEBUG
+ static void validate_sort(const SkEdge* edge)
+ {
+ int y = kEDGE_HEAD_Y;
+
+ while (edge->fFirstY != SK_MaxS32)
+ {
+ 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/src/core/SkShader.cpp b/src/core/SkShader.cpp
new file mode 100644
index 0000000..dd9c859
--- /dev/null
+++ b/src/core/SkShader.cpp
@@ -0,0 +1,284 @@
+/* libs/graphics/sgl/SkShader.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/core/SkSinTable.h b/src/core/SkSinTable.h
new file mode 100644
index 0000000..9b4477d
--- /dev/null
+++ b/src/core/SkSinTable.h
@@ -0,0 +1,285 @@
+/* libs/corecg/SkSinTable.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/core/SkSpriteBlitter.h b/src/core/SkSpriteBlitter.h
new file mode 100644
index 0000000..9f7764d
--- /dev/null
+++ b/src/core/SkSpriteBlitter.h
@@ -0,0 +1,55 @@
+/* libs/graphics/sgl/SkSpriteBlitter.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/core/SkSpriteBlitterTemplate.h b/src/core/SkSpriteBlitterTemplate.h
new file mode 100644
index 0000000..2ab2b3a
--- /dev/null
+++ b/src/core/SkSpriteBlitterTemplate.h
@@ -0,0 +1,91 @@
+/* libs/graphics/sgl/SkSpriteBlitterTemplate.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+
+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/src/core/SkSpriteBlitter_ARGB32.cpp b/src/core/SkSpriteBlitter_ARGB32.cpp
new file mode 100644
index 0000000..6addde7
--- /dev/null
+++ b/src/core/SkSpriteBlitter_ARGB32.cpp
@@ -0,0 +1,314 @@
+/* libs/graphics/sgl/SkSpriteBlitter_ARGB32.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/core/SkSpriteBlitter_RGB16.cpp b/src/core/SkSpriteBlitter_RGB16.cpp
new file mode 100644
index 0000000..a158637
--- /dev/null
+++ b/src/core/SkSpriteBlitter_RGB16.cpp
@@ -0,0 +1,320 @@
+/* libs/graphics/sgl/SkSpriteBlitter_RGB16.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkSpriteBlitter.h"
+#include "SkBlitRow.h"
+#include "SkTemplates.h"
+#include "SkUtils.h"
+#include "SkColorPriv.h"
+
+#define D16_S32A_Opaque_Pixel(dst, sc) \
+do { \
+ if (sc) { \
+ *dst = SkSrcOver32To16(sc, *dst); \
+ } \
+} 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/src/core/SkString.cpp b/src/core/SkString.cpp
new file mode 100644
index 0000000..2e5d946
--- /dev/null
+++ b/src/core/SkString.cpp
@@ -0,0 +1,642 @@
+/* libs/graphics/sgl/SkString.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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);
+ if (x == 10000) {
+ x -= 1;
+ }
+ *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 <stdio.h>
+
+// number of bytes (on the stack) to receive the printf result
+static const size_t kBufferSize = 256;
+
+#ifdef SK_BUILD_FOR_WIN
+ #define VSNPRINTF _vsnprintf
+#else
+ #define VSNPRINTF vsnprintf
+#endif
+
+#define ARGS_TO_BUFFER(format, buffer, size) \
+ do { \
+ va_list args; \
+ va_start(args, format); \
+ VSNPRINTF(buffer, size, format, args); \
+ va_end(args); \
+ } while (0)
+
+void SkString::printf(const char format[], ...) {
+ char buffer[kBufferSize];
+ ARGS_TO_BUFFER(format, buffer, kBufferSize);
+
+ this->set(buffer, strlen(buffer));
+}
+
+void SkString::appendf(const char format[], ...) {
+ char buffer[kBufferSize];
+ ARGS_TO_BUFFER(format, buffer, kBufferSize);
+
+ this->append(buffer, strlen(buffer));
+}
+
+void SkString::prependf(const char format[], ...) {
+ char buffer[kBufferSize];
+ ARGS_TO_BUFFER(format, buffer, kBufferSize);
+
+ this->prepend(buffer, strlen(buffer));
+}
+
+#undef VSNPRINTF
+
+///////////////////////////////////////////////////////////////////////////
+
+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/src/core/SkStroke.cpp b/src/core/SkStroke.cpp
new file mode 100644
index 0000000..86dff48
--- /dev/null
+++ b/src/core/SkStroke.cpp
@@ -0,0 +1,645 @@
+/*
+ * Copyright (C) 2006-2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "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);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_SCALAR_IS_FIXED
+ /* return non-zero if the path is too big, and should be shrunk to avoid
+ overflows during intermediate calculations. Note that we compute the
+ bounds for this. If we had a custom callback/walker for paths, we could
+ perhaps go faster by using that, and just perform the abs | in that
+ routine
+ */
+ static int needs_to_shrink(const SkPath& path) {
+ SkRect r;
+ path.computeBounds(&r, SkPath::kFast_BoundsType);
+ SkFixed mask = SkAbs32(r.fLeft);
+ mask |= SkAbs32(r.fTop);
+ mask |= SkAbs32(r.fRight);
+ mask |= SkAbs32(r.fBottom);
+ // we need the top 3 bits clear (after abs) to avoid overflow
+ return mask >> 29;
+ }
+
+ static void identity_proc(SkPoint pts[], int count) {}
+ static void shift_down_2_proc(SkPoint pts[], int count) {
+ for (int i = 0; i < count; i++) {
+ pts->fX >>= 2;
+ pts->fY >>= 2;
+ pts += 1;
+ }
+ }
+ #define APPLY_PROC(proc, pts, count) proc(pts, count)
+#else // float does need any of this
+ #define APPLY_PROC(proc, pts, count)
+#endif
+
+void SkStroke::strokePath(const SkPath& src, SkPath* dst) const {
+ SkASSERT(&src != NULL && dst != NULL);
+
+ SkScalar radius = SkScalarHalf(fWidth);
+
+ dst->reset();
+ if (radius <= 0) {
+ return;
+ }
+
+#ifdef SK_SCALAR_IS_FIXED
+ void (*proc)(SkPoint pts[], int count) = identity_proc;
+ if (needs_to_shrink(src)) {
+ proc = shift_down_2_proc;
+ radius >>= 2;
+ if (radius == 0) {
+ return;
+ }
+ }
+#endif
+
+ SkPathStroker stroker(radius, 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:
+ APPLY_PROC(proc, &pts[0], 1);
+ stroker.moveTo(pts[0]);
+ break;
+ case SkPath::kLine_Verb:
+ APPLY_PROC(proc, &pts[1], 1);
+ stroker.lineTo(pts[1]);
+ lastSegment = verb;
+ break;
+ case SkPath::kQuad_Verb:
+ APPLY_PROC(proc, &pts[1], 2);
+ stroker.quadTo(pts[1], pts[2]);
+ lastSegment = verb;
+ break;
+ case SkPath::kCubic_Verb:
+ APPLY_PROC(proc, &pts[1], 3);
+ 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);
+
+#ifdef SK_SCALAR_IS_FIXED
+ // undo our previous down_shift
+ if (shift_down_2_proc == proc) {
+ // need a real shift methid on path. antialias paths could use this too
+ SkMatrix matrix;
+ matrix.setScale(SkIntToScalar(4), SkIntToScalar(4));
+ dst->transform(matrix);
+ }
+#endif
+
+ 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/src/core/SkStrokerPriv.cpp b/src/core/SkStrokerPriv.cpp
new file mode 100644
index 0000000..07833ca
--- /dev/null
+++ b/src/core/SkStrokerPriv.cpp
@@ -0,0 +1,275 @@
+/* libs/graphics/sgl/SkStrokerPriv.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/core/SkStrokerPriv.h b/src/core/SkStrokerPriv.h
new file mode 100644
index 0000000..ecb9bde
--- /dev/null
+++ b/src/core/SkStrokerPriv.h
@@ -0,0 +1,50 @@
+/* libs/graphics/sgl/SkStrokerPriv.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/core/SkTSearch.cpp b/src/core/SkTSearch.cpp
new file mode 100644
index 0000000..bab348f
--- /dev/null
+++ b/src/core/SkTSearch.cpp
@@ -0,0 +1,219 @@
+/* libs/graphics/sgl/SkTSearch.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/core/SkTSort.h b/src/core/SkTSort.h
new file mode 100644
index 0000000..fba49e2
--- /dev/null
+++ b/src/core/SkTSort.h
@@ -0,0 +1,65 @@
+/* libs/graphics/sgl/SkTSort.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/core/SkTemplatesPriv.h b/src/core/SkTemplatesPriv.h
new file mode 100644
index 0000000..b0a95a0
--- /dev/null
+++ b/src/core/SkTemplatesPriv.h
@@ -0,0 +1,84 @@
+/* libs/graphics/sgl/SkTemplatesPriv.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/core/SkTypeface.cpp b/src/core/SkTypeface.cpp
new file mode 100644
index 0000000..9821c51
--- /dev/null
+++ b/src/core/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/src/core/SkUnPreMultiply.cpp b/src/core/SkUnPreMultiply.cpp
new file mode 100644
index 0000000..371af32
--- /dev/null
+++ b/src/core/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/src/core/SkUtils.cpp b/src/core/SkUtils.cpp
new file mode 100644
index 0000000..4f5ba6e
--- /dev/null
+++ b/src/core/SkUtils.cpp
@@ -0,0 +1,574 @@
+/* libs/graphics/sgl/SkUtils.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/core/SkWriter32.cpp b/src/core/SkWriter32.cpp
new file mode 100644
index 0000000..61d0051
--- /dev/null
+++ b/src/core/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/src/core/SkXfermode.cpp b/src/core/SkXfermode.cpp
new file mode 100644
index 0000000..e8a202d
--- /dev/null
+++ b/src/core/SkXfermode.cpp
@@ -0,0 +1,978 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "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);
+}
+
+// idea for higher precision blends in xfer procs (and slightly faster)
+// see DstATop as a probable caller
+static U8CPU mulmuldiv255round(U8CPU a, U8CPU b, U8CPU c, U8CPU d) {
+ SkASSERT(a <= 255);
+ SkASSERT(b <= 255);
+ SkASSERT(c <= 255);
+ SkASSERT(d <= 255);
+ unsigned prod = SkMulS16(a, b) + SkMulS16(c, d) + 128;
+ unsigned result = (prod + (prod >> 8)) >> 8;
+ SkASSERT(result <= 255);
+ return result;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+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/src/effects/Sk1DPathEffect.cpp b/src/effects/Sk1DPathEffect.cpp
new file mode 100644
index 0000000..fe299c9
--- /dev/null
+++ b/src/effects/Sk1DPathEffect.cpp
@@ -0,0 +1,206 @@
+/* libs/graphics/effects/Sk1DPathEffect.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/effects/Sk2DPathEffect.cpp b/src/effects/Sk2DPathEffect.cpp
new file mode 100644
index 0000000..405b194
--- /dev/null
+++ b/src/effects/Sk2DPathEffect.cpp
@@ -0,0 +1,107 @@
+/* libs/graphics/effects/Sk2DPathEffect.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/effects/SkAvoidXfermode.cpp b/src/effects/SkAvoidXfermode.cpp
new file mode 100644
index 0000000..eed4012
--- /dev/null
+++ b/src/effects/SkAvoidXfermode.cpp
@@ -0,0 +1,257 @@
+/* libs/graphics/effects/SkAvoidXfermode.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/effects/SkBlurDrawLooper.cpp b/src/effects/SkBlurDrawLooper.cpp
new file mode 100644
index 0000000..6ad0136
--- /dev/null
+++ b/src/effects/SkBlurDrawLooper.cpp
@@ -0,0 +1,91 @@
+#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;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static SkFlattenable::Registrar gReg("SkBlurDrawLooper",
+ SkBlurDrawLooper::CreateProc);
+
diff --git a/src/effects/SkBlurMask.cpp b/src/effects/SkBlurMask.cpp
new file mode 100644
index 0000000..a7b3202
--- /dev/null
+++ b/src/effects/SkBlurMask.cpp
@@ -0,0 +1,332 @@
+/* libs/graphics/effects/SkBlurMask.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/effects/SkBlurMask.h b/src/effects/SkBlurMask.h
new file mode 100644
index 0000000..8f61d54
--- /dev/null
+++ b/src/effects/SkBlurMask.h
@@ -0,0 +1,40 @@
+/* libs/graphics/effects/SkBlurMask.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/effects/SkBlurMaskFilter.cpp b/src/effects/SkBlurMaskFilter.cpp
new file mode 100644
index 0000000..2db60a5
--- /dev/null
+++ b/src/effects/SkBlurMaskFilter.cpp
@@ -0,0 +1,123 @@
+/* libs/graphics/effects/SkBlurMaskFilter.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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&);
+
+ static SkFlattenable* CreateProc(SkFlattenableReadBuffer&);
+
+private:
+ SkScalar fRadius;
+ SkBlurMaskFilter::BlurStyle fBlurStyle;
+
+ 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);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static SkFlattenable::Registrar gReg("SkBlurMaskFilter",
+ SkBlurMaskFilterImpl::CreateProc);
+
diff --git a/src/effects/SkColorFilters.cpp b/src/effects/SkColorFilters.cpp
new file mode 100644
index 0000000..50be1ad
--- /dev/null
+++ b/src/effects/SkColorFilters.cpp
@@ -0,0 +1,553 @@
+/* libs/graphics/effects/SkColorFilters.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/effects/SkColorMatrixFilter.cpp b/src/effects/SkColorMatrixFilter.cpp
new file mode 100644
index 0000000..07c8d2f
--- /dev/null
+++ b/src/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;
+
+ // cast to (int) to keep the expression signed for the shift
+ result[0] = (array[0] * (int)r + array[4]) >> shift;
+ result[1] = (array[6] * (int)g + array[9]) >> shift;
+ result[2] = (array[12] * (int)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;
+
+ // cast to (int) to keep the expression signed for the shift
+ result[0] = (array[0] * (int)r + array[4]) >> 16;
+ result[1] = (array[6] * (int)g + array[9]) >> 16;
+ result[2] = (array[12] * (int)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 = 9 - 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);
+ }
+}
+
+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];
+
+ // expand to 8bit components (since our matrix translate is 8bit biased
+ unsigned r = SkPacked16ToR32(c);
+ unsigned g = SkPacked16ToG32(c);
+ unsigned b = SkPacked16ToB32(c);
+
+ proc(state, r, g, b, 0);
+
+ r = pin(result[0], SK_R32_MASK);
+ g = pin(result[1], SK_G32_MASK);
+ b = pin(result[2], SK_B32_MASK);
+
+ // now packed it back down to 16bits (hmmm, could dither...)
+ dst[i] = SkPack888ToRGB16(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/src/effects/SkCornerPathEffect.cpp b/src/effects/SkCornerPathEffect.cpp
new file mode 100644
index 0000000..43d571a
--- /dev/null
+++ b/src/effects/SkCornerPathEffect.cpp
@@ -0,0 +1,157 @@
+/* libs/graphics/effects/SkCornerPathEffect.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/effects/SkDashPathEffect.cpp b/src/effects/SkDashPathEffect.cpp
new file mode 100644
index 0000000..48581b5
--- /dev/null
+++ b/src/effects/SkDashPathEffect.cpp
@@ -0,0 +1,178 @@
+/* libs/graphics/effects/SkDashPathEffect.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/effects/SkDiscretePathEffect.cpp b/src/effects/SkDiscretePathEffect.cpp
new file mode 100644
index 0000000..6286045
--- /dev/null
+++ b/src/effects/SkDiscretePathEffect.cpp
@@ -0,0 +1,105 @@
+/* libs/graphics/effects/SkDiscretePathEffect.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/effects/SkEmbossMask.cpp b/src/effects/SkEmbossMask.cpp
new file mode 100644
index 0000000..28e38a1
--- /dev/null
+++ b/src/effects/SkEmbossMask.cpp
@@ -0,0 +1,182 @@
+/* libs/graphics/effects/SkEmbossMask.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/effects/SkEmbossMask.h b/src/effects/SkEmbossMask.h
new file mode 100644
index 0000000..8e56d6f
--- /dev/null
+++ b/src/effects/SkEmbossMask.h
@@ -0,0 +1,29 @@
+/* libs/graphics/effects/SkEmbossMask.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef SkEmbossMask_DEFINED
+#define SkEmbossMask_DEFINED
+
+#include "SkEmbossMaskFilter.h"
+
+class SkEmbossMask {
+public:
+ static void Emboss(SkMask* mask, const SkEmbossMaskFilter::Light&);
+};
+
+#endif
+
diff --git a/src/effects/SkEmbossMaskFilter.cpp b/src/effects/SkEmbossMaskFilter.cpp
new file mode 100644
index 0000000..fcfad9d
--- /dev/null
+++ b/src/effects/SkEmbossMaskFilter.cpp
@@ -0,0 +1,141 @@
+/* libs/graphics/effects/SkEmbossMaskFilter.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/effects/SkEmbossMask_Table.h b/src/effects/SkEmbossMask_Table.h
new file mode 100644
index 0000000..775fe25
--- /dev/null
+++ b/src/effects/SkEmbossMask_Table.h
@@ -0,0 +1,1046 @@
+/* libs/graphics/effects/SkEmbossMask_Table.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#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/src/effects/SkGradientShader.cpp b/src/effects/SkGradientShader.cpp
new file mode 100644
index 0000000..a1674cb
--- /dev/null
+++ b/src/effects/SkGradientShader.cpp
@@ -0,0 +1,1562 @@
+/* libs/graphics/effects/SkGradientShader.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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);
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////
+
+// assumes colors is SkColor* and pos is SkScalar*
+#define EXPAND_1_COLOR(count) \
+ SkColor tmp[2]; \
+ do { \
+ if (1 == count) { \
+ tmp[0] = tmp[1] = colors[0]; \
+ colors = tmp; \
+ pos = NULL; \
+ count = 2; \
+ } \
+ } while (0)
+
+SkShader* SkGradientShader::CreateLinear( const SkPoint pts[2],
+ const SkColor colors[], const SkScalar pos[], int colorCount,
+ SkShader::TileMode mode, SkUnitMapper* mapper)
+{
+ if (NULL == pts || NULL == colors || colorCount < 1) {
+ return NULL;
+ }
+ EXPAND_1_COLOR(colorCount);
+
+ 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)
+{
+ if (radius <= 0 || NULL == colors || colorCount < 1) {
+ return NULL;
+ }
+ EXPAND_1_COLOR(colorCount);
+
+ 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)
+{
+ if (NULL == colors || count < 1) {
+ return NULL;
+ }
+ EXPAND_1_COLOR(count);
+
+ 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/src/effects/SkKernel33MaskFilter.cpp b/src/effects/SkKernel33MaskFilter.cpp
new file mode 100644
index 0000000..a30ea4a
--- /dev/null
+++ b/src/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/src/effects/SkLayerDrawLooper.cpp b/src/effects/SkLayerDrawLooper.cpp
new file mode 100644
index 0000000..f2d8ba8
--- /dev/null
+++ b/src/effects/SkLayerDrawLooper.cpp
@@ -0,0 +1,130 @@
+#include "SkCanvas.h"
+#include "SkLayerDrawLooper.h"
+#include "SkPaint.h"
+
+SkLayerDrawLooper::SkLayerDrawLooper() {
+ fRecs = NULL;
+ fCount = 0;
+}
+
+SkLayerDrawLooper::~SkLayerDrawLooper() {
+ Rec* rec = fRecs;
+ while (rec) {
+ Rec* next = rec->fNext;
+ SkDELETE(rec);
+ rec = next;
+ }
+}
+
+SkPaint* SkLayerDrawLooper::addLayer(SkScalar dx, SkScalar dy) {
+ fCount += 1;
+
+ Rec* rec = SkNEW(Rec);
+ rec->fNext = fRecs;
+ rec->fOffset.set(dx, dy);
+ fRecs = rec;
+
+ return &rec->fPaint;
+}
+
+void SkLayerDrawLooper::init(SkCanvas* canvas, SkPaint* paint) {
+ fIter.fSavedPaint = *paint;
+ fIter.fPaint = paint;
+ fIter.fCanvas = canvas;
+ fIter.fRec = fRecs;
+ canvas->save(SkCanvas::kMatrix_SaveFlag);
+}
+
+bool SkLayerDrawLooper::next() {
+ Rec* rec = fIter.fRec;
+ if (rec) {
+ *fIter.fPaint = rec->fPaint;
+ fIter.fCanvas->restore();
+ fIter.fCanvas->save(SkCanvas::kMatrix_SaveFlag);
+ fIter.fCanvas->translate(rec->fOffset.fX, rec->fOffset.fY);
+
+ fIter.fRec = rec->fNext;
+ return true;
+ }
+ return false;
+}
+
+void SkLayerDrawLooper::restore() {
+ fIter.fCanvas->restore();
+ *fIter.fPaint = fIter.fSavedPaint;
+}
+
+SkLayerDrawLooper::Rec* SkLayerDrawLooper::Rec::Reverse(Rec* head) {
+ Rec* rec = head;
+ Rec* prev = NULL;
+ while (rec) {
+ Rec* next = rec->fNext;
+ rec->fNext = prev;
+ prev = rec;
+ rec = next;
+ }
+ return prev;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkLayerDrawLooper::flatten(SkFlattenableWriteBuffer& buffer) {
+ this->INHERITED::flatten(buffer);
+
+#ifdef SK_DEBUG
+ {
+ Rec* rec = fRecs;
+ int count = 0;
+ while (rec) {
+ rec = rec->fNext;
+ count += 1;
+ }
+ SkASSERT(count == fCount);
+ }
+#endif
+
+ buffer.writeInt(fCount);
+
+ Rec* rec = fRecs;
+ for (int i = 0; i < fCount; i++) {
+ buffer.writeScalar(rec->fOffset.fX);
+ buffer.writeScalar(rec->fOffset.fY);
+ rec->fPaint.flatten(buffer);
+ rec = rec->fNext;
+ }
+}
+
+SkLayerDrawLooper::SkLayerDrawLooper(SkFlattenableReadBuffer& buffer)
+ : INHERITED(buffer) {
+ fRecs = NULL;
+ fCount = 0;
+
+ int count = buffer.readInt();
+
+ for (int i = 0; i < count; i++) {
+ SkScalar dx = buffer.readScalar();
+ SkScalar dy = buffer.readScalar();
+ this->addLayer(dx, dy)->unflatten(buffer);
+ }
+ SkASSERT(count == fCount);
+
+ // we're in reverse order, so fix it now
+ fRecs = Rec::Reverse(fRecs);
+
+#ifdef SK_DEBUG
+ {
+ Rec* rec = fRecs;
+ int n = 0;
+ while (rec) {
+ rec = rec->fNext;
+ n += 1;
+ }
+ SkASSERT(count == n);
+ }
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static SkFlattenable::Registrar gReg("SkLayerDrawLooper",
+ SkLayerDrawLooper::CreateProc);
diff --git a/src/effects/SkLayerRasterizer.cpp b/src/effects/SkLayerRasterizer.cpp
new file mode 100644
index 0000000..390e3a3
--- /dev/null
+++ b/src/effects/SkLayerRasterizer.cpp
@@ -0,0 +1,242 @@
+/* libs/graphics/effects/SkLayerRasterizer.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/effects/SkPaintFlagsDrawFilter.cpp b/src/effects/SkPaintFlagsDrawFilter.cpp
new file mode 100644
index 0000000..ed2df88
--- /dev/null
+++ b/src/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/src/effects/SkPixelXorXfermode.cpp b/src/effects/SkPixelXorXfermode.cpp
new file mode 100644
index 0000000..a5599e2
--- /dev/null
+++ b/src/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/src/effects/SkRadialGradient_Table.h b/src/effects/SkRadialGradient_Table.h
new file mode 100644
index 0000000..2336237
--- /dev/null
+++ b/src/effects/SkRadialGradient_Table.h
@@ -0,0 +1,147 @@
+/* libs/graphics/effects/SkRadialGradient_Table.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+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/src/effects/SkTransparentShader.cpp b/src/effects/SkTransparentShader.cpp
new file mode 100644
index 0000000..6b79839f
--- /dev/null
+++ b/src/effects/SkTransparentShader.cpp
@@ -0,0 +1,144 @@
+/* libs/graphics/effects/SkTransparentShader.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/gl/SkGL.cpp b/src/gl/SkGL.cpp
new file mode 100644
index 0000000..0634709
--- /dev/null
+++ b/src/gl/SkGL.cpp
@@ -0,0 +1,528 @@
+#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:
+#ifdef SK_GL_SUPPORT_COMPRESSEDTEXIMAGE2D
+ *format = GL_PALETTE8_RGBA8_OES;
+ *type = GL_UNSIGNED_BYTE; // unused I think
+#else
+ // we promote index to argb32
+ *format = GL_RGBA;
+ *type = GL_UNSIGNED_BYTE;
+#endif
+ break;
+ case SkBitmap::kA8_Config:
+ *format = GL_ALPHA;
+ *type = GL_UNSIGNED_BYTE;
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+#define SK_GL_SIZE_OF_PALETTE (256 * sizeof(SkPMColor))
+
+size_t SkGL::ComputeTextureMemorySize(const SkBitmap& bitmap) {
+ int shift = 0;
+ size_t adder = 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:
+#ifdef SK_GL_SUPPORT_COMPRESSEDTEXIMAGE2D
+ // account for the colortable
+ adder = SK_GL_SIZE_OF_PALETTE;
+#else
+ // we promote index to argb32
+ shift = 2;
+#endif
+ break;
+ default:
+ return 0;
+ }
+ return (bitmap.getSize() << shift) + adder;
+}
+
+#ifdef SK_GL_SUPPORT_COMPRESSEDTEXIMAGE2D
+/* Fill out buffer with the compressed format GL expects from a colortable
+ based bitmap. [palette (colortable) + indices].
+
+ At the moment I always take the 8bit version, since that's what my data
+ is. I could detect that the colortable.count is <= 16, and then repack the
+ indices as nibbles to save RAM, but it would take more time (i.e. a lot
+ slower than memcpy), so I'm skipping that for now.
+
+ GL wants a full 256 palette entry, even though my ctable is only as big
+ as the colortable.count says it is. I presume it is OK to leave any
+ trailing entries uninitialized, since none of my indices should exceed
+ ctable->count().
+*/
+static void build_compressed_data(void* buffer, const SkBitmap& bitmap) {
+ SkASSERT(SkBitmap::kIndex8_Config == bitmap.config());
+
+ SkColorTable* ctable = bitmap.getColorTable();
+ uint8_t* dst = (uint8_t*)buffer;
+
+ memcpy(dst, ctable->lockColors(), ctable->count() * sizeof(SkPMColor));
+ ctable->unlockColors(false);
+
+ // always skip a full 256 number of entries, even if we memcpy'd fewer
+ dst += SK_GL_SIZE_OF_PALETTE;
+ memcpy(dst, bitmap.getPixels(), bitmap.getSize());
+}
+#endif
+
+/* Return true if the bitmap cannot be supported in its current config as a
+ texture, and it needs to be promoted to ARGB32.
+ */
+static bool needToPromoteTo32bit(const SkBitmap& bitmap) {
+ if (bitmap.config() == SkBitmap::kIndex8_Config) {
+#ifdef SK_GL_SUPPORT_COMPRESSEDTEXIMAGE2D
+ const int w = bitmap.width();
+ const int h = bitmap.height();
+ if (SkNextPow2(w) == w && SkNextPow2(h) == h) {
+ // we can handle Indx8 if we're a POW2
+ return false;
+ }
+#endif
+ return true; // must promote to ARGB32
+ }
+ return false;
+}
+
+GLuint SkGL::BindNewTexture(const SkBitmap& origBitmap, SkPoint* max) {
+ SkBitmap tmpBitmap;
+ const SkBitmap* bitmap = &origBitmap;
+
+ if (needToPromoteTo32bit(origBitmap)) {
+ origBitmap.copyTo(&tmpBitmap, SkBitmap::kARGB_8888_Config);
+ // now bitmap points to our temp, which has been promoted to 32bits
+ bitmap = &tmpBitmap;
+ }
+
+ GLenum format, type;
+ if (!canBeTexture(*bitmap, &format, &type)) {
+ return 0;
+ }
+
+ SkAutoLockPixels alp(*bitmap);
+ if (!bitmap->readyToDraw()) {
+ 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
+#ifdef SK_GL_SUPPORT_COMPRESSEDTEXIMAGE2D
+ if (SkBitmap::kIndex8_Config == bitmap->config()) {
+ size_t imagesize = bitmap->getSize() + SK_GL_SIZE_OF_PALETTE;
+ SkAutoMalloc storage(imagesize);
+
+ build_compressed_data(storage.get(), *bitmap);
+ // we only support POW2 here (GLES 1.0 restriction)
+ SkASSERT(ow == nw);
+ SkASSERT(oh == nh);
+ glCompressedTexImage2D(GL_TEXTURE_2D, 0, format, ow, oh, 0,
+ imagesize, storage.get());
+ } else // fall through to non-compressed logic
+#endif
+ {
+ 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/src/gl/SkGL.h b/src/gl/SkGL.h
new file mode 100644
index 0000000..d4cd3b6
--- /dev/null
+++ b/src/gl/SkGL.h
@@ -0,0 +1,303 @@
+#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
+
+///////////////////////////////////////////////////////////////////////////////
+
+#if GL_OES_compressed_paletted_texture
+ #define SK_GL_SUPPORT_COMPRESSEDTEXIMAGE2D
+#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/src/gl/SkGLCanvas.cpp b/src/gl/SkGLCanvas.cpp
new file mode 100644
index 0000000..2e93209
--- /dev/null
+++ b/src/gl/SkGLCanvas.cpp
@@ -0,0 +1,179 @@
+#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);
+
+SkGLDevice::TexCache* SkGLDevice::LockTexCache(const SkBitmap& bitmap,
+ GLuint* name, SkPoint* size) {
+ SkAutoMutexAcquire amc(gTextureCacheMutex);
+
+ 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);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkGLTextCache.h"
+
+static bool deleteCachesProc(SkGlyphCache* cache, void* texturesAreValid) {
+ void* auxData;
+ if (cache->getAuxProcData(SkGLDevice::GlyphCacheAuxProc, &auxData)) {
+ bool valid = texturesAreValid != NULL;
+ SkGLTextCache* textCache = static_cast<SkGLTextCache*>(auxData);
+ // call this before delete, in case valid is false
+ textCache->deleteAllStrikes(valid);
+ // now free the memory for the cache itself
+ SkDELETE(textCache);
+ // now remove the entry in the glyphcache (does not call the proc)
+ cache->removeAuxProc(SkGLDevice::GlyphCacheAuxProc);
+ }
+ return false; // keep going
+}
+
+void SkGLCanvas::DeleteAllTextures() {
+ // free the textures in our cache
+
+ gTextureCacheMutex.acquire();
+ gTextureCache.deleteAllCaches(true);
+ gTextureCacheMutex.release();
+
+ // now free the textures in the font cache
+
+ SkGlyphCache::VisitAllCaches(deleteCachesProc, reinterpret_cast<void*>(true));
+}
+
+void SkGLCanvas::AbandonAllTextures() {
+ // abandon the textures in our cache
+
+ gTextureCacheMutex.acquire();
+ gTextureCache.deleteAllCaches(false);
+ gTextureCacheMutex.release();
+
+ // abandon the textures in the font cache
+
+ SkGlyphCache::VisitAllCaches(deleteCachesProc, reinterpret_cast<void*>(false));
+}
+
diff --git a/src/gl/SkGLDevice.cpp b/src/gl/SkGLDevice.cpp
new file mode 100644
index 0000000..70968e2
--- /dev/null
+++ b/src/gl/SkGLDevice.cpp
@@ -0,0 +1,761 @@
+#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);
+ }
+
+ 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;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+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.readyToDraw()) {
+ 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.readyToDraw()) {
+ 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.readyToDraw()) {
+ 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"
+
+void SkGLDevice::GlyphCacheAuxProc(void* data) {
+ SkDebugf("-------------- delete text texture cache\n");
+ 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(SkGLDevice::GlyphCacheAuxProc, &auxData)) {
+ textCache = (SkGLTextCache*)auxData;
+ }
+ if (NULL == textCache) {
+ // need to create one
+ textCache = SkNEW(SkGLTextCache);
+ gcache->setAuxProc(SkGLDevice::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/src/gl/SkGLDevice.h b/src/gl/SkGLDevice.h
new file mode 100644
index 0000000..0fc9e47
--- /dev/null
+++ b/src/gl/SkGLDevice.h
@@ -0,0 +1,124 @@
+#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();
+
+ // used to identify GLTextCache data in the glyphcache
+ static void GlyphCacheAuxProc(void* data);
+
+ 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/src/gl/SkGLDevice_FBO.cpp b/src/gl/SkGLDevice_FBO.cpp
new file mode 100644
index 0000000..552d619
--- /dev/null
+++ b/src/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/src/gl/SkGLDevice_FBO.h b/src/gl/SkGLDevice_FBO.h
new file mode 100644
index 0000000..d695ff0
--- /dev/null
+++ b/src/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/src/gl/SkGLDevice_SWLayer.cpp b/src/gl/SkGLDevice_SWLayer.cpp
new file mode 100644
index 0000000..4b75d4c
--- /dev/null
+++ b/src/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/src/gl/SkGLDevice_SWLayer.h b/src/gl/SkGLDevice_SWLayer.h
new file mode 100644
index 0000000..7e61370
--- /dev/null
+++ b/src/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/src/gl/SkGLTextCache.cpp b/src/gl/SkGLTextCache.cpp
new file mode 100644
index 0000000..141e100
--- /dev/null
+++ b/src/gl/SkGLTextCache.cpp
@@ -0,0 +1,191 @@
+#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() {
+ bzero(fStrikeList, sizeof(fStrikeList));
+}
+
+SkGLTextCache::~SkGLTextCache() {
+ this->deleteAllStrikes(true);
+}
+
+void SkGLTextCache::deleteAllStrikes(bool texturesAreValid) {
+ for (size_t i = 0; i < SK_ARRAY_COUNT(fStrikeList); i++) {
+ Strike* strike = fStrikeList[i];
+ while (strike != NULL) {
+ Strike* next = strike->fNext;
+ if (!texturesAreValid) {
+ strike->abandonTexture();
+ }
+ SkDELETE(strike);
+ strike = next;
+ }
+ }
+ bzero(fStrikeList, sizeof(fStrikeList));
+}
+
+SkGLTextCache::Strike* SkGLTextCache::findGlyph(const SkGlyph& glyph,
+ int* offset) {
+ 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) {
+ 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/src/gl/SkGLTextCache.h b/src/gl/SkGLTextCache.h
new file mode 100644
index 0000000..eb552aa
--- /dev/null
+++ b/src/gl/SkGLTextCache.h
@@ -0,0 +1,86 @@
+#ifndef SkGLTextCache_DEFINED
+#define SkGLTextCache_DEFINED
+
+#include "SkGL.h"
+
+class SkGlyph;
+
+class SkGLTextCache {
+public:
+ SkGLTextCache();
+ ~SkGLTextCache();
+
+ /** Delete all of the strikes in the cache. Pass true if the texture IDs are
+ still valid, in which case glDeleteTextures will be called. Pass false
+ if they are invalid (e.g. the gl-context has changed), in which case
+ they will just be abandoned.
+ */
+ void deleteAllStrikes(bool texturesAreValid);
+
+ 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 abandonTexture() { 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:
+ 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/src/gl/SkTextureCache.cpp b/src/gl/SkTextureCache.cpp
new file mode 100644
index 0000000..17b37ca
--- /dev/null
+++ b/src/gl/SkTextureCache.cpp
@@ -0,0 +1,363 @@
+#include "SkTextureCache.h"
+
+//#define TRACE_HASH_HITS
+//#define TRACE_TEXTURE_CACHE_PURGE
+
+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();
+}
+
+void SkTextureCache::deleteAllCaches(bool texturesAreValid) {
+ this->validate();
+
+ Entry* entry = fHead;
+ while (entry) {
+ Entry* next = entry->fNext;
+ if (!texturesAreValid) {
+ entry->abandonTexture();
+ }
+ SkDELETE(entry);
+ entry = next;
+ }
+
+ fSorted.reset();
+ bzero(fHash, sizeof(fHash));
+
+ fTexCount = 0;
+ fTexSize = 0;
+
+ fTail = fHead = NULL;
+
+ 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
+#ifdef TRACE_TEXTURE_CACHE_PURGE
+ SkDebugf("---- purge texture cache %d size=%d\n",
+ entry->name(), entry->memSize());
+#endif
+ 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);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#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/src/gl/SkTextureCache.h b/src/gl/SkTextureCache.h
new file mode 100644
index 0000000..0bc3091
--- /dev/null
+++ b/src/gl/SkTextureCache.h
@@ -0,0 +1,161 @@
+#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);
+
+ /** Deletes all the caches. Pass true if the texture IDs are still valid,
+ and if so, it will call glDeleteTextures. Pass false if the texture IDs
+ are invalid (e.g. the gl-context has changed), in which case they will
+ just be abandoned.
+ */
+ void deleteAllCaches(bool texturesAreValid);
+
+ 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 abandonTexture() { 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/src/images/SkBitmap_RLEPixels.h b/src/images/SkBitmap_RLEPixels.h
new file mode 100644
index 0000000..c83bc69
--- /dev/null
+++ b/src/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/src/images/SkCreateRLEPixelRef.cpp b/src/images/SkCreateRLEPixelRef.cpp
new file mode 100644
index 0000000..5756237
--- /dev/null
+++ b/src/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/src/images/SkFDStream.cpp b/src/images/SkFDStream.cpp
new file mode 100644
index 0000000..db4a51a
--- /dev/null
+++ b/src/images/SkFDStream.cpp
@@ -0,0 +1,85 @@
+#include "SkStream.h"
+#include <unistd.h>
+
+//#define TRACE_FDSTREAM
+
+SkFDStream::SkFDStream(int fileDesc, bool closeWhenDone)
+ : fFD(fileDesc), fCloseWhenDone(closeWhenDone) {
+}
+
+SkFDStream::~SkFDStream() {
+ if (fFD >= 0 && fCloseWhenDone) {
+ ::close(fFD);
+ }
+}
+
+bool SkFDStream::rewind() {
+ if (fFD >= 0) {
+ off_t value = ::lseek(fFD, 0, SEEK_SET);
+#ifdef TRACE_FDSTREAM
+ if (value) {
+ SkDebugf("xxxxxxxxxxxxxx rewind failed %d\n", value);
+ }
+#endif
+ return value == 0;
+ }
+ return false;
+}
+
+size_t SkFDStream::read(void* buffer, size_t size) {
+ if (fFD >= 0) {
+ if (buffer == NULL && size == 0) { // request total size
+ off_t curr = ::lseek(fFD, 0, SEEK_CUR);
+ if (curr < 0) {
+#ifdef TRACE_FDSTREAM
+ SkDebugf("xxxxxxxxxxxxx lseek failed 0 CURR\n");
+#endif
+ return 0; // error
+ }
+ off_t size = ::lseek(fFD, 0, SEEK_END);
+ if (size < 0) {
+#ifdef TRACE_FDSTREAM
+ SkDebugf("xxxxxxxxxxxxx lseek failed 0 END\n");
+#endif
+ size = 0; // error
+ }
+ if (::lseek(fFD, curr, SEEK_SET) != curr) {
+ // can't restore, error
+#ifdef TRACE_FDSTREAM
+ SkDebugf("xxxxxxxxxxxxx lseek failed %d SET\n", curr);
+#endif
+ return 0;
+ }
+ return size;
+ } else if (NULL == buffer) { // skip
+ off_t oldCurr = ::lseek(fFD, 0, SEEK_CUR);
+ if (oldCurr < 0) {
+#ifdef TRACE_FDSTREAM
+ SkDebugf("xxxxxxxxxxxxx lseek1 failed %d CUR\n", oldCurr);
+#endif
+ return 0; // error;
+ }
+ off_t newCurr = ::lseek(fFD, size, SEEK_CUR);
+ if (newCurr < 0) {
+#ifdef TRACE_FDSTREAM
+ SkDebugf("xxxxxxxxxxxxx lseek2 failed %d CUR\n", newCurr);
+#endif
+ return 0; // error;
+ }
+ // return the actual amount we skipped
+ return newCurr - oldCurr;
+ } else { // read
+ ssize_t actual = ::read(fFD, buffer, size);
+ // our API can't return an error, so we return 0
+ if (actual < 0) {
+#ifdef TRACE_FDSTREAM
+ SkDebugf("xxxxxxxxxxxxx read failed %d actual %d\n", size, actual);
+#endif
+ actual = 0;
+ }
+ return actual;
+ }
+ }
+ return 0;
+}
+
diff --git a/src/images/SkFlipPixelRef.cpp b/src/images/SkFlipPixelRef.cpp
new file mode 100644
index 0000000..95403cc
--- /dev/null
+++ b/src/images/SkFlipPixelRef.cpp
@@ -0,0 +1,127 @@
+#include "SkFlipPixelRef.h"
+#include "SkFlattenable.h"
+#include "SkRegion.h"
+
+SkFlipPixelRef::SkFlipPixelRef(SkBitmap::Config config, int width, int height)
+: fFlipper(width, height) {
+ fConfig = config;
+ fSize = SkBitmap::ComputeSize(config, width, height);
+ fStorage = sk_malloc_throw(fSize << 1);
+ fPage0 = fStorage;
+ fPage1 = (char*)fStorage + fSize;
+}
+
+SkFlipPixelRef::~SkFlipPixelRef() {
+ sk_free(fStorage);
+}
+
+const SkRegion& SkFlipPixelRef::beginUpdate(SkBitmap* device) {
+ void* writeAddr;
+ const void* readAddr;
+ this->getFrontBack(&readAddr, &writeAddr);
+
+ device->setConfig(fConfig, fFlipper.width(), fFlipper.height());
+ device->setPixels(writeAddr);
+
+ SkRegion copyBits;
+ const SkRegion& dirty = fFlipper.update(©Bits);
+
+ SkFlipPixelRef::CopyBitsFromAddr(*device, copyBits, readAddr);
+ return dirty;
+}
+
+void SkFlipPixelRef::endUpdate() {
+ this->swapPages();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void* SkFlipPixelRef::onLockPixels(SkColorTable** ct) {
+ fMutex.acquire();
+ *ct = NULL;
+ return fPage0;
+}
+
+void SkFlipPixelRef::onUnlockPixels() {
+ fMutex.release();
+}
+
+void SkFlipPixelRef::swapPages() {
+ fMutex.acquire();
+ SkTSwap<void*>(fPage0, fPage1);
+ fMutex.release();
+}
+
+void SkFlipPixelRef::flatten(SkFlattenableWriteBuffer& buffer) const {
+ this->INHERITED::flatten(buffer);
+
+ buffer.write32(fSize);
+ // only need to write page0
+ buffer.writePad(fPage0, fSize);
+}
+
+SkFlipPixelRef::SkFlipPixelRef(SkFlattenableReadBuffer& buffer)
+ : INHERITED(buffer, NULL) {
+ fSize = buffer.readU32();
+ fStorage = sk_malloc_throw(fSize << 1);
+ fPage0 = fStorage;
+ fPage1 = (char*)fStorage + fSize;
+ buffer.read(fPage0, fSize);
+}
+
+SkPixelRef* SkFlipPixelRef::Create(SkFlattenableReadBuffer& buffer) {
+ return SkNEW_ARGS(SkFlipPixelRef, (buffer));
+}
+
+static SkPixelRef::Registrar::Registrar reg("SkFlipPixelRef",
+ SkFlipPixelRef::Create);
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void copyRect(const SkBitmap& dst, const SkIRect& rect,
+ const void* srcAddr, int shift) {
+ const size_t offset = rect.fTop * dst.rowBytes() + (rect.fLeft << shift);
+ char* dstP = static_cast<char*>(dst.getPixels()) + offset;
+ const char* srcP = static_cast<const char*>(srcAddr) + offset;
+ const size_t rb = dst.rowBytes();
+ const size_t bytes = rect.width() << shift;
+
+ int height = rect.height();
+ while (--height >= 0) {
+ memcpy(dstP, srcP, bytes);
+ dstP += rb;
+ srcP += rb;
+ }
+}
+
+static int getShift(SkBitmap::Config config) {
+ switch (config) {
+ case SkBitmap::kARGB_8888_Config:
+ return 2;
+ case SkBitmap::kRGB_565_Config:
+ case SkBitmap::kARGB_4444_Config:
+ return 1;
+ case SkBitmap::kIndex8_Config:
+ case SkBitmap::kA8_Config:
+ return 0;
+ default:
+ return -1; // signal not supported
+ }
+}
+
+void SkFlipPixelRef::CopyBitsFromAddr(const SkBitmap& dst, const SkRegion& clip,
+ const void* srcAddr) {
+ const int shift = getShift(dst.config());
+ if (shift < 0) {
+ return;
+ }
+
+ const SkIRect bounds = {0, 0, dst.width(), dst.height()};
+ SkRegion::Cliperator iter(clip, bounds);
+
+ while (!iter.done()) {
+ copyRect(dst, iter.rect(), srcAddr, shift);
+ iter.next();
+ }
+}
+
diff --git a/src/images/SkImageDecoder.cpp b/src/images/SkImageDecoder.cpp
new file mode 100644
index 0000000..18b52d6
--- /dev/null
+++ b/src/images/SkImageDecoder.cpp
@@ -0,0 +1,190 @@
+/* libs/graphics/images/SkImageDecoder.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkImageDecoder.h"
+#include "SkBitmap.h"
+#include "SkPixelRef.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::decode(SkStream* stream, SkBitmap* bm,
+ SkBitmap::Config pref, Mode mode) {
+ SkBitmap tmp;
+
+ // we reset this to false before calling onDecode
+ fShouldCancelDecode = false;
+
+ // pass a temporary bitmap, so that if we return false, we are assured of
+ // leaving the caller's bitmap untouched.
+ if (this->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;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool SkImageDecoder::DecodeFile(const char file[], SkBitmap* bm,
+ SkBitmap::Config pref, Mode mode) {
+ SkASSERT(file);
+ SkASSERT(bm);
+
+ SkFILEStream stream(file);
+ if (stream.isValid()) {
+ if (SkImageDecoder::DecodeStream(&stream, bm, pref, mode)) {
+ bm->pixelRef()->setURI(file);
+ }
+ return true;
+ }
+ return false;
+}
+
+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);
+
+ bool success = false;
+ SkImageDecoder* codec = SkImageDecoder::Factory(stream);
+
+ if (NULL != codec) {
+ success = codec->decode(stream, bm, pref, mode);
+ delete codec;
+ }
+ return success;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#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);
+}
+
+bool SkImageEncoder::EncodeFile(const char file[], const SkBitmap& bm, Type t,
+ int quality) {
+ SkAutoTDelete<SkImageEncoder> enc(SkImageEncoder::Create(t));
+ return enc.get() && enc.get()->encodeFile(file, bm, quality);
+}
+
+bool SkImageEncoder::EncodeStream(SkWStream* stream, const SkBitmap& bm, Type t,
+ int quality) {
+ SkAutoTDelete<SkImageEncoder> enc(SkImageEncoder::Create(t));
+ return enc.get() && enc.get()->encodeStream(stream, bm, quality);
+}
+
+#endif
+
diff --git a/src/images/SkImageDecoder_fpdfemb.cpp b/src/images/SkImageDecoder_fpdfemb.cpp
new file mode 100644
index 0000000..7f37e3d
--- /dev/null
+++ b/src/images/SkImageDecoder_fpdfemb.cpp
@@ -0,0 +1,236 @@
+/*
+ * Copyright 2007, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SkImageDecoder.h"
+#include "SkScaledBitmapSampler.h"
+#include "SkStream.h"
+#include "SkColorPriv.h"
+#include "SkTDArray.h"
+
+#include "fpdfemb.h"
+
+class SkFPDFEMBImageDecoder : public SkImageDecoder {
+public:
+ SkFPDFEMBImageDecoder() {}
+
+ virtual Format getFormat() const {
+ return kBMP_Format;
+ }
+
+protected:
+ virtual bool onDecode(SkStream* stream, SkBitmap* bm,
+ SkBitmap::Config pref, Mode mode);
+
+private:
+ bool render(FPDFEMB_PAGE page, const FPDFEMB_RECT& bounds, SkBitmap* bm,
+ SkBitmap::Config prefConfig, SkImageDecoder::Mode mode);
+};
+
+SkImageDecoder* SkImageDecoder_FPDFEMB_Factory(SkStream*);
+SkImageDecoder* SkImageDecoder_FPDFEMB_Factory(SkStream* stream) {
+ static const char kPDFSig[] = { '%', 'P', 'D', 'F' };
+
+ size_t len = stream->getLength();
+ char buffer[sizeof(kPDFSig)];
+
+ SkDebugf("---- SkImageDecoder_FPDFEMB_Factory len=%d\n", len);
+
+ if (len != 12683) { return NULL; }
+
+ if (len > sizeof(kPDFSig) &&
+ stream->read(buffer, sizeof(kPDFSig)) == sizeof(kPDFSig) &&
+ !memcmp(buffer, kPDFSig, sizeof(kPDFSig))) {
+ return SkNEW(SkFPDFEMBImageDecoder);
+ }
+ return NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+extern "C" {
+ static void* pdf_alloc(FPDFEMB_MEMMGR* pMgr, unsigned int size) {
+ void* addr = sk_malloc_throw(size);
+ // SkDebugf("---- pdf_alloc %d %p\n", size, addr);
+ return addr;
+ }
+
+ static void* pdf_alloc_nl(FPDFEMB_MEMMGR* pMgr, unsigned int size) {
+ void* addr = sk_malloc_flags(size, 0);
+ // SkDebugf("---- pdf_alloc_nl %d %p\n", size, addr);
+ return addr;
+ }
+
+ static void* pdf_realloc(FPDFEMB_MEMMGR*, void* addr, unsigned int size) {
+ void* newaddr = sk_realloc_throw(addr, size);
+ // SkDebugf("---- pdf_realloc %p %d %p\n", addr, size, newaddr);
+ return newaddr;
+ }
+
+ static void pdf_free(FPDFEMB_MEMMGR* pMgr, void* pointer) {
+ // SkDebugf("---- pdf_free %p\n", pointer);
+ sk_free(pointer);
+ }
+
+ void FX_OUTPUT_LOG_FUNC(const char* format, ...) {
+ SkDebugf("---- LOG_FUNC %s\n", format);
+ }
+
+ static unsigned int file_getsize(FPDFEMB_FILE_ACCESS* file) {
+ SkStream* stream = (SkStream*)file->user;
+ return stream->getLength();
+ }
+
+ static FPDFEMB_RESULT file_readblock(FPDFEMB_FILE_ACCESS* file, void* dst,
+ unsigned int offset, unsigned int size) {
+ SkStream* stream = (SkStream*)file->user;
+// SkDebugf("---- readblock %p %p %d %d\n", stream, dst, offset, size);
+ if (!stream->rewind()) {
+ SkDebugf("---- rewind failed\n");
+ return FPDFERR_ERROR;
+ }
+ if (stream->skip(offset) != offset) {
+ SkDebugf("---- skip failed\n");
+ return FPDFERR_ERROR;
+ }
+ if (stream->read(dst, size) != size) {
+ SkDebugf("---- read failed\n");
+ return FPDFERR_ERROR;
+ }
+ return FPDFERR_SUCCESS;
+ }
+
+ static void pdf_oom_handler(void* memory, int size) {
+ SkDebugf("======== pdf OOM %p %d\n", memory, size);
+ }
+}
+
+static inline int PDF2Pixels(int x) { return x / 100; }
+static inline SkScalar PDF2Scalar(int x) {
+ return SkScalarMulDiv(SK_Scalar1, x, 100);
+}
+
+bool SkFPDFEMBImageDecoder::render(FPDFEMB_PAGE page, const FPDFEMB_RECT& bounds, SkBitmap* bm,
+ SkBitmap::Config prefConfig, SkImageDecoder::Mode mode) {
+ int width = PDF2Pixels(bounds.right - bounds.left);
+ int height = PDF2Pixels(bounds.top - bounds.bottom);
+
+ SkDebugf("----- bitmap size [%d %d], mode=%d\n", width, height, mode);
+ bm->setConfig(SkBitmap::kARGB_8888_Config, width, height);
+ if (SkImageDecoder::kDecodeBounds_Mode == mode) {
+ return true;
+ }
+
+ // USE THE CODEC TO ALLOCATE THE PIXELS!!!!
+ if (!this->allocPixelRef(bm, NULL)) {
+ SkDebugf("----- failed to alloc pixels\n");
+ return false;
+ }
+
+ bm->eraseColor(0);
+
+ FPDFEMB_RESULT result;
+ FPDFEMB_BITMAP dib;
+
+ result = FPDFEMB_CreateDIB(width, height, FPDFDIB_BGRA, bm->getPixels(),
+ bm->rowBytes(), &dib);
+ SkDebugf("---- createdib %d\n", result);
+
+ result = FPDFEMB_StartRender(dib, page, 0, 0, width, height, 0, 0, NULL, NULL);
+ SkDebugf("---- render %d\n", result);
+
+ result = FPDFEMB_DestroyDIB(dib);
+ SkDebugf("---- destroydib %d\n", result);
+
+ SkPMColor* dst = bm->getAddr32(0, 0);
+ const uint8_t* src = (uint8_t*)dst;
+ int n = bm->getSize() >> 2;
+ for (int i = 0; i < n; i++) {
+ int b = *src++;
+ int g = *src++;
+ int r = *src++;
+ int a = *src++;
+ *dst++ = SkPackARGB32(a, r, g, b);
+ }
+
+ return true;
+}
+
+#define USE_FIXED_MEM (4 * 1024 * 1024)
+
+bool SkFPDFEMBImageDecoder::onDecode(SkStream* stream, SkBitmap* bm,
+ SkBitmap::Config prefConfig, Mode mode) {
+
+ FPDFEMB_RESULT result;
+#ifdef USE_FIXED_MEM
+ SkAutoMalloc storage(USE_FIXED_MEM);
+ result = FPDFEMB_InitFixedMemory(storage.get(), USE_FIXED_MEM,
+ pdf_oom_handler);
+#else
+ FPDFEMB_MEMMGR memmgr;
+ memmgr.Alloc = pdf_alloc;
+ memmgr.AllocNL = pdf_alloc_nl;
+ memmgr.Realloc = pdf_realloc;
+ memmgr.Free = pdf_free;
+
+ result = FPDFEMB_Init(&memmgr);
+#endif
+ SkDebugf("----- SkImageDecoder_FPDFEMB_Factory init %d, streamLen = %d\n", result, stream->getLength());
+
+ FPDFEMB_FILE_ACCESS file;
+ file.GetSize = file_getsize;
+ file.ReadBlock = file_readblock;
+ file.user = stream;
+
+ FPDFEMB_DOCUMENT document;
+ result = FPDFEMB_StartLoadDocument(&file, NULL, &document, NULL);
+ SkDebugf("----- SkImageDecoder_FPDFEMB_Factory open %d %p\n", result, document);
+
+ int pageCount = FPDFEMB_GetPageCount(document);
+ SkDebugf("----- SkImageDecoder_FPDFEMB_Factory pageCount %d\n", pageCount);
+
+ if (pageCount > 0) {
+ FPDFEMB_PAGE page;
+ result = FPDFEMB_LoadPage(document, 0, &page);
+ SkDebugf("----- SkImageDecoder_FPDFEMB_Factory load page %d\n", result);
+
+ int width, height;
+ result = FPDFEMB_GetPageSize(page, &width, &height);
+ SkDebugf("----- SkImageDecoder_FPDFEMB_Factory page size %d [%d %d]\n", result, width, height);
+
+ FPDFEMB_RECT rect;
+ result = FPDFEMB_GetPageBBox(page, &rect);
+ SkDebugf("----- SkImageDecoder_FPDFEMB_Factory page rect %d [%d %d %d %d]\n", result,
+ rect.left, rect.top, rect.right, rect.bottom);
+
+ SkDebugf("----- SkImageDecoder_FPDFEMB_Factory begin page parse...\n");
+ result = FPDFEMB_StartParse(page, false, NULL);
+ SkDebugf("----- SkImageDecoder_FPDFEMB_Factory page parse %d\n", result);
+
+ if (0 == result) {
+ this->render(page, rect, bm, prefConfig, mode);
+ }
+
+ result = FPDFEMB_ClosePage(page);
+ SkDebugf("----- SkImageDecoder_FPDFEMB_Factory close page %d\n", result);
+ }
+
+ result = FPDFEMB_CloseDocument(document);
+ SkDebugf("----- SkImageDecoder_FPDFEMB_Factory close %d\n", result);
+
+ // FPDFEMB_Exit();
+
+ return true;
+}
diff --git a/src/images/SkImageDecoder_libbmp.cpp b/src/images/SkImageDecoder_libbmp.cpp
new file mode 100644
index 0000000..32a7a6d
--- /dev/null
+++ b/src/images/SkImageDecoder_libbmp.cpp
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2007, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "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/src/images/SkImageDecoder_libgif.cpp b/src/images/SkImageDecoder_libgif.cpp
new file mode 100644
index 0000000..519366a
--- /dev/null
+++ b/src/images/SkImageDecoder_libgif.cpp
@@ -0,0 +1,340 @@
+/* libs/graphics/images/SkImageDecoder_libgif.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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->Image.ColorMap;
+ if (NULL == cmap) {
+ cmap = gif->SColorMap;
+ }
+ // 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;
+}
+
+static bool error_return(GifFileType* gif, const SkBitmap& bm,
+ const char msg[]) {
+#if 0
+ SkDebugf("libgif error <%s> bitmap [%d %d] pixels %p colortable %p\n",
+ msg, bm.width(), bm.height(), bm.getPixels(), bm.getColorTable());
+#endif
+ return false;
+}
+
+bool SkGIFImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* bm,
+ SkBitmap::Config prefConfig, Mode mode) {
+ GifFileType* gif = DGifOpen(sk_stream, DecodeCallBackProc);
+ if (NULL == gif) {
+ return error_return(gif, *bm, "DGifOpen");
+ }
+
+ 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 error_return(gif, *bm, "DGifGetRecordType");
+ }
+
+ switch (recType) {
+ case IMAGE_DESC_RECORD_TYPE: {
+ if (DGifGetImageDesc(gif) == GIF_ERROR) {
+ return error_return(gif, *bm, "IMAGE_DESC_RECORD_TYPE");
+ }
+
+ if (gif->ImageCount < 1) { // sanity check
+ return error_return(gif, *bm, "ImageCount < 1");
+ }
+
+ width = gif->SWidth;
+ height = gif->SHeight;
+ if (width <= 0 || height <= 0 ||
+ !this->chooseFromOneChoice(SkBitmap::kIndex8_Config,
+ width, height)) {
+ return error_return(gif, *bm, "chooseFromOneChoice");
+ }
+
+ 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 error_return(gif, *bm, "TopLeft");
+ }
+
+ // now we decode the colortable
+ int colorCount = 0;
+ {
+ const ColorMapObject* cmap = find_colormap(gif);
+ if (NULL == cmap) {
+ return error_return(gif, *bm, "null cmap");
+ }
+
+ 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 error_return(gif, *bm, "allocPixelRef");
+ }
+ }
+
+ 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 error_return(gif, *bm, "non-pos inner width/height");
+ }
+
+ // 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 error_return(gif, *bm, "interlace DGifGetLine");
+ }
+ iter.next();
+ }
+ }
+ else
+ {
+ // easy, non-interlace case
+ for (int y = 0; y < innerHeight; y++) {
+ if (DGifGetLine(gif, scanline, innerWidth) == GIF_ERROR) {
+ return error_return(gif, *bm, "DGifGetLine");
+ }
+ scanline += rowBytes;
+ }
+ }
+ goto DONE;
+ } break;
+
+ case EXTENSION_RECORD_TYPE:
+ if (DGifGetExtension(gif, &temp_save.Function,
+ &extData) == GIF_ERROR) {
+ return error_return(gif, *bm, "DGifGetExtension");
+ }
+
+ while (extData != NULL) {
+ /* Create an extension block with our data */
+ if (AddExtensionBlock(&temp_save, extData[0],
+ &extData[1]) == GIF_ERROR) {
+ return error_return(gif, *bm, "AddExtensionBlock");
+ }
+ if (DGifGetExtensionNext(gif, &extData) == GIF_ERROR) {
+ return error_return(gif, *bm, "DGifGetExtensionNext");
+ }
+ 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/src/images/SkImageDecoder_libico.cpp b/src/images/SkImageDecoder_libico.cpp
new file mode 100644
index 0000000..b179a6b
--- /dev/null
+++ b/src/images/SkImageDecoder_libico.cpp
@@ -0,0 +1,388 @@
+/* libs/graphics/images/SkImageDecoder_libico.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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);
+
+
+static int calculateRowBytesFor8888(int w, int bitCount)
+{
+ // Default rowBytes is w << 2 for kARGB_8888
+ // In the case of a 4 bit image with an odd width, we need to add some
+ // so we can go off the end of the drawn bitmap.
+ // Add 4 to ensure that it is still a multiple of 4.
+ if (4 == bitCount && (w & 0x1)) {
+ return (w + 1) << 2;
+ }
+ // Otherwise return 0, which will allow it to be calculated automatically.
+ return 0;
+}
+
+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
+
+ bm->setConfig(SkBitmap::kARGB_8888_Config, w, h, calculateRowBytesFor8888(w, bitCount));
+
+ 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;
+ // Read all of the bits in this byte.
+ int i = x + 8;
+ // Pin to the width so we do not write outside the bounds of
+ // our color table.
+ i = i > w ? w : i;
+ // While loop to check all 8 bits individually.
+ 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/src/images/SkImageDecoder_libjpeg.cpp b/src/images/SkImageDecoder_libjpeg.cpp
new file mode 100644
index 0000000..492de23
--- /dev/null
+++ b/src/images/SkImageDecoder_libjpeg.cpp
@@ -0,0 +1,811 @@
+/*
+ * Copyright 2007, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "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"
+ #include "jerror.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) {
+ static const char gHeader[] = { 0xFF, 0xD8, 0xFF };
+ static const size_t HEADER_SIZE = sizeof(gHeader);
+
+ char buffer[HEADER_SIZE];
+ size_t len = stream->read(buffer, HEADER_SIZE);
+
+ if (len != HEADER_SIZE) {
+ return NULL; // can't read enough
+ }
+
+ if (memcmp(buffer, gHeader, HEADER_SIZE)) {
+ return NULL;
+ }
+
+ 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, SkImageDecoder* decoder);
+
+ SkStream* fStream;
+ const void* fMemoryBase;
+ size_t fMemoryBaseSize;
+ SkImageDecoder* fDecoder;
+ 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;
+ if (src->fDecoder != NULL && src->fDecoder->shouldCancelDecode()) {
+ return FALSE;
+ }
+ 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) {
+ 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 bytesToSkip = num_bytes - src->bytes_in_buffer;
+
+ // check if the skip amount exceeds the current buffer
+ if (bytesToSkip > 0) {
+ size_t bytes = src->fStream->skip(bytesToSkip);
+ if (bytes != (size_t)bytesToSkip) {
+// SkDebugf("xxxxxxxxxxxxxx failure to skip request %d actual %d\n", bytesToSkip, bytes);
+ 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()) {
+ SkDebugf("xxxxxxxxxxxxxx failure to rewind\n");
+ cinfo->err->error_exit((j_common_ptr)cinfo);
+ return FALSE;
+ }
+ src->next_input_byte = (const JOCTET*)src->fBuffer;
+ src->bytes_in_buffer = 0;
+ return TRUE;
+}
+
+static void sk_term_source(j_decompress_ptr /*cinfo*/) {}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void skmem_init_source(j_decompress_ptr cinfo) {
+ sk_source_mgr* src = (sk_source_mgr*)cinfo->src;
+ src->next_input_byte = (const JOCTET*)src->fMemoryBase;
+ src->bytes_in_buffer = src->fMemoryBaseSize;
+}
+
+static boolean skmem_fill_input_buffer(j_decompress_ptr cinfo) {
+ SkDebugf("xxxxxxxxxxxxxx skmem_fill_input_buffer called\n");
+ return FALSE;
+}
+
+static void skmem_skip_input_data(j_decompress_ptr cinfo, long num_bytes) {
+ sk_source_mgr* src = (sk_source_mgr*)cinfo->src;
+// SkDebugf("xxxxxxxxxxxxxx skmem_skip_input_data called %d\n", num_bytes);
+ src->next_input_byte = (const JOCTET*)((const char*)src->next_input_byte + num_bytes);
+ src->bytes_in_buffer -= num_bytes;
+}
+
+static boolean skmem_resync_to_restart(j_decompress_ptr cinfo, int desired) {
+ SkDebugf("xxxxxxxxxxxxxx skmem_resync_to_restart called\n");
+ return TRUE;
+}
+
+static void skmem_term_source(j_decompress_ptr /*cinfo*/) {}
+
+///////////////////////////////////////////////////////////////////////////////
+
+sk_source_mgr::sk_source_mgr(SkStream* stream, SkImageDecoder* decoder) : fStream(stream) {
+ fDecoder = decoder;
+ const void* baseAddr = stream->getMemoryBase();
+ if (baseAddr && false) {
+ fMemoryBase = baseAddr;
+ fMemoryBaseSize = stream->getLength();
+
+ init_source = skmem_init_source;
+ fill_input_buffer = skmem_fill_input_buffer;
+ skip_input_data = skmem_skip_input_data;
+ resync_to_restart = skmem_resync_to_restart;
+ term_source = skmem_term_source;
+ } else {
+ fMemoryBase = NULL;
+ fMemoryBaseSize = 0;
+
+ 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;
+ }
+// SkDebugf("**************** use memorybase %p %d\n", fMemoryBase, fMemoryBaseSize);
+}
+
+#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 bool 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);
+ if (row_count != 1) {
+ return false;
+ }
+ }
+ return true;
+}
+
+// This guy exists just to aid in debugging, as it allows debuggers to just
+// set a break-point in one place to see all error exists.
+static bool return_false(const jpeg_decompress_struct& cinfo,
+ const SkBitmap& bm, const char msg[]) {
+#if 0
+ SkDebugf("libjpeg error %d <%s> from %s [%d %d]", cinfo.err->msg_code,
+ cinfo.err->jpeg_message_table[cinfo.err->msg_code], msg,
+ bm.width(), bm.height());
+#endif
+ return false; // must always return false
+}
+
+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, this);
+
+ 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 return_false(cinfo, *bm, "setjmp");
+ }
+
+ jpeg_create_decompress(&cinfo);
+ autoClean.set(&cinfo);
+
+ //jpeg_stdio_src(&cinfo, file);
+ cinfo.src = &sk_stream;
+
+ int status = jpeg_read_header(&cinfo, true);
+ if (status != JPEG_HEADER_OK) {
+ return return_false(cinfo, *bm, "read_header");
+ }
+
+ /* 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;
+
+ /* this gives about 30% performance improvement. In theory it may
+ reduce the visual quality, in practice I'm not seeing a difference
+ */
+ cinfo.do_fancy_upsampling = 0;
+
+ /* this gives another few percents */
+ cinfo.do_block_smoothing = 0;
+
+ /* default format is RGB */
+ cinfo.out_color_space = JCS_RGB;
+
+ 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;
+ }
+
+#ifdef ANDROID_RGB
+ cinfo.dither_mode = JDITHER_NONE;
+ if (config == SkBitmap::kARGB_8888_Config) {
+ cinfo.out_color_space = JCS_RGBA_8888;
+ } else if (config == SkBitmap::kRGB_565_Config) {
+ if (sampleSize == 1) {
+ // SkScaledBitmapSampler can't handle RGB_565 yet,
+ // so don't even try.
+ cinfo.out_color_space = JCS_RGB_565;
+ if (this->getDitherImage()) {
+ cinfo.dither_mode = JDITHER_ORDERED;
+ }
+ }
+ }
+#endif
+
+ /* 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.
+ */
+ if (!jpeg_start_decompress(&cinfo)) {
+ return return_false(cinfo, *bm, "start_decompress");
+ }
+
+ /* 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;
+
+
+ // should we allow the Chooser (if present) to pick a config for us???
+ if (!this->chooseFromOneChoice(config, cinfo.output_width,
+ cinfo.output_height)) {
+ return return_false(cinfo, *bm, "chooseFromOneChoice");
+ }
+
+#ifdef ANDROID_RGB
+ /* short-circuit the SkScaledBitmapSampler when possible, as this gives
+ a significant performance boost.
+ */
+ if (sampleSize == 1 &&
+ ((config == SkBitmap::kARGB_8888_Config &&
+ cinfo.out_color_space == JCS_RGBA_8888) ||
+ (config == SkBitmap::kRGB_565_Config &&
+ cinfo.out_color_space == JCS_RGB_565)))
+ {
+ bm->setConfig(config, cinfo.output_width, cinfo.output_height);
+ bm->setIsOpaque(true);
+ if (SkImageDecoder::kDecodeBounds_Mode == mode) {
+ return true;
+ }
+ if (!this->allocPixelRef(bm, NULL)) {
+ return return_false(cinfo, *bm, "allocPixelRef");
+ }
+ SkAutoLockPixels alp(*bm);
+ JSAMPLE* rowptr = (JSAMPLE*)bm->getPixels();
+ INT32 const bpr = bm->rowBytes();
+
+ while (cinfo.output_scanline < cinfo.output_height) {
+ int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);
+ // if row_count == 0, then we didn't get a scanline, so abort.
+ // if we supported partial images, we might return true in this case
+ if (0 == row_count) {
+ return return_false(cinfo, *bm, "read_scanlines");
+ }
+ if (this->shouldCancelDecode()) {
+ return return_false(cinfo, *bm, "shouldCancelDecode");
+ }
+ rowptr += bpr;
+ }
+ jpeg_finish_decompress(&cinfo);
+ return true;
+ }
+#endif
+
+ // check for supported formats
+ SkScaledBitmapSampler::SrcConfig sc;
+ if (3 == cinfo.out_color_components && JCS_RGB == cinfo.out_color_space) {
+ sc = SkScaledBitmapSampler::kRGB;
+#ifdef ANDROID_RGB
+ } else if (JCS_RGBA_8888 == cinfo.out_color_space) {
+ sc = SkScaledBitmapSampler::kRGBX;
+ //} else if (JCS_RGB_565 == cinfo.out_color_space) {
+ // sc = SkScaledBitmapSampler::kRGB_565;
+#endif
+ } else if (1 == cinfo.out_color_components &&
+ JCS_GRAYSCALE == cinfo.out_color_space) {
+ sc = SkScaledBitmapSampler::kGray;
+ } else {
+ return return_false(cinfo, *bm, "jpeg colorspace");
+ }
+
+ 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 return_false(cinfo, *bm, "allocPixelRef");
+ }
+
+ SkAutoLockPixels alp(*bm);
+ if (!sampler.begin(bm, sc, this->getDitherImage())) {
+ return return_false(cinfo, *bm, "sampler.begin");
+ }
+
+ uint8_t* srcRow = (uint8_t*)srcStorage.alloc(cinfo.output_width * 4);
+
+ // Possibly skip initial rows [sampler.srcY0]
+ if (!skip_src_rows(&cinfo, srcRow, sampler.srcY0())) {
+ return return_false(cinfo, *bm, "skip rows");
+ }
+
+ // now loop through scanlines until y == bm->height() - 1
+ for (int y = 0;; y++) {
+ JSAMPLE* rowptr = (JSAMPLE*)srcRow;
+ int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);
+ if (0 == row_count) {
+ return return_false(cinfo, *bm, "read_scanlines");
+ }
+ if (this->shouldCancelDecode()) {
+ return return_false(cinfo, *bm, "shouldCancelDecode");
+ }
+
+ sampler.next(srcRow);
+ if (bm->height() - 1 == y) {
+ // we're done
+ break;
+ }
+
+ if (!skip_src_rows(&cinfo, srcRow, sampler.srcDY() - 1)) {
+ return return_false(cinfo, *bm, "skip rows");
+ }
+ }
+
+ // we formally skip the rest, so we don't get a complaint from libjpeg
+ if (!skip_src_rows(&cinfo, srcRow,
+ cinfo.output_height - cinfo.output_scanline)) {
+ return return_false(cinfo, *bm, "skip rows");
+ }
+ jpeg_finish_decompress(&cinfo);
+
+// SkDebugf("------------------- bm2 size %d [%d %d] %d\n", bm->getSize(), bm->width(), bm->height(), bm->config());
+ 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)) {
+ ERREXIT(cinfo, JERR_FILE_WRITE);
+ return false;
+ }
+
+ 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)) {
+ ERREXIT(cinfo, JERR_FILE_WRITE);
+ return;
+ }
+ }
+ 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 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);
+
+ // allocate these before set call setjmp
+ SkAutoMalloc oneRow;
+ SkAutoLockColors ctLocker;
+
+ 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();
+ uint8_t* oneRowP = (uint8_t*)oneRow.alloc(width * 3);
+
+ const SkPMColor* colors = ctLocker.lockColors(bm);
+ 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/src/images/SkImageDecoder_libpng.cpp b/src/images/SkImageDecoder_libpng.cpp
new file mode 100644
index 0000000..862ebf1
--- /dev/null
+++ b/src/images/SkImageDecoder_libpng.cpp
@@ -0,0 +1,795 @@
+/* libs/graphics/images/SkImageDecoder_libpng.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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) {
+#if 0
+ SkDebugf("------ png error %s\n", msg);
+#endif
+ 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;
+}
+
+static bool substituteTranspColor(SkBitmap* bm, SkPMColor match) {
+ SkASSERT(bm->config() == SkBitmap::kARGB_8888_Config);
+
+ bool reallyHasAlpha = false;
+
+ for (int y = bm->height() - 1; y >= 0; --y) {
+ SkPMColor* p = bm->getAddr32(0, y);
+ for (int x = bm->width() - 1; x >= 0; --x) {
+ if (match == *p) {
+ *p = 0;
+ reallyHasAlpha = true;
+ }
+ p += 1;
+ }
+ }
+ return reallyHasAlpha;
+}
+
+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);
+
+ /* 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);
+ }
+
+ SkBitmap::Config config;
+ bool hasAlpha = false;
+ bool doDither = this->getDitherImage();
+ SkPMColor theTranspColor = 0; // 0 tells us not to try to match
+
+ // 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 transpColor = NULL;
+ int numTransp = 0;
+
+ png_get_tRNS(png_ptr, info_ptr, NULL, &numTransp, &transpColor);
+
+ bool valid = png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS);
+
+ if (valid && numTransp == 1 && transpColor != NULL) {
+ /* Compute our transparent color, which we'll match against later.
+ We don't really handle 16bit components properly here, since we
+ do our compare *after* the values have been knocked down to 8bit
+ which means we will find more matches than we should. The real
+ fix seems to be to see the actual 16bit components, do the
+ compare, and then knock it down to 8bits ourselves.
+ */
+ if (color_type & PNG_COLOR_MASK_COLOR) {
+ if (16 == bit_depth) {
+ theTranspColor = SkPackARGB32(0xFF, transpColor->red >> 8,
+ transpColor->green >> 8, transpColor->blue >> 8);
+ } else {
+ theTranspColor = SkPackARGB32(0xFF, transpColor->red,
+ transpColor->green, transpColor->blue);
+ }
+ } else { // gray
+ if (16 == bit_depth) {
+ theTranspColor = SkPackARGB32(0xFF, transpColor->gray >> 8,
+ transpColor->gray >> 8, transpColor->gray >> 8);
+ } else {
+ theTranspColor = SkPackARGB32(0xFF, transpColor->gray,
+ transpColor->gray, transpColor->gray);
+ }
+ }
+ }
+
+ if (valid ||
+ 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
+
+ // 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)) {
+ 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) {
+#if 0
+ SkDEBUGF(("Image doesn't really have alpha [%d %d]\n",
+ origWidth, origHeight));
+#endif
+ }
+ }
+
+ /* read rest of file, and get additional chunks in info_ptr - REQUIRED */
+ png_read_end(png_ptr, info_ptr);
+
+ if (0 != theTranspColor) {
+ reallyHasAlpha |= substituteTranspColor(decodedBitmap, theTranspColor);
+ }
+ 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 void transform_scanline_index8(const char* SK_RESTRICT src, int width,
+ char* SK_RESTRICT dst) {
+ memcpy(dst, src, width);
+}
+
+static transform_scanline_proc choose_proc(SkBitmap::Config config,
+ bool hasAlpha) {
+ // we don't care about search on alpha if we're kIndex8, since only the
+ // colortable packing cares about that distinction, not the pixels
+ if (SkBitmap::kIndex8_Config == config) {
+ hasAlpha = false; // we store false in the table entries for kIndex8
+ }
+
+ 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 },
+ { SkBitmap::kIndex8_Config, false, transform_scanline_index8 },
+ };
+
+ 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;
+}
+
+// return the minimum legal bitdepth (by png standards) for this many colortable
+// entries. SkBitmap always stores in 8bits per pixel, but for colorcount <= 16,
+// we can use fewer bits per in png
+static int computeBitDepth(int colorCount) {
+#if 0
+ int bits = SkNextLog2(colorCount);
+ SkASSERT(bits >= 1 && bits <= 8);
+ // now we need bits itself to be a power of 2 (e.g. 1, 2, 4, 8)
+ return SkNextPow2(bits);
+#else
+ // for the moment, we don't know how to pack bitdepth < 8
+ return 8;
+#endif
+}
+
+/* Pack palette[] with the corresponding colors, and if hasAlpha is true, also
+ pack trans[] and return the number of trans[] entries written. If hasAlpha
+ is false, the return value will always be 0.
+
+ Note: this routine takes care of unpremultiplying the RGB values when we
+ have alpha in the colortable, since png doesn't support premul colors
+*/
+static int pack_palette(SkColorTable* ctable, png_color* SK_RESTRICT palette,
+ png_byte* SK_RESTRICT trans, bool hasAlpha) {
+ SkAutoLockColors alc(ctable);
+ const SkPMColor* SK_RESTRICT colors = alc.colors();
+ const int ctCount = ctable->count();
+ int i, num_trans = 0;
+
+ if (hasAlpha) {
+ /* first see if we have some number of fully opaque at the end of the
+ ctable. PNG allows num_trans < num_palette, but all of the trans
+ entries must come first in the palette. If I was smarter, I'd
+ reorder the indices and ctable so that all non-opaque colors came
+ first in the palette. But, since that would slow down the encode,
+ I'm leaving the indices and ctable order as is, and just looking
+ at the tail of the ctable for opaqueness.
+ */
+ num_trans = ctCount;
+ for (i = ctCount - 1; i >= 0; --i) {
+ if (SkGetPackedA32(colors[i]) != 0xFF) {
+ break;
+ }
+ num_trans -= 1;
+ }
+
+ const SkUnPreMultiply::Scale* SK_RESTRICT table =
+ SkUnPreMultiply::GetScaleTable();
+
+ for (i = 0; i < num_trans; i++) {
+ const SkPMColor c = *colors++;
+ const unsigned a = SkGetPackedA32(c);
+ const SkUnPreMultiply::Scale s = table[a];
+ trans[i] = a;
+ palette[i].red = SkUnPreMultiply::ApplyScale(s, SkGetPackedR32(c));
+ palette[i].green = SkUnPreMultiply::ApplyScale(s,SkGetPackedG32(c));
+ palette[i].blue = SkUnPreMultiply::ApplyScale(s, SkGetPackedB32(c));
+ }
+ // now fall out of this if-block to use common code for the trailing
+ // opaque entries
+ }
+
+ // these (remaining) entries are opaque
+ for (i = num_trans; i < ctCount; i++) {
+ SkPMColor c = *colors++;
+ palette[i].red = SkGetPackedR32(c);
+ palette[i].green = SkGetPackedG32(c);
+ palette[i].blue = SkGetPackedB32(c);
+ }
+ return num_trans;
+}
+
+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();
+ int colorType = PNG_COLOR_MASK_COLOR;
+ int bitDepth = 8; // default for color
+ png_color_8 sig_bit;
+
+ switch (config) {
+ case SkBitmap::kIndex8_Config:
+ colorType |= PNG_COLOR_MASK_PALETTE;
+ // fall through to the ARGB_8888 case
+ case SkBitmap::kARGB_8888_Config:
+ sig_bit.red = 8;
+ sig_bit.green = 8;
+ sig_bit.blue = 8;
+ sig_bit.alpha = 8;
+ break;
+ case SkBitmap::kARGB_4444_Config:
+ sig_bit.red = 4;
+ sig_bit.green = 4;
+ sig_bit.blue = 4;
+ sig_bit.alpha = 4;
+ break;
+ case SkBitmap::kRGB_565_Config:
+ sig_bit.red = 5;
+ sig_bit.green = 6;
+ sig_bit.blue = 5;
+ sig_bit.alpha = 0;
+ break;
+ default:
+ return false;
+ }
+
+ if (hasAlpha) {
+ // don't specify alpha if we're a palette, even if our ctable has alpha
+ if (!(colorType & PNG_COLOR_MASK_PALETTE)) {
+ colorType |= PNG_COLOR_MASK_ALPHA;
+ }
+ } else {
+ sig_bit.alpha = 0;
+ }
+
+ SkAutoLockPixels alp(bitmap);
+ // readyToDraw checks for pixels (and colortable if that is required)
+ if (!bitmap.readyToDraw()) {
+ return false;
+ }
+
+ // we must do this after we have locked the pixels
+ SkColorTable* ctable = bitmap.getColorTable();
+ if (NULL != ctable) {
+ if (ctable->count() == 0) {
+ return false;
+ }
+ // check if we can store in fewer than 8 bits
+ bitDepth = computeBitDepth(ctable->count());
+ }
+
+ 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(),
+ bitDepth, colorType,
+ PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
+ PNG_FILTER_TYPE_BASE);
+
+#if 0 // need to support this some day
+ /* 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);
+
+ /* 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/src/images/SkImageDecoder_libpvjpeg.cpp b/src/images/SkImageDecoder_libpvjpeg.cpp
new file mode 100644
index 0000000..9177741
--- /dev/null
+++ b/src/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/src/images/SkImageDecoder_wbmp.cpp b/src/images/SkImageDecoder_wbmp.cpp
new file mode 100644
index 0000000..9d188f6
--- /dev/null
+++ b/src/images/SkImageDecoder_wbmp.cpp
@@ -0,0 +1,167 @@
+/**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/images/SkImageRef.cpp b/src/images/SkImageRef.cpp
new file mode 100644
index 0000000..90c37b6
--- /dev/null
+++ b/src/images/SkImageRef.cpp
@@ -0,0 +1,165 @@
+#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->decode(stream, bitmap, config, mode);
+}
+
+bool SkImageRef::prepareBitmap(SkImageDecoder::Mode mode) {
+ SkASSERT(&gImageRefMutex == this->mutex());
+
+ if (fErrorInDecoding) {
+ return false;
+ }
+
+ /* As soon as we really know our config, we record it, so that on
+ subsequent calls to the codec, we are sure we will always get the same
+ result.
+ */
+ if (SkBitmap::kNo_Config != fBitmap.config()) {
+ fConfig = fBitmap.config();
+ }
+
+ 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", this->getURI());
+ } else {
+ SkDebugf("--- ImageRef: <%s> failed in codec for %d mode\n",
+ this->getURI(), 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/src/images/SkImageRefPool.cpp b/src/images/SkImageRefPool.cpp
new file mode 100644
index 0000000..e322507
--- /dev/null
+++ b/src/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->getURI(),
+ 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->getURI(),
+ 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->getURI());
+
+ ref = ref->fNext;
+ }
+#endif
+}
+
diff --git a/src/images/SkImageRefPool.h b/src/images/SkImageRefPool.h
new file mode 100644
index 0000000..b2eb7b3
--- /dev/null
+++ b/src/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/src/images/SkImageRef_GlobalPool.cpp b/src/images/SkImageRef_GlobalPool.cpp
new file mode 100644
index 0000000..1f0bc43
--- /dev/null
+++ b/src/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/src/images/SkMMapStream.cpp b/src/images/SkMMapStream.cpp
new file mode 100644
index 0000000..2aee945
--- /dev/null
+++ b/src/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/src/images/SkMovie.cpp b/src/images/SkMovie.cpp
new file mode 100644
index 0000000..7186ed5
--- /dev/null
+++ b/src/images/SkMovie.cpp
@@ -0,0 +1,101 @@
+#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::DecodeMemory(const void* data, size_t length) {
+ SkMemoryStream stream(data, length, false);
+ return SkMovie::DecodeStream(&stream);
+}
+
+SkMovie* SkMovie::DecodeFile(const char path[])
+{
+ SkMovie* movie = NULL;
+
+ SkFILEStream stream(path);
+ if (stream.isValid()) {
+ movie = SkMovie::DecodeStream(&stream);
+ }
+#ifdef SK_DEBUG
+ else {
+ SkDebugf("Movie file not found <%s>\n", path);
+ }
+#endif
+
+ return movie;
+}
+
diff --git a/src/images/SkMovie_gif.cpp b/src/images/SkMovie_gif.cpp
new file mode 100644
index 0000000..ca9c812
--- /dev/null
+++ b/src/images/SkMovie_gif.cpp
@@ -0,0 +1,224 @@
+/* libs/graphics/images/SkImageDecoder_libgif.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkMovie.h"
+#include "SkColor.h"
+#include "SkColorPriv.h"
+#include "SkStream.h"
+#include "SkTemplates.h"
+
+#include "gif_lib.h"
+
+class SkGIFMovie : public SkMovie {
+public:
+ 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_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) {
+ stream->rewind();
+ 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?
+ 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;
+ }
+
+ const int width = gif->SWidth;
+ const int height = gif->SHeight;
+ if (width <= 0 || height <= 0) {
+ return false;
+ }
+
+ SavedImage* gif_image = fCurrSavedImage;
+ SkBitmap::Config config = SkBitmap::kIndex8_Config;
+
+ SkColorTable* colorTable = SkNEW_ARGS(SkColorTable, (cmap->ColorCount));
+ SkAutoUnref aur(colorTable);
+
+ bm->setConfig(config, width, height, 0);
+ if (!bm->allocPixels(colorTable)) {
+ return false;
+ }
+
+ 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/src/images/SkPageFlipper.cpp b/src/images/SkPageFlipper.cpp
new file mode 100644
index 0000000..526ba09
--- /dev/null
+++ b/src/images/SkPageFlipper.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SkPageFlipper.h"
+
+SkPageFlipper::SkPageFlipper() {
+ fWidth = 0;
+ fHeight = 0;
+ fDirty0 = &fDirty0Storage;
+ fDirty1 = &fDirty1Storage;
+
+ fDirty0->setEmpty();
+ fDirty1->setEmpty();
+}
+
+SkPageFlipper::SkPageFlipper(int width, int height) {
+ fWidth = width;
+ fHeight = height;
+ fDirty0 = &fDirty0Storage;
+ fDirty1 = &fDirty1Storage;
+
+ fDirty0->setRect(0, 0, width, height);
+ fDirty1->setEmpty();
+}
+
+void SkPageFlipper::resize(int width, int height) {
+ fWidth = width;
+ fHeight = height;
+
+ // this is the opposite of the constructors
+ fDirty1->setRect(0, 0, width, height);
+ fDirty0->setEmpty();
+}
+
+void SkPageFlipper::inval() {
+ fDirty1->setRect(0, 0, fWidth, fHeight);
+}
+
+void SkPageFlipper::inval(const SkIRect& rect) {
+ SkIRect r;
+ r.set(0, 0, fWidth, fHeight);
+ if (r.intersect(rect)) {
+ fDirty1->op(r, SkRegion::kUnion_Op);
+ }
+}
+
+void SkPageFlipper::inval(const SkRegion& rgn) {
+ SkRegion r;
+ r.setRect(0, 0, fWidth, fHeight);
+ if (r.op(rgn, SkRegion::kIntersect_Op)) {
+ fDirty1->op(r, SkRegion::kUnion_Op);
+ }
+}
+
+void SkPageFlipper::inval(const SkRect& rect, bool antialias) {
+ SkIRect r;
+ rect.round(&r);
+ if (antialias) {
+ r.inset(-1, -1);
+ }
+ this->inval(r);
+}
+
+const SkRegion& SkPageFlipper::update(SkRegion* copyBits) {
+ // Copy over anything new from page0 that isn't dirty in page1
+ copyBits->op(*fDirty0, *fDirty1, SkRegion::kDifference_Op);
+ SkTSwap<SkRegion*>(fDirty0, fDirty1);
+ fDirty1->setEmpty();
+ return *fDirty0;
+}
+
+
diff --git a/src/images/SkScaledBitmapSampler.cpp b/src/images/SkScaledBitmapSampler.cpp
new file mode 100644
index 0000000..15f4432
--- /dev/null
+++ b/src/images/SkScaledBitmapSampler.cpp
@@ -0,0 +1,338 @@
+/*
+ * Copyright 2007, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "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/src/images/SkScaledBitmapSampler.h b/src/images/SkScaledBitmapSampler.h
new file mode 100644
index 0000000..0bb9924
--- /dev/null
+++ b/src/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/src/images/SkStream.cpp b/src/images/SkStream.cpp
new file mode 100644
index 0000000..b199a1b
--- /dev/null
+++ b/src/images/SkStream.cpp
@@ -0,0 +1,856 @@
+/* libs/graphics/images/SkStream.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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);
+ proxy->ref();
+ 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
+ proxy->ref();
+ 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()
+{
+ fProxy->unref();
+ 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 (0 == size) {
+ return 0;
+ }
+
+ // skip size bytes
+ if (NULL == buffer) {
+ size_t remaining = fBufferSize - fBufferOffset;
+ if (remaining >= size) {
+ fBufferOffset += size;
+ return size;
+ }
+ // if we get here, we are being asked to skip beyond our current buffer
+ // so reset our offset to force a read next time, and skip the diff
+ // in our proxy
+ fBufferOffset = fOrigBufferSize;
+ return remaining + fProxy->read(NULL, size - remaining);
+ }
+
+ 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"
+
+#ifdef SK_SUPPORT_UNITTEST
+#define MAX_SIZE (256 * 1024)
+
+static void random_fill(SkRandom& rand, void* buffer, size_t size) {
+ char* p = (char*)buffer;
+ char* stop = p + size;
+ while (p < stop) {
+ *p++ = (char)(rand.nextU() >> 8);
+ }
+}
+
+static void test_buffer() {
+ SkRandom rand;
+ SkAutoMalloc am(MAX_SIZE * 2);
+ char* storage = (char*)am.get();
+ char* storage2 = storage + MAX_SIZE;
+
+ random_fill(rand, storage, MAX_SIZE);
+
+ for (int sizeTimes = 0; sizeTimes < 100; sizeTimes++) {
+ int size = rand.nextU() % MAX_SIZE;
+ if (size == 0) {
+ size = MAX_SIZE;
+ }
+ for (int times = 0; times < 100; times++) {
+ int bufferSize = 1 + (rand.nextU() & 0xFFFF);
+ SkMemoryStream mstream(storage, size);
+ SkBufferStream bstream(&mstream, bufferSize);
+
+ int bytesRead = 0;
+ while (bytesRead < size) {
+ int s = 17 + (rand.nextU() & 0xFFFF);
+ int ss = bstream.read(storage2, s);
+ SkASSERT(ss > 0 && ss <= s);
+ SkASSERT(bytesRead + ss <= size);
+ SkASSERT(memcmp(storage + bytesRead, storage2, ss) == 0);
+ bytesRead += ss;
+ }
+ SkASSERT(bytesRead == size);
+ }
+ }
+}
+#endif
+
+void SkStream::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);
+ }
+ }
+ test_buffer();
+#endif
+}
+
+void SkWStream::UnitTest()
+{
+#ifdef SK_SUPPORT_UNITTEST
+ {
+ 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/src/images/bmpdecoderhelper.cpp b/src/images/bmpdecoderhelper.cpp
new file mode 100644
index 0000000..acabf44
--- /dev/null
+++ b/src/images/bmpdecoderhelper.cpp
@@ -0,0 +1,376 @@
+/*
+ * Copyright 2007, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+// 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/src/images/bmpdecoderhelper.h b/src/images/bmpdecoderhelper.h
new file mode 100644
index 0000000..07f0ae5
--- /dev/null
+++ b/src/images/bmpdecoderhelper.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2007, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef IMAGE_CODEC_BMPDECODERHELPER_H__
+#define IMAGE_CODEC_BMPDECODERHELPER_H__
+
+///////////////////////////////////////////////////////////////////////////////
+// this section is my current "glue" between google3 code and android.
+// will be fixed 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/src/images/fpdfemb_ext.h b/src/images/fpdfemb_ext.h
new file mode 100644
index 0000000..d82c4df
--- /dev/null
+++ b/src/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/src/ports/SkFontHost_FONTPATH.cpp b/src/ports/SkFontHost_FONTPATH.cpp
new file mode 100644
index 0000000..3cbccaf
--- /dev/null
+++ b/src/ports/SkFontHost_FONTPATH.cpp
@@ -0,0 +1,415 @@
+/* libs/graphics/ports/SkFontHost_android.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/ports/SkFontHost_FreeType.cpp b/src/ports/SkFontHost_FreeType.cpp
new file mode 100644
index 0000000..5855eec
--- /dev/null
+++ b/src/ports/SkFontHost_FreeType.cpp
@@ -0,0 +1,829 @@
+/* libs/graphics/ports/SkFontHost_FreeType.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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
+#ifdef FT_ADVANCES_H
+#include FT_ADVANCES_H
+#endif
+
+//#define ENABLE_GLYPH_SPEW // for tracing calls
+//#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));
+
+ bzero(&fFTStream, sizeof(fFTStream));
+ fFTStream.size = fSkStream->getLength();
+ 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
+
+ switch (fRec.fHints) {
+ case kNo_Hints:
+ flags |= FT_LOAD_NO_HINTING;
+ break;
+ case kSubpixel_Hints:
+ flags |= FT_LOAD_FORCE_AUTOHINT;
+ render_flags = FT_LOAD_TARGET_LIGHT;
+ break;
+ case kNormal_Hints:
+ flags |= FT_LOAD_FORCE_AUTOHINT;
+#ifdef ANDROID
+ /* Switch to light hinting (vertical only) to address some chars
+ that behaved poorly with NORMAL. In the future we could consider
+ making this choice exposed at runtime to the caller.
+ */
+ render_flags = FT_LOAD_TARGET_LIGHT;
+#endif
+ 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() {
+ /* In the off-chance that a font has been removed, we want to error out
+ right away, so call resolve just to be sure.
+
+ TODO: perhaps we can skip this, by walking the global font cache and
+ killing all of the contexts when we know that a given fontID is going
+ away...
+ */
+ 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;
+ }
+}
+
+static void set_glyph_metrics_on_error(SkGlyph* glyph) {
+ glyph->fRsbDelta = 0;
+ glyph->fLsbDelta = 0;
+ glyph->fWidth = 0;
+ glyph->fHeight = 0;
+ glyph->fTop = 0;
+ glyph->fLeft = 0;
+ glyph->fAdvanceX = 0;
+ glyph->fAdvanceY = 0;
+}
+
+void SkScalerContext_FreeType::generateAdvance(SkGlyph* glyph) {
+#ifdef FT_ADVANCES_H
+ /* unhinted and light hinted text have linearly scaled advances
+ * which are very cheap to compute with some font formats...
+ */
+ {
+ SkAutoMutexAcquire ac(gFTMutex);
+
+ if (this->setupSize()) {
+ set_glyph_metrics_on_error(glyph);
+ return;
+ }
+
+ FT_Error error;
+ FT_Fixed advance;
+
+ error = FT_Get_Advance( fFace, glyph->getGlyphID(fBaseGlyphCount),
+ fLoadGlyphFlags | FT_ADVANCE_FLAG_FAST_ONLY,
+ &advance );
+ if (0 == error) {
+ glyph->fRsbDelta = 0;
+ glyph->fLsbDelta = 0;
+ glyph->fAdvanceX = advance; // advance *2/3; //DEBUG
+ glyph->fAdvanceY = 0;
+ return;
+ }
+ }
+#endif /* FT_ADVANCES_H */
+ /* otherwise, we need to load/hint the glyph, which is slower */
+ this->generateMetrics(glyph);
+ 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:
+ set_glyph_metrics_on_error(glyph);
+ 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;
+ // negate dy since freetype-y-goes-up and skia-y-goes-down
+ 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;
+ // negate dy since freetype-y-goes-up and skia-y-goes-down
+ dy = -dy;
+ }
+ 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();
+}
+
+void SkScalerContext_FreeType::generateFontMetrics(SkPaint::FontMetrics* mx,
+ SkPaint::FontMetrics* my) {
+ if (NULL == mx && NULL == my) {
+ return;
+ }
+
+ SkAutoMutexAcquire ac(gFTMutex);
+
+ if (this->setupSize()) {
+ if (mx) {
+ bzero(mx, sizeof(SkPaint::FontMetrics));
+ }
+ if (my) {
+ bzero(my, 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/src/ports/SkFontHost_android.cpp b/src/ports/SkFontHost_android.cpp
new file mode 100644
index 0000000..0922e7b
--- /dev/null
+++ b/src/ports/SkFontHost_android.cpp
@@ -0,0 +1,637 @@
+/* libs/graphics/ports/SkFontHost_android.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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;
+ virtual const char* getUniqueString() const = 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*) {}
+ virtual const char* getUniqueString() const { return NULL; }
+
+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);
+ }
+ virtual const char* getUniqueString() const {
+ const char* str = strrchr(fPath.c_str(), '/');
+ if (str) {
+ str += 1; // skip the '/'
+ }
+ return str;
+ }
+
+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", "georgia", "baskerville",
+ "goudy", "fantasy", "cursive", "ITC Stone Serif", 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 },
+#ifdef NO_FALLBACK_FONT
+ { "DroidSans.ttf", gFBNames }
+#else
+ { "DroidSansFallback.ttf", gFBNames }
+#endif
+};
+
+#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);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkFontHost::Serialize(const SkTypeface* face, SkWStream* stream) {
+ const char* name = ((FamilyTypeface*)face)->getUniqueString();
+
+ stream->write8((uint8_t)face->getStyle());
+
+ if (NULL == name || 0 == *name) {
+ stream->writePackedUInt(0);
+// SkDebugf("--- fonthost serialize null\n");
+ } else {
+ uint32_t len = strlen(name);
+ stream->writePackedUInt(len);
+ stream->write(name, len);
+// SkDebugf("--- fonthost serialize <%s> %d\n", name, face->getStyle());
+ }
+}
+
+SkTypeface* SkFontHost::Deserialize(SkStream* stream) {
+ load_system_fonts();
+
+ int style = stream->readU8();
+
+ int len = stream->readPackedUInt();
+ if (len > 0) {
+ SkString str;
+ str.resize(len);
+ stream->read(str.writable_str(), len);
+
+ const FontInitRec* rec = gSystemFonts;
+ for (size_t i = 0; i < SK_ARRAY_COUNT(gSystemFonts); i++) {
+ if (strcmp(rec[i].fFileName, str.c_str()) == 0) {
+ // backup until we hit the fNames
+ for (int j = i; j >= 0; --j) {
+ if (rec[j].fNames != NULL) {
+ return SkFontHost::FindTypeface(NULL, rec[j].fNames[0],
+ (SkTypeface::Style)style);
+ }
+ }
+ }
+ }
+ }
+ return SkFontHost::FindTypeface(NULL, NULL, (SkTypeface::Style)style);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+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) {
+// SkDebugf("======= familyName <%s>\n", 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/src/ports/SkFontHost_ascender.cpp b/src/ports/SkFontHost_ascender.cpp
new file mode 100644
index 0000000..2148850
--- /dev/null
+++ b/src/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/src/ports/SkFontHost_gamma.cpp b/src/ports/SkFontHost_gamma.cpp
new file mode 100644
index 0000000..0b95bce
--- /dev/null
+++ b/src/ports/SkFontHost_gamma.cpp
@@ -0,0 +1,118 @@
+#include "SkFontHost.h"
+#include <math.h>
+
+// define this to use pre-compiled tables for gamma. This is slightly faster,
+// and doesn't create any RW global memory, but means we cannot change the
+// gamma at runtime.
+#define USE_PREDEFINED_GAMMA_TABLES
+
+#ifndef USE_PREDEFINED_GAMMA_TABLES
+ // define this if you want to spew out the "C" code for the tables, given
+ // the current values for SK_BLACK_GAMMA and SK_WHITE_GAMMA.
+ #define DUMP_GAMMA_TABLESx
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef USE_PREDEFINED_GAMMA_TABLES
+
+#include "sk_predefined_gamma.h"
+
+#else // use writable globals for gamma tables
+
+static bool gGammaIsBuilt;
+static uint8_t gBlackGamma[256], gWhiteGamma[256];
+
+#define SK_BLACK_GAMMA (1.4f)
+#define SK_WHITE_GAMMA (1/1.4f)
+
+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);
+ }
+}
+
+#ifdef DUMP_GAMMA_TABLES
+
+#include "SkString.h"
+
+static void dump_a_table(const char name[], const uint8_t table[],
+ float gamma) {
+ SkDebugf("\n");
+ SkDebugf("\/\/ Gamma table for %g\n", gamma);
+ SkDebugf("static const uint8_t %s[] = {\n", name);
+ for (int y = 0; y < 16; y++) {
+ SkString line, tmp;
+ for (int x = 0; x < 16; x++) {
+ tmp.printf("0x%02X, ", *table++);
+ line.append(tmp);
+ }
+ SkDebugf(" %s\n", line.c_str());
+ }
+ SkDebugf("};\n");
+}
+
+#endif
+
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkFontHost::GetGammaTables(const uint8_t* tables[2])
+{
+#ifndef USE_PREDEFINED_GAMMA_TABLES
+ if (!gGammaIsBuilt)
+ {
+ build_power_table(gBlackGamma, SK_BLACK_GAMMA);
+ build_power_table(gWhiteGamma, SK_WHITE_GAMMA);
+ gGammaIsBuilt = true;
+
+#ifdef DUMP_GAMMA_TABLES
+ dump_a_table("gBlackGamma", gBlackGamma, SK_BLACK_GAMMA);
+ dump_a_table("gWhiteGamma", gWhiteGamma, SK_WHITE_GAMMA);
+#endif
+ }
+#endif
+ tables[0] = gBlackGamma;
+ tables[1] = gWhiteGamma;
+}
+
+// If the luminance is <= this value, then apply the black gamma table
+#define BLACK_GAMMA_THRESHOLD 0x40
+
+// If the luminance is >= this value, then apply the white gamma table
+#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/src/ports/SkFontHost_linux.cpp b/src/ports/SkFontHost_linux.cpp
new file mode 100644
index 0000000..f75718d
--- /dev/null
+++ b/src/ports/SkFontHost_linux.cpp
@@ -0,0 +1,604 @@
+/* libs/graphics/ports/SkFontHost_android.cpp
+ **
+ ** Copyright 2006, The Android Open Source Project
+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ ** http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ */
+
+#include "SkFontHost.h"
+#include "SkDescriptor.h"
+#include "SkMMapStream.h"
+#include "SkOSFile.h"
+#include "SkPaint.h"
+#include "SkString.h"
+#include "SkStream.h"
+#include "SkThread.h"
+#include "SkTSearch.h"
+#include <stdio.h>
+
+#define FONT_CACHE_MEMORY_BUDGET (1 * 1024 * 1024)
+
+#ifndef SK_FONT_FILE_PREFIX
+ #define SK_FONT_FILE_PREFIX "/usr/share/fonts/truetype/msttcorefonts/"
+#endif
+
+SkTypeface::Style find_name_and_style(SkStream* stream, SkString* name);
+
+static void GetFullPathForSysFonts(SkString* full, const char name[])
+{
+ 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 FamilyRec* find_familyrec(const char name[]) {
+ const NameFamilyPair* list = gNameList.begin();
+ int index = SkStrLCSearch(&list[0].fName, gNameList.count(), name,
+ sizeof(list[0]));
+ return index >= 0 ? list[index].fFamily : NULL;
+}
+
+static SkTypeface* find_typeface(const char name[], SkTypeface::Style style) {
+ FamilyRec* rec = find_familyrec(name);
+ return rec ? find_best_face(rec, style) : 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, FamilyRec* family)
+ : SkTypeface(style, sk_atomic_inc(&gUniqueFontID) + 1)
+ {
+ fIsSysFont = sysFont;
+
+ SkAutoMutexAcquire ac(gFamilyMutex);
+
+ if (NULL == family) {
+ family = SkNEW(FamilyRec);
+ }
+ family->fFaces[style] = this;
+ fFamilyRec = family; // just record it so we can return it if asked
+ }
+
+ 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; }
+ FamilyRec* getFamily() const { return fFamilyRec; }
+
+ virtual SkStream* openStream() = 0;
+ virtual void closeStream(SkStream*) = 0;
+ virtual const char* getUniqueString() const = 0;
+
+private:
+ FamilyRec* fFamilyRec; // we don't own this, just point to it
+ bool fIsSysFont;
+
+ typedef SkTypeface INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class StreamTypeface : public FamilyTypeface {
+public:
+ StreamTypeface(Style style, bool sysFont, FamilyRec* family,
+ SkStream* stream)
+ : INHERITED(style, sysFont, family)
+ {
+ fStream = stream;
+ }
+ virtual ~StreamTypeface()
+ {
+ SkDELETE(fStream);
+ }
+
+ // overrides
+ virtual SkStream* openStream() { return fStream; }
+ virtual void closeStream(SkStream*) {}
+ virtual const char* getUniqueString() const { return NULL; }
+
+private:
+ SkStream* fStream;
+
+ typedef FamilyTypeface INHERITED;
+};
+
+class FileTypeface : public FamilyTypeface {
+public:
+ FileTypeface(Style style, bool sysFont, FamilyRec* family,
+ const char path[])
+ : INHERITED(style, sysFont, family) {
+ 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);
+ }
+ virtual const char* getUniqueString() const {
+ const char* str = strrchr(fPath.c_str(), '/');
+ if (str) {
+ str += 1; // skip the '/'
+ }
+ return str;
+ }
+
+private:
+ SkString fPath;
+
+ typedef FamilyTypeface INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static bool get_name_and_style(const char path[], SkString* name,
+ SkTypeface::Style* style)
+{
+ SkMMAPStream stream(path);
+ if (stream.getLength() > 0) {
+ *style = find_name_and_style(&stream, name);
+ return true;
+ }
+ else {
+ SkFILEStream stream(path);
+ if (stream.getLength() > 0) {
+ *style = find_name_and_style(&stream, name);
+ return true;
+ }
+ }
+
+ SkDebugf("---- failed to open <%s> as a font\n", path);
+ return false;
+}
+
+// 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;
+ }
+
+ SkOSFile::Iter iter(SK_FONT_FILE_PREFIX, ".ttf");
+ SkString name;
+
+ while (iter.next(&name, false)) {
+ SkString filename;
+ GetFullPathForSysFonts(&filename, name.c_str());
+// while (filename.size() == 0) { filename.set("/usr/share/fonts/truetype/msttcorefonts/Arial.ttf");
+
+ SkString realname;
+ SkTypeface::Style style;
+
+ if (!get_name_and_style(filename.c_str(), &realname, &style)) {
+ SkDebugf("------ can't load <%s> as a font\n", filename.c_str());
+ continue;
+ }
+
+// SkDebugf("font: <%s> %d <%s>\n", realname.c_str(), style, filename.c_str());
+
+ FamilyRec* family = find_familyrec(realname.c_str());
+ // this constructor puts us into the global gFamilyHead llist
+ FamilyTypeface* tf = SkNEW_ARGS(FileTypeface,
+ (style,
+ true, // system-font (cannot delete)
+ family, // what family to join
+ filename.c_str()) // filename
+ );
+
+ if (NULL == family) {
+ add_name(realname.c_str(), tf->getFamily());
+ }
+ }
+
+ // 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
+ static const char* gDefaultNames[] = {
+ "Arial", "Verdana", "Times New Roman", NULL
+ };
+ const char** names = gDefaultNames;
+ while (*names) {
+ SkTypeface* tf = find_typeface(*names++, SkTypeface::kNormal);
+ if (tf) {
+ gDefaultNormal = tf;
+ break;
+ }
+ }
+ // check if we found *something*
+ if (NULL == gDefaultNormal) {
+ if (NULL == gFamilyHead) {
+ sk_throw();
+ }
+ for (int i = 0; i < 4; i++) {
+ if ((gDefaultNormal = gFamilyHead->fFaces[i]) != NULL) {
+ break;
+ }
+ }
+ }
+ if (NULL == gDefaultNormal) {
+ sk_throw();
+ }
+ gFallBackTypeface = gDefaultNormal;
+ gDefaultFamily = find_family(gDefaultNormal);
+
+// SkDebugf("---- default %p head %p family %p\n", gDefaultNormal, gFamilyHead, gDefaultFamily);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkFontHost::Serialize(const SkTypeface* face, SkWStream* stream) {
+#if 0
+ const char* name = ((FamilyTypeface*)face)->getUniqueString();
+
+ stream->write8((uint8_t)face->getStyle());
+
+ if (NULL == name || 0 == *name) {
+ stream->writePackedUInt(0);
+ // SkDebugf("--- fonthost serialize null\n");
+ } else {
+ uint32_t len = strlen(name);
+ stream->writePackedUInt(len);
+ stream->write(name, len);
+ // SkDebugf("--- fonthost serialize <%s> %d\n", name, face->getStyle());
+ }
+#endif
+ sk_throw();
+}
+
+SkTypeface* SkFontHost::Deserialize(SkStream* stream) {
+#if 0
+ load_system_fonts();
+
+ int style = stream->readU8();
+
+ int len = stream->readPackedUInt();
+ if (len > 0) {
+ SkString str;
+ str.resize(len);
+ stream->read(str.writable_str(), len);
+
+ const FontInitRec* rec = gSystemFonts;
+ for (size_t i = 0; i < SK_ARRAY_COUNT(gSystemFonts); i++) {
+ if (strcmp(rec[i].fFileName, str.c_str()) == 0) {
+ // backup until we hit the fNames
+ for (int j = i; j >= 0; --j) {
+ if (rec[j].fNames != NULL) {
+ return SkFontHost::FindTypeface(NULL, rec[j].fNames[0],
+ (SkTypeface::Style)style);
+ }
+ }
+ }
+ }
+ }
+ return SkFontHost::FindTypeface(NULL, NULL, (SkTypeface::Style)style);
+#endif
+ sk_throw();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+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) {
+ // SkDebugf("======= familyName <%s>\n", 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/src/ports/SkFontHost_mac.cpp b/src/ports/SkFontHost_mac.cpp
new file mode 100755
index 0000000..af44cf5
--- /dev/null
+++ b/src/ports/SkFontHost_mac.cpp
@@ -0,0 +1,563 @@
+/*
+ ** Copyright 2006, The Android Open Source Project
+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ ** http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+*/
+
+#include "SkFontHost.h"
+#include "SkDescriptor.h"
+
+// Give 1MB font cache budget
+#define FONT_CACHE_MEMORY_BUDGET (1024 * 1024)
+
+const char* gDefaultfont = "Arial"; // hard code for now
+static SkMutex gFTMutex;
+
+inline SkPoint F32PtToSkPoint(const Float32Point p)
+{
+ SkPoint sp = { SkFloatToFixed(p.x),SkFloatToFixed(p.y) };
+ return sp;
+}
+
+static inline uint32_t _rotl(uint32_t v, uint32_t r)
+{
+ return (v << r | v >> (32 - r));
+}
+
+// This will generate a unique ID based on the fontname + fontstyle
+// and also used by upper layer
+uint32_t FontFaceChecksum(const char *name,SkTypeface::Style style)
+{
+ if (!name) return style;
+
+ char* q = (char*)name;
+
+ // From "Performance in Practice of String Hashing Functions"
+ // Ramakrishna & Zobel
+ const uint32_t L = 5;
+ const uint32_t R = 2;
+
+ uint32_t h = 0x12345678;
+ while (*q) {
+ uint32_t ql = tolower(*q);
+ h ^= ((h << L) + (h >> R) + ql);
+ q ++;
+ }
+
+ // add style
+ h = _rotl(h, 3) ^ style;
+
+ return h;
+}
+
+#pragma mark -
+struct SkFaceRec {
+ SkFaceRec* fNext;
+ uint32_t fRefCnt;
+ ATSUFontID fFontID;
+ ATSUStyle fStyle;
+
+ SkFaceRec() : fFontID(0), fRefCnt(0), fStyle(NULL) {};
+
+ ~SkFaceRec() {
+ if (fStyle) {
+ ::ATSUDisposeStyle(fStyle);
+ fStyle = NULL;
+ }
+ }
+
+ uint32_t ref() {
+ return ++fRefCnt;
+ }
+};
+
+// Font Face list
+static SkFaceRec* gFaceRecHead = NULL;
+
+static SkFaceRec* find_ft_face(const ATSUFontID fontID) {
+ SkFaceRec* rec = gFaceRecHead;
+ while (rec) {
+ if (rec->fFontID == fontID) {
+ return rec;
+ }
+ rec = rec->fNext;
+ }
+
+ return NULL;
+}
+
+static SkFaceRec* insert_ft_face(const ATSUFontID afontID, const ATSUStyle atsuStyle) {
+ SkFaceRec* rec = find_ft_face(afontID);
+ if (rec) {
+ return rec; // found?
+ }
+
+ rec = SkNEW(SkFaceRec);
+ rec->fFontID = afontID;
+ rec->fStyle = atsuStyle;
+ rec->fNext = gFaceRecHead;
+ gFaceRecHead = rec;
+
+ return rec;
+}
+
+static void unref_ft_face(const ATSUFontID fontID) {
+
+ SkFaceRec* rec = gFaceRecHead;
+ SkFaceRec* prev = NULL;
+ while (rec) {
+ SkFaceRec* next = rec->fNext;
+ if (rec->fFontID == fontID) {
+ if (--rec->fRefCnt == 0) {
+ if (prev)
+ prev->fNext = next;
+ else
+ gFaceRecHead = next;
+
+ SkDELETE(rec);
+ }
+ return;
+ }
+ prev = rec;
+ rec = next;
+ }
+ SkASSERT("shouldn't get here, face not in list");
+}
+
+#pragma mark -
+
+// have to do this because SkTypeface::SkTypeface() is protected
+class SkTypeface_Mac : public SkTypeface {
+public:
+ SkTypeface_Mac(SkTypeface::Style style, uint32_t id) : SkTypeface(style, id) {}
+
+ ~SkTypeface_Mac() {}
+};
+
+#pragma mark -
+
+static SkTypeface* CreateTypeface_(const char *name, const SkTypeface::Style style) {
+
+ OSStatus err;
+ ATSUStyle atsuStyle;
+ ::ATSUCreateStyle(&atsuStyle);
+ if (name != NULL) {
+ static const ATSUAttributeTag fontTag = kATSUFontTag;
+ static const ByteCount fontTagSize = sizeof(ATSUFontID);
+
+ ATSUFontID fontID = 0;
+#if 1
+ err = ::ATSUFindFontFromName(
+ name,strlen(name),kFontNoNameCode, /* instead of regular, kFontFamilyName returns bold and/or italic sometimes, but why this works?? */
+ kFontMacintoshPlatform,kFontNoScriptCode,kFontNoLanguageCode,&fontID);
+#else
+ CFStringRef cfontName = CFStringCreateWithCString(NULL, name, kCFStringEncodingASCII);
+ ATSFontRef fontRef = ::ATSFontFindFromName(cfontName,kATSOptionFlagsDefault);
+ fontID = ::FMGetFontFromATSFontRef(fontRef);
+ CFRelease(cfontName);
+#endif
+ if (0 != fontID) {
+ const ATSUAttributeValuePtr values[] = { &fontID };
+ err = ::ATSUSetAttributes(atsuStyle,1,&fontTag,&fontTagSize,values);
+ }
+ else {
+ }
+ }
+ if (style != SkTypeface::kNormal) {
+ Boolean fontItalic = ((style & SkTypeface::kItalic) != 0);
+ Boolean fontBold = ((style & SkTypeface::kBold) != 0);
+ const ATSUAttributeTag tags[2] = { kATSUQDBoldfaceTag, kATSUQDItalicTag };
+ const ATSUAttributeValuePtr values[2] = { &fontBold, &fontItalic };
+ const ByteCount sizes[2] = { sizeof(Boolean), sizeof(Boolean) };
+ err = ::ATSUSetAttributes(atsuStyle,2,tags,sizes,values);
+ }
+
+ uint32_t cs = FontFaceChecksum(name,style);
+ SkTypeface_Mac* ptypeface = new SkTypeface_Mac(style,cs);
+
+ if (NULL == ptypeface) {
+ SkASSERT(false);
+ return NULL;
+ }
+
+ SkFaceRec* rec = insert_ft_face(cs, atsuStyle);
+ SkASSERT(rec);
+
+ return ptypeface;
+}
+
+static SkTypeface* CreateTypeface_(const SkFaceRec* rec, const SkTypeface::Style style) {
+
+ OSStatus err;
+ ATSUStyle atsuStyle;
+ err = ::ATSUCreateAndCopyStyle(rec->fStyle, &atsuStyle);
+
+ Boolean fontItalic = ((style & SkTypeface::kItalic) != 0);
+ Boolean fontBold = ((style & SkTypeface::kBold) != 0);
+ const ATSUAttributeTag tags[2] = { kATSUQDBoldfaceTag, kATSUQDItalicTag };
+ const ATSUAttributeValuePtr values[2] = { &fontBold, &fontItalic };
+ const ByteCount sizes[2] = { sizeof(Boolean), sizeof(Boolean) };
+ err = ::ATSUSetAttributes(atsuStyle,2,tags,sizes,values);
+
+ // get old font id and name
+ ATSUFontID fontID = 0;
+ ByteCount actual = 0;
+ err = ::ATSUGetAttribute(rec->fStyle,kATSUFontTag,sizeof(ATSUFontID),&fontID,&actual);
+
+ ByteCount actualLength = 0;
+ char *fontname = NULL;
+ err = ::ATSUFindFontName(fontID , kFontFamilyName, kFontUnicodePlatform, kFontNoScriptCode,
+ kFontNoLanguageCode , 0 , NULL , &actualLength , NULL );
+ if ( err == noErr)
+ {
+ actualLength += 1 ;
+ fontname = (char*)malloc( actualLength );
+ err = ::ATSUFindFontName(fontID, kFontFamilyName, kFontUnicodePlatform, kFontNoScriptCode,
+ kFontNoLanguageCode, actualLength, fontname , NULL, NULL);
+ }
+
+ SkTypeface_Mac* ptypeface = NULL;
+ if (fontname == NULL) {
+ ptypeface = new SkTypeface_Mac(style,rec->fFontID);
+ return ptypeface;
+ }
+ else {
+ uint32_t cs = FontFaceChecksum(fontname,style);
+ ptypeface = new SkTypeface_Mac(style, cs);
+
+ if (NULL == ptypeface) {
+ SkASSERT(false);
+ return NULL;
+ }
+
+ free(fontname);
+
+ insert_ft_face(cs,atsuStyle);
+ }
+ return ptypeface;
+}
+
+#pragma mark -
+
+class SkScalerContext_Mac : public SkScalerContext {
+public:
+ SkScalerContext_Mac(const SkDescriptor* desc);
+ virtual ~SkScalerContext_Mac();
+
+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 generateLineHeight(SkPoint* ascent, SkPoint* descent);
+ virtual void generateFontMetrics(SkPaint::FontMetrics* mX, SkPaint::FontMetrics* mY);
+ virtual SkDeviceContext getDC() { return NULL; } // not implemented on Mac
+
+private:
+ ATSUTextLayout fLayout;
+ ATSUStyle fStyle;
+
+ static OSStatus MoveTo(const Float32Point *pt, void *cb);
+ static OSStatus Line(const Float32Point *pt, void *cb);
+ static OSStatus Curve(const Float32Point *pt1, const Float32Point *pt2, const Float32Point *pt3, void *cb);
+ static OSStatus Close(void *cb);
+};
+
+SkScalerContext_Mac::SkScalerContext_Mac(const SkDescriptor* desc)
+ : SkScalerContext(desc), fLayout(0), fStyle(0)
+{
+ SkAutoMutexAcquire ac(gFTMutex);
+ OSStatus err;
+
+ SkFaceRec* rec = find_ft_face(fRec.fFontID);
+ if (rec) {
+ rec->ref();
+ err = ::ATSUCreateAndCopyStyle(rec->fStyle, &fStyle);
+ }
+ else {
+ SkASSERT(false);
+ // create a default
+ err = ::ATSUCreateStyle(&fStyle);
+ }
+
+ uint32_t size = SkFixedFloor(fRec.fTextSize);
+ Fixed fixedSize = IntToFixed(size);
+ static const ATSUAttributeTag sizeTag = kATSUSizeTag;
+ static const ByteCount sizeTagSize = sizeof(Fixed);
+ const ATSUAttributeValuePtr values[] = { &fixedSize };
+ err = ::ATSUSetAttributes(fStyle,1,&sizeTag,&sizeTagSize,values);
+
+ err = ::ATSUCreateTextLayout(&fLayout);
+}
+
+SkScalerContext_Mac::~SkScalerContext_Mac()
+{
+ unref_ft_face(fRec.fFontID);
+
+ ::ATSUDisposeTextLayout(fLayout);
+ ::ATSUDisposeStyle(fStyle);
+}
+
+unsigned SkScalerContext_Mac::generateGlyphCount() const
+{
+ return 0xFFFF;
+}
+
+uint16_t SkScalerContext_Mac::generateCharToGlyph(SkUnichar uni)
+{
+ SkAutoMutexAcquire ac(gFTMutex);
+
+ OSStatus err;
+ UniChar achar = uni;
+ err = ::ATSUSetTextPointerLocation(fLayout,&achar,0,1,1);
+ err = ::ATSUSetRunStyle(fLayout,fStyle,kATSUFromTextBeginning,kATSUToTextEnd);
+
+ ATSLayoutRecord *layoutPtr;
+ ItemCount count;
+ ATSGlyphRef glyph;
+
+ err = ::ATSUDirectGetLayoutDataArrayPtrFromTextLayout(fLayout,0,kATSUDirectDataLayoutRecordATSLayoutRecordCurrent,(void**)&layoutPtr,&count);
+ glyph = layoutPtr->glyphID;
+ ::ATSUDirectReleaseLayoutDataArrayPtr(NULL,kATSUDirectDataLayoutRecordATSLayoutRecordCurrent,(void**)&layoutPtr);
+ return glyph;
+}
+
+void SkScalerContext_Mac::generateAdvance(SkGlyph* glyph) {
+ this->generateMetrics(glyph);
+}
+
+void SkScalerContext_Mac::generateMetrics(SkGlyph* glyph)
+{
+ GlyphID glyphID = glyph->fID;
+ ATSGlyphScreenMetrics metrics= { 0 };
+
+ glyph->fRsbDelta = 0;
+ glyph->fLsbDelta = 0;
+
+ OSStatus err = ATSUGlyphGetScreenMetrics(fStyle,1,&glyphID,0,true,true,&metrics);
+ if (err == noErr) {
+ glyph->fAdvanceX = SkFloatToFixed(metrics.deviceAdvance.x);
+ glyph->fAdvanceY = SkFloatToFixed(metrics.deviceAdvance.y);
+ //glyph->fWidth = metrics.width;
+ //glyph->fHeight = metrics.height;
+ glyph->fWidth = metrics.width + ceil(metrics.sideBearing.x - metrics.otherSideBearing.x);
+ glyph->fHeight = metrics.height + ceil(metrics.sideBearing.y - metrics.otherSideBearing.y) + 1;
+
+ glyph->fTop = -metrics.topLeft.y;
+ glyph->fLeft = metrics.topLeft.x;
+ }
+}
+
+void SkScalerContext_Mac::generateFontMetrics(SkPaint::FontMetrics* mx, SkPaint::FontMetrics* my) {
+ //SkASSERT(false);
+ if (mx)
+ memset(mx, 0, sizeof(SkPaint::FontMetrics));
+ if (my)
+ memset(my, 0, sizeof(SkPaint::FontMetrics));
+ return;
+}
+
+void SkScalerContext_Mac::generateImage(const SkGlyph& glyph)
+{
+ SkAutoMutexAcquire ac(gFTMutex);
+
+ GlyphID glyphID = glyph.fID;
+ ATSGlyphScreenMetrics metrics= { 0 };
+
+ SkASSERT(fLayout);
+ OSStatus err = ::ATSUGlyphGetScreenMetrics(fStyle,1,&glyphID,0,true,true,&metrics);
+
+// uint32_t w = metrics.width;
+// uint32_t h = metrics.height;
+// uint32_t pitch = (w + 3) & ~0x3;
+// if (pitch != glyph.rowBytes()) {
+// SkASSERT(false); // it's different from previously cacluated in generateMetrics(), so the size of glyph.fImage buffer is incorrect!
+// }
+
+ CGColorSpaceRef greyColorSpace = ::CGColorSpaceCreateWithName(kCGColorSpaceGenericGray);
+ CGContextRef contextRef = ::CGBitmapContextCreate((uint8_t*)glyph.fImage, glyph.fWidth, glyph.fHeight, 8, glyph.rowBytes(), greyColorSpace, kCGImageAlphaNone);
+ if (!contextRef) {
+ SkASSERT(false);
+ return;
+ }
+
+ ::CGContextSetFillColorSpace(contextRef, greyColorSpace);
+ ::CGContextSetStrokeColorSpace(contextRef, greyColorSpace);
+
+ ::CGContextSetGrayFillColor(contextRef, 0.0, 1.0);
+ ::CGContextFillRect(contextRef, ::CGRectMake(0, 0, glyph.fWidth, glyph.fHeight));
+
+ ::CGContextSetGrayFillColor(contextRef, 1.0, 1.0);
+ ::CGContextSetGrayStrokeColor(contextRef, 1.0, 1.0);
+ ::CGContextSetTextDrawingMode(contextRef, kCGTextFill);
+
+ ATSUAttributeTag tag = kATSUCGContextTag;
+ ByteCount size = sizeof(CGContextRef);
+ ATSUAttributeValuePtr value = &contextRef;
+ err = ::ATSUSetLayoutControls(fLayout,1,&tag,&size,&value);
+ err = ::ATSUDrawText(fLayout,kATSUFromTextBeginning,kATSUToTextEnd,FloatToFixed(-metrics.topLeft.x),FloatToFixed(glyph.fHeight-metrics.topLeft.y));
+ ::CGContextRelease(contextRef);
+}
+
+void SkScalerContext_Mac::generatePath(const SkGlyph& glyph, SkPath* path)
+{
+ SkAutoMutexAcquire ac(gFTMutex);
+ OSStatus err,result;
+
+ err = ::ATSUGlyphGetCubicPaths(
+ fStyle,glyph.fID,
+ &SkScalerContext_Mac::MoveTo,
+ &SkScalerContext_Mac::Line,
+ &SkScalerContext_Mac::Curve,
+ &SkScalerContext_Mac::Close,
+ path,&result);
+ SkASSERT(err == noErr);
+}
+
+void SkScalerContext_Mac::generateLineHeight(SkPoint* ascent, SkPoint* descent)
+{
+ ATSUTextMeasurement textAscent, textDescent;
+ ByteCount actual = 0;
+ OSStatus err = ::ATSUGetAttribute(fStyle,kATSULineAscentTag,sizeof(ATSUTextMeasurement),&textAscent,&actual);
+ ascent->set(0,textAscent);
+ err = ::ATSUGetAttribute(fStyle,kATSULineDescentTag,sizeof(ATSUTextMeasurement),&textDescent,&actual);
+ descent->set(0,textDescent);
+}
+
+OSStatus SkScalerContext_Mac::MoveTo(const Float32Point *pt, void *cb)
+{
+ reinterpret_cast<SkPath*>(cb)->moveTo(F32PtToSkPoint(*pt));
+ return noErr;
+}
+
+OSStatus SkScalerContext_Mac::Line(const Float32Point *pt, void *cb)
+{
+ reinterpret_cast<SkPath*>(cb)->lineTo(F32PtToSkPoint(*pt));
+ return noErr;
+}
+
+OSStatus SkScalerContext_Mac::Curve(const Float32Point *pt1, const Float32Point *pt2, const Float32Point *pt3, void *cb)
+{
+ reinterpret_cast<SkPath*>(cb)->cubicTo(F32PtToSkPoint(*pt1),F32PtToSkPoint(*pt2),F32PtToSkPoint(*pt3));
+ return noErr;
+}
+
+OSStatus SkScalerContext_Mac::Close(void *cb)
+{
+ reinterpret_cast<SkPath*>(cb)->close();
+ return noErr;
+}
+
+#pragma mark -
+
+void SkFontHost::Serialize(const SkTypeface* face, SkWStream* stream) {
+ SkASSERT(!"SkFontHost::Serialize unimplemented");
+}
+
+SkTypeface* SkFontHost::Deserialize(SkStream* stream) {
+ SkASSERT(!"SkFontHost::Deserialize unimplemented");
+ return NULL;
+}
+
+SkTypeface* SkFontHost::CreateTypeface(SkStream* stream) {
+
+ //Should not be used on Mac, keep linker happy
+ SkASSERT(false);
+ return CreateTypeface_(gDefaultfont,SkTypeface::kNormal);
+}
+
+SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc)
+{
+ return new SkScalerContext_Mac(desc);
+}
+
+SkScalerContext* SkFontHost::CreateFallbackScalerContext(const SkScalerContext::Rec& rec)
+{
+ SkAutoDescriptor ad(sizeof(rec) + sizeof(gDefaultfont) + SkDescriptor::ComputeOverhead(2));
+ SkDescriptor* desc = ad.getDesc();
+
+ desc->init();
+ SkScalerContext::Rec* newRec =
+ (SkScalerContext::Rec*)desc->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
+
+ CreateTypeface_(gDefaultfont,SkTypeface::kNormal);
+ newRec->fFontID = FontFaceChecksum(gDefaultfont,SkTypeface::kNormal);
+ desc->computeChecksum();
+
+ return SkFontHost::CreateScalerContext(desc);
+}
+
+
+ /** 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
+ This MUST not return NULL.
+ */
+
+SkTypeface* SkFontHost::FindTypeface(const SkTypeface* familyFace, const char familyName[], SkTypeface::Style style) {
+
+ SkAutoMutexAcquire ac(gFTMutex);
+
+ // clip to legal style bits
+ style = (SkTypeface::Style)(style & SkTypeface::kBoldItalic);
+
+ SkTypeface* tf = NULL;
+
+ if (NULL == familyFace && NULL == familyName) {
+ tf = CreateTypeface_(gDefaultfont,style);
+ }
+ else {
+ if (NULL != familyFace) {
+ uint32_t id = familyFace->uniqueID();
+ SkFaceRec* rec = find_ft_face(id);
+ if (!rec) {
+ SkASSERT(false);
+ tf = CreateTypeface_(gDefaultfont,style);
+ }
+ else {
+ tf = CreateTypeface_(rec,style);
+ }
+ }
+ else {
+ tf = CreateTypeface_(familyName,style);
+ }
+ }
+
+ if (NULL == tf) {
+ tf = CreateTypeface_(gDefaultfont,style);
+ }
+ return tf;
+
+}
+
+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
+}
+
+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/src/ports/SkFontHost_none.cpp b/src/ports/SkFontHost_none.cpp
new file mode 100644
index 0000000..b45cf16
--- /dev/null
+++ b/src/ports/SkFontHost_none.cpp
@@ -0,0 +1,82 @@
+/* Copyright 2006-2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkFontHost::Serialize(const SkTypeface* face, SkWStream* stream) {
+ SkASSERT(!"SkFontHost::Serialize unimplemented");
+}
+
+SkTypeface* SkFontHost::Deserialize(SkStream* stream) {
+ SkASSERT(!"SkFontHost::Deserialize 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/src/ports/SkFontHost_win.cpp b/src/ports/SkFontHost_win.cpp
new file mode 100644
index 0000000..c2ee51e
--- /dev/null
+++ b/src/ports/SkFontHost_win.cpp
@@ -0,0 +1,601 @@
+/*
+ ** Copyright 2006, The Android Open Source Project
+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ ** http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ */
+
+#include "SkString.h"
+//#include "SkStream.h"
+
+#include "SkFontHost.h"
+#include "SkDescriptor.h"
+#include "SkThread.h"
+
+#ifdef WIN32
+#include "windows.h"
+#include "tchar.h"
+
+// client3d has to undefine this for now
+#define CAN_USE_LOGFONT_NAME
+
+static SkMutex gFTMutex;
+
+// these globals are loaded (once) by get_default_font()
+static LOGFONT gDefaultFont = {0};
+
+static const uint16_t BUFFERSIZE = (16384 - 32);
+static uint8_t glyphbuf[BUFFERSIZE];
+
+// Give 1MB font cache budget
+#define FONT_CACHE_MEMORY_BUDGET (1024 * 1024)
+
+static inline FIXED SkFixedToFIXED(SkFixed x) {
+ return *(FIXED*)(&x);
+}
+
+static inline FIXED SkScalarToFIXED(SkScalar x) {
+ return SkFixedToFIXED(SkScalarToFixed(x));
+}
+
+// This will generate a unique ID based on the fontname + fontstyle
+// and also used by upper layer
+uint32_t FontFaceChecksum(const TCHAR *q, SkTypeface::Style style)
+{
+ if (!q) return style;
+
+ // From "Performance in Practice of String Hashing Functions"
+ // Ramakrishna & Zobel
+ const uint32_t L = 5;
+ const uint32_t R = 2;
+
+ uint32_t h = 0x12345678;
+ while (*q) {
+ //uint32_t ql = tolower(*q);
+ h ^= ((h << L) + (h >> R) + *q);
+ q ++;
+ }
+
+ // add style
+ h = _rotl(h, 3) ^ style;
+
+ return h;
+}
+
+static SkTypeface::Style GetFontStyle(const LOGFONT& lf) {
+ int style = SkTypeface::kNormal;
+ if (lf.lfWeight == FW_SEMIBOLD || lf.lfWeight == FW_DEMIBOLD || lf.lfWeight == FW_BOLD)
+ style |= SkTypeface::kBold;
+ if (lf.lfItalic)
+ style |= SkTypeface::kItalic;
+
+ return (SkTypeface::Style)style;
+}
+
+struct SkFaceRec {
+ SkFaceRec* fNext;
+ uint32_t fRefCnt;
+ uint32_t fFontID; // checksum of fFace
+ LOGFONT fFace;
+
+ SkFaceRec() : fFontID(-1), fRefCnt(0) {
+ memset(&fFace, 0, sizeof(LOGFONT));
+ }
+ ~SkFaceRec() {}
+
+ uint32_t ref() {
+ return ++fRefCnt;
+ }
+};
+
+// Font Face list
+static SkFaceRec* gFaceRecHead = NULL;
+
+static SkFaceRec* find_ft_face(uint32_t fontID) {
+ SkFaceRec* rec = gFaceRecHead;
+ while (rec) {
+ if (rec->fFontID == fontID) {
+ return rec;
+ }
+ rec = rec->fNext;
+ }
+
+ return NULL;
+}
+
+static SkFaceRec* insert_ft_face(const LOGFONT& lf) {
+ // need a const char*
+ uint32_t id = FontFaceChecksum(&(lf.lfFaceName[0]), GetFontStyle(lf));
+ SkFaceRec* rec = find_ft_face(id);
+ if (rec) {
+ return rec; // found?
+ }
+
+ rec = SkNEW(SkFaceRec);
+ rec->fFontID = id;
+ memcpy(&(rec->fFace), &lf, sizeof(LOGFONT));
+ rec->fNext = gFaceRecHead;
+ gFaceRecHead = rec;
+
+ return rec;
+}
+
+static void unref_ft_face(uint32_t fontID) {
+
+ SkFaceRec* rec = gFaceRecHead;
+ SkFaceRec* prev = NULL;
+ while (rec) {
+ SkFaceRec* next = rec->fNext;
+ if (rec->fFontID == fontID) {
+ if (--rec->fRefCnt == 0) {
+ if (prev)
+ prev->fNext = next;
+ else
+ gFaceRecHead = next;
+
+ SkDELETE(rec);
+ }
+ return;
+ }
+ prev = rec;
+ rec = next;
+ }
+ SkASSERT("shouldn't get here, face not in list");
+}
+
+// have to do this because SkTypeface::SkTypeface() is protected
+class FontFaceRec_Typeface : public SkTypeface {
+public:
+
+ FontFaceRec_Typeface(Style style, uint32_t id) : SkTypeface(style, id) {};
+
+ virtual ~FontFaceRec_Typeface() {};
+};
+
+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
+
+ if (gDefaultFont.lfFaceName[0] != 0) {
+ return &gDefaultFont;
+ }
+
+ NONCLIENTMETRICS ncm;
+ ncm.cbSize = sizeof(NONCLIENTMETRICS);
+ SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0);
+
+ memcpy(&gDefaultFont, &(ncm.lfMessageFont), sizeof(LOGFONT));
+
+ return &gDefaultFont;
+}
+
+static SkTypeface* CreateTypeface_(const LOGFONT& lf) {
+
+ SkTypeface::Style style = GetFontStyle(lf);
+ FontFaceRec_Typeface* ptypeface = new FontFaceRec_Typeface(style, FontFaceChecksum(lf.lfFaceName, style));
+
+ if (NULL == ptypeface) {
+ SkASSERT(false);
+ return NULL;
+ }
+
+ SkFaceRec* rec = insert_ft_face(lf);
+ SkASSERT(rec);
+
+ return ptypeface;
+}
+
+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 generateAdvance(SkGlyph* glyph);
+ 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);
+ virtual void generateFontMetrics(SkPaint::FontMetrics* mX, SkPaint::FontMetrics* mY);
+ //virtual SkDeviceContext getDC() {return ddc;}
+private:
+ uint32_t fFontID;
+ LOGFONT lf;
+ MAT2 mat22;
+ HDC ddc;
+ HFONT savefont;
+ HFONT font;
+};
+
+SkScalerContext_Windows::SkScalerContext_Windows(const SkDescriptor* desc) : SkScalerContext(desc), ddc(0), font(0), savefont(0) {
+ SkAutoMutexAcquire ac(gFTMutex);
+
+ fFontID = fRec.fFontID;
+ SkFaceRec* rec = find_ft_face(fRec.fFontID);
+ if (rec) {
+ rec->ref();
+ memcpy(&lf, &(rec->fFace), sizeof(LOGFONT));
+ }
+ else {
+ SkASSERT(false);
+ memcpy(&lf, &gDefaultFont, sizeof(LOGFONT));
+ }
+
+ mat22.eM11 = SkScalarToFIXED(fRec.fPost2x2[0][0]);
+ mat22.eM12 = SkScalarToFIXED(-fRec.fPost2x2[0][1]);
+ mat22.eM21 = SkScalarToFIXED(fRec.fPost2x2[1][0]);
+ mat22.eM22 = SkScalarToFIXED(-fRec.fPost2x2[1][1]);
+
+ ddc = ::CreateCompatibleDC(NULL);
+ SetBkMode(ddc, TRANSPARENT);
+
+ lf.lfHeight = SkScalarFloor(fRec.fTextSize);
+ font = CreateFontIndirect(&lf);
+ savefont = (HFONT)SelectObject(ddc, font);
+}
+
+SkScalerContext_Windows::~SkScalerContext_Windows() {
+ unref_ft_face(fFontID);
+
+ if (ddc) {
+ ::SelectObject(ddc, savefont);
+ ::DeleteDC(ddc);
+ ddc = NULL;
+ }
+ if (font) {
+ ::DeleteObject(font);
+ }
+}
+
+unsigned SkScalerContext_Windows::generateGlyphCount() const {
+ return 0xFFFF;
+ // return fFace->num_glyphs;
+}
+
+uint16_t SkScalerContext_Windows::generateCharToGlyph(SkUnichar uni) {
+
+ //uint16_t index = 0;
+ //GetGlyphIndicesW(ddc, &(uint16_t&)uni, 1, &index, 0);
+ //return index;
+
+ // let's just use the uni as index on Windows
+ return SkToU16(uni);
+}
+
+void SkScalerContext_Windows::generateAdvance(SkGlyph* glyph) {
+ this->generateMetrics(glyph);
+}
+
+void SkScalerContext_Windows::generateMetrics(SkGlyph* glyph) {
+
+ SkASSERT(ddc);
+
+ GLYPHMETRICS gm;
+ memset(&gm, 0, sizeof(gm));
+
+ glyph->fRsbDelta = 0;
+ glyph->fLsbDelta = 0;
+
+ UINT glyphIndexFlag = 0; //glyph->fIsCodePoint ? 0 : GGO_GLYPH_INDEX;
+ // UINT glyphIndexFlag = GGO_GLYPH_INDEX;
+ // 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->getGlyphID(0), GGO_GRAY8_BITMAP | glyphIndexFlag, &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 = SkToS16(gm.gmptGlyphOrigin.y - gm.gmBlackBoxY);
+ glyph->fLeft = SkToS16(gm.gmptGlyphOrigin.x);
+ glyph->fAdvanceX = SkIntToFixed(gm.gmCellIncX);
+ glyph->fAdvanceY = -SkIntToFixed(gm.gmCellIncY);
+ } else {
+ glyph->fWidth = 0;
+ }
+
+#if 0
+ char buf[1024];
+ sprintf(buf, "generateMetrics: id:%d, w=%d, h=%d, font:%s, fh:%d\n", glyph->fID, glyph->fWidth, glyph->fHeight, lf.lfFaceName, lf.lfHeight);
+ OutputDebugString(buf);
+#endif
+}
+
+void SkScalerContext_Windows::generateFontMetrics(SkPaint::FontMetrics* mx, SkPaint::FontMetrics* my) {
+ //SkASSERT(false);
+ if (mx)
+ memset(mx, 0, sizeof(SkPaint::FontMetrics));
+ if (my)
+ memset(my, 0, sizeof(SkPaint::FontMetrics));
+ return;
+}
+
+void SkScalerContext_Windows::generateImage(const SkGlyph& glyph) {
+
+ SkAutoMutexAcquire ac(gFTMutex);
+
+ SkASSERT(ddc);
+
+ GLYPHMETRICS gm;
+ memset(&gm, 0, sizeof(gm));
+
+#if 0
+ char buf[1024];
+ sprintf(buf, "generateImage: id:%d, w=%d, h=%d, font:%s,fh:%d\n", glyph.fID, glyph.fWidth, glyph.fHeight, lf.lfFaceName, lf.lfHeight);
+ OutputDebugString(buf);
+#endif
+
+ uint32_t bytecount = 0;
+ UINT glyphIndexFlag = 0; //glyph.fIsCodePoint ? 0 : GGO_GLYPH_INDEX;
+ // UINT glyphIndexFlag = GGO_GLYPH_INDEX;
+ uint32_t total_size = GetGlyphOutlineW(ddc, glyph.fID, GGO_GRAY8_BITMAP | glyphIndexFlag, &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.fID, GGO_GRAY8_BITMAP | glyphIndexFlag, &gm, total_size, pBuff, &mat22);
+
+ SkASSERT(total_size != GDI_ERROR);
+
+ SkASSERT(glyph.fWidth == gm.gmBlackBoxX);
+ SkASSERT(glyph.fHeight == gm.gmBlackBoxY);
+
+ uint8_t* dst = (uint8_t*)glyph.fImage;
+ uint32_t pitch = (gm.gmBlackBoxX + 3) & ~0x3;
+ if (pitch != glyph.rowBytes()) {
+ SkASSERT(false); // glyph.fImage has different rowsize!?
+ }
+
+ 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++;
+ bytecount++;
+ }
+ memset(dst, 0, glyph.rowBytes() - glyph.fWidth);
+ dst += glyph.rowBytes() - glyph.fWidth;
+ }
+
+ delete[] pBuff;
+ }
+ }
+
+ SkASSERT(GDI_ERROR != total_size && total_size >= 0);
+
+}
+
+void SkScalerContext_Windows::generatePath(const SkGlyph& glyph, SkPath* path) {
+
+ SkAutoMutexAcquire ac(gFTMutex);
+
+ SkASSERT(&glyph && path);
+ SkASSERT(ddc);
+
+ path->reset();
+
+#if 0
+ char buf[1024];
+ sprintf(buf, "generatePath: id:%d, w=%d, h=%d, font:%s,fh:%d\n", glyph.fID, glyph.fWidth, glyph.fHeight, lf.lfFaceName, lf.lfHeight);
+ OutputDebugString(buf);
+#endif
+
+ GLYPHMETRICS gm;
+ UINT glyphIndexFlag = 0; //glyph.fIsCodePoint ? 0 : GGO_GLYPH_INDEX;
+ uint32_t total_size = GetGlyphOutlineW(ddc, glyph.fID, GGO_NATIVE | glyphIndexFlag, &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(SkFixedToScalar(*(SkFixed*)(&th->pfxStart.x)), SkFixedToScalar(*(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(SkFixedToScalar(*(SkFixed*)(&pc->apfx[i].x)), SkFixedToScalar(*(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(SkFixedToScalar(*(SkFixed*)(&pnt_b.x)), SkFixedToScalar(*(SkFixed*)(&pnt_b.y)), SkFixedToScalar(*(SkFixed*)(&pnt_c.x)), SkFixedToScalar(*(SkFixed*)(&pnt_c.y)));
+ }
+ }
+ cur_poly += sizeof(uint16_t) * 2 + sizeof(POINTFX) * pc->cpfx;
+ }
+ cur_glyph += th->cb;
+ path->close();
+ }
+ }
+ else {
+ SkASSERT(false);
+ }
+ //char buf[1024];
+ //sprintf(buf, "generatePath: count:%d\n", count);
+ //OutputDebugString(buf);
+}
+
+
+// Note: not sure this is the correct implementation
+void SkScalerContext_Windows::generateLineHeight(SkPoint* ascent, SkPoint* descent) {
+
+ SkASSERT(ddc);
+
+ 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);
+ }
+
+ return;
+}
+
+void SkFontHost::Serialize(const SkTypeface* face, SkWStream* stream) {
+ SkASSERT(!"SkFontHost::Serialize unimplemented");
+}
+
+SkTypeface* SkFontHost::Deserialize(SkStream* stream) {
+ SkASSERT(!"SkFontHost::Deserialize unimplemented");
+ return NULL;
+}
+
+SkTypeface* SkFontHost::CreateTypeface(SkStream* stream) {
+
+ //Should not be used on Windows, keep linker happy
+ SkASSERT(false);
+ get_default_font();
+ return CreateTypeface_(gDefaultFont);
+}
+
+SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc) {
+ return SkNEW_ARGS(SkScalerContext_Windows, (desc));
+}
+
+SkScalerContext* SkFontHost::CreateFallbackScalerContext(const SkScalerContext::Rec& rec) {
+ get_default_font();
+
+ SkAutoDescriptor ad(sizeof(rec) + sizeof(gDefaultFont) + SkDescriptor::ComputeOverhead(2));
+ SkDescriptor* desc = ad.getDesc();
+
+ desc->init();
+ SkScalerContext::Rec* newRec =
+ (SkScalerContext::Rec*)desc->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
+
+ get_default_font();
+ CreateTypeface_(gDefaultFont);
+ newRec->fFontID = FontFaceChecksum(gDefaultFont.lfFaceName, GetFontStyle(gDefaultFont));
+ desc->computeChecksum();
+
+ return SkFontHost::CreateScalerContext(desc);
+}
+
+/** 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
+ This MUST not return NULL.
+ */
+
+SkTypeface* SkFontHost::FindTypeface(const SkTypeface* familyFace, const char familyName[], SkTypeface::Style style) {
+
+ SkAutoMutexAcquire ac(gFTMutex);
+
+#ifndef CAN_USE_LOGFONT_NAME
+ familyName = NULL;
+ familyFace = NULL;
+#endif
+
+ // clip to legal style bits
+ style = (SkTypeface::Style)(style & SkTypeface::kBoldItalic);
+
+ SkTypeface* tf = NULL;
+ if (NULL == familyFace && NULL == familyName) {
+ LOGFONT lf;
+ get_default_font();
+ memcpy(&lf, &gDefaultFont, sizeof(LOGFONT));
+ lf.lfWeight = (style & SkTypeface::kBold) != 0 ? FW_BOLD : FW_NORMAL ;
+ lf.lfItalic = ((style & SkTypeface::kItalic) != 0);
+ tf = CreateTypeface_(lf);
+ } else {
+#ifdef CAN_USE_LOGFONT_NAME
+ LOGFONT lf;
+ if (NULL != familyFace) {
+ uint32_t id = familyFace->uniqueID();
+ SkFaceRec* rec = find_ft_face(id);
+ if (!rec) {
+ SkASSERT(false);
+ get_default_font();
+ memcpy(&lf, &gDefaultFont, sizeof(LOGFONT));
+ }
+ else {
+ memcpy(&lf, &(rec->fFace), sizeof(LOGFONT));
+ }
+ }
+ else {
+ memset(&lf, 0, sizeof(LOGFONT));
+
+ lf.lfHeight = -11; // default
+ lf.lfQuality = PROOF_QUALITY;
+ lf.lfCharSet = DEFAULT_CHARSET;
+
+ _tcsncpy(lf.lfFaceName, familyName, LF_FACESIZE);
+ lf.lfFaceName[LF_FACESIZE-1] = '\0';
+ }
+
+ // use the style desired
+ lf.lfWeight = (style & SkTypeface::kBold) != 0 ? FW_BOLD : FW_NORMAL ;
+ lf.lfItalic = ((style & SkTypeface::kItalic) != 0);
+ tf = CreateTypeface_(lf);
+#endif
+ }
+
+ if (NULL == tf) {
+ get_default_font();
+ tf = CreateTypeface_(gDefaultFont);
+ }
+ return tf;
+}
+
+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
+}
+
+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/src/ports/SkGlobals_global.cpp b/src/ports/SkGlobals_global.cpp
new file mode 100644
index 0000000..d87568b
--- /dev/null
+++ b/src/ports/SkGlobals_global.cpp
@@ -0,0 +1,28 @@
+/* libs/graphics/ports/SkGlobals_global.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkGlobals.h"
+#include "SkThread.h"
+
+static SkGlobals::BootStrap gBootStrap;
+
+SkGlobals::BootStrap& SkGlobals::GetBootStrap()
+{
+ return gBootStrap;
+}
+
+
diff --git a/src/ports/SkImageDecoder_Factory.cpp b/src/ports/SkImageDecoder_Factory.cpp
new file mode 100644
index 0000000..d0053cf
--- /dev/null
+++ b/src/ports/SkImageDecoder_Factory.cpp
@@ -0,0 +1,104 @@
+/* libs/graphics/ports/SkImageDecoder_Factory.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkImageDecoder.h"
+#include "SkMovie.h"
+#include "SkStream.h"
+
+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*);
+extern SkImageDecoder* SkImageDecoder_JPEG_Factory(SkStream*);
+
+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 },
+ { SkImageDecoder_JPEG_Factory, SkImageDecoder::kJPEG_Format }
+};
+
+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;
+}
+
+/////////////////////////////////////////////////////////////////////////
+
+typedef SkMovie* (*SkMovieFactoryProc)(SkStream*);
+
+extern SkMovie* SkMovie_GIF_Factory(SkStream*);
+
+static const SkMovieFactoryProc gMovieProcs[] = {
+ SkMovie_GIF_Factory
+};
+
+SkMovie* SkMovie::DecodeStream(SkStream* stream) {
+ for (unsigned i = 0; i < SK_ARRAY_COUNT(gMovieProcs); i++) {
+ SkMovie* movie = gMovieProcs[i](stream);
+ if (NULL != movie) {
+ return movie;
+ }
+ stream->rewind();
+ }
+ 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/src/ports/SkImageRef_ashmem.cpp b/src/ports/SkImageRef_ashmem.cpp
new file mode 100644
index 0000000..a904bae
--- /dev/null
+++ b/src/ports/SkImageRef_ashmem.cpp
@@ -0,0 +1,203 @@
+#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();
+ this->closeFD();
+}
+
+void SkImageRef_ashmem::closeFD() {
+ 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);
+ fRec.fFD = -1;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+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->getURI());
+
+ 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;
+ }
+ this->closeFD();
+ 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();
+
+ if (-1 != fRec.fFD) {
+ SkASSERT(fRec.fAddr);
+ SkASSERT(fRec.fPinned);
+
+ ashmem_unpin_region(fRec.fFD, 0, 0);
+ fRec.fPinned = false;
+ }
+
+ // we clear this with or without an error, since we've either closed or
+ // unpinned the region
+ fBitmap.setPixels(NULL, NULL);
+}
+
diff --git a/src/ports/SkImageRef_ashmem.h b/src/ports/SkImageRef_ashmem.h
new file mode 100644
index 0000000..193a01d
--- /dev/null
+++ b/src/ports/SkImageRef_ashmem.h
@@ -0,0 +1,35 @@
+#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:
+ void closeFD();
+
+ SkColorTable* fCT;
+ SkAshmemRec fRec;
+
+ typedef SkImageRef INHERITED;
+};
+
+#endif
diff --git a/src/ports/SkOSEvent_android.cpp b/src/ports/SkOSEvent_android.cpp
new file mode 100644
index 0000000..59d6191
--- /dev/null
+++ b/src/ports/SkOSEvent_android.cpp
@@ -0,0 +1,155 @@
+/* libs/graphics/ports/SkOSEvent_android.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/ports/SkOSEvent_dummy.cpp b/src/ports/SkOSEvent_dummy.cpp
new file mode 100644
index 0000000..f061b6e
--- /dev/null
+++ b/src/ports/SkOSEvent_dummy.cpp
@@ -0,0 +1,28 @@
+/* libs/graphics/ports/SkOSEvent_dummy.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkEvent.h"
+
+void SkEvent::SignalNonEmptyQueue()
+{
+
+}
+
+void SkEvent::SignalQueueTimer(SkMSec delay)
+{
+
+}
diff --git a/src/ports/SkOSFile_stdio.cpp b/src/ports/SkOSFile_stdio.cpp
new file mode 100644
index 0000000..7438f7b
--- /dev/null
+++ b/src/ports/SkOSFile_stdio.cpp
@@ -0,0 +1,106 @@
+/* libs/graphics/ports/SkOSFile_stdio.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/ports/SkThread_none.cpp b/src/ports/SkThread_none.cpp
new file mode 100644
index 0000000..37a3834
--- /dev/null
+++ b/src/ports/SkThread_none.cpp
@@ -0,0 +1,49 @@
+/* libs/graphics/ports/SkThread_none.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/ports/SkThread_pthread.cpp b/src/ports/SkThread_pthread.cpp
new file mode 100644
index 0000000..4ee857d
--- /dev/null
+++ b/src/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/src/ports/SkThread_win.cpp b/src/ports/SkThread_win.cpp
new file mode 100644
index 0000000..d3f3e21
--- /dev/null
+++ b/src/ports/SkThread_win.cpp
@@ -0,0 +1,64 @@
+/* libs/graphics/ports/SkThread_none.cpp
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include <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/src/ports/SkTime_Unix.cpp b/src/ports/SkTime_Unix.cpp
new file mode 100644
index 0000000..1bf3a76
--- /dev/null
+++ b/src/ports/SkTime_Unix.cpp
@@ -0,0 +1,50 @@
+/* libs/graphics/ports/SkTime_Unix.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/ports/SkXMLParser_empty.cpp b/src/ports/SkXMLParser_empty.cpp
new file mode 100644
index 0000000..9a27306
--- /dev/null
+++ b/src/ports/SkXMLParser_empty.cpp
@@ -0,0 +1,36 @@
+/* libs/graphics/ports/SkXMLParser_empty.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+// 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/src/ports/SkXMLParser_expat.cpp b/src/ports/SkXMLParser_expat.cpp
new file mode 100644
index 0000000..7694d50
--- /dev/null
+++ b/src/ports/SkXMLParser_expat.cpp
@@ -0,0 +1,149 @@
+/* libs/graphics/ports/SkXMLParser_expat.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/ports/SkXMLParser_tinyxml.cpp b/src/ports/SkXMLParser_tinyxml.cpp
new file mode 100644
index 0000000..7f57b80
--- /dev/null
+++ b/src/ports/SkXMLParser_tinyxml.cpp
@@ -0,0 +1,96 @@
+/* libs/graphics/ports/SkXMLParser_tinyxml.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/ports/SkXMLPullParser_expat.cpp b/src/ports/SkXMLPullParser_expat.cpp
new file mode 100644
index 0000000..949c7a9
--- /dev/null
+++ b/src/ports/SkXMLPullParser_expat.cpp
@@ -0,0 +1,222 @@
+/* libs/graphics/ports/SkXMLParser_expat.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/ports/sk_predefined_gamma.h b/src/ports/sk_predefined_gamma.h
new file mode 100644
index 0000000..0818b30
--- /dev/null
+++ b/src/ports/sk_predefined_gamma.h
@@ -0,0 +1,44 @@
+#ifndef SK_PREDEFINED_GAMMA_H
+#define SK_PREDEFINED_GAMMA_H
+
+// Gamma table for 1.4
+static const uint8_t gBlackGamma[] = {
+ 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x03, 0x03, 0x04, 0x04, 0x04, 0x05,
+ 0x05, 0x06, 0x06, 0x07, 0x07, 0x08, 0x08, 0x09, 0x09, 0x0A, 0x0A, 0x0B, 0x0C, 0x0C, 0x0D, 0x0D,
+ 0x0E, 0x0F, 0x0F, 0x10, 0x10, 0x11, 0x12, 0x12, 0x13, 0x14, 0x14, 0x15, 0x16, 0x16, 0x17, 0x18,
+ 0x19, 0x19, 0x1A, 0x1B, 0x1C, 0x1C, 0x1D, 0x1E, 0x1F, 0x1F, 0x20, 0x21, 0x22, 0x22, 0x23, 0x24,
+ 0x25, 0x26, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x31,
+ 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40,
+ 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50,
+ 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60,
+ 0x61, 0x62, 0x63, 0x64, 0x65, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71,
+ 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7C, 0x7D, 0x7E, 0x7F, 0x80, 0x81, 0x82, 0x84,
+ 0x85, 0x86, 0x87, 0x88, 0x89, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, 0x91, 0x92, 0x93, 0x94, 0x95, 0x97,
+ 0x98, 0x99, 0x9A, 0x9B, 0x9D, 0x9E, 0x9F, 0xA0, 0xA1, 0xA3, 0xA4, 0xA5, 0xA6, 0xA8, 0xA9, 0xAA,
+ 0xAB, 0xAD, 0xAE, 0xAF, 0xB0, 0xB2, 0xB3, 0xB4, 0xB5, 0xB7, 0xB8, 0xB9, 0xBB, 0xBC, 0xBD, 0xBE,
+ 0xC0, 0xC1, 0xC2, 0xC4, 0xC5, 0xC6, 0xC8, 0xC9, 0xCA, 0xCB, 0xCD, 0xCE, 0xCF, 0xD1, 0xD2, 0xD3,
+ 0xD5, 0xD6, 0xD7, 0xD9, 0xDA, 0xDB, 0xDD, 0xDE, 0xDF, 0xE1, 0xE2, 0xE3, 0xE5, 0xE6, 0xE8, 0xE9,
+ 0xEA, 0xEC, 0xED, 0xEE, 0xF0, 0xF1, 0xF2, 0xF4, 0xF5, 0xF7, 0xF8, 0xF9, 0xFB, 0xFC, 0xFE, 0xFF,
+};
+
+// Gamma table for 0.714286
+static const uint8_t gWhiteGamma[] = {
+ 0x00, 0x05, 0x08, 0x0B, 0x0D, 0x0F, 0x12, 0x14, 0x16, 0x17, 0x19, 0x1B, 0x1D, 0x1E, 0x20, 0x22,
+ 0x23, 0x25, 0x26, 0x28, 0x29, 0x2B, 0x2C, 0x2E, 0x2F, 0x31, 0x32, 0x33, 0x35, 0x36, 0x37, 0x39,
+ 0x3A, 0x3B, 0x3C, 0x3E, 0x3F, 0x40, 0x41, 0x43, 0x44, 0x45, 0x46, 0x48, 0x49, 0x4A, 0x4B, 0x4C,
+ 0x4D, 0x4E, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E,
+ 0x5F, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E,
+ 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E,
+ 0x7F, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D,
+ 0x8E, 0x8F, 0x8F, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x97, 0x98, 0x99, 0x9A, 0x9B,
+ 0x9C, 0x9D, 0x9E, 0x9E, 0x9F, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9,
+ 0xAA, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB4, 0xB5, 0xB6,
+ 0xB7, 0xB8, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBC, 0xBD, 0xBE, 0xBF, 0xC0, 0xC0, 0xC1, 0xC2, 0xC3,
+ 0xC4, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCC, 0xCD, 0xCE, 0xCF, 0xCF,
+ 0xD0, 0xD1, 0xD2, 0xD3, 0xD3, 0xD4, 0xD5, 0xD6, 0xD6, 0xD7, 0xD8, 0xD9, 0xD9, 0xDA, 0xDB, 0xDC,
+ 0xDC, 0xDD, 0xDE, 0xDF, 0xDF, 0xE0, 0xE1, 0xE2, 0xE2, 0xE3, 0xE4, 0xE5, 0xE5, 0xE6, 0xE7, 0xE8,
+ 0xE8, 0xE9, 0xEA, 0xEB, 0xEB, 0xEC, 0xED, 0xEE, 0xEE, 0xEF, 0xF0, 0xF1, 0xF1, 0xF2, 0xF3, 0xF3,
+ 0xF4, 0xF5, 0xF6, 0xF6, 0xF7, 0xF8, 0xF9, 0xF9, 0xFA, 0xFB, 0xFB, 0xFC, 0xFD, 0xFE, 0xFE, 0xFF,
+};
+
+#endif
diff --git a/src/svg/SkSVG.cpp b/src/svg/SkSVG.cpp
new file mode 100644
index 0000000..bcd62bf
--- /dev/null
+++ b/src/svg/SkSVG.cpp
@@ -0,0 +1,36 @@
+/* libs/graphics/svg/SkSVG.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/svg/SkSVGCircle.cpp b/src/svg/SkSVGCircle.cpp
new file mode 100644
index 0000000..d27521d
--- /dev/null
+++ b/src/svg/SkSVGCircle.cpp
@@ -0,0 +1,53 @@
+/* libs/graphics/svg/SkSVGCircle.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/svg/SkSVGCircle.h b/src/svg/SkSVGCircle.h
new file mode 100644
index 0000000..343a367
--- /dev/null
+++ b/src/svg/SkSVGCircle.h
@@ -0,0 +1,32 @@
+/* libs/graphics/svg/SkSVGCircle.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/svg/SkSVGClipPath.cpp b/src/svg/SkSVGClipPath.cpp
new file mode 100644
index 0000000..bd71429
--- /dev/null
+++ b/src/svg/SkSVGClipPath.cpp
@@ -0,0 +1,48 @@
+/* libs/graphics/svg/SkSVGClipPath.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/svg/SkSVGClipPath.h b/src/svg/SkSVGClipPath.h
new file mode 100644
index 0000000..6fa17b8
--- /dev/null
+++ b/src/svg/SkSVGClipPath.h
@@ -0,0 +1,31 @@
+/* libs/graphics/svg/SkSVGClipPath.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/svg/SkSVGDefs.cpp b/src/svg/SkSVGDefs.cpp
new file mode 100644
index 0000000..0499075
--- /dev/null
+++ b/src/svg/SkSVGDefs.cpp
@@ -0,0 +1,32 @@
+/* libs/graphics/svg/SkSVGDefs.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/svg/SkSVGDefs.h b/src/svg/SkSVGDefs.h
new file mode 100644
index 0000000..45f0106
--- /dev/null
+++ b/src/svg/SkSVGDefs.h
@@ -0,0 +1,31 @@
+/* libs/graphics/svg/SkSVGDefs.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/svg/SkSVGElements.cpp b/src/svg/SkSVGElements.cpp
new file mode 100644
index 0000000..8e195c7
--- /dev/null
+++ b/src/svg/SkSVGElements.cpp
@@ -0,0 +1,96 @@
+/* libs/graphics/svg/SkSVGElements.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/svg/SkSVGElements.h b/src/svg/SkSVGElements.h
new file mode 100644
index 0000000..ed3f921
--- /dev/null
+++ b/src/svg/SkSVGElements.h
@@ -0,0 +1,81 @@
+/* libs/graphics/svg/SkSVGElements.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/svg/SkSVGEllipse.cpp b/src/svg/SkSVGEllipse.cpp
new file mode 100644
index 0000000..3752b83
--- /dev/null
+++ b/src/svg/SkSVGEllipse.cpp
@@ -0,0 +1,55 @@
+/* libs/graphics/svg/SkSVGEllipse.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/svg/SkSVGEllipse.h b/src/svg/SkSVGEllipse.h
new file mode 100644
index 0000000..566f16c
--- /dev/null
+++ b/src/svg/SkSVGEllipse.h
@@ -0,0 +1,33 @@
+/* libs/graphics/svg/SkSVGEllipse.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/svg/SkSVGFeColorMatrix.cpp b/src/svg/SkSVGFeColorMatrix.cpp
new file mode 100644
index 0000000..a758704
--- /dev/null
+++ b/src/svg/SkSVGFeColorMatrix.cpp
@@ -0,0 +1,32 @@
+/* libs/graphics/svg/SkSVGFeColorMatrix.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/svg/SkSVGFeColorMatrix.h b/src/svg/SkSVGFeColorMatrix.h
new file mode 100644
index 0000000..431d532
--- /dev/null
+++ b/src/svg/SkSVGFeColorMatrix.h
@@ -0,0 +1,34 @@
+/* libs/graphics/svg/SkSVGFeColorMatrix.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/svg/SkSVGFilter.cpp b/src/svg/SkSVGFilter.cpp
new file mode 100644
index 0000000..1d32e56
--- /dev/null
+++ b/src/svg/SkSVGFilter.cpp
@@ -0,0 +1,33 @@
+/* libs/graphics/svg/SkSVGFilter.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/svg/SkSVGFilter.h b/src/svg/SkSVGFilter.h
new file mode 100644
index 0000000..6ec9bd5
--- /dev/null
+++ b/src/svg/SkSVGFilter.h
@@ -0,0 +1,36 @@
+/* libs/graphics/svg/SkSVGFilter.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/svg/SkSVGG.cpp b/src/svg/SkSVGG.cpp
new file mode 100644
index 0000000..f73b8b8
--- /dev/null
+++ b/src/svg/SkSVGG.cpp
@@ -0,0 +1,24 @@
+/* libs/graphics/svg/SkSVGG.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkSVGG.h"
+
+DEFINE_SVG_NO_INFO(G)
+
+void SkSVGG::translate(SkSVGParser& parser, bool defState) {
+ INHERITED::translate(parser, defState);
+}
diff --git a/src/svg/SkSVGG.h b/src/svg/SkSVGG.h
new file mode 100644
index 0000000..4c5c68a
--- /dev/null
+++ b/src/svg/SkSVGG.h
@@ -0,0 +1,29 @@
+/* libs/graphics/svg/SkSVGG.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/svg/SkSVGGradient.cpp b/src/svg/SkSVGGradient.cpp
new file mode 100644
index 0000000..c89bdc9
--- /dev/null
+++ b/src/svg/SkSVGGradient.cpp
@@ -0,0 +1,123 @@
+/* libs/graphics/svg/SkSVGGradient.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/svg/SkSVGGradient.h b/src/svg/SkSVGGradient.h
new file mode 100644
index 0000000..873b258
--- /dev/null
+++ b/src/svg/SkSVGGradient.h
@@ -0,0 +1,37 @@
+/* libs/graphics/svg/SkSVGGradient.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/svg/SkSVGGroup.cpp b/src/svg/SkSVGGroup.cpp
new file mode 100644
index 0000000..069aa56
--- /dev/null
+++ b/src/svg/SkSVGGroup.cpp
@@ -0,0 +1,53 @@
+/* libs/graphics/svg/SkSVGGroup.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/svg/SkSVGGroup.h b/src/svg/SkSVGGroup.h
new file mode 100644
index 0000000..2e34cbb
--- /dev/null
+++ b/src/svg/SkSVGGroup.h
@@ -0,0 +1,36 @@
+/* libs/graphics/svg/SkSVGGroup.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/svg/SkSVGImage.cpp b/src/svg/SkSVGImage.cpp
new file mode 100644
index 0000000..b641c9f7
--- /dev/null
+++ b/src/svg/SkSVGImage.cpp
@@ -0,0 +1,52 @@
+/* libs/graphics/svg/SkSVGImage.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/svg/SkSVGImage.h b/src/svg/SkSVGImage.h
new file mode 100644
index 0000000..2f73b0d
--- /dev/null
+++ b/src/svg/SkSVGImage.h
@@ -0,0 +1,36 @@
+/* libs/graphics/svg/SkSVGImage.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/svg/SkSVGLine.cpp b/src/svg/SkSVGLine.cpp
new file mode 100644
index 0000000..105822a
--- /dev/null
+++ b/src/svg/SkSVGLine.cpp
@@ -0,0 +1,38 @@
+/* libs/graphics/svg/SkSVGLine.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/svg/SkSVGLine.h b/src/svg/SkSVGLine.h
new file mode 100644
index 0000000..f2cfce9
--- /dev/null
+++ b/src/svg/SkSVGLine.h
@@ -0,0 +1,33 @@
+/* libs/graphics/svg/SkSVGLine.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/svg/SkSVGLinearGradient.cpp b/src/svg/SkSVGLinearGradient.cpp
new file mode 100644
index 0000000..a474d15
--- /dev/null
+++ b/src/svg/SkSVGLinearGradient.cpp
@@ -0,0 +1,52 @@
+/* libs/graphics/svg/SkSVGLinearGradient.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/svg/SkSVGLinearGradient.h b/src/svg/SkSVGLinearGradient.h
new file mode 100644
index 0000000..85e28e7
--- /dev/null
+++ b/src/svg/SkSVGLinearGradient.h
@@ -0,0 +1,36 @@
+/* libs/graphics/svg/SkSVGLinearGradient.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/svg/SkSVGMask.cpp b/src/svg/SkSVGMask.cpp
new file mode 100644
index 0000000..eb93118
--- /dev/null
+++ b/src/svg/SkSVGMask.cpp
@@ -0,0 +1,41 @@
+/* libs/graphics/svg/SkSVGMask.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/svg/SkSVGMask.h b/src/svg/SkSVGMask.h
new file mode 100644
index 0000000..6e349b4
--- /dev/null
+++ b/src/svg/SkSVGMask.h
@@ -0,0 +1,37 @@
+/* libs/graphics/svg/SkSVGMask.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/svg/SkSVGMetadata.cpp b/src/svg/SkSVGMetadata.cpp
new file mode 100644
index 0000000..7c9e6ba
--- /dev/null
+++ b/src/svg/SkSVGMetadata.cpp
@@ -0,0 +1,32 @@
+/* libs/graphics/svg/SkSVGMetadata.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/svg/SkSVGMetadata.h b/src/svg/SkSVGMetadata.h
new file mode 100644
index 0000000..f3b5b6c
--- /dev/null
+++ b/src/svg/SkSVGMetadata.h
@@ -0,0 +1,31 @@
+/* libs/graphics/svg/SkSVGMetadata.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/svg/SkSVGPaintState.cpp b/src/svg/SkSVGPaintState.cpp
new file mode 100644
index 0000000..7fc90c7
--- /dev/null
+++ b/src/svg/SkSVGPaintState.cpp
@@ -0,0 +1,463 @@
+/* libs/graphics/svg/SkSVGPaintState.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/svg/SkSVGParser.cpp b/src/svg/SkSVGParser.cpp
new file mode 100644
index 0000000..b55c5ed
--- /dev/null
+++ b/src/svg/SkSVGParser.cpp
@@ -0,0 +1,443 @@
+/* libs/graphics/svg/SkSVGParser.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/svg/SkSVGPath.cpp b/src/svg/SkSVGPath.cpp
new file mode 100644
index 0000000..a916c30
--- /dev/null
+++ b/src/svg/SkSVGPath.cpp
@@ -0,0 +1,45 @@
+/* libs/graphics/svg/SkSVGPath.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/svg/SkSVGPath.h b/src/svg/SkSVGPath.h
new file mode 100644
index 0000000..8a0c210
--- /dev/null
+++ b/src/svg/SkSVGPath.h
@@ -0,0 +1,30 @@
+/* libs/graphics/svg/SkSVGPath.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/svg/SkSVGPolygon.cpp b/src/svg/SkSVGPolygon.cpp
new file mode 100644
index 0000000..283422c
--- /dev/null
+++ b/src/svg/SkSVGPolygon.cpp
@@ -0,0 +1,41 @@
+/* libs/graphics/svg/SkSVGPolygon.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/svg/SkSVGPolygon.h b/src/svg/SkSVGPolygon.h
new file mode 100644
index 0000000..1f27c5c
--- /dev/null
+++ b/src/svg/SkSVGPolygon.h
@@ -0,0 +1,31 @@
+/* libs/graphics/svg/SkSVGPolygon.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/svg/SkSVGPolyline.cpp b/src/svg/SkSVGPolyline.cpp
new file mode 100644
index 0000000..8432c95
--- /dev/null
+++ b/src/svg/SkSVGPolyline.cpp
@@ -0,0 +1,51 @@
+/* libs/graphics/svg/SkSVGPolyline.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/svg/SkSVGPolyline.h b/src/svg/SkSVGPolyline.h
new file mode 100644
index 0000000..7f25129
--- /dev/null
+++ b/src/svg/SkSVGPolyline.h
@@ -0,0 +1,35 @@
+/* libs/graphics/svg/SkSVGPolyline.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/svg/SkSVGRadialGradient.cpp b/src/svg/SkSVGRadialGradient.cpp
new file mode 100644
index 0000000..bba8b94
--- /dev/null
+++ b/src/svg/SkSVGRadialGradient.cpp
@@ -0,0 +1,50 @@
+/* libs/graphics/svg/SkSVGRadialGradient.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/svg/SkSVGRadialGradient.h b/src/svg/SkSVGRadialGradient.h
new file mode 100644
index 0000000..5b04186
--- /dev/null
+++ b/src/svg/SkSVGRadialGradient.h
@@ -0,0 +1,38 @@
+/* libs/graphics/svg/SkSVGRadialGradient.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/svg/SkSVGRect.cpp b/src/svg/SkSVGRect.cpp
new file mode 100644
index 0000000..03fdfdc
--- /dev/null
+++ b/src/svg/SkSVGRect.cpp
@@ -0,0 +1,43 @@
+/* libs/graphics/svg/SkSVGRect.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/svg/SkSVGRect.h b/src/svg/SkSVGRect.h
new file mode 100644
index 0000000..d06372b
--- /dev/null
+++ b/src/svg/SkSVGRect.h
@@ -0,0 +1,34 @@
+/* libs/graphics/svg/SkSVGRect.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/svg/SkSVGSVG.cpp b/src/svg/SkSVGSVG.cpp
new file mode 100644
index 0000000..1678fc1
--- /dev/null
+++ b/src/svg/SkSVGSVG.cpp
@@ -0,0 +1,82 @@
+/* libs/graphics/svg/SkSVGSVG.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/svg/SkSVGSVG.h b/src/svg/SkSVGSVG.h
new file mode 100644
index 0000000..f331ccd
--- /dev/null
+++ b/src/svg/SkSVGSVG.h
@@ -0,0 +1,39 @@
+/* libs/graphics/svg/SkSVGSVG.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/svg/SkSVGStop.cpp b/src/svg/SkSVGStop.cpp
new file mode 100644
index 0000000..0d1d76c
--- /dev/null
+++ b/src/svg/SkSVGStop.cpp
@@ -0,0 +1,32 @@
+/* libs/graphics/svg/SkSVGStop.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/svg/SkSVGStop.h b/src/svg/SkSVGStop.h
new file mode 100644
index 0000000..dd11d18
--- /dev/null
+++ b/src/svg/SkSVGStop.h
@@ -0,0 +1,31 @@
+/* libs/graphics/svg/SkSVGStop.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/svg/SkSVGSymbol.cpp b/src/svg/SkSVGSymbol.cpp
new file mode 100644
index 0000000..f8729d7
--- /dev/null
+++ b/src/svg/SkSVGSymbol.cpp
@@ -0,0 +1,30 @@
+/* libs/graphics/svg/SkSVGSymbol.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/svg/SkSVGSymbol.h b/src/svg/SkSVGSymbol.h
new file mode 100644
index 0000000..c1439dc
--- /dev/null
+++ b/src/svg/SkSVGSymbol.h
@@ -0,0 +1,30 @@
+/* libs/graphics/svg/SkSVGSymbol.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/svg/SkSVGText.cpp b/src/svg/SkSVGText.cpp
new file mode 100644
index 0000000..1c18a74
--- /dev/null
+++ b/src/svg/SkSVGText.cpp
@@ -0,0 +1,47 @@
+/* libs/graphics/svg/SkSVGText.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/svg/SkSVGText.h b/src/svg/SkSVGText.h
new file mode 100644
index 0000000..98b0155
--- /dev/null
+++ b/src/svg/SkSVGText.h
@@ -0,0 +1,40 @@
+/* libs/graphics/svg/SkSVGText.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/svg/SkSVGUse.cpp b/src/svg/SkSVGUse.cpp
new file mode 100644
index 0000000..ba7b256
--- /dev/null
+++ b/src/svg/SkSVGUse.cpp
@@ -0,0 +1,38 @@
+/* libs/graphics/svg/SkSVGUse.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/svg/SkSVGUse.h b/src/svg/SkSVGUse.h
new file mode 100644
index 0000000..ff85ce6
--- /dev/null
+++ b/src/svg/SkSVGUse.h
@@ -0,0 +1,36 @@
+/* libs/graphics/svg/SkSVGUse.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/utils/SkCamera.cpp b/src/utils/SkCamera.cpp
new file mode 100644
index 0000000..b02499f
--- /dev/null
+++ b/src/utils/SkCamera.cpp
@@ -0,0 +1,449 @@
+/* libs/graphics/effects/SkCamera.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/utils/SkColorMatrix.cpp b/src/utils/SkColorMatrix.cpp
new file mode 100644
index 0000000..0a20990
--- /dev/null
+++ b/src/utils/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/src/utils/SkCullPoints.cpp b/src/utils/SkCullPoints.cpp
new file mode 100644
index 0000000..23d00b6
--- /dev/null
+++ b/src/utils/SkCullPoints.cpp
@@ -0,0 +1,168 @@
+/* libs/graphics/effects/SkCullPoints.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/utils/SkDumpCanvas.cpp b/src/utils/SkDumpCanvas.cpp
new file mode 100644
index 0000000..fb203ef
--- /dev/null
+++ b/src/utils/SkDumpCanvas.cpp
@@ -0,0 +1,398 @@
+#include "SkDumpCanvas.h"
+#include "SkPixelRef.h"
+#include "SkString.h"
+#include <stdarg.h>
+
+// needed just to know that these are all subclassed from SkFlattenable
+#include "SkShader.h"
+#include "SkPathEffect.h"
+#include "SkXfermode.h"
+#include "SkColorFilter.h"
+#include "SkPathEffect.h"
+#include "SkMaskFilter.h"
+
+static void toString(const SkRect& r, SkString* str) {
+ str->printf("[(%g %g) %g %g]",
+ SkScalarToFloat(r.fLeft), SkScalarToFloat(r.fTop),
+ SkScalarToFloat(r.width()), SkScalarToFloat(r.height()));
+}
+
+static void toString(const SkIRect& r, SkString* str) {
+ str->printf("[(%d %d) %d %d]", r.fLeft, r.fTop, r.width(), r.height());
+}
+
+static void toString(const SkPath& path, SkString* str) {
+ if (path.isEmpty()) {
+ str->set("path:empty");
+ } else {
+ SkRect bounds;
+ path.computeBounds(&bounds, SkPath::kFast_BoundsType);
+ toString(bounds, str);
+ str->append("]");
+ str->prepend("path:[");
+ }
+}
+
+static const char* toString(SkRegion::Op op) {
+ static const char* gOpNames[] = {
+ "DIFF", "SECT", "UNION", "XOR", "RDIFF", "REPLACE"
+ };
+ return gOpNames[op];
+}
+
+static void toString(const SkRegion& rgn, SkString* str) {
+ toString(rgn.getBounds(), str);
+ str->prepend("Region:[");
+ str->append("]");
+ if (rgn.isComplex()) {
+ str->append(".complex");
+ }
+}
+
+static const char* toString(SkCanvas::VertexMode vm) {
+ static const char* gVMNames[] = {
+ "TRIANGLES", "STRIP", "FAN"
+ };
+ return gVMNames[vm];
+}
+
+static const char* toString(SkCanvas::PointMode pm) {
+ static const char* gPMNames[] = {
+ "POINTS", "LINES", "POLYGON"
+ };
+ return gPMNames[pm];
+}
+
+static const char* toString(SkBitmap::Config config) {
+ static const char* gConfigNames[] = {
+ "NONE", "A1", "A8", "INDEX8", "565", "4444", "8888", "RLE"
+ };
+ return gConfigNames[config];
+}
+
+static void toString(const SkBitmap& bm, SkString* str) {
+ str->printf("bitmap:[%d %d] %s", bm.width(), bm.height(),
+ toString(bm.config()));
+
+ SkPixelRef* pr = bm.pixelRef();
+ if (NULL == pr) {
+ // show null or the explicit pixel address (rare)
+ str->appendf(" pixels:%p", bm.getPixels());
+ } else {
+ const char* uri = pr->getURI();
+ if (uri) {
+ str->appendf(" uri:\"%s\"", uri);
+ } else {
+ str->appendf(" pixelref:%p", pr);
+ }
+ }
+}
+
+static void toString(const void* text, size_t len, SkPaint::TextEncoding enc,
+ SkString* str) {
+ switch (enc) {
+ case SkPaint::kUTF8_TextEncoding:
+ str->printf("\"%.*s\"%s", SkMax32(len, 32), text,
+ len > 32 ? "..." : "");
+ break;
+ case SkPaint::kUTF16_TextEncoding:
+ str->printf("\"%.*S\"%s", SkMax32(len, 32), text,
+ len > 64 ? "..." : "");
+ break;
+ case SkPaint::kGlyphID_TextEncoding:
+ str->set("<glyphs>");
+ break;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkDumpCanvas::SkDumpCanvas(Dumper* dumper) {
+ dumper->safeRef();
+ fDumper = dumper;
+
+ static const int WIDE_OPEN = 16384;
+ SkBitmap emptyBitmap;
+
+ emptyBitmap.setConfig(SkBitmap::kNo_Config, WIDE_OPEN, WIDE_OPEN);
+ this->setBitmapDevice(emptyBitmap);
+}
+
+SkDumpCanvas::~SkDumpCanvas() {
+ fDumper->safeUnref();
+}
+
+void SkDumpCanvas::dump(Verb verb, const SkPaint* paint,
+ const char format[], ...) {
+ static const size_t BUFFER_SIZE = 1024;
+
+ char buffer[BUFFER_SIZE];
+ va_list args;
+ va_start(args, format);
+ vsnprintf(buffer, BUFFER_SIZE, format, args);
+ va_end(args);
+
+ if (fDumper) {
+ fDumper->dump(this, verb, buffer, paint);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int SkDumpCanvas::save(SaveFlags flags) {
+ this->dump(kSave_Verb, NULL, "save(0x%X)", flags);
+ return this->INHERITED::save(flags);
+}
+
+int SkDumpCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
+ SaveFlags flags) {
+ this->dump(kSave_Verb, paint, "saveLayer(0x%X)", flags);
+ return this->INHERITED::saveLayer(bounds, paint, flags);
+}
+
+void SkDumpCanvas::restore() {
+ this->INHERITED::restore();
+ this->dump(kRestore_Verb, NULL, "restore");
+}
+
+bool SkDumpCanvas::translate(SkScalar dx, SkScalar dy) {
+ this->dump(kMatrix_Verb, NULL, "translate(%g %g)",
+ SkScalarToFloat(dx), SkScalarToFloat(dy));
+ return this->INHERITED::translate(dx, dy);
+}
+
+bool SkDumpCanvas::scale(SkScalar sx, SkScalar sy) {
+ this->dump(kMatrix_Verb, NULL, "scale(%g %g)",
+ SkScalarToFloat(sx), SkScalarToFloat(sy));
+ return this->INHERITED::scale(sx, sy);
+}
+
+bool SkDumpCanvas::rotate(SkScalar degrees) {
+ this->dump(kMatrix_Verb, NULL, "rotate(%g)", SkScalarToFloat(degrees));
+ return this->INHERITED::rotate(degrees);
+}
+
+bool SkDumpCanvas::skew(SkScalar sx, SkScalar sy) {
+ this->dump(kMatrix_Verb, NULL, "skew(%g %g)",
+ SkScalarToFloat(sx), SkScalarToFloat(sy));
+ return this->INHERITED::skew(sx, sy);
+}
+
+bool SkDumpCanvas::concat(const SkMatrix& matrix) {
+ SkString str;
+ matrix.toDumpString(&str);
+ this->dump(kMatrix_Verb, NULL, "concat(%s)", str.c_str());
+ return this->INHERITED::concat(matrix);
+}
+
+void SkDumpCanvas::setMatrix(const SkMatrix& matrix) {
+ SkString str;
+ matrix.toDumpString(&str);
+ this->dump(kMatrix_Verb, NULL, "setMatrix(%s)", str.c_str());
+ this->INHERITED::setMatrix(matrix);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool SkDumpCanvas::clipRect(const SkRect& rect, SkRegion::Op op) {
+ SkString str;
+ toString(rect, &str);
+ this->dump(kClip_Verb, NULL, "clipRect(%s %s)", str.c_str(), toString(op));
+ return this->INHERITED::clipRect(rect, op);
+}
+
+bool SkDumpCanvas::clipPath(const SkPath& path, SkRegion::Op op) {
+ SkString str;
+ toString(path, &str);
+ this->dump(kClip_Verb, NULL, "clipPath(%s %s)", str.c_str(), toString(op));
+ return this->INHERITED::clipPath(path, op);
+}
+
+bool SkDumpCanvas::clipRegion(const SkRegion& deviceRgn, SkRegion::Op op) {
+ SkString str;
+ toString(deviceRgn, &str);
+ this->dump(kClip_Verb, NULL, "clipRegion(%s %s)", str.c_str(),
+ toString(op));
+ return this->INHERITED::clipRegion(deviceRgn, op);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkDumpCanvas::drawPaint(const SkPaint& paint) {
+ this->dump(kDrawPaint_Verb, &paint, "drawPaint()");
+}
+
+void SkDumpCanvas::drawPoints(PointMode mode, size_t count,
+ const SkPoint pts[], const SkPaint& paint) {
+ this->dump(kDrawPoints_Verb, &paint, "drawPoints(%s, %d)", toString(mode),
+ count);
+}
+
+void SkDumpCanvas::drawRect(const SkRect& rect, const SkPaint& paint) {
+ SkString str;
+ toString(rect, &str);
+ this->dump(kDrawRect_Verb, &paint, "drawRect(%s)", str.c_str());
+}
+
+void SkDumpCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
+ SkString str;
+ toString(path, &str);
+ this->dump(kDrawPath_Verb, &paint, "drawPath(%s)", str.c_str());
+}
+
+void SkDumpCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
+ const SkPaint* paint) {
+ SkString str;
+ toString(bitmap, &str);
+ this->dump(kDrawBitmap_Verb, paint, "drawBitmap(%s (%g %g))", str.c_str(),
+ SkScalarToFloat(x), SkScalarToFloat(y));
+}
+
+void SkDumpCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
+ const SkRect& dst, const SkPaint* paint) {
+ SkString bs, rs;
+ toString(bitmap, &bs);
+ toString(dst, &rs);
+ // show the src-rect only if its not everything
+ if (src && (src->fLeft > 0 || src->fTop > 0 ||
+ src->fRight < bitmap.width() ||
+ src->fBottom < bitmap.height())) {
+ SkString ss;
+ toString(*src, &ss);
+ rs.prependf("%s ", ss.c_str());
+ }
+
+ this->dump(kDrawBitmap_Verb, paint, "drawBitmapRect(%s %s)",
+ bs.c_str(), rs.c_str());
+}
+
+void SkDumpCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& m,
+ const SkPaint* paint) {
+ SkString bs, ms;
+ toString(bitmap, &bs);
+ m.toDumpString(&ms);
+ this->dump(kDrawBitmap_Verb, paint, "drawBitmapMatrix(%s %s)",
+ bs.c_str(), ms.c_str());
+}
+
+void SkDumpCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
+ const SkPaint* paint) {
+ SkString str;
+ toString(bitmap, &str);
+ this->dump(kDrawBitmap_Verb, paint, "drawSprite(%s (%d %d))", str.c_str(),
+ x, y);
+}
+
+void SkDumpCanvas::drawText(const void* text, size_t byteLength, SkScalar x,
+ SkScalar y, const SkPaint& paint) {
+ SkString str;
+ toString(text, byteLength, paint.getTextEncoding(), &str);
+ this->dump(kDrawText_Verb, &paint, "drawText(%s [%d] (%g %g))", str.c_str(),
+ byteLength, SkScalarToFloat(x), SkScalarToFloat(y));
+}
+
+void SkDumpCanvas::drawPosText(const void* text, size_t byteLength,
+ const SkPoint pos[], const SkPaint& paint) {
+ SkString str;
+ toString(text, byteLength, paint.getTextEncoding(), &str);
+ this->dump(kDrawText_Verb, &paint, "drawPosText(%s [%d] (%g %g ...))",
+ str.c_str(), byteLength, SkScalarToFloat(pos[0].fX),
+ SkScalarToFloat(pos[0].fY));
+}
+
+void SkDumpCanvas::drawPosTextH(const void* text, size_t byteLength,
+ const SkScalar xpos[], SkScalar constY,
+ const SkPaint& paint) {
+ SkString str;
+ toString(text, byteLength, paint.getTextEncoding(), &str);
+ this->dump(kDrawText_Verb, &paint, "drawPosTextH(%s [%d] (%g %g ...))",
+ str.c_str(), byteLength, SkScalarToFloat(xpos[0]),
+ SkScalarToFloat(constY));
+}
+
+void SkDumpCanvas::drawTextOnPath(const void* text, size_t byteLength,
+ const SkPath& path, const SkMatrix* matrix,
+ const SkPaint& paint) {
+ SkString str;
+ toString(text, byteLength, paint.getTextEncoding(), &str);
+ this->dump(kDrawText_Verb, &paint, "drawTextOnPath(%s [%d])",
+ str.c_str(), byteLength);
+}
+
+void SkDumpCanvas::drawPicture(SkPicture& picture) {
+ this->dump(kDrawPicture_Verb, NULL, "drawPicture(%p)", &picture);
+}
+
+void SkDumpCanvas::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) {
+ this->dump(kDrawVertices_Verb, &paint, "drawVertices(%s [%d] [%g %g ...]",
+ toString(vmode), vertexCount, SkScalarToFloat(vertices[0].fX),
+ SkScalarToFloat(vertices[0].fY));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+SkFormatDumper::SkFormatDumper(void (*proc)(const char*, void*), void* refcon) {
+ fProc = proc;
+ fRefcon = refcon;
+}
+
+static void appendPtr(SkString* str, const void* ptr, const char name[]) {
+ if (ptr) {
+ str->appendf(" %s:%p", name, ptr);
+ }
+}
+
+static void appendFlattenable(SkString* str, const SkFlattenable* ptr,
+ const char name[]) {
+ if (ptr) {
+ SkString info;
+ if (ptr->toDumpString(&info)) {
+ str->appendf(" %s", info.c_str());
+ } else {
+ str->appendf(" %s:%p", name, ptr);
+ }
+ }
+}
+
+void SkFormatDumper::dump(SkDumpCanvas* canvas, SkDumpCanvas::Verb verb,
+ const char str[], const SkPaint* p) {
+ SkString msg, tab;
+ const int level = canvas->getSaveCount() - 1;
+ SkASSERT(level >= 0);
+ for (int i = 0; i < level; i++) {
+ tab.append("\t");
+ }
+ msg.printf("%s%s", tab.c_str(), str);
+
+ if (p) {
+ msg.appendf(" color:0x%08X flags:%X", p->getColor(), p->getFlags());
+ appendFlattenable(&msg, p->getShader(), "shader");
+ appendFlattenable(&msg, p->getXfermode(), "xfermode");
+ appendFlattenable(&msg, p->getPathEffect(), "pathEffect");
+ appendFlattenable(&msg, p->getMaskFilter(), "maskFilter");
+ appendFlattenable(&msg, p->getPathEffect(), "pathEffect");
+ appendFlattenable(&msg, p->getColorFilter(), "filter");
+
+ if (SkDumpCanvas::kDrawText_Verb == verb) {
+ msg.appendf(" textSize:%g", SkScalarToFloat(p->getTextSize()));
+ appendPtr(&msg, p->getTypeface(), "typeface");
+ }
+ }
+
+ fProc(msg.c_str(), fRefcon);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void dumpToDebugf(const char text[], void*) {
+ SkDebugf("%s\n", text);
+}
+
+SkDebugfDumper::SkDebugfDumper() : INHERITED(dumpToDebugf, NULL) {}
+
+
diff --git a/src/utils/SkInterpolator.cpp b/src/utils/SkInterpolator.cpp
new file mode 100644
index 0000000..e4ecd95
--- /dev/null
+++ b/src/utils/SkInterpolator.cpp
@@ -0,0 +1,339 @@
+/*
+ * Copyright (C) 2006-2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "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/src/utils/SkNinePatch.cpp b/src/utils/SkNinePatch.cpp
new file mode 100644
index 0000000..b8e11fb
--- /dev/null
+++ b/src/utils/SkNinePatch.cpp
@@ -0,0 +1,287 @@
+/*
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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
+ SkAutoLockPixels alp(bitmap);
+ // after the lock, it is valid to check
+ if (!bitmap.readyToDraw()) {
+ 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);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void drawNineViaRects(SkCanvas* canvas, const SkRect& dst,
+ const SkBitmap& bitmap, const SkIRect& margins,
+ const SkPaint* paint) {
+ const int32_t srcX[4] = {
+ 0, margins.fLeft, bitmap.width() - margins.fRight, bitmap.width()
+ };
+ const int32_t srcY[4] = {
+ 0, margins.fTop, bitmap.height() - margins.fBottom, bitmap.height()
+ };
+ const SkScalar dstX[4] = {
+ dst.fLeft, dst.fLeft + SkIntToScalar(margins.fLeft),
+ dst.fRight - SkIntToScalar(margins.fRight), dst.fRight
+ };
+ const SkScalar dstY[4] = {
+ dst.fTop, dst.fTop + SkIntToScalar(margins.fTop),
+ dst.fBottom - SkIntToScalar(margins.fBottom), dst.fBottom
+ };
+
+ SkIRect s;
+ SkRect d;
+ for (int y = 0; y < 3; y++) {
+ s.fTop = srcY[y];
+ s.fBottom = srcY[y+1];
+ d.fTop = dstY[y];
+ d.fBottom = dstY[y+1];
+ for (int x = 0; x < 3; x++) {
+ s.fLeft = srcX[x];
+ s.fRight = srcX[x+1];
+ d.fLeft = dstX[x];
+ d.fRight = dstX[x+1];
+ canvas->drawBitmapRect(bitmap, &s, d, paint);
+ }
+ }
+}
+
+void SkNinePatch::DrawNine(SkCanvas* canvas, const SkRect& bounds,
+ const SkBitmap& bitmap, const SkIRect& margins,
+ const SkPaint* paint) {
+ /** Our vertices code has numerical precision problems if the transformed
+ coordinates land directly on a 1/2 pixel boundary. To work around that
+ for now, we only take the vertices case if we are in opengl. Also,
+ when not in GL, the vertices impl is slower (more math) than calling
+ the viaRects code.
+ */
+ if (canvas->getViewport(NULL)) { // returns true for OpenGL
+ 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);
+ } else {
+ drawNineViaRects(canvas, bounds, bitmap, margins, paint);
+ }
+}
diff --git a/src/utils/SkProxyCanvas.cpp b/src/utils/SkProxyCanvas.cpp
new file mode 100644
index 0000000..2a02b45
--- /dev/null
+++ b/src/utils/SkProxyCanvas.cpp
@@ -0,0 +1,162 @@
+#include "SkProxyCanvas.h"
+
+SkProxyCanvas::SkProxyCanvas(SkCanvas* proxy) : fProxy(proxy) {
+ fProxy->safeRef();
+}
+
+SkProxyCanvas::~SkProxyCanvas() {
+ fProxy->safeUnref();
+}
+
+void SkProxyCanvas::setProxy(SkCanvas* proxy) {
+ SkRefCnt_SafeAssign(fProxy, proxy);
+}
+
+///////////////////////////////// Overrides ///////////
+
+bool SkProxyCanvas::getViewport(SkIPoint* size) const {
+ return fProxy->getViewport(size);
+}
+
+bool SkProxyCanvas::setViewport(int x, int y) {
+ return fProxy->setViewport(x, y);
+}
+
+SkDevice* SkProxyCanvas::setBitmapDevice(const SkBitmap& bitmap) {
+ return fProxy->setBitmapDevice(bitmap);
+}
+
+int SkProxyCanvas::save(SaveFlags flags) {
+ return fProxy->save(flags);
+}
+
+int SkProxyCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
+ SaveFlags flags) {
+ return fProxy->saveLayer(bounds, paint, flags);
+}
+
+void SkProxyCanvas::restore() {
+ fProxy->restore();
+}
+
+bool SkProxyCanvas::translate(SkScalar dx, SkScalar dy) {
+ return fProxy->translate(dx, dy);
+}
+
+bool SkProxyCanvas::scale(SkScalar sx, SkScalar sy) {
+ return fProxy->scale(sx, sy);
+}
+
+bool SkProxyCanvas::rotate(SkScalar degrees) {
+ return fProxy->rotate(degrees);
+}
+
+bool SkProxyCanvas::skew(SkScalar sx, SkScalar sy) {
+ return fProxy->skew(sx, sy);
+}
+
+bool SkProxyCanvas::concat(const SkMatrix& matrix) {
+ return fProxy->concat(matrix);
+}
+
+void SkProxyCanvas::setMatrix(const SkMatrix& matrix) {
+ fProxy->setMatrix(matrix);
+}
+
+bool SkProxyCanvas::clipRect(const SkRect& rect, SkRegion::Op op) {
+ return fProxy->clipRect(rect, op);
+}
+
+bool SkProxyCanvas::clipPath(const SkPath& path, SkRegion::Op op) {
+ return fProxy->clipPath(path, op);
+}
+
+bool SkProxyCanvas::clipRegion(const SkRegion& deviceRgn, SkRegion::Op op) {
+ return fProxy->clipRegion(deviceRgn, op);
+}
+
+void SkProxyCanvas::drawPaint(const SkPaint& paint) {
+ fProxy->drawPaint(paint);
+}
+
+void SkProxyCanvas::drawPoints(PointMode mode, size_t count,
+ const SkPoint pts[], const SkPaint& paint) {
+ fProxy->drawPoints(mode, count, pts, paint);
+}
+
+void SkProxyCanvas::drawRect(const SkRect& rect, const SkPaint& paint) {
+ fProxy->drawRect(rect, paint);
+}
+
+void SkProxyCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
+ fProxy->drawPath(path, paint);
+}
+
+void SkProxyCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
+ const SkPaint* paint) {
+ fProxy->drawBitmap(bitmap, x, y, paint);
+}
+
+void SkProxyCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
+ const SkRect& dst, const SkPaint* paint) {
+ fProxy->drawBitmapRect(bitmap, src, dst, paint);
+}
+
+void SkProxyCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& m,
+ const SkPaint* paint) {
+ fProxy->drawBitmapMatrix(bitmap, m, paint);
+}
+
+void SkProxyCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
+ const SkPaint* paint) {
+ fProxy->drawSprite(bitmap, x, y, paint);
+}
+
+void SkProxyCanvas::drawText(const void* text, size_t byteLength, SkScalar x,
+ SkScalar y, const SkPaint& paint) {
+ fProxy->drawText(text, byteLength, x, y, paint);
+}
+
+void SkProxyCanvas::drawPosText(const void* text, size_t byteLength,
+ const SkPoint pos[], const SkPaint& paint) {
+ fProxy->drawPosText(text, byteLength, pos, paint);
+}
+
+void SkProxyCanvas::drawPosTextH(const void* text, size_t byteLength,
+ const SkScalar xpos[], SkScalar constY,
+ const SkPaint& paint) {
+ fProxy->drawPosTextH(text, byteLength, xpos, constY, paint);
+}
+
+void SkProxyCanvas::drawTextOnPath(const void* text, size_t byteLength,
+ const SkPath& path, const SkMatrix* matrix,
+ const SkPaint& paint) {
+ fProxy->drawTextOnPath(text, byteLength, path, matrix, paint);
+}
+
+void SkProxyCanvas::drawPicture(SkPicture& picture) {
+ fProxy->drawPicture(picture);
+}
+
+void SkProxyCanvas::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) {
+ fProxy->drawVertices(vmode, vertexCount, vertices, texs, colors,
+ xmode, indices, indexCount, paint);
+}
+
+SkBounder* SkProxyCanvas::setBounder(SkBounder* bounder) {
+ return fProxy->setBounder(bounder);
+}
+
+SkDrawFilter* SkProxyCanvas::setDrawFilter(SkDrawFilter* filter) {
+ return fProxy->setDrawFilter(filter);
+}
+
+SkDevice* SkProxyCanvas::createDevice(SkBitmap::Config config, int width,
+ int height, bool isOpaque, bool isForLayer) {
+ return fProxy->createDevice(config, width, height, isOpaque, isForLayer);
+}
+
diff --git a/src/utils/SkUnitMappers.cpp b/src/utils/SkUnitMappers.cpp
new file mode 100644
index 0000000..0363a2b
--- /dev/null
+++ b/src/utils/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/src/views/SkBGViewArtist.cpp b/src/views/SkBGViewArtist.cpp
new file mode 100644
index 0000000..07da123
--- /dev/null
+++ b/src/views/SkBGViewArtist.cpp
@@ -0,0 +1,24 @@
+#include "SkBGViewArtist.h"
+#include "SkCanvas.h"
+#include "SkParsePaint.h"
+
+SkBGViewArtist::SkBGViewArtist(SkColor c)
+{
+ fPaint.setColor(c);
+}
+
+SkBGViewArtist::~SkBGViewArtist()
+{
+}
+
+void SkBGViewArtist::onDraw(SkView*, SkCanvas* canvas)
+{
+ // only works for views that are clipped their bounds.
+ canvas->drawPaint(fPaint);
+}
+
+void SkBGViewArtist::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ SkPaint_Inflate(&fPaint, dom, node);
+}
+
diff --git a/src/views/SkBorderView.cpp b/src/views/SkBorderView.cpp
new file mode 100644
index 0000000..3356782
--- /dev/null
+++ b/src/views/SkBorderView.cpp
@@ -0,0 +1,87 @@
+#include "SkBorderView.h"
+#include "SkAnimator.h"
+#include "SkWidgetViews.h"
+#include "SkSystemEventTypes.h"
+#include "SkTime.h"
+#include "SkStackViewLayout.h"
+
+SkBorderView::SkBorderView() : fTop(SkIntToScalar(0)), fLeft(SkIntToScalar(0)),
+ fRight(SkIntToScalar(0)), fBottom(SkIntToScalar(0))
+{
+ fAnim.setHostEventSink(this);
+ init_skin_anim(kBorder_SkinEnum, &fAnim);
+}
+
+SkBorderView::~SkBorderView()
+{
+
+}
+
+void SkBorderView::setSkin(const char skin[])
+{
+ init_skin_anim(skin, &fAnim);
+}
+
+/* virtual */ void SkBorderView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ this->INHERITED::onInflate(dom, node);
+}
+
+/*virtual*/ void SkBorderView::onSizeChange()
+{
+ this->INHERITED::onSizeChange();
+ SkEvent evt("user");
+ evt.setString("id", "setDim");
+ evt.setScalar("dimX", this->width());
+ evt.setScalar("dimY", this->height());
+ fAnim.doUserEvent(evt);
+}
+
+/*virtual*/ void SkBorderView::onDraw(SkCanvas* canvas)
+{
+ SkPaint paint;
+ SkAnimator::DifferenceType diff = fAnim.draw(canvas, &paint, SkTime::GetMSecs());
+
+ if (diff == SkAnimator::kDifferent)
+ this->inval(nil);
+ else if (diff == SkAnimator::kPartiallyDifferent)
+ {
+ SkRect bounds;
+ fAnim.getInvalBounds(&bounds);
+ this->inval(&bounds);
+ }
+}
+
+/*virtual*/ bool SkBorderView::onEvent(const SkEvent& evt)
+{
+ if (evt.isType(SK_EventType_Inval))
+ {
+ this->inval(nil);
+ return true;
+ }
+ if (evt.isType("recommendDim"))
+ {
+ evt.findScalar("leftMargin", &fLeft);
+ evt.findScalar("rightMargin", &fRight);
+ evt.findScalar("topMargin", &fTop);
+ evt.findScalar("bottomMargin", &fBottom);
+
+ //setup_views.cpp uses SkView::Layout instead of SkStackViewLayout
+ //but that gives me an error
+ SkStackViewLayout* layout;
+ fMargin.set(fLeft, fTop, fRight, fBottom);
+ if (this->getLayout())
+ {
+ layout = (SkStackViewLayout*)this->getLayout();
+ layout->setMargin(fMargin);
+ }
+ else
+ {
+ layout = new SkStackViewLayout;
+ layout->setMargin(fMargin);
+ this->setLayout(layout)->unref();
+ }
+ this->invokeLayout();
+ }
+ return this->INHERITED::onEvent(evt);
+}
diff --git a/src/views/SkEvent.cpp b/src/views/SkEvent.cpp
new file mode 100644
index 0000000..67549b4
--- /dev/null
+++ b/src/views/SkEvent.cpp
@@ -0,0 +1,565 @@
+/* libs/graphics/views/SkEvent.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/views/SkEventSink.cpp b/src/views/SkEventSink.cpp
new file mode 100644
index 0000000..c8fe35c
--- /dev/null
+++ b/src/views/SkEventSink.cpp
@@ -0,0 +1,345 @@
+/* libs/graphics/views/SkEventSink.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/views/SkImageView.cpp b/src/views/SkImageView.cpp
new file mode 100644
index 0000000..f19e913
--- /dev/null
+++ b/src/views/SkImageView.cpp
@@ -0,0 +1,296 @@
+#include "SkImageView.h"
+#include "SkAnimator.h"
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkImageDecoder.h"
+#include "SkMatrix.h"
+#include "SkSystemEventTypes.h"
+#include "SkTime.h"
+
+SkImageView::SkImageView()
+{
+ fMatrix = nil;
+ fScaleType = kMatrix_ScaleType;
+
+ fData.fAnim = nil; // handles initializing the other union values
+ fDataIsAnim = true;
+
+ fUriIsValid = false; // an empty string is not valid
+}
+
+SkImageView::~SkImageView()
+{
+ if (fMatrix)
+ sk_free(fMatrix);
+
+ this->freeData();
+}
+
+void SkImageView::getUri(SkString* uri) const
+{
+ if (uri)
+ *uri = fUri;
+}
+
+void SkImageView::setUri(const char uri[])
+{
+ if (!fUri.equals(uri))
+ {
+ fUri.set(uri);
+ this->onUriChange();
+ }
+}
+
+void SkImageView::setUri(const SkString& uri)
+{
+ if (fUri != uri)
+ {
+ fUri = uri;
+ this->onUriChange();
+ }
+}
+
+void SkImageView::setScaleType(ScaleType st)
+{
+ SkASSERT((unsigned)st <= kFitEnd_ScaleType);
+
+ if ((ScaleType)fScaleType != st)
+ {
+ fScaleType = SkToU8(st);
+ if (fUriIsValid)
+ this->inval(nil);
+ }
+}
+
+bool SkImageView::getImageMatrix(SkMatrix* matrix) const
+{
+ if (fMatrix)
+ {
+ SkASSERT(!fMatrix->isIdentity());
+ if (matrix)
+ *matrix = *fMatrix;
+ return true;
+ }
+ else
+ {
+ if (matrix)
+ matrix->reset();
+ return false;
+ }
+}
+
+void SkImageView::setImageMatrix(const SkMatrix* matrix)
+{
+ bool changed = false;
+
+ if (matrix && !matrix->isIdentity())
+ {
+ if (fMatrix == nil)
+ fMatrix = (SkMatrix*)sk_malloc_throw(sizeof(SkMatrix));
+ *fMatrix = *matrix;
+ changed = true;
+ }
+ else // set us to identity
+ {
+ if (fMatrix)
+ {
+ SkASSERT(!fMatrix->isIdentity());
+ sk_free(fMatrix);
+ fMatrix = nil;
+ changed = true;
+ }
+ }
+
+ // only redraw if we changed our matrix and we're not in scaleToFit mode
+ if (changed && this->getScaleType() == kMatrix_ScaleType && fUriIsValid)
+ this->inval(nil);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+bool SkImageView::onEvent(const SkEvent& evt)
+{
+ if (evt.isType(SK_EventType_Inval))
+ {
+ if (fUriIsValid)
+ this->inval(nil);
+ return true;
+ }
+ return this->INHERITED::onEvent(evt);
+}
+
+static inline SkMatrix::ScaleToFit scaleTypeToScaleToFit(SkImageView::ScaleType st)
+{
+ SkASSERT(st != SkImageView::kMatrix_ScaleType);
+ SkASSERT((unsigned)st <= SkImageView::kFitEnd_ScaleType);
+
+ SkASSERT(SkImageView::kFitXY_ScaleType - 1 == SkMatrix::kFill_ScaleToFit);
+ SkASSERT(SkImageView::kFitStart_ScaleType - 1 == SkMatrix::kStart_ScaleToFit);
+ SkASSERT(SkImageView::kFitCenter_ScaleType - 1 == SkMatrix::kCenter_ScaleToFit);
+ SkASSERT(SkImageView::kFitEnd_ScaleType - 1 == SkMatrix::kEnd_ScaleToFit);
+
+ return (SkMatrix::ScaleToFit)(st - 1);
+}
+
+void SkImageView::onDraw(SkCanvas* canvas)
+{
+ SkRect src;
+ if (!this->getDataBounds(&src))
+ {
+ SkDEBUGCODE(canvas->drawColor(SK_ColorRED);)
+ return; // nothing to draw
+ }
+
+ SkAutoCanvasRestore restore(canvas, true);
+ SkMatrix matrix;
+
+ if (this->getScaleType() == kMatrix_ScaleType)
+ (void)this->getImageMatrix(&matrix);
+ else
+ {
+ SkRect dst;
+ dst.set(0, 0, this->width(), this->height());
+ matrix.setRectToRect(src, dst, scaleTypeToScaleToFit(this->getScaleType()));
+ }
+ canvas->concat(matrix);
+
+ SkPaint paint;
+
+ paint.setAntiAlias(true);
+
+ if (fDataIsAnim)
+ {
+ SkMSec now = SkTime::GetMSecs();
+
+ SkAnimator::DifferenceType diff = fData.fAnim->draw(canvas, &paint, now);
+
+SkDEBUGF(("SkImageView : now = %X[%12.3f], diff = %d\n", now, now/1000., diff));
+
+ if (diff == SkAnimator::kDifferent)
+ this->inval(nil);
+ else if (diff == SkAnimator::kPartiallyDifferent)
+ {
+ SkRect bounds;
+ fData.fAnim->getInvalBounds(&bounds);
+ matrix.mapRect(&bounds); // get the bounds into view coordinates
+ this->inval(&bounds);
+ }
+ }
+ else
+ canvas->drawBitmap(*fData.fBitmap, 0, 0, &paint);
+}
+
+void SkImageView::onInflate(const SkDOM& dom, const SkDOMNode* node)
+{
+ this->INHERITED::onInflate(dom, node);
+
+ const char* src = dom.findAttr(node, "src");
+ if (src)
+ this->setUri(src);
+
+ int index = dom.findList(node, "scaleType", "matrix,fitXY,fitStart,fitCenter,fitEnd");
+ if (index >= 0)
+ this->setScaleType((ScaleType)index);
+
+ // need inflate syntax/reader for matrix
+}
+
+/////////////////////////////////////////////////////////////////////////////////////
+
+void SkImageView::onUriChange()
+{
+ if (this->freeData())
+ this->inval(nil);
+ fUriIsValid = true; // give ensureUriIsLoaded() a shot at the new uri
+}
+
+bool SkImageView::freeData()
+{
+ if (fData.fAnim) // test is valid for all union values
+ {
+ if (fDataIsAnim)
+ delete fData.fAnim;
+ else
+ delete fData.fBitmap;
+
+ fData.fAnim = nil; // valid for all union values
+ return true;
+ }
+ return false;
+}
+
+bool SkImageView::getDataBounds(SkRect* bounds)
+{
+ SkASSERT(bounds);
+
+ if (this->ensureUriIsLoaded())
+ {
+ SkScalar width, height;
+
+ if (fDataIsAnim)
+ {
+ if (SkScalarIsNaN(width = fData.fAnim->getScalar("dimensions", "x")) ||
+ SkScalarIsNaN(height = fData.fAnim->getScalar("dimensions", "y")))
+ {
+ // cons up fake bounds
+ width = this->width();
+ height = this->height();
+ }
+ }
+ else
+ {
+ width = SkIntToScalar(fData.fBitmap->width());
+ height = SkIntToScalar(fData.fBitmap->height());
+ }
+ bounds->set(0, 0, width, height);
+ return true;
+ }
+ return false;
+}
+
+bool SkImageView::ensureUriIsLoaded()
+{
+ if (fData.fAnim) // test is valid for all union values
+ {
+ SkASSERT(fUriIsValid);
+ return true;
+ }
+ if (!fUriIsValid)
+ return false;
+
+ // try to load the url
+ if (fUri.endsWith(".xml")) // assume it is screenplay
+ {
+ SkAnimator* anim = new SkAnimator;
+
+ if (!anim->decodeURI(fUri.c_str()))
+ {
+ delete anim;
+ fUriIsValid = false;
+ return false;
+ }
+ anim->setHostEventSink(this);
+
+ fData.fAnim = anim;
+ fDataIsAnim = true;
+ }
+ else // assume it is an image format
+ {
+ #if 0
+ SkBitmap* bitmap = new SkBitmap;
+
+ if (!SkImageDecoder::DecodeURL(fUri.c_str(), bitmap))
+ {
+ delete bitmap;
+ fUriIsValid = false;
+ return false;
+ }
+ fData.fBitmap = bitmap;
+ fDataIsAnim = false;
+ #else
+ return false;
+ #endif
+ }
+ return true;
+}
+
diff --git a/src/views/SkListView.cpp b/src/views/SkListView.cpp
new file mode 100644
index 0000000..27218a9
--- /dev/null
+++ b/src/views/SkListView.cpp
@@ -0,0 +1,895 @@
+#include "SkWidget.h"
+#include "SkCanvas.h"
+#include "SkEvent.h"
+#include "SkKey.h"
+#include "SkParsePaint.h"
+#include "SkSystemEventTypes.h"
+
+#if 0
+
+SkEvent* SkListSource::getEvent(int index)
+{
+ return nil;
+}
+
+#include "SkOSFile.h"
+
+class SkDirListSource : public SkListSource {
+public:
+ SkDirListSource(const char path[], const char suffix[], const char target[])
+ : fPath(path), fSuffix(suffix), fTarget(target)
+ {
+ fCount = -1;
+ }
+ virtual int countRows()
+ {
+ if (fCount < 0)
+ {
+ fCount = 0;
+ fIter.reset(fPath.c_str(), fSuffix.c_str());
+ while (fIter.next(nil))
+ fCount += 1;
+ fIter.reset(fPath.c_str(), fSuffix.c_str());
+ fIndex = 0;
+ }
+ return fCount;
+ }
+ virtual void getRow(int index, SkString* left, SkString* right)
+ {
+ (void)this->countRows();
+ SkASSERT((unsigned)index < (unsigned)fCount);
+
+ if (fIndex > index)
+ {
+ fIter.reset(fPath.c_str(), fSuffix.c_str());
+ fIndex = 0;
+ }
+
+ while (fIndex < index)
+ {
+ fIter.next(nil);
+ fIndex += 1;
+ }
+
+ if (fIter.next(left))
+ {
+ if (left)
+ left->remove(left->size() - fSuffix.size(), fSuffix.size());
+ }
+ else
+ {
+ if (left)
+ left->reset();
+ }
+ if (right) // only set to ">" if we know we're on a sub-directory
+ right->reset();
+
+ fIndex += 1;
+ }
+ virtual SkEvent* getEvent(int index)
+ {
+ SkASSERT((unsigned)index < (unsigned)fCount);
+
+ SkEvent* evt = new SkEvent();
+ SkString label;
+
+ this->getRow(index, &label, nil);
+ evt->setString("name", label.c_str());
+
+ int c = fPath.c_str()[fPath.size() - 1];
+ if (c != '/' && c != '\\')
+ label.prepend("/");
+ label.prepend(fPath);
+ label.append(fSuffix);
+ evt->setString("path", label.c_str());
+ evt->setS32("index", index);
+ evt->setS32("duration", 22);
+ evt->setType(fTarget);
+ return evt;
+ }
+
+private:
+ SkString fPath, fSuffix;
+ SkString fTarget;
+ SkOSFile::Iter fIter;
+ int fCount;
+ int fIndex;
+};
+
+SkListSource* SkListSource::CreateFromDir(const char path[], const char suffix[], const char target[])
+{
+ return new SkDirListSource(path, suffix, target);
+}
+
+//////////////////////////////////////////////////////////////////
+
+class SkDOMListSource : public SkListSource {
+public:
+ enum Type {
+ kUnknown_Type,
+ kDir_Type,
+ kToggle_Type
+ };
+ struct ItemRec {
+ SkString fLabel;
+ SkString fTail, fAltTail;
+ SkString fTarget;
+ Type fType;
+ };
+
+ SkDOMListSource(const SkDOM& dom, const SkDOM::Node* node) : fDirTail(">")
+ {
+ const SkDOM::Node* child = dom.getFirstChild(node, "item");
+ int count = 0;
+
+ while (child)
+ {
+ count += 1;
+ child = dom.getNextSibling(child, "item");
+ }
+
+ fCount = count;
+ fList = nil;
+ if (count)
+ {
+ ItemRec* rec = fList = new ItemRec[count];
+
+ child = dom.getFirstChild(node, "item");
+ while (child)
+ {
+ rec->fLabel.set(dom.findAttr(child, "label"));
+ rec->fTail.set(dom.findAttr(child, "tail"));
+ rec->fAltTail.set(dom.findAttr(child, "alt-tail"));
+ rec->fTarget.set(dom.findAttr(child, "target"));
+ rec->fType = kUnknown_Type;
+
+ int index = dom.findList(child, "type", "dir,toggle");
+ if (index >= 0)
+ rec->fType = (Type)(index + 1);
+
+ child = dom.getNextSibling(child, "item");
+ rec += 1;
+ }
+ }
+ }
+ virtual ~SkDOMListSource()
+ {
+ delete[] fList;
+ }
+ virtual int countRows()
+ {
+ return fCount;
+ }
+ virtual void getRow(int index, SkString* left, SkString* right)
+ {
+ SkASSERT((unsigned)index < (unsigned)fCount);
+
+ if (left)
+ *left = fList[index].fLabel;
+ if (right)
+ *right = fList[index].fType == kDir_Type ? fDirTail : fList[index].fTail;
+ }
+ virtual SkEvent* getEvent(int index)
+ {
+ SkASSERT((unsigned)index < (unsigned)fCount);
+
+ if (fList[index].fType == kDir_Type)
+ {
+ SkEvent* evt = new SkEvent();
+ evt->setType(fList[index].fTarget);
+ evt->setFast32(index);
+ return evt;
+ }
+ if (fList[index].fType == kToggle_Type)
+ fList[index].fTail.swap(fList[index].fAltTail);
+
+ return nil;
+ }
+
+private:
+ int fCount;
+ ItemRec* fList;
+ SkString fDirTail;
+};
+
+SkListSource* SkListSource::CreateFromDOM(const SkDOM& dom, const SkDOM::Node* node)
+{
+ return new SkDOMListSource(dom, node);
+}
+
+//////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////
+
+SkListView::SkListView(U32 flags) : SkWidgetView(flags)
+{
+ fSource = nil;
+ fScrollIndex = 0;
+ fCurrIndex = -1;
+ fRowHeight = SkIntToScalar(16);
+ fVisibleRowCount = 0;
+ fStrCache = nil;
+
+ fPaint[kBG_Attr].setColor(0);
+ fPaint[kNormalText_Attr].setTextSize(SkIntToScalar(14));
+ fPaint[kHiliteText_Attr].setTextSize(SkIntToScalar(14));
+ fPaint[kHiliteText_Attr].setColor(SK_ColorWHITE);
+ fPaint[kHiliteCell_Attr].setColor(SK_ColorBLUE);
+}
+
+SkListView::~SkListView()
+{
+ delete[] fStrCache;
+ fSource->safeUnref();
+}
+
+void SkListView::setRowHeight(SkScalar height)
+{
+ SkASSERT(height >= 0);
+
+ if (fRowHeight != height)
+ {
+ fRowHeight = height;
+ this->inval(nil);
+ this->onSizeChange();
+ }
+}
+
+void SkListView::setSelection(int index)
+{
+ if (fCurrIndex != index)
+ {
+ this->invalSelection();
+ fCurrIndex = index;
+ this->invalSelection();
+ this->ensureSelectionIsVisible();
+
+ {
+ SkEvent evt;
+ evt.setType("listview-selection");
+ evt.setFast32(index);
+ this->sendEventToParents(evt);
+ }
+ }
+}
+
+void SkListView::moveSelectionUp()
+{
+ if (fSource)
+ {
+ int index = fCurrIndex;
+ if (index < 0) // no selection
+ index = fSource->countRows() - 1;
+ else
+ index = SkMax32(index - 1, 0);
+ this->setSelection(index);
+ }
+}
+
+void SkListView::moveSelectionDown()
+{
+ if (fSource)
+ {
+ int index = fCurrIndex;
+ if (index < 0) // no selection
+ index = 0;
+ else
+ index = SkMin32(index + 1, fSource->countRows() - 1);
+ this->setSelection(index);
+ }
+}
+
+void SkListView::invalSelection()
+{
+ SkRect r;
+ if (this->getRowRect(fCurrIndex, &r))
+ this->inval(&r);
+}
+
+void SkListView::ensureSelectionIsVisible()
+{
+ if (fSource == nil)
+ return;
+
+ if ((unsigned)fCurrIndex < (unsigned)fSource->countRows())
+ {
+ int index = this->logicalToVisualIndex(fCurrIndex);
+
+ if ((unsigned)index >= (unsigned)fVisibleRowCount) // need to scroll
+ {
+ if (index < 0) // too high
+ fScrollIndex = fCurrIndex;
+ else
+ fScrollIndex = fCurrIndex - fVisibleRowCount + 1;
+ SkASSERT((unsigned)fScrollIndex < (unsigned)fSource->countRows());
+
+ this->dirtyStrCache();
+ this->inval(nil);
+ }
+ }
+}
+
+bool SkListView::getRowRect(int index, SkRect* r) const
+{
+ SkASSERT(r);
+ index = this->logicalToVisualIndex(index);
+ if (index >= 0)
+ {
+ SkScalar top = index * fRowHeight;
+
+ if (top < this->height())
+ {
+ if (r)
+ r->set(0, top, this->width(), top + fRowHeight);
+ return true;
+ }
+ }
+ return false;
+}
+
+SkPaint& SkListView::paint(Attr attr)
+{
+ SkASSERT((unsigned)attr < kAttrCount);
+ return fPaint[attr];
+}
+
+SkListSource* SkListView::setListSource(SkListSource* src)
+{
+ if (fSource != src)
+ {
+ SkRefCnt_SafeAssign(fSource, src);
+ this->dirtyStrCache();
+ this->ensureSelectionIsVisible();
+ this->inval(nil);
+ }
+ return src;
+}
+
+void SkListView::onDraw(SkCanvas* canvas)
+{
+ this->INHERITED::onDraw(canvas);
+
+ canvas->drawPaint(fPaint[kBG_Attr]);
+
+ int visibleCount = SkMin32(fVisibleRowCount, fSource->countRows() - fScrollIndex);
+ if (visibleCount == 0)
+ return;
+
+ this->ensureStrCache(visibleCount);
+ int currIndex = this->logicalToVisualIndex(fCurrIndex);
+
+ if ((unsigned)currIndex < (unsigned)visibleCount)
+ {
+ SkAutoCanvasRestore restore(canvas, true);
+ SkRect r;
+
+ canvas->translate(0, currIndex * fRowHeight);
+ (void)this->getRowRect(fScrollIndex, &r);
+ canvas->drawRect(r, fPaint[kHiliteCell_Attr]);
+ }
+
+ SkPaint* p;
+ SkScalar y, x = SkIntToScalar(6);
+ SkScalar rite = this->width() - x;
+
+ {
+ SkScalar ascent, descent;
+ fPaint[kNormalText_Attr].measureText(0, nil, &ascent, &descent);
+ y = SkScalarHalf(fRowHeight - descent + ascent) - ascent;
+ }
+
+ for (int i = 0; i < visibleCount; i++)
+ {
+ if (i == currIndex)
+ p = &fPaint[kHiliteText_Attr];
+ else
+ p = &fPaint[kNormalText_Attr];
+
+ p->setTextAlign(SkPaint::kLeft_Align);
+ canvas->drawText(fStrCache[i].c_str(), fStrCache[i].size(), x, y, *p);
+ p->setTextAlign(SkPaint::kRight_Align);
+ canvas->drawText(fStrCache[i + visibleCount].c_str(), fStrCache[i + visibleCount].size(), rite, y, *p);
+ canvas->translate(0, fRowHeight);
+ }
+}
+
+void SkListView::onSizeChange()
+{
+ SkScalar count = SkScalarDiv(this->height(), fRowHeight);
+ int n = SkScalarFloor(count);
+
+ // only want to show rows that are mostly visible
+ if (n == 0 || count - SkIntToScalar(n) > SK_Scalar1*75/100)
+ n += 1;
+
+ if (fVisibleRowCount != n)
+ {
+ fVisibleRowCount = n;
+ this->ensureSelectionIsVisible();
+ this->dirtyStrCache();
+ }
+}
+
+void SkListView::dirtyStrCache()
+{
+ if (fStrCache)
+ {
+ delete[] fStrCache;
+ fStrCache = nil;
+ }
+}
+
+void SkListView::ensureStrCache(int count)
+{
+ if (fStrCache == nil)
+ {
+ fStrCache = new SkString[count << 1];
+
+ if (fSource)
+ for (int i = 0; i < count; i++)
+ fSource->getRow(i + fScrollIndex, &fStrCache[i], &fStrCache[i + count]);
+ }
+}
+
+bool SkListView::onEvent(const SkEvent& evt)
+{
+ if (evt.isType(SK_EventType_Key))
+ {
+ switch (evt.getFast32()) {
+ case kUp_SkKey:
+ this->moveSelectionUp();
+ return true;
+ case kDown_SkKey:
+ this->moveSelectionDown();
+ return true;
+ case kRight_SkKey:
+ case kOK_SkKey:
+ if (fSource && fCurrIndex >= 0)
+ {
+ SkEvent* evt = fSource->getEvent(fCurrIndex);
+ if (evt)
+ {
+ SkView* view = this->sendEventToParents(*evt);
+ delete evt;
+ return view != nil;
+ }
+ else // hack to make toggle work
+ {
+ this->dirtyStrCache();
+ this->inval(nil);
+ }
+ }
+ break;
+ }
+ }
+ return this->INHERITED::onEvent(evt);
+}
+
+void SkListView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ this->INHERITED::onInflate(dom, node);
+
+ SkScalar x;
+ const SkDOM::Node* child;
+
+ if (dom.findScalar(node, "row-height", &x))
+ this->setRowHeight(x);
+
+ if ((child = dom.getFirstChild(node, "hilite-paint")) != nil)
+ SkPaint_Inflate(&this->paint(kHiliteCell_Attr), dom, child);
+
+ // look for a listsource
+ {
+ SkListSource* src = nil;
+
+ if ((child = dom.getFirstChild(node, "file-listsource")) != nil)
+ {
+ const char* path = dom.findAttr(child, "path");
+ if (path)
+ src = SkListSource::CreateFromDir( path,
+ dom.findAttr(child, "filter"),
+ dom.findAttr(child, "target"));
+ }
+ else if ((child = dom.getFirstChild(node, "xml-listsource")) != nil)
+ {
+ src = SkListSource::CreateFromDOM(dom, child);
+ }
+
+ if (src)
+ {
+ this->setListSource(src)->unref();
+ this->setSelection(0);
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////////
+
+#include "SkImageDecoder.h"
+#include "SkShader.h"
+
+class SkScrollBarView : public SkView {
+public:
+ SkScrollBarView(const char bg[], const char fg[])
+ {
+ fBGRef = SkBitmapRef::Decode(bg, true);
+ fFGRef = SkBitmapRef::Decode(fg, true);
+
+ if (fBGRef)
+ this->setWidth(SkIntToScalar(fBGRef->bitmap().width()));
+ }
+ ~SkScrollBarView()
+ {
+ delete fBGRef;
+ delete fFGRef;
+ }
+protected:
+ virtual void onDraw(SkCanvas* canvas)
+ {
+ if (fBGRef == nil) return;
+
+ SkPaint paint;
+
+ SkShader* shader = SkShader::CreateBitmapShader(fBGRef->bitmap(), false, SkPaint::kNo_FilterType, SkShader::kClamp_TileMode);
+ paint.setShader(shader)->unref();
+
+ canvas->drawPaint(paint);
+ }
+private:
+ SkBitmapRef* fBGRef, *fFGRef;
+};
+
+SkGridView::SkGridView(U32 flags) : SkWidgetView(flags)
+{
+ fSource = nil;
+ fCurrIndex = -1;
+ fVisibleCount.set(0, 0);
+
+ fPaint[kBG_Attr].setColor(SK_ColorWHITE);
+ fPaint[kHiliteCell_Attr].setColor(SK_ColorYELLOW);
+ fPaint[kHiliteCell_Attr].setStyle(SkPaint::kStroke_Style);
+ fPaint[kHiliteCell_Attr].setAntiAliasOn(true);
+ fPaint[kHiliteCell_Attr].setStrokeWidth(SK_Scalar1*3);
+
+ fScrollBar = new SkScrollBarView("icons/scrollbarGrey.jpg", "icons/scrollbarBlue.jpg");
+ this->attachChildToFront(fScrollBar)->unref();
+ fScrollBar->setVisibleP(true);
+}
+
+SkGridView::~SkGridView()
+{
+ fSource->safeUnref();
+}
+
+void SkGridView::getCellSize(SkPoint* size) const
+{
+ if (size)
+ *size = fCellSize;
+}
+
+void SkGridView::setCellSize(SkScalar x, SkScalar y)
+{
+ SkASSERT(x >= 0 && y >= 0);
+
+ if (!fCellSize.equals(x, y))
+ {
+ fCellSize.set(x, y);
+ this->inval(nil);
+ }
+}
+
+void SkGridView::setSelection(int index)
+{
+ if (fCurrIndex != index)
+ {
+ this->invalSelection();
+ fCurrIndex = index;
+ this->invalSelection();
+ this->ensureSelectionIsVisible();
+
+ // this generates the click
+ {
+ SkEvent evt;
+ evt.setType("listview-selection");
+ evt.setFast32(index);
+ this->sendEventToParents(evt);
+ }
+ }
+}
+
+void SkGridView::moveSelectionUp()
+{
+ if (fSource)
+ {
+ int index = fCurrIndex;
+ if (index < 0) // no selection
+ index = fSource->countRows() - 1;
+ else
+ index = SkMax32(index - 1, 0);
+ this->setSelection(index);
+ }
+}
+
+void SkGridView::moveSelectionDown()
+{
+ if (fSource)
+ {
+ int index = fCurrIndex;
+ if (index < 0) // no selection
+ index = 0;
+ else
+ index = SkMin32(index + 1, fSource->countRows() - 1);
+ this->setSelection(index);
+ }
+}
+
+void SkGridView::invalSelection()
+{
+ SkRect r;
+ if (this->getCellRect(fCurrIndex, &r))
+ {
+ SkScalar inset = 0;
+ if (fPaint[kHiliteCell_Attr].getStyle() != SkPaint::kFill_Style)
+ inset += fPaint[kHiliteCell_Attr].getStrokeWidth() / 2;
+ if (fPaint[kHiliteCell_Attr].isAntiAliasOn())
+ inset += SK_Scalar1;
+ r.inset(-inset, -inset);
+ this->inval(&r);
+ }
+}
+
+void SkGridView::ensureSelectionIsVisible()
+{
+ if (fSource == nil)
+ return;
+#if 0
+ if ((unsigned)fCurrIndex < (unsigned)fSource->countRows())
+ {
+ int index = this->logicalToVisualIndex(fCurrIndex);
+
+ if ((unsigned)index >= (unsigned)fVisibleRowCount) // need to scroll
+ {
+ if (index < 0) // too high
+ fScrollIndex = fCurrIndex;
+ else
+ fScrollIndex = fCurrIndex - fVisibleRowCount + 1;
+ SkASSERT((unsigned)fScrollIndex < (unsigned)fSource->countRows());
+
+ this->dirtyStrCache();
+ this->inval(nil);
+ }
+ }
+#endif
+}
+
+bool SkGridView::getCellRect(int index, SkRect* r) const
+{
+ if (fVisibleCount.fY == 0)
+ return false;
+
+ index = this->logicalToVisualIndex(index);
+ if (index >= 0)
+ {
+ SkRect bounds;
+ int row = index / fVisibleCount.fY;
+ int col = index % fVisibleCount.fY;
+
+ bounds.set(0, 0, fCellSize.fX, fCellSize.fY);
+ bounds.offset(col * (fCellSize.fX + SkIntToScalar(col > 0)),
+ row * (fCellSize.fY + SkIntToScalar(row > 0)));
+
+ if (bounds.fTop < this->height())
+ {
+ if (r)
+ *r = bounds;
+ return true;
+ }
+ }
+ return false;
+}
+
+SkPaint& SkGridView::paint(Attr attr)
+{
+ SkASSERT((unsigned)attr < kAttrCount);
+ return fPaint[attr];
+}
+
+SkListSource* SkGridView::setListSource(SkListSource* src)
+{
+ if (fSource != src)
+ {
+ SkRefCnt_SafeAssign(fSource, src);
+ // this->dirtyStrCache();
+ this->ensureSelectionIsVisible();
+ this->inval(nil);
+ }
+ return src;
+}
+
+#include "SkShader.h"
+
+static void copybits(SkCanvas* canvas, const SkBitmap& bm, const SkRect& dst, const SkPaint& paint)
+{
+ SkRect src;
+ SkMatrix matrix;
+
+ src.set(0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height()));
+ if (matrix.setRectToRect(src, dst))
+ {
+ SkPaint p(paint);
+ SkShader* shader = SkShader::CreateBitmapShader(bm, false, SkPaint::kNo_FilterType, SkShader::kClamp_TileMode);
+ p.setShader(shader)->unref();
+
+ shader->setLocalMatrix(matrix);
+ canvas->drawRect(dst, p);
+ }
+}
+
+#include "SkImageDecoder.h"
+
+void SkGridView::onDraw(SkCanvas* canvas)
+{
+ this->INHERITED::onDraw(canvas);
+
+ canvas->drawPaint(fPaint[kBG_Attr]);
+
+ if (fSource == nil)
+ return;
+
+#if 0
+ int visibleCount = SkMin32(fVisibleRowCount, fSource->countRows() - fScrollIndex);
+ if (visibleCount == 0)
+ return;
+
+ this->ensureStrCache(visibleCount);
+ int currIndex = this->logicalToVisualIndex(fCurrIndex);
+#endif
+
+ SkPaint p;
+ for (int i = 0; i < fSource->countRows(); i++)
+ {
+ bool forced = false;
+ SkEvent* evt = fSource->getEvent(i);
+ SkASSERT(evt);
+ SkString path(evt->findString("path"));
+ delete evt;
+
+ SkBitmapRef* bmr = SkBitmapRef::Decode(path.c_str(), false);
+ if (bmr == nil)
+ {
+ bmr = SkBitmapRef::Decode(path.c_str(), true);
+ if (bmr)
+ forced = true;
+ }
+
+ if (bmr)
+ {
+ SkAutoTDelete<SkBitmapRef> autoRef(bmr);
+ SkRect r;
+ if (!this->getCellRect(i, &r))
+ break;
+ copybits(canvas, bmr->bitmap(), r, p);
+ }
+ // only draw one forced bitmap at a time
+ if (forced)
+ {
+ this->inval(nil); // could inval only the remaining visible cells...
+ break;
+ }
+ }
+
+ // draw the hilite
+ {
+ SkRect r;
+ if (fCurrIndex >= 0 && this->getCellRect(fCurrIndex, &r))
+ canvas->drawRect(r, fPaint[kHiliteCell_Attr]);
+ }
+}
+
+static int check_count(int n, SkScalar s)
+{
+ // only want to show cells that are mostly visible
+ if (n == 0 || s - SkIntToScalar(n) > SK_Scalar1*75/100)
+ n += 1;
+ return n;
+}
+
+void SkGridView::onSizeChange()
+{
+ fScrollBar->setHeight(this->height());
+ fScrollBar->setLoc(this->locX() + this->width() - fScrollBar->width(), 0);
+
+ if (fCellSize.equals(0, 0))
+ {
+ fVisibleCount.set(0, 0);
+ return;
+ }
+
+ SkScalar rows = SkScalarDiv(this->height(), fCellSize.fY);
+ SkScalar cols = SkScalarDiv(this->width(), fCellSize.fX);
+ int y = SkScalarFloor(rows);
+ int x = SkScalarFloor(cols);
+
+ y = check_count(y, rows);
+ x = check_count(x, cols);
+
+ if (!fVisibleCount.equals(x, y))
+ {
+ fVisibleCount.set(x, y);
+ this->ensureSelectionIsVisible();
+ // this->dirtyStrCache();
+ }
+}
+
+bool SkGridView::onEvent(const SkEvent& evt)
+{
+ if (evt.isType(SK_EventType_Key))
+ {
+ switch (evt.getFast32()) {
+ case kUp_SkKey:
+ this->moveSelectionUp();
+ return true;
+ case kDown_SkKey:
+ this->moveSelectionDown();
+ return true;
+ case kRight_SkKey:
+ case kOK_SkKey:
+ if (fSource && fCurrIndex >= 0)
+ {
+ SkEvent* evt = fSource->getEvent(fCurrIndex);
+ if (evt)
+ {
+ // augment the event with our local rect
+ (void)this->getCellRect(fCurrIndex, (SkRect*)evt->setScalars("local-rect", 4, nil));
+
+ SkView* view = this->sendEventToParents(*evt);
+ delete evt;
+ return view != nil;
+ }
+ }
+ break;
+ }
+ }
+ return this->INHERITED::onEvent(evt);
+}
+
+void SkGridView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ this->INHERITED::onInflate(dom, node);
+
+ SkScalar x[2];
+ const SkDOM::Node* child;
+
+ if (dom.findScalars(node, "cell-size", x, 2))
+ this->setCellSize(x[0], x[1]);
+
+ if ((child = dom.getFirstChild(node, "hilite-paint")) != nil)
+ SkPaint_Inflate(&this->paint(kHiliteCell_Attr), dom, child);
+
+ // look for a listsource
+ {
+ SkListSource* src = nil;
+
+ if ((child = dom.getFirstChild(node, "file-listsource")) != nil)
+ {
+ const char* path = dom.findAttr(child, "path");
+ if (path)
+ src = SkListSource::CreateFromDir( path,
+ dom.findAttr(child, "filter"),
+ dom.findAttr(child, "target"));
+ }
+ else if ((child = dom.getFirstChild(node, "xml-listsource")) != nil)
+ {
+ src = SkListSource::CreateFromDOM(dom, child);
+ }
+
+ if (src)
+ {
+ this->setListSource(src)->unref();
+ this->setSelection(0);
+ }
+ }
+ this->onSizeChange();
+}
+
+#endif
diff --git a/src/views/SkListWidget.cpp b/src/views/SkListWidget.cpp
new file mode 100644
index 0000000..82e5d78
--- /dev/null
+++ b/src/views/SkListWidget.cpp
@@ -0,0 +1,623 @@
+#include "SkWidgetViews.h"
+
+#include "SkAnimator.h"
+#include "SkScrollBarView.h"
+
+extern void init_skin_anim(const char name[], SkAnimator*);
+
+struct SkListView::BindingRec {
+ SkString fSlotName;
+ int fFieldIndex;
+};
+
+SkListView::SkListView()
+{
+ fSource = nil; // our list-source
+ fScrollBar = nil;
+ fAnims = nil; // array of animators[fVisibleRowCount]
+ fBindings = nil; // our fields->slot array
+ fBindingCount = 0; // number of entries in fSlots array
+ fScrollIndex = 0; // number of cells to skip before first visible cell
+ fCurrIndex = -1; // index of "selected" cell
+ fVisibleRowCount = 0; // number of cells that can fit in our bounds
+ fAnimContentDirty = true; // true if fAnims[] have their correct content
+ fAnimFocusDirty = true;
+
+ fHeights[kNormal_Height] = SkIntToScalar(16);
+ fHeights[kSelected_Height] = SkIntToScalar(16);
+
+ this->setFlags(this->getFlags() | kFocusable_Mask);
+}
+
+SkListView::~SkListView()
+{
+ fScrollBar->safeUnref();
+ fSource->safeUnref();
+ delete[] fAnims;
+ delete[] fBindings;
+}
+
+void SkListView::setHasScrollBar(bool hasSB)
+{
+ if (hasSB != this->hasScrollBar())
+ {
+ if (hasSB)
+ {
+ SkASSERT(fScrollBar == nil);
+ fScrollBar = (SkScrollBarView*)SkWidgetFactory(kScroll_WidgetEnum);
+ fScrollBar->setVisibleP(true);
+ this->attachChildToFront(fScrollBar);
+ fScrollBar->setHeight(this->height()); // assume it auto-sets its width
+ // fScrollBar->setLoc(this->getContentWidth(), 0);
+ fScrollBar->setLoc(this->width()-SkIntToScalar(10), 0);
+ }
+ else
+ {
+ SkASSERT(fScrollBar);
+ fScrollBar->detachFromParent();
+ fScrollBar->unref();
+ fScrollBar = nil;
+ }
+ this->dirtyCache(kAnimContent_DirtyFlag);
+ }
+}
+
+void SkListView::setSelection(int index)
+{
+ if (fCurrIndex != index)
+ {
+ fAnimFocusDirty = true;
+ this->inval(nil);
+
+ this->invalSelection();
+ fCurrIndex = index;
+ this->invalSelection();
+ this->ensureSelectionIsVisible();
+ }
+}
+
+bool SkListView::moveSelectionUp()
+{
+ if (fSource)
+ {
+ int index = fCurrIndex;
+ if (index < 0) // no selection
+ index = fSource->countRecords() - 1;
+ else
+ index = SkMax32(index - 1, 0);
+
+ if (fCurrIndex != index)
+ {
+ this->setSelection(index);
+ return true;
+ }
+ }
+ return false;
+}
+
+bool SkListView::moveSelectionDown()
+{
+ if (fSource)
+ {
+ int index = fCurrIndex;
+ if (index < 0) // no selection
+ index = 0;
+ else
+ index = SkMin32(index + 1, fSource->countRecords() - 1);
+
+ if (fCurrIndex != index)
+ {
+ this->setSelection(index);
+ return true;
+ }
+ }
+ return false;
+}
+
+void SkListView::invalSelection()
+{
+ SkRect r;
+ if (this->getRowRect(fCurrIndex, &r))
+ this->inval(&r);
+}
+
+void SkListView::ensureSelectionIsVisible()
+{
+ if (fSource && (unsigned)fCurrIndex < (unsigned)fSource->countRecords())
+ {
+ int index = this->logicalToVisualIndex(fCurrIndex);
+
+ if ((unsigned)index >= (unsigned)fVisibleRowCount) // need to scroll
+ {
+ int newIndex;
+
+ if (index < 0) // too high
+ newIndex = fCurrIndex;
+ else
+ newIndex = fCurrIndex - fVisibleRowCount + 1;
+ SkASSERT((unsigned)newIndex < (unsigned)fSource->countRecords());
+ this->inval(nil);
+
+ if (fScrollIndex != newIndex)
+ {
+ fScrollIndex = newIndex;
+ if (fScrollBar)
+ fScrollBar->setStart(newIndex);
+ this->dirtyCache(kAnimContent_DirtyFlag);
+ }
+ }
+ }
+}
+
+SkScalar SkListView::getContentWidth() const
+{
+ SkScalar width = this->width();
+
+ if (fScrollBar)
+ {
+ width -= fScrollBar->width();
+ if (width < 0)
+ width = 0;
+ }
+ return width;
+}
+
+bool SkListView::getRowRect(int index, SkRect* r) const
+{
+ SkASSERT(r);
+
+ index = this->logicalToVisualIndex(index);
+ if (index >= 0)
+ {
+ int selection = this->logicalToVisualIndex(fCurrIndex);
+
+ SkScalar height = fHeights[index == selection ? kSelected_Height : kNormal_Height];
+ SkScalar top = index * fHeights[kNormal_Height];
+
+ if (index > selection && selection >= 0)
+ top += fHeights[kSelected_Height] - fHeights[kNormal_Height];
+
+ if (top < this->height())
+ {
+ if (r)
+ r->set(0, top, this->getContentWidth(), top + height);
+ return true;
+ }
+ }
+ return false;
+}
+
+SkListSource* SkListView::setListSource(SkListSource* src)
+{
+ if (fSource != src)
+ {
+ SkRefCnt_SafeAssign(fSource, src);
+ this->ensureSelectionIsVisible();
+ this->inval(nil);
+
+ if (fScrollBar)
+ fScrollBar->setTotal(fSource->countRecords());
+ }
+ return src;
+}
+
+void SkListView::dirtyCache(unsigned dirtyFlags)
+{
+ if (dirtyFlags & kAnimCount_DirtyFlag)
+ {
+ delete fAnims;
+ fAnims = nil;
+ fAnimContentDirty = true;
+ fAnimFocusDirty = true;
+ }
+ if (dirtyFlags & kAnimContent_DirtyFlag)
+ {
+ if (!fAnimContentDirty)
+ {
+ this->inval(nil);
+ fAnimContentDirty = true;
+ }
+ fAnimFocusDirty = true;
+ }
+}
+
+bool SkListView::ensureCache()
+{
+ if (fSkinName.size() == 0)
+ return false;
+
+ if (fAnims == nil)
+ {
+ int n = SkMax32(1, fVisibleRowCount);
+
+ SkASSERT(fAnimContentDirty);
+ fAnims = new SkAnimator[n];
+ for (int i = 0; i < n; i++)
+ {
+ fAnims[i].setHostEventSink(this);
+ init_skin_anim(fSkinName.c_str(), &fAnims[i]);
+ }
+
+ fHeights[kNormal_Height] = fAnims[0].getScalar("idleHeight", "value");
+ fHeights[kSelected_Height] = fAnims[0].getScalar("focusedHeight", "value");
+
+ fAnimFocusDirty = true;
+ }
+
+ if (fAnimContentDirty && fSource)
+ {
+ fAnimContentDirty = false;
+
+ SkString str;
+ SkEvent evt("user");
+ evt.setString("id", "setFields");
+ evt.setS32("rowCount", fVisibleRowCount);
+
+ SkEvent dimEvt("user");
+ dimEvt.setString("id", "setDim");
+ dimEvt.setScalar("dimX", this->getContentWidth());
+ dimEvt.setScalar("dimY", this->height());
+
+ for (int i = fScrollIndex; i < fScrollIndex + fVisibleRowCount; i++)
+ {
+ evt.setS32("relativeIndex", i - fScrollIndex);
+ for (int j = 0; j < fBindingCount; j++)
+ {
+ fSource->getRecord(i, fBindings[j].fFieldIndex, &str);
+//SkDEBUGF(("getRecord(%d,%d,%s) slot(%s)\n", i, fBindings[j].fFieldIndex, str.c_str(), fBindings[j].fSlotName.c_str()));
+ evt.setString(fBindings[j].fSlotName.c_str(), str.c_str());
+ }
+ (void)fAnims[i % fVisibleRowCount].doUserEvent(evt);
+ (void)fAnims[i % fVisibleRowCount].doUserEvent(dimEvt);
+ }
+ fAnimFocusDirty = true;
+ }
+
+ if (fAnimFocusDirty)
+ {
+//SkDEBUGF(("service fAnimFocusDirty\n"));
+ fAnimFocusDirty = false;
+
+ SkEvent focusEvt("user");
+ focusEvt.setString("id", "setFocus");
+
+ for (int i = fScrollIndex; i < fScrollIndex + fVisibleRowCount; i++)
+ {
+ focusEvt.setS32("FOCUS", i == fCurrIndex);
+ (void)fAnims[i % fVisibleRowCount].doUserEvent(focusEvt);
+ }
+ }
+
+ return true;
+}
+
+void SkListView::ensureVisibleRowCount()
+{
+ SkScalar height = this->height();
+ int n = 0;
+
+ if (height > 0)
+ {
+ n = 1;
+ height -= fHeights[kSelected_Height];
+ if (height > 0)
+ {
+ SkScalar count = SkScalarDiv(height, fHeights[kNormal_Height]);
+ n += SkScalarFloor(count);
+ if (count - SkIntToScalar(n) > SK_Scalar1*3/4)
+ n += 1;
+
+ // SkDebugf("count %g, n %d\n", count/65536., n);
+ }
+ }
+
+ if (fVisibleRowCount != n)
+ {
+ if (fScrollBar)
+ fScrollBar->setShown(n);
+
+ fVisibleRowCount = n;
+ this->ensureSelectionIsVisible();
+ this->dirtyCache(kAnimCount_DirtyFlag | kAnimContent_DirtyFlag);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+#include "SkSystemEventTypes.h"
+#include "SkTime.h"
+
+void SkListView::onSizeChange()
+{
+ this->INHERITED::onSizeChange();
+
+ if (fScrollBar)
+ fScrollBar->setLoc(this->width()-SkIntToScalar(10), 0);
+
+ this->ensureVisibleRowCount();
+}
+
+void SkListView::onDraw(SkCanvas* canvas)
+{
+ this->INHERITED::onDraw(canvas);
+
+ this->ensureVisibleRowCount();
+
+ int visibleCount = SkMin32(fVisibleRowCount, fSource->countRecords() - fScrollIndex);
+ if (visibleCount == 0 || !this->ensureCache())
+ return;
+
+//SkDebugf("visibleCount %d scrollIndex %d currIndex %d\n", visibleCount, fScrollIndex, fCurrIndex);
+
+ SkAutoCanvasRestore ar(canvas, true);
+ SkMSec now = SkTime::GetMSecs();
+ SkRect bounds;
+
+ bounds.fLeft = 0;
+ bounds.fRight = this->getContentWidth();
+ bounds.fBottom = 0;
+ // assign bounds.fTop inside the loop
+
+ // hack to reveal our bounds for debugging
+ if (this->hasFocus())
+ canvas->drawARGB(0x11, 0, 0, 0xFF);
+ else
+ canvas->drawARGB(0x11, 0x88, 0x88, 0x88);
+
+ for (int i = fScrollIndex; i < fScrollIndex + visibleCount; i++)
+ {
+ SkPaint paint;
+ SkScalar height = fHeights[i == fCurrIndex ? kSelected_Height : kNormal_Height];
+
+ bounds.fTop = bounds.fBottom;
+ bounds.fBottom += height;
+
+ canvas->save();
+ if (fAnims[i % fVisibleRowCount].draw(canvas, &paint, now) != SkAnimator::kNotDifferent)
+ this->inval(&bounds);
+ canvas->restore();
+
+ canvas->translate(0, height);
+ }
+}
+
+bool SkListView::onEvent(const SkEvent& evt)
+{
+ if (evt.isType(SK_EventType_Key))
+ {
+ switch (evt.getFast32()) {
+ case kUp_SkKey:
+ return this->moveSelectionUp();
+ case kDown_SkKey:
+ return this->moveSelectionDown();
+ case kRight_SkKey:
+ case kOK_SkKey:
+ this->postWidgetEvent();
+ return true;
+ default:
+ break;
+ }
+ }
+ return this->INHERITED::onEvent(evt);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+static const char gListViewEventSlot[] = "sk-listview-slot-name";
+
+/*virtual*/ bool SkListView::onPrepareWidgetEvent(SkEvent* evt)
+{
+ if (fSource && fCurrIndex >= 0 && this->INHERITED::onPrepareWidgetEvent(evt) &&
+ fSource->prepareWidgetEvent(evt, fCurrIndex))
+ {
+ evt->setS32(gListViewEventSlot, fCurrIndex);
+ return true;
+ }
+ return false;
+}
+
+int SkListView::GetWidgetEventListIndex(const SkEvent& evt)
+{
+ int32_t index;
+
+ return evt.findS32(gListViewEventSlot, &index) ? index : -1;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+void SkListView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ this->INHERITED::onInflate(dom, node);
+
+ {
+ bool hasScrollBar;
+ if (dom.findBool(node, "scrollBar", &hasScrollBar))
+ this->setHasScrollBar(hasScrollBar);
+ }
+
+ const SkDOM::Node* child;
+
+ if ((child = dom.getFirstChild(node, "bindings")) != nil)
+ {
+ delete[] fBindings;
+ fBindings = nil;
+ fBindingCount = 0;
+
+ SkListSource* listSrc = SkListSource::Factory(dom.findAttr(child, "data-fields"));
+ SkASSERT(listSrc);
+ fSkinName.set(dom.findAttr(child, "skin-slots"));
+ SkASSERT(fSkinName.size());
+
+ this->setListSource(listSrc)->unref();
+
+ int count = dom.countChildren(child, "bind");
+ if (count > 0)
+ {
+ fBindings = new BindingRec[count];
+ count = 0; // reuse this to count up to the number of valid bindings
+
+ child = dom.getFirstChild(child, "bind");
+ SkASSERT(child);
+ do {
+ const char* fieldName = dom.findAttr(child, "field");
+ const char* slotName = dom.findAttr(child, "slot");
+ if (fieldName && slotName)
+ {
+ fBindings[count].fFieldIndex = listSrc->findFieldIndex(fieldName);
+ if (fBindings[count].fFieldIndex >= 0)
+ fBindings[count++].fSlotName.set(slotName);
+ }
+ } while ((child = dom.getNextSibling(child, "bind")) != nil);
+
+ fBindingCount = SkToU16(count);
+ if (count == 0)
+ {
+ SkDEBUGF(("SkListView::onInflate: no valid <bind> elements in <listsource>\n"));
+ delete[] fBindings;
+ }
+ }
+ this->dirtyCache(kAnimCount_DirtyFlag);
+ this->setSelection(0);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////////////
+
+class SkXMLListSource : public SkListSource {
+public:
+ SkXMLListSource(const char doc[], size_t len);
+ virtual ~SkXMLListSource()
+ {
+ delete[] fFields;
+ delete[] fRecords;
+ }
+
+ virtual int countFields() { return fFieldCount; }
+ virtual void getFieldName(int index, SkString* field)
+ {
+ SkASSERT((unsigned)index < (unsigned)fFieldCount);
+ if (field)
+ *field = fFields[index];
+ }
+ virtual int findFieldIndex(const char field[])
+ {
+ for (int i = 0; i < fFieldCount; i++)
+ if (fFields[i].equals(field))
+ return i;
+ return -1;
+ }
+
+ virtual int countRecords() { return fRecordCount; }
+ virtual void getRecord(int rowIndex, int fieldIndex, SkString* data)
+ {
+ SkASSERT((unsigned)rowIndex < (unsigned)fRecordCount);
+ SkASSERT((unsigned)fieldIndex < (unsigned)fFieldCount);
+ if (data)
+ *data = fRecords[rowIndex * fFieldCount + fieldIndex];
+ }
+
+ virtual bool prepareWidgetEvent(SkEvent* evt, int rowIndex)
+ {
+ // hack, for testing right now. Need the xml to tell us what to jam in and where
+ SkString data;
+
+ this->getRecord(rowIndex, 0, &data);
+ evt->setString("xml-listsource", data.c_str());
+ return true;
+ }
+
+private:
+ SkString* fFields; // [fFieldCount]
+ SkString* fRecords; // [fRecordCount][fFieldCount]
+ int fFieldCount, fRecordCount;
+};
+
+#include "SkDOM.h"
+
+SkXMLListSource::SkXMLListSource(const char doc[], size_t len)
+{
+ fFieldCount = fRecordCount = 0;
+ fFields = fRecords = nil;
+
+ SkDOM dom;
+
+ const SkDOM::Node* node = dom.build(doc, len);
+ SkASSERT(node);
+ const SkDOM::Node* child;
+
+ child = dom.getFirstChild(node, "fields");
+ if (child)
+ {
+ fFieldCount = dom.countChildren(child, "field");
+ fFields = new SkString[fFieldCount];
+
+ int n = 0;
+ child = dom.getFirstChild(child, "field");
+ while (child)
+ {
+ fFields[n].set(dom.findAttr(child, "name"));
+ child = dom.getNextSibling(child, "field");
+ n += 1;
+ }
+ SkASSERT(n == fFieldCount);
+ }
+
+ child = dom.getFirstChild(node, "records");
+ if (child)
+ {
+ fRecordCount = dom.countChildren(child, "record");
+ fRecords = new SkString[fRecordCount * fFieldCount];
+
+ int n = 0;
+ child = dom.getFirstChild(child, "record");
+ while (child)
+ {
+ for (int i = 0; i < fFieldCount; i++)
+ fRecords[n * fFieldCount + i].set(dom.findAttr(child, fFields[i].c_str()));
+ child = dom.getNextSibling(child, "record");
+ n += 1;
+ }
+ SkASSERT(n == fRecordCount);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////
+
+SkListSource* SkListSource::Factory(const char name[])
+{
+ static const char gDoc[] =
+ "<db name='contacts.db'>"
+ "<fields>"
+ "<field name='name'/>"
+ "<field name='work-num'/>"
+ "<field name='home-num'/>"
+ "<field name='type'/>"
+ "</fields>"
+ "<records>"
+ "<record name='Andy McFadden' work-num='919 357-1234' home-num='919 123-4567' type='0'/>"
+ "<record name='Brian Swetland' work-num='919 123-1234' home-num='929 123-4567' type='1' />"
+ "<record name='Chris Desalvo' work-num='919 345-1234' home-num='949 123-4567' type='1' />"
+ "<record name='Chris White' work-num='919 234-1234' home-num='939 123-4567' type='2' />"
+ "<record name='Dan Bornstein' work-num='919 357-1234' home-num='919 123-4567' type='0' />"
+ "<record name='Don Cung' work-num='919 123-1234' home-num='929 123-4567' type='2' />"
+ "<record name='Eric Fischer' work-num='919 345-1234' home-num='949 123-4567' type='2' />"
+ "<record name='Ficus Kirkpatric' work-num='919 234-1234' home-num='939 123-4567' type='1' />"
+ "<record name='Jack Veenstra' work-num='919 234-1234' home-num='939 123-4567' type='2' />"
+ "<record name='Jeff Yaksick' work-num='919 234-1234' home-num='939 123-4567' type='0' />"
+ "<record name='Joe Onorato' work-num='919 234-1234' home-num='939 123-4567' type='0' />"
+ "<record name='Mathias Agopian' work-num='919 234-1234' home-num='939 123-4567' type='1' />"
+ "<record name='Mike Fleming' work-num='919 234-1234' home-num='939 123-4567' type='2' />"
+ "<record name='Nick Sears' work-num='919 234-1234' home-num='939 123-4567' type='1' />"
+ "<record name='Rich Miner' work-num='919 234-1234' home-num='939 123-4567' type='1' />"
+ "<record name='Tracey Cole' work-num='919 234-1234' home-num='939 123-4567' type='0' />"
+ "<record name='Wei Huang' work-num='919 234-1234' home-num='939 123-4567' type='0' />"
+ "</records>"
+ "</db>";
+
+//SkDebugf("doc size %d\n", sizeof(gDoc)-1);
+ return new SkXMLListSource(gDoc, sizeof(gDoc) - 1);
+}
+
+
+
diff --git a/src/views/SkMetaData.cpp b/src/views/SkMetaData.cpp
new file mode 100644
index 0000000..c366bd3
--- /dev/null
+++ b/src/views/SkMetaData.cpp
@@ -0,0 +1,405 @@
+/* libs/graphics/views/SkMetaData.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/views/SkOSFile.cpp b/src/views/SkOSFile.cpp
new file mode 100644
index 0000000..c8eeeea
--- /dev/null
+++ b/src/views/SkOSFile.cpp
@@ -0,0 +1,223 @@
+#include "SkOSFile.h"
+
+#ifdef SK_BUILD_FOR_WIN
+
+static U16* concat_to_16(const char src[], const char suffix[])
+{
+ size_t i, len = strlen(src);
+ size_t len2 = 3 + (suffix ? strlen(suffix) : 0);
+ U16* dst = (U16*)sk_malloc_throw((len + len2) * sizeof(U16));
+
+ for (i = 0; i < len; i++)
+ dst[i] = src[i];
+
+ if (i > 0 && dst[i-1] != '/')
+ dst[i++] = '/';
+ dst[i++] = '*';
+
+ if (suffix)
+ {
+ while (*suffix)
+ dst[i++] = *suffix++;
+ }
+ dst[i] = 0;
+ SkASSERT(i + 1 <= len + len2);
+
+ return dst;
+}
+
+SkUTF16_Str::SkUTF16_Str(const char src[])
+{
+ size_t len = strlen(src);
+
+ fStr = (U16*)sk_malloc_throw((len + 1) * sizeof(U16));
+ for (size_t i = 0; i < len; i++)
+ fStr[i] = src[i];
+ fStr[i] = 0;
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+SkOSFile::Iter::Iter() : fHandle(0), fPath16(nil)
+{
+}
+
+SkOSFile::Iter::Iter(const char path[], const char suffix[]) : fHandle(0), fPath16(nil)
+{
+ this->reset(path, suffix);
+}
+
+SkOSFile::Iter::~Iter()
+{
+ sk_free(fPath16);
+ if (fHandle)
+ ::FindClose(fHandle);
+}
+
+void SkOSFile::Iter::reset(const char path[], const char suffix[])
+{
+ if (fHandle)
+ {
+ ::FindClose(fHandle);
+ fHandle = 0;
+ }
+ if (path == nil)
+ path = "";
+
+ sk_free(fPath16);
+ fPath16 = concat_to_16(path, suffix);
+}
+
+static bool is_magic_dir(const U16 dir[])
+{
+ // return true for "." and ".."
+ return dir[0] == '.' && (dir[1] == 0 || dir[1] == '.' && dir[2] == 0);
+}
+
+static bool get_the_file(HANDLE handle, SkString* name, WIN32_FIND_DATAW* dataPtr, bool getDir)
+{
+ WIN32_FIND_DATAW data;
+
+ if (dataPtr == nil)
+ {
+ if (::FindNextFileW(handle, &data))
+ dataPtr = &data;
+ else
+ return false;
+ }
+
+ for (;;)
+ {
+ if (getDir)
+ {
+ if ((dataPtr->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && !is_magic_dir(dataPtr->cFileName))
+ break;
+ }
+ else
+ {
+ if (!(dataPtr->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
+ break;
+ }
+ if (!::FindNextFileW(handle, dataPtr))
+ return false;
+ }
+ // if we get here, we've found a file/dir
+ if (name)
+ name->setUTF16(dataPtr->cFileName);
+ return true;
+}
+
+bool SkOSFile::Iter::next(SkString* name, bool getDir)
+{
+ WIN32_FIND_DATAW data;
+ WIN32_FIND_DATAW* dataPtr = nil;
+
+ if (fHandle == 0) // our first time
+ {
+ if (fPath16 == nil || *fPath16 == 0) // check for no path
+ return false;
+
+ fHandle = ::FindFirstFileW(fPath16, &data);
+ if (fHandle != 0 && fHandle != (HANDLE)~0)
+ dataPtr = &data;
+ }
+ return fHandle != (HANDLE)~0 && get_the_file(fHandle, name, dataPtr, getDir);
+}
+
+#elif defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_UNIX)
+
+#if 0
+OSStatus FSPathMakeRef (
+ const UInt8 * path,
+ FSRef * ref,
+ Boolean * isDirectory
+);
+#endif
+
+SkOSFile::Iter::Iter() : fDIR(0)
+{
+}
+
+SkOSFile::Iter::Iter(const char path[], const char suffix[]) : fDIR(0)
+{
+ this->reset(path, suffix);
+}
+
+SkOSFile::Iter::~Iter()
+{
+ if (fDIR)
+ ::closedir(fDIR);
+}
+
+void SkOSFile::Iter::reset(const char path[], const char suffix[])
+{
+ if (fDIR)
+ {
+ ::closedir(fDIR);
+ fDIR = 0;
+ }
+
+ fPath.set(path);
+ if (path)
+ {
+ fDIR = ::opendir(path);
+ fSuffix.set(suffix);
+ }
+ else
+ fSuffix.reset();
+}
+
+// returns true if suffix is empty, or if str ends with suffix
+static bool issuffixfor(const SkString& suffix, const char str[])
+{
+ size_t suffixLen = suffix.size();
+ size_t strLen = strlen(str);
+
+ return strLen >= suffixLen &&
+ suffixLen == 0 ||
+ memcmp(suffix.c_str(), str + strLen - suffixLen, suffixLen) == 0;
+}
+
+#include <sys/stat.h>
+
+bool SkOSFile::Iter::next(SkString* name, bool getDir)
+{
+ if (fDIR)
+ {
+ dirent* entry;
+
+ while ((entry = ::readdir(fDIR)) != NULL)
+ {
+ struct stat s;
+ SkString str(fPath);
+
+ if (!str.endsWith("/") && !str.endsWith("\\"))
+ str.append("/");
+ str.append(entry->d_name);
+
+ if (0 == stat(str.c_str(), &s))
+ {
+ if (getDir)
+ {
+ if (s.st_mode & S_IFDIR)
+ break;
+ }
+ else
+ {
+ if (!(s.st_mode & S_IFDIR) && issuffixfor(fSuffix, entry->d_name))
+ break;
+ }
+ }
+ }
+ if (entry) // we broke out with a file
+ {
+ if (name)
+ name->set(entry->d_name);
+ return true;
+ }
+ }
+ return false;
+}
+
+#endif
+
diff --git a/src/views/SkOSMenu.cpp b/src/views/SkOSMenu.cpp
new file mode 100644
index 0000000..3760ddd
--- /dev/null
+++ b/src/views/SkOSMenu.cpp
@@ -0,0 +1,53 @@
+#include "SkOSMenu.h"
+
+static int gOSMenuCmd = 7000;
+
+SkOSMenu::SkOSMenu(const char title[])
+{
+ fTitle = title;
+}
+
+SkOSMenu::~SkOSMenu()
+{
+}
+
+int SkOSMenu::countItems() const
+{
+ return fItems.count();
+}
+
+void SkOSMenu::appendItem(const char title[], const char eventType[], int32_t eventData)
+{
+ Item* item = fItems.append();
+
+ item->fTitle = title;
+ item->fEventType = eventType;
+ item->fEventData = eventData;
+ item->fOSCmd = ++gOSMenuCmd;
+}
+
+SkEvent* SkOSMenu::createEvent(uint32_t os_cmd)
+{
+ const Item* iter = fItems.begin();
+ const Item* stop = fItems.end();
+
+ while (iter < stop)
+ {
+ if (iter->fOSCmd == os_cmd)
+ {
+ SkEvent* evt = new SkEvent(iter->fEventType);
+ evt->setFast32(iter->fEventData);
+ return evt;
+ }
+ iter++;
+ }
+ return NULL;
+}
+
+const char* SkOSMenu::getItem(int index, uint32_t* cmdID) const
+{
+ if (cmdID)
+ *cmdID = fItems[index].fOSCmd;
+ return fItems[index].fTitle;
+}
+
diff --git a/src/views/SkOSSound.cpp b/src/views/SkOSSound.cpp
new file mode 100644
index 0000000..13cd037
--- /dev/null
+++ b/src/views/SkOSSound.cpp
@@ -0,0 +1,343 @@
+#include "SkOSSound.h"
+
+#ifdef SK_BUILD_FOR_WIN
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+
+#include <Mmreg.h>
+#if defined _WIN32 && _MSC_VER >= 1300 // disable nameless struct/union
+#pragma warning ( push )
+#pragma warning ( disable : 4201 )
+#endif
+#include <Mmsystem.h>
+#if defined _WIN32 && _MSC_VER >= 1300
+#pragma warning ( pop )
+#endif
+#include <stdio.h>
+
+class CWaveFile {
+public:
+ BOOL Open(const char path[]);
+ void Close();
+
+ long Read(char* pData, long nLength);
+
+ long GetLength() const {return m_nLength;}
+ WAVEFORMATEX* GetWaveFormat() {return (&m_Format);}
+
+protected:
+ FILE* m_pFile;
+ long m_nLength;
+ WAVEFORMATEX m_Format;
+
+private:
+ enum {
+ WF_OFFSET_FORMATTAG = 20,
+ WF_OFFSET_CHANNELS = 22,
+ WF_OFFSET_SAMPLESPERSEC = 24,
+ WF_OFFSET_AVGBYTESPERSEC = 28,
+ WF_OFFSET_BLOCKALIGN = 32,
+ WF_OFFSET_BITSPERSAMPLE = 34,
+ WF_OFFSET_DATASIZE = 40,
+ WF_OFFSET_DATA = 44,
+ WF_HEADER_SIZE = WF_OFFSET_DATA
+ };
+};
+
+BOOL CWaveFile::Open(const char path[])
+{
+ BYTE aHeader[WF_HEADER_SIZE];
+
+/* hResInfo = FindResource (hInst, lpName, "WAVE");
+
+ if (hResInfo == NULL)
+ return FALSE;
+
+ // Load the wave resource.
+ hRes = LoadResource (hInst, hResInfo);
+
+ if (hRes == NULL)
+ return FALSE;
+
+ // Lock the wave resource and play it.
+ lpRes = LockResource (0);
+*/
+
+
+ // open file
+// m_pFile = _tfopen(szFileName, TEXT("rb"));
+ m_pFile = fopen(path, "rb");
+ if (!m_pFile) {
+ return FALSE;
+ }
+
+ // set file length
+ fseek(m_pFile, 0, SEEK_END);
+ m_nLength = ftell(m_pFile) - WF_HEADER_SIZE;
+
+ // set the format attribute members
+ fseek(m_pFile, 0, SEEK_SET);
+ fread(aHeader, 1, WF_HEADER_SIZE, m_pFile);
+ m_Format.wFormatTag = *((WORD*) (aHeader + WF_OFFSET_FORMATTAG));
+ m_Format.nChannels = *((WORD*) (aHeader + WF_OFFSET_CHANNELS));
+ m_Format.nSamplesPerSec = *((DWORD*) (aHeader + WF_OFFSET_SAMPLESPERSEC));
+ m_Format.nAvgBytesPerSec = *((DWORD*) (aHeader + WF_OFFSET_AVGBYTESPERSEC));
+ m_Format.nBlockAlign = *((WORD*) (aHeader + WF_OFFSET_BLOCKALIGN));
+ m_Format.wBitsPerSample = *((WORD*) (aHeader + WF_OFFSET_BITSPERSAMPLE));
+
+ return TRUE;
+}
+
+void CWaveFile::Close()
+{
+ fclose(m_pFile);
+}
+
+long CWaveFile::Read(char* pData, long nLength)
+{
+ return fread(pData, 1, nLength, m_pFile);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+
+struct SkOSSoundWave {
+ HWAVEOUT hwo;
+ WAVEHDR whdr;
+ DWORD dwOldVolume;
+ CWaveFile waveFile;
+ HANDLE hDoneEvent;
+};
+
+static SkOSSoundWave gWave;
+static bool gWavePaused;
+static U8 gVolume;
+static bool gInited = false;
+
+static void init_wave()
+{
+ if (gInited == false)
+ {
+ gWave.hwo = nil;
+ gWavePaused = false;
+ gVolume = 0x80;
+ gInited = true;
+ }
+}
+
+MMRESULT StartWave(const char path[], SkOSSoundWave* wave, U32 vol);
+MMRESULT EndWave(SkOSSoundWave* wave);
+
+#define MAX_ERRMSG 256
+
+//#include "SkOSFile.h" // for utf16
+
+void SkOSSound::Play(const char path[])
+{
+ init_wave();
+
+ if (gWave.hwo != nil)
+ SkOSSound::Stop();
+
+ U32 v32 = (gVolume << 8) | gVolume; // fill it out to 16bits
+ v32 |= v32 << 16; // set the left and right channels
+
+ StartWave(path, &gWave, v32);
+ gWavePaused = false;
+}
+
+bool SkOSSound::TogglePause()
+{
+ init_wave();
+
+ if (gWavePaused)
+ SkOSSound::Resume();
+ else
+ SkOSSound::Pause();
+ return !gWavePaused;
+}
+
+
+void SkOSSound::Pause()
+{
+ init_wave();
+
+ if (gWave.hwo == nil || (gWave.whdr.dwFlags & WHDR_DONE))
+ return;
+ waveOutPause(gWave.hwo);
+ gWavePaused = true;
+}
+
+void SkOSSound::Resume()
+{
+ init_wave();
+
+ if (gWave.hwo == nil || (gWave.whdr.dwFlags & WHDR_DONE))
+ return;
+ waveOutRestart(gWave.hwo);
+ gWavePaused = false;
+}
+
+void SkOSSound::Stop()
+{
+ init_wave();
+
+// if (gWave.hwo == nil || (gWave.whdr.dwFlags & WHDR_DONE))
+ if (gWave.hwo == nil)
+ return;
+ waveOutReset(gWave.hwo);
+ EndWave(&gWave);
+ gWavePaused = false;
+ gWave.hwo = nil;
+}
+
+U8 SkOSSound::GetVolume()
+{
+ init_wave();
+ return gVolume;
+}
+
+void SkOSSound::SetVolume(U8CPU vol)
+{
+ if ((int)vol < 0)
+ vol = 0;
+ else if (vol > 255)
+ vol = 255;
+
+ init_wave();
+ gVolume = SkToU8(vol);
+
+ if (gWave.hwo)
+ {
+ unsigned long v32 = (vol << 8) | vol; // fill it out to 16bits
+ v32 |= v32 << 16; // set the left and right channels
+ waveOutSetVolume(gWave.hwo, v32);
+ }
+}
+
+#if 0
+unsigned long SoundManager::GetPosition()
+{
+ if (fWave.hwo == nil)
+ return 0;
+ MMTIME time;
+ time.wType = TIME_MS;
+ if (waveOutGetPosition(fWave.hwo, &time, sizeof(time)) == MMSYSERR_NOERROR &&
+ time.wType == TIME_MS)
+ {
+ return time.u.ms;
+ }
+ return 0;
+}
+#endif
+
+MMRESULT StartWave(const char path[], SkOSSoundWave* wave, U32 vol)
+{
+ HWAVEOUT hwo = nil;
+// WAVEHDR whdr;
+ MMRESULT mmres = 0;
+// CWaveFile waveFile;
+// HANDLE hDoneEvent = wave.hDoneEvent =
+// CreateEvent(NULL, FALSE, FALSE, TEXT("DONE_EVENT"));
+ UINT devId;
+// DWORD dwOldVolume;
+
+ // Open wave file
+ if (!wave->waveFile.Open(path)) {
+// TCHAR szErrMsg[MAX_ERRMSG];
+// _stprintf(szErrMsg, TEXT("Unable to open file: %s\n"), szWavFile);
+// MessageBox(NULL, szErrMsg, TEXT("File I/O Error"), MB_OK);
+ return MMSYSERR_NOERROR;
+ }
+
+ // Open audio device
+ for (devId = 0; devId < waveOutGetNumDevs(); devId++)
+ {
+ mmres = waveOutOpen(&hwo, devId, wave->waveFile.GetWaveFormat(), 0, 0, CALLBACK_NULL);
+ if (mmres == MMSYSERR_NOERROR)
+ {
+ wave->hwo = hwo;
+ break;
+ }
+ }
+ if (mmres != MMSYSERR_NOERROR)
+ {
+ SkDEBUGCODE(SkDebugf("waveOutOpen(%s) -> %d\n", path, mmres);)
+ return mmres;
+ }
+
+ // Set volume
+ mmres = waveOutGetVolume(hwo, &wave->dwOldVolume);
+ if (mmres != MMSYSERR_NOERROR) {
+ return mmres;
+ }
+
+ waveOutSetVolume(hwo, vol);
+ if (mmres != MMSYSERR_NOERROR) {
+ return mmres;
+ }
+
+ // Initialize wave header
+ ZeroMemory(&wave->whdr, sizeof(WAVEHDR));
+ wave->whdr.lpData = new char[wave->waveFile.GetLength()];
+ wave->whdr.dwBufferLength = wave->waveFile.GetLength();
+ wave->whdr.dwUser = 0;
+ wave->whdr.dwFlags = 0;
+ wave->whdr.dwLoops = 0;
+ wave->whdr.dwBytesRecorded = 0;
+ wave->whdr.lpNext = 0;
+ wave->whdr.reserved = 0;
+
+ // Play buffer
+ wave->waveFile.Read(wave->whdr.lpData, wave->whdr.dwBufferLength);
+
+ mmres = waveOutPrepareHeader(hwo, &wave->whdr, sizeof(WAVEHDR));
+ if (mmres != MMSYSERR_NOERROR) {
+ return mmres;
+ }
+
+ mmres = waveOutWrite(hwo, &wave->whdr, sizeof(WAVEHDR));
+// if (mmres != MMSYSERR_NOERROR) {
+ return mmres;
+// }
+}
+
+#if 0
+void IdleWave(Wave& wave)
+{
+ // Wait for audio to finish playing
+ while (!(wave.whdr.dwFlags & WHDR_DONE)) {
+ WaitForSingleObject(wave.hDoneEvent, INFINITE);
+ }
+}
+#endif
+
+MMRESULT EndWave(SkOSSoundWave* wave)
+{
+ HWAVEOUT hwo = wave->hwo;
+ MMRESULT mmres;
+ // Clean up
+ mmres = waveOutUnprepareHeader(hwo, &wave->whdr, sizeof(WAVEHDR));
+ if (mmres != MMSYSERR_NOERROR) {
+ return mmres;
+ }
+
+ waveOutSetVolume(hwo, wave->dwOldVolume);
+ if (mmres != MMSYSERR_NOERROR) {
+ return mmres;
+ }
+
+ mmres = waveOutClose(hwo);
+ if (mmres != MMSYSERR_NOERROR) {
+ return mmres;
+ }
+
+ delete [] wave->whdr.lpData;
+ wave->waveFile.Close();
+
+ return MMSYSERR_NOERROR;
+}
+
+#endif /* SK_BUILD_FOR_WIN */
+
diff --git a/src/views/SkParsePaint.cpp b/src/views/SkParsePaint.cpp
new file mode 100644
index 0000000..7bf0244
--- /dev/null
+++ b/src/views/SkParsePaint.cpp
@@ -0,0 +1,103 @@
+#include "SkParsePaint.h"
+#include "SkTSearch.h"
+#include "SkParse.h"
+#include "SkImageDecoder.h"
+#include "SkGradientShader.h"
+
+static SkShader* inflate_shader(const SkDOM& dom, const SkDOM::Node* node)
+{
+ if ((node = dom.getFirstChild(node, "shader")) == nil)
+ return nil;
+
+ const char* str;
+
+ if (dom.hasAttr(node, "type", "linear-gradient"))
+ {
+ SkColor colors[2];
+ SkPoint pts[2];
+
+ colors[0] = colors[1] = SK_ColorBLACK; // need to initialized the alpha to opaque, since FindColor doesn't set it
+ if ((str = dom.findAttr(node, "c0")) != nil &&
+ SkParse::FindColor(str, &colors[0]) &&
+ (str = dom.findAttr(node, "c1")) != nil &&
+ SkParse::FindColor(str, &colors[1]) &&
+ dom.findScalars(node, "p0", &pts[0].fX, 2) &&
+ dom.findScalars(node, "p1", &pts[1].fX, 2))
+ {
+ SkShader::TileMode mode = SkShader::kClamp_TileMode;
+ int index;
+
+ if ((index = dom.findList(node, "tile-mode", "clamp,repeat,mirror")) >= 0)
+ mode = (SkShader::TileMode)index;
+ return SkGradientShader::CreateLinear(pts, colors, nil, 2, mode);
+ }
+ }
+ else if (dom.hasAttr(node, "type", "bitmap"))
+ {
+ if ((str = dom.findAttr(node, "src")) == nil)
+ return nil;
+
+ SkBitmap bm;
+
+ if (SkImageDecoder::DecodeFile(str, &bm))
+ {
+ SkShader::TileMode mode = SkShader::kRepeat_TileMode;
+ int index;
+
+ if ((index = dom.findList(node, "tile-mode", "clamp,repeat,mirror")) >= 0)
+ mode = (SkShader::TileMode)index;
+
+ return SkShader::CreateBitmapShader(bm, mode, mode);
+ }
+ }
+ return nil;
+}
+
+void SkPaint_Inflate(SkPaint* paint, const SkDOM& dom, const SkDOM::Node* node)
+{
+ SkASSERT(paint);
+ SkASSERT(&dom);
+ SkASSERT(node);
+
+ SkScalar x;
+
+ if (dom.findScalar(node, "stroke-width", &x))
+ paint->setStrokeWidth(x);
+ if (dom.findScalar(node, "text-size", &x))
+ paint->setTextSize(x);
+
+ bool b;
+
+ SkASSERT("legacy: use is-stroke" && !dom.findBool(node, "is-frame", &b));
+
+ if (dom.findBool(node, "is-stroke", &b))
+ paint->setStyle(b ? SkPaint::kStroke_Style : SkPaint::kFill_Style);
+ if (dom.findBool(node, "is-antialias", &b))
+ paint->setAntiAlias(b);
+ if (dom.findBool(node, "is-lineartext", &b))
+ paint->setLinearText(b);
+
+ const char* str = dom.findAttr(node, "color");
+ if (str)
+ {
+ SkColor c = paint->getColor();
+ if (SkParse::FindColor(str, &c))
+ paint->setColor(c);
+ }
+
+ // do this AFTER parsing for the color
+ if (dom.findScalar(node, "opacity", &x))
+ {
+ x = SkMaxScalar(0, SkMinScalar(x, SK_Scalar1));
+ paint->setAlpha(SkScalarRound(x * 255));
+ }
+
+ int index = dom.findList(node, "text-anchor", "left,center,right");
+ if (index >= 0)
+ paint->setTextAlign((SkPaint::Align)index);
+
+ SkShader* shader = inflate_shader(dom, node);
+ if (shader)
+ paint->setShader(shader)->unref();
+}
+
diff --git a/src/views/SkProgressBarView.cpp b/src/views/SkProgressBarView.cpp
new file mode 100644
index 0000000..21349a5
--- /dev/null
+++ b/src/views/SkProgressBarView.cpp
@@ -0,0 +1,102 @@
+#include "SkProgressBarView.h"
+#include "SkAnimator.h"
+#include "SkWidgetViews.h"
+#include "SkTime.h"
+#include "SkSystemEventTypes.h"
+
+SkProgressBarView::SkProgressBarView()
+{
+ init_skin_anim(kProgress_SkinEnum, &fAnim);
+ fAnim.setHostEventSink(this);
+ fProgress = 0;
+ fMax = 100;
+
+}
+
+void SkProgressBarView::changeProgress(int diff)
+{
+ int newProg = fProgress + diff;
+ if (newProg > 0 && newProg < fMax)
+ this->setProgress(newProg);
+ //otherwise i'll just leave it as it is
+ //this implies that if a new max and progress are set, max must be set first
+}
+
+/*virtual*/ void SkProgressBarView::onDraw(SkCanvas* canvas)
+{
+ SkPaint paint;
+ SkAnimator::DifferenceType diff = fAnim.draw(canvas, &paint, SkTime::GetMSecs());
+
+ if (diff == SkAnimator::kDifferent)
+ this->inval(nil);
+ else if (diff == SkAnimator::kPartiallyDifferent)
+ {
+ SkRect bounds;
+ fAnim.getInvalBounds(&bounds);
+ this->inval(&bounds);
+ }
+}
+
+/*virtual*/ bool SkProgressBarView::onEvent(const SkEvent& evt)
+{
+ if (evt.isType(SK_EventType_Inval))
+ {
+ this->inval(nil);
+ return true;
+ }
+ if (evt.isType("recommendDim"))
+ {
+ SkScalar height;
+
+ if (evt.findScalar("y", &height))
+ this->setHeight(height);
+ return true;
+ }
+ return this->INHERITED::onEvent(evt);
+}
+
+/*virtual*/ void SkProgressBarView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ this->INHERITED::onInflate(dom, node);
+ int32_t temp;
+ if (dom.findS32(node, "max", &temp))
+ this->setMax(temp);
+ if (dom.findS32(node, "progress", &temp))
+ this->setProgress(temp);
+}
+
+/*virtual*/ void SkProgressBarView::onSizeChange()
+{
+ this->INHERITED::onSizeChange();
+ SkEvent evt("user");
+ evt.setString("id", "setDim");
+ evt.setScalar("dimX", this->width());
+ evt.setScalar("dimY", this->height());
+ fAnim.doUserEvent(evt);
+}
+
+void SkProgressBarView::reset()
+{
+ fProgress = 0;
+ SkEvent e("user");
+ e.setString("id", "reset");
+ fAnim.doUserEvent(e);
+}
+
+void SkProgressBarView::setMax(int max)
+{
+ fMax = max;
+ SkEvent e("user");
+ e.setString("id", "setMax");
+ e.setS32("newMax", max);
+ fAnim.doUserEvent(e);
+}
+
+void SkProgressBarView::setProgress(int progress)
+{
+ fProgress = progress;
+ SkEvent e("user");
+ e.setString("id", "setProgress");
+ e.setS32("newProgress", progress);
+ fAnim.doUserEvent(e);
+}
diff --git a/src/views/SkProgressView.cpp b/src/views/SkProgressView.cpp
new file mode 100644
index 0000000..a34c284
--- /dev/null
+++ b/src/views/SkProgressView.cpp
@@ -0,0 +1,125 @@
+#include "SkWidget.h"
+#include "SkCanvas.h"
+#include "SkShader.h"
+#include "SkInterpolator.h"
+#include "SkTime.h"
+
+SkProgressView::SkProgressView(uint32_t flags) : SkView(flags), fOnShader(NULL), fOffShader(NULL)
+{
+ fValue = 0;
+ fMax = 0;
+ fInterp = NULL;
+ fDoInterp = false;
+}
+
+SkProgressView::~SkProgressView()
+{
+ delete fInterp;
+ fOnShader->safeUnref();
+ fOffShader->safeUnref();
+}
+
+void SkProgressView::setMax(U16CPU max)
+{
+ if (fMax != max)
+ {
+ fMax = SkToU16(max);
+ if (fValue > 0)
+ this->inval(NULL);
+ }
+}
+
+void SkProgressView::setValue(U16CPU value)
+{
+ if (fValue != value)
+ {
+ if (fDoInterp)
+ {
+ if (fInterp)
+ delete fInterp;
+ fInterp = new SkInterpolator(1, 2);
+ SkScalar x = (SkScalar)(fValue << 8);
+ fInterp->setKeyFrame(0, SkTime::GetMSecs(), &x, 0);
+ x = (SkScalar)(value << 8);
+ fInterp->setKeyFrame(1, SkTime::GetMSecs() + 333, &x);
+ }
+ fValue = SkToU16(value);
+ this->inval(NULL);
+ }
+}
+
+void SkProgressView::onDraw(SkCanvas* canvas)
+{
+ if (fMax == 0)
+ return;
+
+ SkFixed percent;
+
+ if (fInterp)
+ {
+ SkScalar x;
+ if (fInterp->timeToValues(SkTime::GetMSecs(), &x) == SkInterpolator::kFreezeEnd_Result)
+ {
+ delete fInterp;
+ fInterp = NULL;
+ }
+ percent = (SkFixed)x; // now its 16.8
+ percent = SkMax32(0, SkMin32(percent, fMax << 8)); // now its pinned
+ percent = SkFixedDiv(percent, fMax << 8); // now its 0.16
+ this->inval(NULL);
+ }
+ else
+ {
+ U16CPU value = SkMax32(0, SkMin32(fValue, fMax));
+ percent = SkFixedDiv(value, fMax);
+ }
+
+
+ SkRect r;
+ SkPaint p;
+
+ r.set(0, 0, this->width(), this->height());
+ p.setAntiAlias(true);
+
+ r.fRight = r.fLeft + SkScalarMul(r.width(), SkFixedToScalar(percent));
+ p.setStyle(SkPaint::kFill_Style);
+
+ p.setColor(SK_ColorDKGRAY);
+ p.setShader(fOnShader);
+ canvas->drawRect(r, p);
+
+ p.setColor(SK_ColorWHITE);
+ p.setShader(fOffShader);
+ r.fLeft = r.fRight;
+ r.fRight = this->width() - SK_Scalar1;
+ if (r.width() > 0)
+ canvas->drawRect(r, p);
+}
+
+#include "SkImageDecoder.h"
+
+static SkShader* inflate_shader(const char file[])
+{
+ SkBitmap bm;
+
+ return SkImageDecoder::DecodeFile(file, &bm) ?
+ SkShader::CreateBitmapShader(bm, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode) :
+ NULL;
+}
+
+void SkProgressView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ this->INHERITED::onInflate(dom, node);
+
+ const char* s;
+
+ SkASSERT(fOnShader == NULL);
+ SkASSERT(fOffShader == NULL);
+
+ if ((s = dom.findAttr(node, "src-on")) != NULL)
+ fOnShader = inflate_shader(s);
+ if ((s = dom.findAttr(node, "src-off")) != NULL)
+ fOffShader = inflate_shader(s);
+ (void)dom.findBool(node, "do-interp", &fDoInterp);
+}
+
diff --git a/src/views/SkScrollBarView.cpp b/src/views/SkScrollBarView.cpp
new file mode 100644
index 0000000..2e087bd
--- /dev/null
+++ b/src/views/SkScrollBarView.cpp
@@ -0,0 +1,139 @@
+#include "SkScrollBarView.h"
+#include "SkAnimator.h"
+#include "SkWidgetViews.h"
+#include "SkSystemEventTypes.h"
+#include "SkTime.h"
+
+//see SkProgressBarView.cpp
+//#include "SkWidgetViews.cpp"
+
+SkScrollBarView::SkScrollBarView()
+{
+ fAnim.setHostEventSink(this);
+ init_skin_anim(kScroll_SkinEnum, &fAnim);
+
+ fTotalLength = 0;
+ fStartPoint = 0;
+ fShownLength = 0;
+
+ this->adjust();
+}
+
+void SkScrollBarView::setStart(unsigned start)
+{
+ if ((int)start < 0)
+ start = 0;
+
+ if (fStartPoint != start)
+ {
+ fStartPoint = start;
+ this->adjust();
+ }
+}
+
+void SkScrollBarView::setShown(unsigned shown)
+{
+ if ((int)shown < 0)
+ shown = 0;
+
+ if (fShownLength != shown)
+ {
+ fShownLength = shown;
+ this->adjust();
+ }
+}
+
+void SkScrollBarView::setTotal(unsigned total)
+{
+ if ((int)total < 0)
+ total = 0;
+
+ if (fTotalLength != total)
+ {
+ fTotalLength = total;
+ this->adjust();
+ }
+}
+
+/* virtual */ void SkScrollBarView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ this->INHERITED::onInflate(dom, node);
+
+ int32_t value;
+ if (dom.findS32(node, "total", &value))
+ this->setTotal(value);
+ if (dom.findS32(node, "shown", &value))
+ this->setShown(value);
+}
+
+/*virtual*/ void SkScrollBarView::onSizeChange()
+{
+ this->INHERITED::onSizeChange();
+ SkEvent evt("user");
+ evt.setString("id", "setDim");
+ evt.setScalar("dimX", this->width());
+ evt.setScalar("dimY", this->height());
+ fAnim.doUserEvent(evt);
+}
+
+/*virtual*/ void SkScrollBarView::onDraw(SkCanvas* canvas)
+{
+ SkPaint paint;
+ SkAnimator::DifferenceType diff = fAnim.draw(canvas, &paint, SkTime::GetMSecs());
+
+ if (diff == SkAnimator::kDifferent)
+ this->inval(NULL);
+ else if (diff == SkAnimator::kPartiallyDifferent)
+ {
+ SkRect bounds;
+ fAnim.getInvalBounds(&bounds);
+ this->inval(&bounds);
+ }
+}
+
+/*virtual*/ bool SkScrollBarView::onEvent(const SkEvent& evt)
+{
+ if (evt.isType(SK_EventType_Inval))
+ {
+ this->inval(NULL);
+ return true;
+ }
+ if (evt.isType("recommendDim"))
+ {
+ SkScalar width;
+
+ if (evt.findScalar("x", &width))
+ this->setWidth(width);
+ return true;
+ }
+
+ return this->INHERITED::onEvent(evt);
+}
+
+void SkScrollBarView::adjust()
+{
+ int total = fTotalLength;
+ int start = fStartPoint;
+ int shown = fShownLength;
+ int hideBar = 0;
+
+ if (total <= 0 || shown <= 0 || shown >= total) // no bar to show
+ {
+ total = 1; // avoid divide-by-zero. should be done by skin/script
+ hideBar = 1; // signal we don't want a thumb
+ }
+ else
+ {
+ if (start + shown > total)
+ start = total - shown;
+ }
+
+ SkEvent e("user");
+ e.setString("id", "adjustScrollBar");
+ e.setScalar("_totalLength", SkIntToScalar(total));
+ e.setScalar("_startPoint", SkIntToScalar(start));
+ e.setScalar("_shownLength", SkIntToScalar(shown));
+// e.setS32("hideBar", hideBar);
+ fAnim.doUserEvent(e);
+}
+
diff --git a/src/views/SkStackViewLayout.cpp b/src/views/SkStackViewLayout.cpp
new file mode 100644
index 0000000..ae2e9e8
--- /dev/null
+++ b/src/views/SkStackViewLayout.cpp
@@ -0,0 +1,262 @@
+#include "SkStackViewLayout.h"
+
+SkStackViewLayout::SkStackViewLayout()
+{
+ fMargin.set(0, 0, 0, 0);
+ fSpacer = 0;
+ fOrient = kHorizontal_Orient;
+ fPack = kStart_Pack;
+ fAlign = kStart_Align;
+ fRound = false;
+}
+
+void SkStackViewLayout::setOrient(Orient ori)
+{
+ SkASSERT((unsigned)ori < kOrientCount);
+ fOrient = SkToU8(ori);
+}
+
+void SkStackViewLayout::getMargin(SkRect* margin) const
+{
+ if (margin)
+ *margin = fMargin;
+}
+
+void SkStackViewLayout::setMargin(const SkRect& margin)
+{
+ fMargin = margin;
+}
+
+void SkStackViewLayout::setSpacer(SkScalar spacer)
+{
+ fSpacer = spacer;
+}
+
+void SkStackViewLayout::setPack(Pack pack)
+{
+ SkASSERT((unsigned)pack < kPackCount);
+ fPack = SkToU8(pack);
+}
+
+void SkStackViewLayout::setAlign(Align align)
+{
+ SkASSERT((unsigned)align < kAlignCount);
+ fAlign = SkToU8(align);
+}
+
+void SkStackViewLayout::setRound(bool r)
+{
+ fRound = SkToU8(r);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+typedef SkScalar (*AlignProc)(SkScalar childLimit, SkScalar parentLimit);
+typedef SkScalar (SkView::*GetSizeProc)() const;
+typedef void (SkView::*SetLocProc)(SkScalar coord);
+typedef void (SkView::*SetSizeProc)(SkScalar coord);
+
+static SkScalar left_align_proc(SkScalar childLimit, SkScalar parentLimit) { return 0; }
+static SkScalar center_align_proc(SkScalar childLimit, SkScalar parentLimit) { return SkScalarHalf(parentLimit - childLimit); }
+static SkScalar right_align_proc(SkScalar childLimit, SkScalar parentLimit) { return parentLimit - childLimit; }
+static SkScalar fill_align_proc(SkScalar childLimit, SkScalar parentLimit) { return 0; }
+
+/* Measure the main-dimension for all the children. If a child is marked flex in that direction
+ ignore its current value but increment the counter for flexChildren
+*/
+static SkScalar compute_children_limit(SkView* parent, GetSizeProc sizeProc, int* count,
+ uint32_t flexMask, int* flexCount)
+{
+ SkView::B2FIter iter(parent);
+ SkView* child;
+ SkScalar limit = 0;
+ int n = 0, flex = 0;
+
+ while ((child = iter.next()) != NULL)
+ {
+ n += 1;
+ if (child->getFlags() & flexMask)
+ flex += 1;
+ else
+ limit += (child->*sizeProc)();
+ }
+ if (count)
+ *count = n;
+ if (flexCount)
+ *flexCount = flex;
+ return limit;
+}
+
+void SkStackViewLayout::onLayoutChildren(SkView* parent)
+{
+ static AlignProc gAlignProcs[] = {
+ left_align_proc,
+ center_align_proc,
+ right_align_proc,
+ fill_align_proc
+ };
+
+ SkScalar startM, endM, crossStartM, crossLimit;
+ GetSizeProc mainGetSizeP, crossGetSizeP;
+ SetLocProc mainLocP, crossLocP;
+ SetSizeProc mainSetSizeP, crossSetSizeP;
+ SkView::Flag_Mask flexMask;
+
+ if (fOrient == kHorizontal_Orient)
+ {
+ startM = fMargin.fLeft;
+ endM = fMargin.fRight;
+ crossStartM = fMargin.fTop;
+ crossLimit = -fMargin.fTop - fMargin.fBottom;
+
+ mainGetSizeP = &SkView::width;
+ crossGetSizeP = &SkView::height;
+ mainLocP = &SkView::setLocX;
+ crossLocP = &SkView::setLocY;
+
+ mainSetSizeP = &SkView::setWidth;
+ crossSetSizeP = &SkView::setHeight;
+
+ flexMask = SkView::kFlexH_Mask;
+ }
+ else
+ {
+ startM = fMargin.fTop;
+ endM = fMargin.fBottom;
+ crossStartM = fMargin.fLeft;
+ crossLimit = -fMargin.fLeft - fMargin.fRight;
+
+ mainGetSizeP = &SkView::height;
+ crossGetSizeP = &SkView::width;
+ mainLocP = &SkView::setLocY;
+ crossLocP = &SkView::setLocX;
+
+ mainSetSizeP = &SkView::setHeight;
+ crossSetSizeP = &SkView::setWidth;
+
+ flexMask = SkView::kFlexV_Mask;
+ }
+ crossLimit += (parent->*crossGetSizeP)();
+ if (fAlign != kStretch_Align)
+ crossSetSizeP = NULL;
+
+ int childCount, flexCount;
+ SkScalar childLimit = compute_children_limit(parent, mainGetSizeP, &childCount, flexMask, &flexCount);
+
+ if (childCount == 0)
+ return;
+
+ childLimit += (childCount - 1) * fSpacer;
+
+ SkScalar parentLimit = (parent->*mainGetSizeP)() - startM - endM;
+ SkScalar pos = startM + gAlignProcs[fPack](childLimit, parentLimit);
+ SkScalar flexAmount = 0;
+ SkView::B2FIter iter(parent);
+ SkView* child;
+
+ if (flexCount > 0 && parentLimit > childLimit)
+ flexAmount = (parentLimit - childLimit) / flexCount;
+
+ while ((child = iter.next()) != NULL)
+ {
+ if (fRound)
+ pos = SkIntToScalar(SkScalarRound(pos));
+ (child->*mainLocP)(pos);
+ SkScalar crossLoc = crossStartM + gAlignProcs[fAlign]((child->*crossGetSizeP)(), crossLimit);
+ if (fRound)
+ crossLoc = SkIntToScalar(SkScalarRound(crossLoc));
+ (child->*crossLocP)(crossLoc);
+
+ if (crossSetSizeP)
+ (child->*crossSetSizeP)(crossLimit);
+ if (child->getFlags() & flexMask)
+ (child->*mainSetSizeP)(flexAmount);
+ pos += (child->*mainGetSizeP)() + fSpacer;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+ static void assert_no_attr(const SkDOM& dom, const SkDOM::Node* node, const char attr[])
+ {
+ const char* value = dom.findAttr(node, attr);
+ if (value)
+ SkDebugf("unknown attribute %s=\"%s\"\n", attr, value);
+ }
+#else
+ #define assert_no_attr(dom, node, attr)
+#endif
+
+void SkStackViewLayout::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ int index;
+ SkScalar value[4];
+
+ if ((index = dom.findList(node, "orient", "horizontal,vertical")) >= 0)
+ this->setOrient((Orient)index);
+ else
+ assert_no_attr(dom, node, "orient");
+
+ if (dom.findScalars(node, "margin", value, 4))
+ {
+ SkRect margin;
+ margin.set(value[0], value[1], value[2], value[3]);
+ this->setMargin(margin);
+ }
+ else
+ assert_no_attr(dom, node, "margin");
+
+ if (dom.findScalar(node, "spacer", value))
+ this->setSpacer(value[0]);
+ else
+ assert_no_attr(dom, node, "spacer");
+
+ if ((index = dom.findList(node, "pack", "start,center,end")) >= 0)
+ this->setPack((Pack)index);
+ else
+ assert_no_attr(dom, node, "pack");
+
+ if ((index = dom.findList(node, "align", "start,center,end,stretch")) >= 0)
+ this->setAlign((Align)index);
+ else
+ assert_no_attr(dom, node, "align");
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////
+
+SkFillViewLayout::SkFillViewLayout()
+{
+ fMargin.setEmpty();
+}
+
+void SkFillViewLayout::getMargin(SkRect* r) const
+{
+ if (r)
+ *r = fMargin;
+}
+
+void SkFillViewLayout::setMargin(const SkRect& margin)
+{
+ fMargin = margin;
+}
+
+void SkFillViewLayout::onLayoutChildren(SkView* parent)
+{
+ SkView::B2FIter iter(parent);
+ SkView* child;
+
+ while ((child = iter.next()) != NULL)
+ {
+ child->setLoc(fMargin.fLeft, fMargin.fTop);
+ child->setSize( parent->width() - fMargin.fRight - fMargin.fLeft,
+ parent->height() - fMargin.fBottom - fMargin.fTop);
+ }
+}
+
+void SkFillViewLayout::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ this->INHERITED::onInflate(dom, node);
+ (void)dom.findScalars(node, "margin", (SkScalar*)&fMargin, 4);
+}
+
diff --git a/src/views/SkTagList.cpp b/src/views/SkTagList.cpp
new file mode 100644
index 0000000..4576ce6
--- /dev/null
+++ b/src/views/SkTagList.cpp
@@ -0,0 +1,71 @@
+/* libs/graphics/views/SkTagList.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/views/SkTagList.h b/src/views/SkTagList.h
new file mode 100644
index 0000000..5f428e4
--- /dev/null
+++ b/src/views/SkTagList.h
@@ -0,0 +1,51 @@
+/* libs/graphics/views/SkTagList.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/views/SkTextBox.cpp b/src/views/SkTextBox.cpp
new file mode 100644
index 0000000..657f648
--- /dev/null
+++ b/src/views/SkTextBox.cpp
@@ -0,0 +1,216 @@
+/* libs/graphics/views/SkTextBox.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/views/SkView.cpp b/src/views/SkView.cpp
new file mode 100644
index 0000000..a027744
--- /dev/null
+++ b/src/views/SkView.cpp
@@ -0,0 +1,760 @@
+#include "SkView.h"
+#include "SkCanvas.h"
+
+////////////////////////////////////////////////////////////////////////
+
+SkView::SkView(uint32_t flags) : fFlags(SkToU8(flags))
+{
+ fWidth = fHeight = 0;
+ fLoc.set(0, 0);
+ fParent = fFirstChild = fNextSibling = fPrevSibling = NULL;
+
+ fContainsFocus = 0;
+}
+
+SkView::~SkView()
+{
+ this->detachAllChildren();
+}
+
+void SkView::setFlags(uint32_t flags)
+{
+ SkASSERT((flags & ~kAllFlagMasks) == 0);
+
+ uint32_t diff = fFlags ^ flags;
+
+ if (diff & kVisible_Mask)
+ this->inval(NULL);
+
+ fFlags = SkToU8(flags);
+
+ if (diff & kVisible_Mask)
+ {
+ this->inval(NULL);
+ }
+}
+
+void SkView::setVisibleP(bool pred)
+{
+ this->setFlags(SkSetClearShift(fFlags, pred, kVisible_Shift));
+}
+
+void SkView::setEnabledP(bool pred)
+{
+ this->setFlags(SkSetClearShift(fFlags, pred, kEnabled_Shift));
+}
+
+void SkView::setFocusableP(bool pred)
+{
+ this->setFlags(SkSetClearShift(fFlags, pred, kFocusable_Shift));
+}
+
+void SkView::setSize(SkScalar width, SkScalar height)
+{
+ width = SkMaxScalar(0, width);
+ height = SkMaxScalar(0, height);
+
+ if (fWidth != width || fHeight != height)
+ {
+ this->inval(NULL);
+ fWidth = width;
+ fHeight = height;
+ this->inval(NULL);
+ this->onSizeChange();
+ this->invokeLayout();
+ }
+}
+
+void SkView::setLoc(SkScalar x, SkScalar y)
+{
+ if (fLoc.fX != x || fLoc.fY != y)
+ {
+ this->inval(NULL);
+ fLoc.set(x, y);
+ this->inval(NULL);
+ }
+}
+
+void SkView::offset(SkScalar dx, SkScalar dy)
+{
+ if (dx || dy)
+ this->setLoc(fLoc.fX + dx, fLoc.fY + dy);
+}
+
+void SkView::draw(SkCanvas* canvas)
+{
+ if (fWidth && fHeight && this->isVisible())
+ {
+ SkRect r;
+ r.set(fLoc.fX, fLoc.fY, fLoc.fX + fWidth, fLoc.fY + fHeight);
+ if (canvas->quickReject(r, SkCanvas::kBW_EdgeType))
+ return;
+
+ SkAutoCanvasRestore as(canvas, true);
+
+ canvas->clipRect(r);
+ canvas->translate(fLoc.fX, fLoc.fY);
+
+ this->onDraw(canvas);
+
+ B2FIter iter(this);
+ SkView* child;
+
+ SkCanvas* childCanvas = this->beforeChildren(canvas);
+
+ while ((child = iter.next()) != NULL)
+ child->draw(childCanvas);
+
+ this->afterChildren(canvas);
+ }
+}
+
+void SkView::inval(SkRect* rect)
+{
+ if (!this->isVisible())
+ return;
+
+ SkRect bounds;
+
+ this->getLocalBounds(&bounds);
+ if (rect && !bounds.intersect(*rect))
+ return;
+
+ rect = &bounds;
+ SkView* view = this;
+
+ for (;;)
+ {
+ if (view->handleInval(bounds))
+ break;
+
+ SkRect parentR;
+ SkView* parent = view->fParent;
+
+ if (parent == NULL || !parent->isVisible())
+ break;
+
+ bounds.offset(view->fLoc.fX, view->fLoc.fY);
+ parent->getLocalBounds(&parentR);
+ if (!bounds.intersect(parentR))
+ return;
+
+ view = parent;
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+bool SkView::setFocusView(SkView* fv)
+{
+ SkView* view = this;
+
+ do {
+ if (view->onSetFocusView(fv))
+ return true;
+ } while ((view = view->fParent) != NULL);
+ return false;
+}
+
+SkView* SkView::getFocusView() const
+{
+ SkView* focus = NULL;
+ const SkView* view = this;
+ do {
+ if (view->onGetFocusView(&focus))
+ break;
+ } while ((view = view->fParent) != NULL);
+ return focus;
+}
+
+bool SkView::hasFocus() const
+{
+ return this == this->getFocusView();
+}
+
+bool SkView::acceptFocus()
+{
+ return this->isFocusable() && this->setFocusView(this);
+}
+
+/*
+ Try to give focus to this view, or its children
+*/
+SkView* SkView::acceptFocus(FocusDirection dir)
+{
+ if (dir == kNext_FocusDirection)
+ {
+ if (this->acceptFocus())
+ return this;
+
+ B2FIter iter(this);
+ SkView* child, *focus;
+ while ((child = iter.next()) != NULL)
+ if ((focus = child->acceptFocus(dir)) != NULL)
+ return focus;
+ }
+ else // prev
+ {
+ F2BIter iter(this);
+ SkView* child, *focus;
+ while ((child = iter.next()) != NULL)
+ if ((focus = child->acceptFocus(dir)) != NULL)
+ return focus;
+
+ if (this->acceptFocus())
+ return this;
+ }
+
+ return NULL;
+}
+
+SkView* SkView::moveFocus(FocusDirection dir)
+{
+ SkView* focus = this->getFocusView();
+
+ if (focus == NULL)
+ { // start with the root
+ focus = this;
+ while (focus->fParent)
+ focus = focus->fParent;
+ }
+
+ SkView* child, *parent;
+
+ if (dir == kNext_FocusDirection)
+ {
+ parent = focus;
+ child = focus->fFirstChild;
+ if (child)
+ goto FIRST_CHILD;
+ else
+ goto NEXT_SIB;
+
+ do {
+ while (child != parent->fFirstChild)
+ {
+ FIRST_CHILD:
+ if ((focus = child->acceptFocus(dir)) != NULL)
+ return focus;
+ child = child->fNextSibling;
+ }
+ NEXT_SIB:
+ child = parent->fNextSibling;
+ parent = parent->fParent;
+ } while (parent != NULL);
+ }
+ else // prevfocus
+ {
+ parent = focus->fParent;
+ if (parent == NULL) // we're the root
+ return focus->acceptFocus(dir);
+ else
+ {
+ child = focus;
+ while (parent)
+ {
+ while (child != parent->fFirstChild)
+ {
+ child = child->fPrevSibling;
+ if ((focus = child->acceptFocus(dir)) != NULL)
+ return focus;
+ }
+ if (parent->acceptFocus())
+ return parent;
+
+ child = parent;
+ parent = parent->fParent;
+ }
+ }
+ }
+ return NULL;
+}
+
+void SkView::onFocusChange(bool gainFocusP)
+{
+ this->inval(NULL);
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+SkView::Click::Click(SkView* target)
+{
+ SkASSERT(target);
+ fTargetID = target->getSinkID();
+ fType = NULL;
+ fWeOwnTheType = false;
+}
+
+SkView::Click::~Click()
+{
+ this->resetType();
+}
+
+void SkView::Click::resetType()
+{
+ if (fWeOwnTheType)
+ {
+ sk_free(fType);
+ fWeOwnTheType = false;
+ }
+ fType = NULL;
+}
+
+bool SkView::Click::isType(const char type[]) const
+{
+ const char* t = fType;
+
+ if (type == t)
+ return true;
+
+ if (type == NULL)
+ type = "";
+ if (t == NULL)
+ t = "";
+ return !strcmp(t, type);
+}
+
+void SkView::Click::setType(const char type[])
+{
+ this->resetType();
+ fType = (char*)type;
+}
+
+void SkView::Click::copyType(const char type[])
+{
+ if (fType != type)
+ {
+ this->resetType();
+ if (type)
+ {
+ size_t len = strlen(type) + 1;
+ fType = (char*)sk_malloc_throw(len);
+ memcpy(fType, type, len);
+ fWeOwnTheType = true;
+ }
+ }
+}
+
+SkView::Click* SkView::findClickHandler(SkScalar x, SkScalar y)
+{
+ if (x < 0 || y < 0 || x >= fWidth || y >= fHeight)
+ return false;
+
+ F2BIter iter(this);
+ SkView* child;
+
+ while ((child = iter.next()) != NULL)
+ {
+ Click* click = child->findClickHandler(x - child->fLoc.fX, y - child->fLoc.fY);
+ if (click)
+ return click;
+ }
+ return this->onFindClickHandler(x, y);
+}
+
+void SkView::DoClickDown(Click* click, int x, int y)
+{
+ SkASSERT(click);
+
+ SkView* target = (SkView*)SkEventSink::FindSink(click->fTargetID);
+ if (target == NULL)
+ return;
+
+ click->fIOrig.set(x, y);
+ click->fICurr = click->fIPrev = click->fIOrig;
+
+ click->fOrig.iset(x, y);
+ target->globalToLocal(&click->fOrig);
+ click->fPrev = click->fCurr = click->fOrig;
+
+ click->fState = Click::kDown_State;
+ target->onClick(click);
+}
+
+void SkView::DoClickMoved(Click* click, int x, int y)
+{
+ SkASSERT(click);
+
+ SkView* target = (SkView*)SkEventSink::FindSink(click->fTargetID);
+ if (target == NULL)
+ return;
+
+ click->fIPrev = click->fICurr;
+ click->fICurr.set(x, y);
+
+ click->fPrev = click->fCurr;
+ click->fCurr.iset(x, y);
+ target->globalToLocal(&click->fCurr);
+
+ click->fState = Click::kMoved_State;
+ target->onClick(click);
+}
+
+void SkView::DoClickUp(Click* click, int x, int y)
+{
+ SkASSERT(click);
+
+ SkView* target = (SkView*)SkEventSink::FindSink(click->fTargetID);
+ if (target == NULL)
+ return;
+
+ click->fIPrev = click->fICurr;
+ click->fICurr.set(x, y);
+
+ click->fPrev = click->fCurr;
+ click->fCurr.iset(x, y);
+ target->globalToLocal(&click->fCurr);
+
+ click->fState = Click::kUp_State;
+ target->onClick(click);
+}
+
+//////////////////////////////////////////////////////////////////////
+
+void SkView::invokeLayout()
+{
+ SkView::Layout* layout = this->getLayout();
+
+ if (layout)
+ layout->layoutChildren(this);
+}
+
+void SkView::onDraw(SkCanvas* canvas)
+{
+ Artist* artist = this->getArtist();
+
+ if (artist)
+ artist->draw(this, canvas);
+}
+
+void SkView::onSizeChange()
+{
+}
+
+SkView::Click* SkView::onFindClickHandler(SkScalar x, SkScalar y)
+{
+ return NULL;
+}
+
+bool SkView::onClick(Click*)
+{
+ return false;
+}
+
+bool SkView::handleInval(const SkRect& r)
+{
+ return false;
+}
+
+//////////////////////////////////////////////////////////////////////
+
+void SkView::getLocalBounds(SkRect* bounds) const
+{
+ if (bounds)
+ bounds->set(0, 0, fWidth, fHeight);
+}
+
+//////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////
+
+void SkView::detachFromParent_NoLayout()
+{
+ if (fParent == NULL)
+ return;
+
+ if (fContainsFocus)
+ (void)this->setFocusView(NULL);
+
+ this->inval(NULL);
+
+ SkView* next = NULL;
+
+ if (fNextSibling != this) // do we have any siblings
+ {
+ fNextSibling->fPrevSibling = fPrevSibling;
+ fPrevSibling->fNextSibling = fNextSibling;
+ next = fNextSibling;
+ }
+
+ if (fParent->fFirstChild == this)
+ fParent->fFirstChild = next;
+
+ fParent = fNextSibling = fPrevSibling = NULL;
+
+ this->unref();
+}
+
+void SkView::detachFromParent()
+{
+ SkView* parent = fParent;
+
+ if (parent)
+ {
+ this->detachFromParent_NoLayout();
+ parent->invokeLayout();
+ }
+}
+
+SkView* SkView::attachChildToBack(SkView* child)
+{
+ SkASSERT(child != this);
+
+ if (child == NULL || fFirstChild == child)
+ goto DONE;
+
+ child->ref();
+ child->detachFromParent_NoLayout();
+
+ if (fFirstChild == NULL)
+ {
+ child->fNextSibling = child;
+ child->fPrevSibling = child;
+ }
+ else
+ {
+ child->fNextSibling = fFirstChild;
+ child->fPrevSibling = fFirstChild->fPrevSibling;
+ fFirstChild->fPrevSibling->fNextSibling = child;
+ fFirstChild->fPrevSibling = child;
+ }
+
+ fFirstChild = child;
+ child->fParent = this;
+ child->inval(NULL);
+
+ this->invokeLayout();
+DONE:
+ return child;
+}
+
+SkView* SkView::attachChildToFront(SkView* child)
+{
+ SkASSERT(child != this);
+
+ if (child == NULL || fFirstChild && fFirstChild->fPrevSibling == child)
+ goto DONE;
+
+ child->ref();
+ child->detachFromParent_NoLayout();
+
+ if (fFirstChild == NULL)
+ {
+ fFirstChild = child;
+ child->fNextSibling = child;
+ child->fPrevSibling = child;
+ }
+ else
+ {
+ child->fNextSibling = fFirstChild;
+ child->fPrevSibling = fFirstChild->fPrevSibling;
+ fFirstChild->fPrevSibling->fNextSibling = child;
+ fFirstChild->fPrevSibling = child;
+ }
+
+ child->fParent = this;
+ child->inval(NULL);
+
+ this->invokeLayout();
+DONE:
+ return child;
+}
+
+void SkView::detachAllChildren()
+{
+ while (fFirstChild)
+ fFirstChild->detachFromParent_NoLayout();
+}
+
+void SkView::globalToLocal(SkScalar x, SkScalar y, SkPoint* local) const
+{
+ SkASSERT(this);
+
+ if (local)
+ {
+ const SkView* view = this;
+ while (view)
+ {
+ x -= view->fLoc.fX;
+ y -= view->fLoc.fY;
+ view = view->fParent;
+ }
+ local->set(x, y);
+ }
+}
+
+//////////////////////////////////////////////////////////////////
+
+/* Even if the subclass overrides onInflate, they should always be
+ sure to call the inherited method, so that we get called.
+*/
+void SkView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ SkScalar x, y;
+
+ x = this->locX();
+ y = this->locY();
+ (void)dom.findScalar(node, "x", &x);
+ (void)dom.findScalar(node, "y", &y);
+ this->setLoc(x, y);
+
+ x = this->width();
+ y = this->height();
+ (void)dom.findScalar(node, "width", &x);
+ (void)dom.findScalar(node, "height", &y);
+ this->setSize(x, y);
+
+ // inflate the flags
+
+ static const char* gFlagNames[] = {
+ "visible", "enabled", "focusable", "flexH", "flexV"
+ };
+ SkASSERT(SK_ARRAY_COUNT(gFlagNames) == kFlagShiftCount);
+
+ bool b;
+ uint32_t flags = this->getFlags();
+ for (unsigned i = 0; i < SK_ARRAY_COUNT(gFlagNames); i++)
+ if (dom.findBool(node, gFlagNames[i], &b))
+ flags = SkSetClearShift(flags, b, i);
+ this->setFlags(flags);
+}
+
+void SkView::inflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ this->onInflate(dom, node);
+}
+
+void SkView::onPostInflate(const SkTDict<SkView*>&)
+{
+ // override in subclass as needed
+}
+
+void SkView::postInflate(const SkTDict<SkView*>& dict)
+{
+ this->onPostInflate(dict);
+
+ B2FIter iter(this);
+ SkView* child;
+ while ((child = iter.next()) != NULL)
+ child->postInflate(dict);
+}
+
+//////////////////////////////////////////////////////////////////
+
+SkView* SkView::sendEventToParents(const SkEvent& evt)
+{
+ SkView* parent = fParent;
+
+ while (parent)
+ {
+ if (parent->doEvent(evt))
+ return parent;
+ parent = parent->fParent;
+ }
+ return NULL;
+}
+
+//////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////
+
+SkView::F2BIter::F2BIter(const SkView* parent)
+{
+ fFirstChild = parent ? parent->fFirstChild : NULL;
+ fChild = fFirstChild ? fFirstChild->fPrevSibling : NULL;
+}
+
+SkView* SkView::F2BIter::next()
+{
+ SkView* curr = fChild;
+
+ if (fChild)
+ {
+ if (fChild == fFirstChild)
+ fChild = NULL;
+ else
+ fChild = fChild->fPrevSibling;
+ }
+ return curr;
+}
+
+SkView::B2FIter::B2FIter(const SkView* parent)
+{
+ fFirstChild = parent ? parent->fFirstChild : NULL;
+ fChild = fFirstChild;
+}
+
+SkView* SkView::B2FIter::next()
+{
+ SkView* curr = fChild;
+
+ if (fChild)
+ {
+ SkView* next = fChild->fNextSibling;
+ if (next == fFirstChild)
+ next = NULL;
+ fChild = next;
+ }
+ return curr;
+}
+
+//////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+
+static inline void show_if_nonzero(const char name[], SkScalar value)
+{
+ if (value)
+ SkDebugf("%s=\"%g\"", name, value/65536.);
+}
+
+static void tab(int level)
+{
+ for (int i = 0; i < level; i++)
+ SkDebugf(" ");
+}
+
+static void dumpview(const SkView* view, int level, bool recurse)
+{
+ tab(level);
+
+ SkDebugf("<view");
+ show_if_nonzero(" x", view->locX());
+ show_if_nonzero(" y", view->locY());
+ show_if_nonzero(" width", view->width());
+ show_if_nonzero(" height", view->height());
+
+ if (recurse)
+ {
+ SkView::B2FIter iter(view);
+ SkView* child;
+ bool noChildren = true;
+
+ while ((child = iter.next()) != NULL)
+ {
+ if (noChildren)
+ SkDebugf(">\n");
+ noChildren = false;
+ dumpview(child, level + 1, true);
+ }
+
+ if (!noChildren)
+ {
+ tab(level);
+ SkDebugf("</view>\n");
+ }
+ else
+ goto ONELINER;
+ }
+ else
+ {
+ ONELINER:
+ SkDebugf(" />\n");
+ }
+}
+
+void SkView::dump(bool recurse) const
+{
+ dumpview(this, 0, recurse);
+}
+
+#endif
diff --git a/src/views/SkViewInflate.cpp b/src/views/SkViewInflate.cpp
new file mode 100644
index 0000000..8f5489d
--- /dev/null
+++ b/src/views/SkViewInflate.cpp
@@ -0,0 +1,139 @@
+#include "SkViewInflate.h"
+#include "SkView.h"
+#include <stdio.h>
+
+SkViewInflate::SkViewInflate() : fIDs(kMinIDStrAlloc), fStrings(kMinIDStrAlloc)
+{
+}
+
+SkViewInflate::~SkViewInflate()
+{
+}
+
+void SkViewInflate::rInflate(const SkDOM& dom, const SkDOM::Node* node, SkView* parent)
+{
+ const char* str = dom.findAttr(node, "id");
+ if (str)
+ fIDs.set(str, parent);
+
+ const SkDOM::Node* child = dom.getFirstChild(node);
+ while (child)
+ {
+ SkView* view = this->createView(dom, child);
+ if (view)
+ {
+ this->rInflate(dom, child, view);
+ parent->attachChildToFront(view)->unref();
+ }
+ else
+ {
+ const char* name = dom.getName(child);
+ const char* target;
+
+ if (!strcmp(name, "listenTo") && (target = dom.findAttr(child, "target")) != NULL)
+ this->addIDStr(&fListenTo, parent, target);
+
+ if (!strcmp(name, "broadcastTo") && (target = dom.findAttr(child, "target")) != NULL)
+ this->addIDStr(&fBroadcastTo, parent, target);
+ }
+ child = dom.getNextSibling(child);
+ }
+
+ parent->setVisibleP(true);
+ this->inflateView(parent, dom, node);
+}
+
+void SkViewInflate::inflateView(SkView* view, const SkDOM& dom, const SkDOM::Node* node)
+{
+ // called after all of view's children have been instantiated.
+ // this may be overridden by a subclass, to load in layout or other helpers
+ // they should call through to us (INHERITED) before or after their patch
+ view->inflate(dom, node);
+}
+
+SkView* SkViewInflate::inflate(const SkDOM& dom, const SkDOM::Node* node, SkView* root)
+{
+ fIDs.reset();
+
+ if (root == NULL)
+ {
+ root = this->createView(dom, node);
+ if (root == NULL)
+ {
+ printf("createView returned NULL on <%s>\n", dom.getName(node));
+ return NULL;
+ }
+ }
+ this->rInflate(dom, node, root);
+
+ // resolve listeners and broadcasters
+ {
+ SkView* target;
+ const IDStr* iter = fListenTo.begin();
+ const IDStr* stop = fListenTo.end();
+ for (; iter < stop; iter++)
+ {
+ if (fIDs.find(iter->fStr, &target))
+ target->addListenerID(iter->fView->getSinkID());
+ }
+
+ iter = fBroadcastTo.begin();
+ stop = fBroadcastTo.end();
+ for (; iter < stop; iter++)
+ {
+ if (fIDs.find(iter->fStr, &target))
+ iter->fView->addListenerID(target->getSinkID());
+ }
+ }
+
+ // now that the tree is built, give everyone a shot at the ID dict
+ root->postInflate(fIDs);
+ return root;
+}
+
+SkView* SkViewInflate::inflate(const char xml[], size_t len, SkView* root)
+{
+ SkDOM dom;
+ const SkDOM::Node* node = dom.build(xml, len);
+
+ return node ? this->inflate(dom, node, root) : NULL;
+}
+
+SkView* SkViewInflate::findViewByID(const char id[]) const
+{
+ SkASSERT(id);
+ SkView* view;
+ return fIDs.find(id, &view) ? view : NULL;
+}
+
+SkView* SkViewInflate::createView(const SkDOM& dom, const SkDOM::Node* node)
+{
+ if (!strcmp(dom.getName(node), "view"))
+ return new SkView;
+ return NULL;
+}
+
+void SkViewInflate::addIDStr(SkTDArray<IDStr>* list, SkView* view, const char* str)
+{
+ size_t len = strlen(str) + 1;
+ IDStr* pair = list->append();
+ pair->fView = view;
+ pair->fStr = (char*)fStrings.alloc(len, SkChunkAlloc::kThrow_AllocFailType);
+ memcpy(pair->fStr, str, len);
+}
+
+#ifdef SK_DEBUG
+void SkViewInflate::dump() const
+{
+ const IDStr* iter = fListenTo.begin();
+ const IDStr* stop = fListenTo.end();
+ for (; iter < stop; iter++)
+ SkDebugf("inflate: listenTo(\"%s\")\n", iter->fStr);
+
+ iter = fBroadcastTo.begin();
+ stop = fBroadcastTo.end();
+ for (; iter < stop; iter++)
+ SkDebugf("inflate: broadcastFrom(\"%s\")\n", iter->fStr);
+}
+#endif
+
diff --git a/src/views/SkViewPriv.cpp b/src/views/SkViewPriv.cpp
new file mode 100644
index 0000000..b03ca8c
--- /dev/null
+++ b/src/views/SkViewPriv.cpp
@@ -0,0 +1,97 @@
+#include "SkViewPriv.h"
+
+//////////////////////////////////////////////////////////////////////
+
+void SkView::Artist::draw(SkView* view, SkCanvas* canvas)
+{
+ SkASSERT(view && canvas);
+ this->onDraw(view, canvas);
+}
+
+void SkView::Artist::inflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ SkASSERT(&dom && node);
+ this->onInflate(dom, node);
+}
+
+void SkView::Artist::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ // subclass should override this as needed
+}
+
+SkView::Artist* SkView::getArtist() const
+{
+ Artist_SkTagList* rec = (Artist_SkTagList*)this->findTagList(kViewArtist_SkTagList);
+ SkASSERT(rec == NULL || rec->fArtist != NULL);
+
+ return rec ? rec->fArtist : NULL;
+}
+
+SkView::Artist* SkView::setArtist(Artist* obj)
+{
+ if (obj == NULL)
+ {
+ this->removeTagList(kViewArtist_SkTagList);
+ }
+ else // add/replace
+ {
+ Artist_SkTagList* rec = (Artist_SkTagList*)this->findTagList(kViewArtist_SkTagList);
+
+ if (rec)
+ SkRefCnt_SafeAssign(rec->fArtist, obj);
+ else
+ this->addTagList(new Artist_SkTagList(obj));
+ }
+ return obj;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+void SkView::Layout::layoutChildren(SkView* parent)
+{
+ SkASSERT(parent);
+ if (parent->width() > 0 && parent->height() > 0)
+ this->onLayoutChildren(parent);
+}
+
+void SkView::Layout::inflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ SkASSERT(&dom && node);
+ this->onInflate(dom, node);
+}
+
+void SkView::Layout::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ // subclass should override this as needed
+}
+
+SkView::Layout* SkView::getLayout() const
+{
+ Layout_SkTagList* rec = (Layout_SkTagList*)this->findTagList(kViewLayout_SkTagList);
+ SkASSERT(rec == NULL || rec->fLayout != NULL);
+
+ return rec ? rec->fLayout : NULL;
+}
+
+SkView::Layout* SkView::setLayout(Layout* obj, bool invokeLayoutNow)
+{
+ if (obj == NULL)
+ {
+ this->removeTagList(kViewLayout_SkTagList);
+ }
+ else // add/replace
+ {
+ Layout_SkTagList* rec = (Layout_SkTagList*)this->findTagList(kViewLayout_SkTagList);
+
+ if (rec)
+ SkRefCnt_SafeAssign(rec->fLayout, obj);
+ else
+ this->addTagList(new Layout_SkTagList(obj));
+ }
+
+ if (invokeLayoutNow)
+ this->invokeLayout();
+
+ return obj;
+}
+
diff --git a/src/views/SkViewPriv.h b/src/views/SkViewPriv.h
new file mode 100644
index 0000000..06ce59b
--- /dev/null
+++ b/src/views/SkViewPriv.h
@@ -0,0 +1,38 @@
+#ifndef SkViewPriv_DEFINED
+#define SkViewPriv_DEFINED
+
+#include "SkView.h"
+#include "SkTagList.h"
+
+struct Layout_SkTagList : SkTagList {
+ SkView::Layout* fLayout;
+
+ Layout_SkTagList(SkView::Layout* layout)
+ : SkTagList(kViewLayout_SkTagList), fLayout(layout)
+ {
+ SkASSERT(layout);
+ layout->ref();
+ }
+ virtual ~Layout_SkTagList()
+ {
+ fLayout->unref();
+ }
+};
+
+struct Artist_SkTagList : SkTagList {
+ SkView::Artist* fArtist;
+
+ Artist_SkTagList(SkView::Artist* artist)
+ : SkTagList(kViewArtist_SkTagList), fArtist(artist)
+ {
+ SkASSERT(artist);
+ artist->ref();
+ }
+ virtual ~Artist_SkTagList()
+ {
+ fArtist->unref();
+ }
+};
+
+#endif
+
diff --git a/src/views/SkWidget.cpp b/src/views/SkWidget.cpp
new file mode 100644
index 0000000..8d945f1
--- /dev/null
+++ b/src/views/SkWidget.cpp
@@ -0,0 +1,323 @@
+#include "SkWidget.h"
+#include "SkCanvas.h"
+#include "SkInterpolator.h"
+#include "SkTime.h"
+#include "SkParsePaint.h"
+
+#if 0
+SkWidgetView::SkWidgetView(U32 flags) : SkView(flags)
+{
+}
+
+SkWidgetView::~SkWidgetView()
+{
+}
+
+const char* SkWidgetView::GetEventType()
+{
+ return "SkWidgetView";
+}
+
+/////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
+
+class SkTextView::Interp {
+public:
+ Interp(const SkString& old, SkMSec now, SkMSec dur, AnimaDir dir) : fOldText(old), fInterp(1, 2)
+ {
+ SkScalar x = 0;
+ fInterp.setKeyFrame(0, now, &x, 0);
+ x = SK_Scalar1;
+ if (dir == kBackward_AnimDir)
+ x = -x;
+ fInterp.setKeyFrame(1, now + dur, &x);
+ }
+ bool draw(SkCanvas* canvas, const SkString& newText, SkScalar x, SkScalar y, SkPaint& paint)
+ {
+ SkScalar scale;
+
+ if (fInterp.timeToValues(SkTime::GetMSecs(), &scale) == SkInterpolator::kFreezeEnd_Result)
+ {
+ canvas->drawText(newText.c_str(), newText.size(), x, y, paint);
+ return false;
+ }
+ else
+ {
+ U8 alpha = paint.getAlpha();
+ SkScalar above, below;
+ (void)paint.measureText(nil, 0, &above, &below);
+ SkScalar height = below - above;
+ SkScalar dy = SkScalarMul(height, scale);
+ if (scale < 0)
+ height = -height;
+
+ // draw the old
+ paint.setAlpha((U8)SkScalarMul(alpha, SK_Scalar1 - SkScalarAbs(scale)));
+ canvas->drawText(fOldText.c_str(), fOldText.size(), x, y - dy, paint);
+ // draw the new
+ paint.setAlpha((U8)SkScalarMul(alpha, SkScalarAbs(scale)));
+ canvas->drawText(newText.c_str(), newText.size(), x, y + height - dy, paint);
+ // restore the paint
+ paint.setAlpha(alpha);
+ return true;
+ }
+ }
+
+private:
+ SkString fOldText;
+ SkInterpolator fInterp;
+};
+
+SkTextView::SkTextView(U32 flags) : SkView(flags), fInterp(nil), fDoInterp(false)
+{
+ fMargin.set(0, 0);
+}
+
+SkTextView::~SkTextView()
+{
+ delete fInterp;
+}
+
+void SkTextView::getText(SkString* str) const
+{
+ if (str)
+ str->set(fText);
+}
+
+void SkTextView::setText(const char text[], AnimaDir dir)
+{
+ if (!fText.equals(text))
+ {
+ SkString tmp(text);
+ this->privSetText(tmp, dir);
+ }
+}
+
+void SkTextView::setText(const char text[], size_t len, AnimaDir dir)
+{
+ if (!fText.equals(text))
+ {
+ SkString tmp(text, len);
+ this->privSetText(tmp, dir);
+ }
+}
+
+void SkTextView::setText(const SkString& src, AnimaDir dir)
+{
+ if (fText != src)
+ this->privSetText(src, dir);
+}
+
+void SkTextView::privSetText(const SkString& src, AnimaDir dir)
+{
+ SkASSERT(fText != src);
+
+ if (fDoInterp)
+ {
+ if (fInterp)
+ delete fInterp;
+ fInterp = new Interp(fText, SkTime::GetMSecs(), 500, dir);
+ }
+ fText = src;
+ this->inval(nil);
+}
+
+/////////////////////////////////////////////////////////////////
+
+void SkTextView::getMargin(SkPoint* margin) const
+{
+ if (margin)
+ *margin = fMargin;
+}
+
+void SkTextView::setMargin(const SkPoint& margin)
+{
+ if (fMargin != margin)
+ {
+ fMargin = margin;
+ this->inval(nil);
+ }
+}
+
+void SkTextView::onDraw(SkCanvas* canvas)
+{
+ this->INHERITED::onDraw(canvas);
+
+ if (fText.size() == 0)
+ return;
+
+ SkPaint::Align align = fPaint.getTextAlign();
+ SkScalar x, y;
+
+ switch (align) {
+ case SkPaint::kLeft_Align:
+ x = fMargin.fX;
+ break;
+ case SkPaint::kCenter_Align:
+ x = SkScalarHalf(this->width());
+ break;
+ default:
+ SkASSERT(align == SkPaint::kRight_Align);
+ x = this->width() - fMargin.fX;
+ break;
+ }
+
+ fPaint.measureText(nil, 0, &y, nil);
+ y = fMargin.fY - y;
+
+ if (fInterp)
+ {
+ if (fInterp->draw(canvas, fText, x, y, fPaint))
+ this->inval(nil);
+ else
+ {
+ delete fInterp;
+ fInterp = nil;
+ }
+ }
+ else
+ canvas->drawText(fText.c_str(), fText.size(), x, y, fPaint);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////
+
+void SkTextView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ this->INHERITED::onInflate(dom, node);
+
+ const char* text = dom.findAttr(node, "text");
+ if (text)
+ this->setText(text);
+
+ SkPoint margin;
+ if (dom.findScalars(node, "margin", (SkScalar*)&margin, 2))
+ this->setMargin(margin);
+ (void)dom.findBool(node, "do-interp", &fDoInterp);
+
+ SkPaint_Inflate(&fPaint, dom, node);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////
+
+SkSliderView::SkSliderView(U32 flags) : SkWidgetView(flags)
+{
+ fValue = 0;
+ fMax = 0;
+}
+
+static U16 actual_value(U16CPU value, U16CPU max)
+{
+ return SkToU16(SkMax32(0, SkMin32(value, max)));
+}
+
+void SkSliderView::setMax(U16CPU max)
+{
+ if (fMax != max)
+ {
+ fMax = SkToU16(max);
+ if (fValue > 0)
+ this->inval(nil);
+ }
+}
+
+void SkSliderView::setValue(U16CPU value)
+{
+ if (fValue != value)
+ {
+ U16 prev = actual_value(fValue, fMax);
+ U16 next = actual_value(value, fMax);
+
+ fValue = SkToU16(value);
+ if (prev != next)
+ {
+ this->inval(nil);
+
+ if (this->hasListeners())
+ {
+ SkEvent evt;
+
+ evt.setType(SkWidgetView::GetEventType());
+ evt.setFast32(this->getSinkID());
+ evt.setS32("sliderValue", next);
+ this->postToListeners(evt);
+ }
+ }
+ }
+}
+
+#include "SkGradientShader.h"
+
+static void setgrad(SkPaint* paint, const SkRect& r)
+{
+ SkPoint pts[2];
+ SkColor colors[2];
+
+#if 0
+ pts[0].set(r.fLeft, r.fTop);
+ pts[1].set(r.fLeft + r.height(), r.fBottom);
+#else
+ pts[0].set(r.fRight, r.fBottom);
+ pts[1].set(r.fRight - r.height(), r.fTop);
+#endif
+ colors[0] = SK_ColorBLUE;
+ colors[1] = SK_ColorWHITE;
+
+ paint->setShader(SkGradientShader::CreateLinear(pts, colors, nil, 2, SkShader::kMirror_TileMode))->unref();
+}
+
+void SkSliderView::onDraw(SkCanvas* canvas)
+{
+ this->INHERITED::onDraw(canvas);
+
+ U16CPU value = SkMax32(0, SkMin32(fValue, fMax));
+
+ SkRect r;
+ SkPaint p;
+
+ r.set(0, 0, this->width(), this->height());
+
+ p.setAntiAliasOn(true);
+ p.setStyle(SkPaint::kStroke_Style);
+ p.setStrokeWidth(SK_Scalar1);
+ r.inset(SK_Scalar1/2, SK_Scalar1/2);
+ canvas->drawRect(r, p);
+
+ if (fMax)
+ {
+ SkFixed percent = SkFixedDiv(value, fMax);
+
+ r.inset(SK_Scalar1/2, SK_Scalar1/2);
+ r.fRight = r.fLeft + SkScalarMul(r.width(), SkFixedToScalar(percent));
+ p.setStyle(SkPaint::kFill_Style);
+ setgrad(&p, r);
+ canvas->drawRect(r, p);
+ }
+
+#if 0
+ r.set(0, 0, this->width(), this->height());
+ r.inset(SK_Scalar1, SK_Scalar1);
+ r.inset(r.width()/2, 0);
+ p.setColor(SK_ColorBLACK);
+ canvas->drawLine(*(SkPoint*)&r.fLeft, *(SkPoint*)&r.fRight, p);
+#endif
+}
+
+SkView::Click* SkSliderView::onFindClickHandler(SkScalar x, SkScalar y)
+{
+ return new Click(this);
+}
+
+bool SkSliderView::onClick(Click* click)
+{
+ if (fMax)
+ {
+ SkScalar percent = SkScalarDiv(click->fCurr.fX + SK_Scalar1, this->width() - SK_Scalar1*2);
+ percent = SkMaxScalar(0, SkMinScalar(percent, SK_Scalar1));
+ this->setValue(SkScalarRound(percent * fMax));
+ return true;
+ }
+ return false;
+}
+
+#endif
+
diff --git a/src/views/SkWidgetViews.cpp b/src/views/SkWidgetViews.cpp
new file mode 100644
index 0000000..e7eb772
--- /dev/null
+++ b/src/views/SkWidgetViews.cpp
@@ -0,0 +1,570 @@
+#include "SkWidgetViews.h"
+#include "SkAnimator.h"
+#include "SkCanvas.h"
+#include "SkPaint.h"
+#include "SkStream.h"
+#include "SkSystemEventTypes.h"
+
+#ifdef SK_DEBUG
+ static void assert_no_attr(const SkDOM& dom, const SkDOM::Node* node, const char attr[])
+ {
+ const char* value = dom.findAttr(node, attr);
+ if (value)
+ SkDebugf("unknown attribute %s=\"%s\"\n", attr, value);
+ }
+#else
+ #define assert_no_attr(dom, node, attr)
+#endif
+/*
+I have moved this to SkWidgetViews.h
+enum SkinEnum {
+ kButton_SkinEnum,
+ kProgress_SkinEnum,
+ kScroll_SkinEnum,
+ kStaticText_SkinEnum,
+
+ kSkinEnumCount
+};
+*/
+
+const char* get_skin_enum_path(SkinEnum se)
+{
+ SkASSERT((unsigned)se < kSkinEnumCount);
+
+ static const char* gSkinPaths[] = {
+ "common/default/default/skins/border3.xml",
+ "common/default/default/skins/button.xml",
+ "common/default/default/skins/progressBar.xml",
+ "common/default/default/skins/scrollBar.xml",
+ "common/default/default/skins/statictextpaint.xml"
+ };
+
+ return gSkinPaths[se];
+}
+
+void init_skin_anim(const char path[], SkAnimator* anim)
+{
+ SkASSERT(path && anim);
+
+ SkFILEStream stream(path);
+
+ if (!stream.isValid())
+ {
+ SkDEBUGF(("init_skin_anim: loading skin failed <%s>\n", path));
+ sk_throw();
+ }
+
+ if (!anim->decodeStream(&stream))
+ {
+ SkDEBUGF(("init_skin_anim: decoding skin failed <%s>\n", path));
+ sk_throw();
+ }
+}
+
+void init_skin_anim(SkinEnum se, SkAnimator* anim)
+{
+ init_skin_anim(get_skin_enum_path(se), anim);
+}
+
+void init_skin_paint(SkinEnum se, SkPaint* paint)
+{
+ SkASSERT(paint);
+
+ SkAnimator anim;
+ SkCanvas canvas;
+
+ init_skin_anim(se, &anim);
+ anim.draw(&canvas, paint, 0);
+}
+
+void inflate_paint(const SkDOM& dom, const SkDOM::Node* node, SkPaint* paint)
+{
+ SkASSERT(paint);
+
+ SkAnimator anim;
+ SkCanvas canvas;
+
+ if (!anim.decodeDOM(dom, node))
+ {
+ SkDEBUGF(("inflate_paint: decoding dom failed\n"));
+ SkDEBUGCODE(dom.dump(node);)
+ sk_throw();
+ }
+ anim.draw(&canvas, paint, 0);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+
+SkWidgetView::SkWidgetView() : SkView(SkView::kFocusable_Mask | SkView::kEnabled_Mask)
+{
+}
+
+const char* SkWidgetView::getLabel() const
+{
+ return fLabel.c_str();
+}
+
+void SkWidgetView::getLabel(SkString* label) const
+{
+ if (label)
+ *label = fLabel;
+}
+
+void SkWidgetView::setLabel(const char label[])
+{
+ this->setLabel(label, label ? strlen(label) : 0);
+}
+
+void SkWidgetView::setLabel(const char label[], size_t len)
+{
+ if (label == nil && fLabel.size() != 0 || !fLabel.equals(label, len))
+ {
+ SkString tmp(label, len);
+
+ this->onLabelChange(fLabel.c_str(), tmp.c_str());
+ fLabel.swap(tmp);
+ }
+}
+
+void SkWidgetView::setLabel(const SkString& label)
+{
+ if (fLabel != label)
+ {
+ this->onLabelChange(fLabel.c_str(), label.c_str());
+ fLabel = label;
+ }
+}
+
+bool SkWidgetView::postWidgetEvent()
+{
+ if (!fEvent.isType(""))
+ {
+ SkEvent evt(fEvent); // make a copy since onPrepareWidgetEvent may edit the event
+
+ if (this->onPrepareWidgetEvent(&evt))
+ {
+ SkDEBUGCODE(evt.dump("SkWidgetView::postWidgetEvent");)
+
+ this->postToListeners(evt); // wonder if this should return true if there are > 0 listeners...
+ return true;
+ }
+ }
+ return false;
+}
+
+/*virtual*/ void SkWidgetView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ this->INHERITED::onInflate(dom, node);
+
+ const char* label = dom.findAttr(node, "label");
+ if (label)
+ this->setLabel(label);
+
+ if ((node = dom.getFirstChild(node, "event")) != nil)
+ fEvent.inflate(dom, node);
+}
+
+/*virtual*/ void SkWidgetView::onLabelChange(const char oldLabel[], const char newLabel[])
+{
+ this->inval(nil);
+}
+
+static const char gWidgetEventSinkIDSlotName[] = "sk-widget-sinkid-slot";
+
+/*virtual*/ bool SkWidgetView::onPrepareWidgetEvent(SkEvent* evt)
+{
+ evt->setS32(gWidgetEventSinkIDSlotName, this->getSinkID());
+ return true;
+}
+
+SkEventSinkID SkWidgetView::GetWidgetEventSinkID(const SkEvent& evt)
+{
+ int32_t sinkID;
+
+ return evt.findS32(gWidgetEventSinkIDSlotName, &sinkID) ? (SkEventSinkID)sinkID : 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*virtual*/ bool SkButtonView::onEvent(const SkEvent& evt)
+{
+ if (evt.isType(SK_EventType_Key) && evt.getFast32() == kOK_SkKey)
+ {
+ this->postWidgetEvent();
+ return true;
+ }
+ return this->INHERITED::onEvent(evt);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+SkCheckButtonView::SkCheckButtonView() : fCheckState(kOff_CheckState)
+{
+}
+
+void SkCheckButtonView::setCheckState(CheckState state)
+{
+ SkASSERT((unsigned)state <= kUnknown_CheckState);
+
+ if (fCheckState != state)
+ {
+ this->onCheckStateChange(this->getCheckState(), state);
+ fCheckState = SkToU8(state);
+ }
+}
+
+/*virtual*/ void SkCheckButtonView::onCheckStateChange(CheckState oldState, CheckState newState)
+{
+ this->inval(nil);
+}
+
+/*virtual*/ void SkCheckButtonView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ this->INHERITED::onInflate(dom, node);
+
+ int index = dom.findList(node, "check-state", "off,on,unknown");
+ if (index >= 0)
+ this->setCheckState((CheckState)index);
+}
+
+static const char gCheckStateSlotName[] = "sk-checkbutton-check-slot";
+
+/*virtual*/ bool SkCheckButtonView::onPrepareWidgetEvent(SkEvent* evt)
+{
+ // could check if we're "disabled", and return false...
+
+ evt->setS32(gCheckStateSlotName, this->getCheckState());
+ return true;
+}
+
+bool SkCheckButtonView::GetWidgetEventCheckState(const SkEvent& evt, CheckState* state)
+{
+ int32_t state32;
+
+ if (evt.findS32(gCheckStateSlotName, &state32))
+ {
+ if (state)
+ *state = (CheckState)state32;
+ return true;
+ }
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+#include "SkTime.h"
+#include <stdio.h>
+
+class SkAnimButtonView : public SkButtonView {
+public:
+ SkAnimButtonView()
+ {
+ fAnim.setHostEventSink(this);
+ init_skin_anim(kButton_SkinEnum, &fAnim);
+ }
+
+protected:
+ virtual void onLabelChange(const char oldLabel[], const char newLabel[])
+ {
+ this->INHERITED::onLabelChange(oldLabel, newLabel);
+
+ SkEvent evt("user");
+ evt.setString("id", "setLabel");
+ evt.setString("LABEL", newLabel);
+ fAnim.doUserEvent(evt);
+ }
+
+ virtual void onFocusChange(bool gainFocus)
+ {
+ this->INHERITED::onFocusChange(gainFocus);
+
+ SkEvent evt("user");
+ evt.setString("id", "setFocus");
+ evt.setS32("FOCUS", gainFocus);
+ fAnim.doUserEvent(evt);
+ }
+
+ virtual void onSizeChange()
+ {
+ this->INHERITED::onSizeChange();
+
+ SkEvent evt("user");
+ evt.setString("id", "setDim");
+ evt.setScalar("dimX", this->width());
+ evt.setScalar("dimY", this->height());
+ fAnim.doUserEvent(evt);
+ }
+
+ virtual void onDraw(SkCanvas* canvas)
+ {
+ SkPaint paint;
+ SkAnimator::DifferenceType diff = fAnim.draw(canvas, &paint, SkTime::GetMSecs());
+
+ if (diff == SkAnimator::kDifferent)
+ this->inval(nil);
+ else if (diff == SkAnimator::kPartiallyDifferent)
+ {
+ SkRect bounds;
+ fAnim.getInvalBounds(&bounds);
+ this->inval(&bounds);
+ }
+ }
+
+ virtual bool onEvent(const SkEvent& evt)
+ {
+ if (evt.isType(SK_EventType_Inval))
+ {
+ this->inval(nil);
+ return true;
+ }
+ if (evt.isType("recommendDim"))
+ {
+ SkScalar height;
+
+ if (evt.findScalar("y", &height))
+ this->setHeight(height);
+ return true;
+ }
+ return this->INHERITED::onEvent(evt);
+ }
+
+ virtual bool onPrepareWidgetEvent(SkEvent* evt)
+ {
+ if (this->INHERITED::onPrepareWidgetEvent(evt))
+ {
+ SkEvent e("user");
+ e.setString("id", "handlePress");
+ (void)fAnim.doUserEvent(e);
+ return true;
+ }
+ return false;
+ }
+
+private:
+ SkAnimator fAnim;
+
+ typedef SkButtonView INHERITED;
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////
+
+#include "SkTextBox.h"
+
+SkStaticTextView::SkStaticTextView()
+{
+ fMargin.set(0, 0);
+ fMode = kFixedSize_Mode;
+ fSpacingAlign = SkTextBox::kStart_SpacingAlign;
+
+ init_skin_paint(kStaticText_SkinEnum, &fPaint);
+}
+
+SkStaticTextView::~SkStaticTextView()
+{
+}
+
+void SkStaticTextView::computeSize()
+{
+ if (fMode == kAutoWidth_Mode)
+ {
+ SkScalar width = fPaint.measureText(fText.c_str(), fText.size());
+ this->setWidth(width + fMargin.fX * 2);
+ }
+ else if (fMode == kAutoHeight_Mode)
+ {
+ SkScalar width = this->width() - fMargin.fX * 2;
+ int lines = width > 0 ? SkTextLineBreaker::CountLines(fText.c_str(), fText.size(), fPaint, width) : 0;
+
+ this->setHeight(lines * fPaint.getFontSpacing() + fMargin.fY * 2);
+ }
+}
+
+void SkStaticTextView::setMode(Mode mode)
+{
+ SkASSERT((unsigned)mode < kModeCount);
+
+ if (fMode != mode)
+ {
+ fMode = SkToU8(mode);
+ this->computeSize();
+ }
+}
+
+void SkStaticTextView::setSpacingAlign(SkTextBox::SpacingAlign align)
+{
+ fSpacingAlign = SkToU8(align);
+ this->inval(nil);
+}
+
+void SkStaticTextView::getMargin(SkPoint* margin) const
+{
+ if (margin)
+ *margin = fMargin;
+}
+
+void SkStaticTextView::setMargin(SkScalar dx, SkScalar dy)
+{
+ if (fMargin.fX != dx || fMargin.fY != dy)
+ {
+ fMargin.set(dx, dy);
+ this->computeSize();
+ this->inval(nil);
+ }
+}
+
+size_t SkStaticTextView::getText(SkString* text) const
+{
+ if (text)
+ *text = fText;
+ return fText.size();
+}
+
+size_t SkStaticTextView::getText(char text[]) const
+{
+ if (text)
+ memcpy(text, fText.c_str(), fText.size());
+ return fText.size();
+}
+
+void SkStaticTextView::setText(const SkString& text)
+{
+ this->setText(text.c_str(), text.size());
+}
+
+void SkStaticTextView::setText(const char text[])
+{
+ if (text == nil)
+ text = "";
+ this->setText(text, strlen(text));
+}
+
+void SkStaticTextView::setText(const char text[], size_t len)
+{
+ if (!fText.equals(text, len))
+ {
+ fText.set(text, len);
+ this->computeSize();
+ this->inval(nil);
+ }
+}
+
+void SkStaticTextView::getPaint(SkPaint* paint) const
+{
+ if (paint)
+ *paint = fPaint;
+}
+
+void SkStaticTextView::setPaint(const SkPaint& paint)
+{
+ if (fPaint != paint)
+ {
+ fPaint = paint;
+ this->computeSize();
+ this->inval(nil);
+ }
+}
+
+void SkStaticTextView::onDraw(SkCanvas* canvas)
+{
+ this->INHERITED::onDraw(canvas);
+
+ if (fText.isEmpty())
+ return;
+
+ SkTextBox box;
+
+ box.setMode(fMode == kAutoWidth_Mode ? SkTextBox::kOneLine_Mode : SkTextBox::kLineBreak_Mode);
+ box.setSpacingAlign(this->getSpacingAlign());
+ box.setBox(fMargin.fX, fMargin.fY, this->width() - fMargin.fX, this->height() - fMargin.fY);
+ box.draw(canvas, fText.c_str(), fText.size(), fPaint);
+}
+
+void SkStaticTextView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ this->INHERITED::onInflate(dom, node);
+
+ int index;
+ if ((index = dom.findList(node, "mode", "fixed,auto-width,auto-height")) >= 0)
+ this->setMode((Mode)index);
+ else
+ assert_no_attr(dom, node, "mode");
+
+ if ((index = dom.findList(node, "spacing-align", "start,center,end")) >= 0)
+ this->setSpacingAlign((SkTextBox::SpacingAlign)index);
+ else
+ assert_no_attr(dom, node, "spacing-align");
+
+ SkScalar s[2];
+ if (dom.findScalars(node, "margin", s, 2))
+ this->setMargin(s[0], s[1]);
+ else
+ assert_no_attr(dom, node, "margin");
+
+ const char* text = dom.findAttr(node, "text");
+ if (text)
+ this->setText(text);
+
+ if ((node = dom.getFirstChild(node, "paint")) != nil &&
+ (node = dom.getFirstChild(node, "screenplay")) != nil)
+ {
+ inflate_paint(dom, node, &fPaint);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////////////////
+
+SkView* SkWidgetFactory(const char name[])
+{
+ if (name == nil)
+ return nil;
+
+ // must be in the same order as the SkSkinWidgetEnum is declared
+ static const char* gNames[] = {
+ "sk-border",
+ "sk-button",
+ "sk-image",
+ "sk-list",
+ "sk-progress",
+ "sk-scroll",
+ "sk-text"
+
+ };
+
+ for (int i = 0; i < SK_ARRAY_COUNT(gNames); i++)
+ if (!strcmp(gNames[i], name))
+ return SkWidgetFactory((SkWidgetEnum)i);
+
+ return nil;
+}
+
+#include "SkImageView.h"
+#include "SkProgressBarView.h"
+#include "SkScrollBarView.h"
+#include "SkBorderView.h"
+
+SkView* SkWidgetFactory(SkWidgetEnum sw)
+{
+ switch (sw) {
+ case kBorder_WidgetEnum:
+ return new SkBorderView;
+ case kButton_WidgetEnum:
+ return new SkAnimButtonView;
+ case kImage_WidgetEnum:
+ return new SkImageView;
+ case kList_WidgetEnum:
+ return new SkListView;
+ case kProgress_WidgetEnum:
+ return new SkProgressBarView;
+ case kScroll_WidgetEnum:
+ return new SkScrollBarView;
+ case kText_WidgetEnum:
+ return new SkStaticTextView;
+ default:
+ SkASSERT(!"unknown enum passed to SkWidgetFactory");
+ break;
+ }
+ return nil;
+}
diff --git a/src/views/SkWidgets.cpp b/src/views/SkWidgets.cpp
new file mode 100644
index 0000000..fe87cd6
--- /dev/null
+++ b/src/views/SkWidgets.cpp
@@ -0,0 +1,554 @@
+#include "SkWidget.h"
+#include "SkCanvas.h"
+#include "SkKey.h"
+#include "SkParsePaint.h"
+#include "SkSystemEventTypes.h"
+#include "SkTextBox.h"
+
+#if 0
+
+#ifdef SK_DEBUG
+ static void assert_no_attr(const SkDOM& dom, const SkDOM::Node* node, const char attr[])
+ {
+ const char* value = dom.findAttr(node, attr);
+ if (value)
+ SkDebugf("unknown attribute %s=\"%s\"\n", attr, value);
+ }
+#else
+ #define assert_no_attr(dom, node, attr)
+#endif
+
+#include "SkAnimator.h"
+#include "SkTime.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+enum SkinType {
+ kPushButton_SkinType,
+ kStaticText_SkinType,
+
+ kSkinTypeCount
+};
+
+struct SkinSuite {
+ SkinSuite();
+ ~SkinSuite()
+ {
+ for (int i = 0; i < kSkinTypeCount; i++)
+ delete fAnimators[i];
+ }
+
+ SkAnimator* get(SkinType);
+
+private:
+ SkAnimator* fAnimators[kSkinTypeCount];
+};
+
+SkinSuite::SkinSuite()
+{
+ static const char kSkinPath[] = "skins/";
+
+ static const char* gSkinNames[] = {
+ "pushbutton_skin.xml",
+ "statictext_skin.xml"
+ };
+
+ for (unsigned i = 0; i < SK_ARRAY_COUNT(gSkinNames); i++)
+ {
+ size_t len = strlen(gSkinNames[i]);
+ SkString path(sizeof(kSkinPath) - 1 + len);
+
+ memcpy(path.writable_str(), kSkinPath, sizeof(kSkinPath) - 1);
+ memcpy(path.writable_str() + sizeof(kSkinPath) - 1, gSkinNames[i], len);
+
+ fAnimators[i] = new SkAnimator;
+ if (!fAnimators[i]->decodeURI(path.c_str()))
+ {
+ delete fAnimators[i];
+ fAnimators[i] = nil;
+ }
+ }
+}
+
+SkAnimator* SkinSuite::get(SkinType st)
+{
+ SkASSERT((unsigned)st < kSkinTypeCount);
+ return fAnimators[st];
+}
+
+static SkinSuite* gSkinSuite;
+
+static SkAnimator* get_skin_animator(SkinType st)
+{
+#if 0
+ if (gSkinSuite == nil)
+ gSkinSuite = new SkinSuite;
+ return gSkinSuite->get(st);
+#else
+ return nil;
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkWidget::Init()
+{
+}
+
+void SkWidget::Term()
+{
+ delete gSkinSuite;
+}
+
+void SkWidget::onEnabledChange()
+{
+ this->inval(nil);
+}
+
+void SkWidget::postWidgetEvent()
+{
+ if (!fEvent.isType("") && this->hasListeners())
+ {
+ this->prepareWidgetEvent(&fEvent);
+ this->postToListeners(fEvent);
+ }
+}
+
+void SkWidget::prepareWidgetEvent(SkEvent*)
+{
+ // override in subclass to add any additional fields before posting
+}
+
+void SkWidget::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ this->INHERITED::onInflate(dom, node);
+
+ if ((node = dom.getFirstChild(node, "event")) != nil)
+ fEvent.inflate(dom, node);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+size_t SkHasLabelWidget::getLabel(SkString* str) const
+{
+ if (str)
+ *str = fLabel;
+ return fLabel.size();
+}
+
+size_t SkHasLabelWidget::getLabel(char buffer[]) const
+{
+ if (buffer)
+ memcpy(buffer, fLabel.c_str(), fLabel.size());
+ return fLabel.size();
+}
+
+void SkHasLabelWidget::setLabel(const SkString& str)
+{
+ this->setLabel(str.c_str(), str.size());
+}
+
+void SkHasLabelWidget::setLabel(const char label[])
+{
+ this->setLabel(label, strlen(label));
+}
+
+void SkHasLabelWidget::setLabel(const char label[], size_t len)
+{
+ if (!fLabel.equals(label, len))
+ {
+ fLabel.set(label, len);
+ this->onLabelChange();
+ }
+}
+
+void SkHasLabelWidget::onLabelChange()
+{
+ // override in subclass
+}
+
+void SkHasLabelWidget::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ this->INHERITED::onInflate(dom, node);
+
+ const char* text = dom.findAttr(node, "label");
+ if (text)
+ this->setLabel(text);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////
+
+void SkButtonWidget::setButtonState(State state)
+{
+ if (fState != state)
+ {
+ fState = state;
+ this->onButtonStateChange();
+ }
+}
+
+void SkButtonWidget::onButtonStateChange()
+{
+ this->inval(nil);
+}
+
+void SkButtonWidget::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ this->INHERITED::onInflate(dom, node);
+
+ int index;
+ if ((index = dom.findList(node, "buttonState", "off,on,unknown")) >= 0)
+ this->setButtonState((State)index);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////
+
+bool SkPushButtonWidget::onEvent(const SkEvent& evt)
+{
+ if (evt.isType(SK_EventType_Key) && evt.getFast32() == kOK_SkKey)
+ {
+ this->postWidgetEvent();
+ return true;
+ }
+ return this->INHERITED::onEvent(evt);
+}
+
+static const char* computeAnimatorState(int enabled, int focused, SkButtonWidget::State state)
+{
+ if (!enabled)
+ return "disabled";
+ if (state == SkButtonWidget::kOn_State)
+ {
+ SkASSERT(focused);
+ return "enabled-pressed";
+ }
+ if (focused)
+ return "enabled-focused";
+ return "enabled";
+}
+
+#include "SkBlurMaskFilter.h"
+#include "SkEmbossMaskFilter.h"
+
+static void create_emboss(SkPaint* paint, SkScalar radius, bool focus, bool pressed)
+{
+ SkEmbossMaskFilter::Light light;
+
+ light.fDirection[0] = SK_Scalar1/2;
+ light.fDirection[1] = SK_Scalar1/2;
+ light.fDirection[2] = SK_Scalar1/3;
+ light.fAmbient = 0x48;
+ light.fSpecular = 0x80;
+
+ if (pressed)
+ {
+ light.fDirection[0] = -light.fDirection[0];
+ light.fDirection[1] = -light.fDirection[1];
+ }
+ if (focus)
+ light.fDirection[2] += SK_Scalar1/4;
+
+ paint->setMaskFilter(new SkEmbossMaskFilter(light, radius))->unref();
+}
+
+void SkPushButtonWidget::onDraw(SkCanvas* canvas)
+{
+ this->INHERITED::onDraw(canvas);
+
+ SkString label;
+ this->getLabel(&label);
+
+ SkAnimator* anim = get_skin_animator(kPushButton_SkinType);
+
+ if (anim)
+ {
+ SkEvent evt("user");
+
+ evt.setString("id", "prime");
+ evt.setScalar("prime-width", this->width());
+ evt.setScalar("prime-height", this->height());
+ evt.setString("prime-text", label);
+ evt.setString("prime-state", computeAnimatorState(this->isEnabled(), this->hasFocus(), this->getButtonState()));
+
+ (void)anim->doUserEvent(evt);
+ SkPaint paint;
+ anim->draw(canvas, &paint, SkTime::GetMSecs());
+ }
+ else
+ {
+ SkRect r;
+ SkPaint p;
+
+ r.set(0, 0, this->width(), this->height());
+ p.setAntiAliasOn(true);
+ p.setColor(SK_ColorBLUE);
+ create_emboss(&p, SkIntToScalar(12)/5, this->hasFocus(), this->getButtonState() == kOn_State);
+ canvas->drawRoundRect(r, SkScalarHalf(this->height()), SkScalarHalf(this->height()), p);
+ p.setMaskFilter(nil);
+
+ p.setTextAlign(SkPaint::kCenter_Align);
+
+ SkTextBox box;
+ box.setMode(SkTextBox::kOneLine_Mode);
+ box.setSpacingAlign(SkTextBox::kCenter_SpacingAlign);
+ box.setBox(0, 0, this->width(), this->height());
+
+// if (this->getButtonState() == kOn_State)
+// p.setColor(SK_ColorRED);
+// else
+ p.setColor(SK_ColorWHITE);
+
+ box.draw(canvas, label.c_str(), label.size(), p);
+ }
+}
+
+SkView::Click* SkPushButtonWidget::onFindClickHandler(SkScalar x, SkScalar y)
+{
+ this->acceptFocus();
+ return new Click(this);
+}
+
+bool SkPushButtonWidget::onClick(Click* click)
+{
+ SkRect r;
+ State state = kOff_State;
+
+ this->getLocalBounds(&r);
+ if (r.contains(click->fCurr))
+ {
+ if (click->fState == Click::kUp_State)
+ this->postWidgetEvent();
+ else
+ state = kOn_State;
+ }
+ this->setButtonState(state);
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+
+SkStaticTextView::SkStaticTextView(U32 flags) : SkView(flags)
+{
+ fMargin.set(0, 0);
+ fMode = kFixedSize_Mode;
+ fSpacingAlign = SkTextBox::kStart_SpacingAlign;
+}
+
+SkStaticTextView::~SkStaticTextView()
+{
+}
+
+void SkStaticTextView::computeSize()
+{
+ if (fMode == kAutoWidth_Mode)
+ {
+ SkScalar width = fPaint.measureText(fText.c_str(), fText.size(), nil, nil);
+ this->setWidth(width + fMargin.fX * 2);
+ }
+ else if (fMode == kAutoHeight_Mode)
+ {
+ SkScalar width = this->width() - fMargin.fX * 2;
+ int lines = width > 0 ? SkTextLineBreaker::CountLines(fText.c_str(), fText.size(), fPaint, width) : 0;
+
+ SkScalar before, after;
+ (void)fPaint.measureText(0, nil, &before, &after);
+
+ this->setHeight(lines * (after - before) + fMargin.fY * 2);
+ }
+}
+
+void SkStaticTextView::setMode(Mode mode)
+{
+ SkASSERT((unsigned)mode < kModeCount);
+
+ if (fMode != mode)
+ {
+ fMode = SkToU8(mode);
+ this->computeSize();
+ }
+}
+
+void SkStaticTextView::setSpacingAlign(SkTextBox::SpacingAlign align)
+{
+ fSpacingAlign = SkToU8(align);
+ this->inval(nil);
+}
+
+void SkStaticTextView::getMargin(SkPoint* margin) const
+{
+ if (margin)
+ *margin = fMargin;
+}
+
+void SkStaticTextView::setMargin(SkScalar dx, SkScalar dy)
+{
+ if (fMargin.fX != dx || fMargin.fY != dy)
+ {
+ fMargin.set(dx, dy);
+ this->computeSize();
+ this->inval(nil);
+ }
+}
+
+size_t SkStaticTextView::getText(SkString* text) const
+{
+ if (text)
+ *text = fText;
+ return fText.size();
+}
+
+size_t SkStaticTextView::getText(char text[]) const
+{
+ if (text)
+ memcpy(text, fText.c_str(), fText.size());
+ return fText.size();
+}
+
+void SkStaticTextView::setText(const SkString& text)
+{
+ this->setText(text.c_str(), text.size());
+}
+
+void SkStaticTextView::setText(const char text[])
+{
+ this->setText(text, strlen(text));
+}
+
+void SkStaticTextView::setText(const char text[], size_t len)
+{
+ if (!fText.equals(text, len))
+ {
+ fText.set(text, len);
+ this->computeSize();
+ this->inval(nil);
+ }
+}
+
+void SkStaticTextView::getPaint(SkPaint* paint) const
+{
+ if (paint)
+ *paint = fPaint;
+}
+
+void SkStaticTextView::setPaint(const SkPaint& paint)
+{
+ if (fPaint != paint)
+ {
+ fPaint = paint;
+ this->computeSize();
+ this->inval(nil);
+ }
+}
+
+void SkStaticTextView::onDraw(SkCanvas* canvas)
+{
+ this->INHERITED::onDraw(canvas);
+
+ if (fText.isEmpty())
+ return;
+
+ SkTextBox box;
+
+ box.setMode(fMode == kAutoWidth_Mode ? SkTextBox::kOneLine_Mode : SkTextBox::kLineBreak_Mode);
+ box.setSpacingAlign(this->getSpacingAlign());
+ box.setBox(fMargin.fX, fMargin.fY, this->width() - fMargin.fX, this->height() - fMargin.fY);
+ box.draw(canvas, fText.c_str(), fText.size(), fPaint);
+}
+
+void SkStaticTextView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ this->INHERITED::onInflate(dom, node);
+
+ int index;
+ if ((index = dom.findList(node, "mode", "fixed,auto-width,auto-height")) >= 0)
+ this->setMode((Mode)index);
+ else
+ assert_no_attr(dom, node, "mode");
+
+ if ((index = dom.findList(node, "spacing-align", "start,center,end")) >= 0)
+ this->setSpacingAlign((SkTextBox::SpacingAlign)index);
+ else
+ assert_no_attr(dom, node, "mode");
+
+ SkScalar s[2];
+ if (dom.findScalars(node, "margin", s, 2))
+ this->setMargin(s[0], s[1]);
+ else
+ assert_no_attr(dom, node, "margin");
+
+ const char* text = dom.findAttr(node, "text");
+ if (text)
+ this->setText(text);
+
+ if ((node = dom.getFirstChild(node, "paint")) != nil)
+ SkPaint_Inflate(&fPaint, dom, node);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#include "SkImageDecoder.h"
+
+SkBitmapView::SkBitmapView(U32 flags) : SkView(flags)
+{
+}
+
+SkBitmapView::~SkBitmapView()
+{
+}
+
+bool SkBitmapView::getBitmap(SkBitmap* bitmap) const
+{
+ if (bitmap)
+ *bitmap = fBitmap;
+ return fBitmap.getConfig() != SkBitmap::kNo_Config;
+}
+
+void SkBitmapView::setBitmap(const SkBitmap* bitmap, bool viewOwnsPixels)
+{
+ if (bitmap)
+ {
+ fBitmap = *bitmap;
+ fBitmap.setOwnsPixels(viewOwnsPixels);
+ }
+}
+
+bool SkBitmapView::loadBitmapFromFile(const char path[])
+{
+ SkBitmap bitmap;
+
+ if (SkImageDecoder::DecodeFile(path, &bitmap))
+ {
+ this->setBitmap(&bitmap, true);
+ bitmap.setOwnsPixels(false);
+ return true;
+ }
+ return false;
+}
+
+void SkBitmapView::onDraw(SkCanvas* canvas)
+{
+ if (fBitmap.getConfig() != SkBitmap::kNo_Config &&
+ fBitmap.width() && fBitmap.height())
+ {
+ SkAutoCanvasRestore restore(canvas, true);
+ SkPaint p;
+
+ p.setFilterType(SkPaint::kBilinear_FilterType);
+ canvas->scale( this->width() / fBitmap.width(),
+ this->height() / fBitmap.height(),
+ 0, 0);
+ canvas->drawBitmap(fBitmap, 0, 0, p);
+ }
+}
+
+void SkBitmapView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ this->INHERITED::onInflate(dom, node);
+
+ const char* src = dom.findAttr(node, "src");
+ if (src)
+ (void)this->loadBitmapFromFile(src);
+}
+
+#endif
+
diff --git a/src/views/SkWindow.cpp b/src/views/SkWindow.cpp
new file mode 100644
index 0000000..5d00d4f
--- /dev/null
+++ b/src/views/SkWindow.cpp
@@ -0,0 +1,367 @@
+#include "SkWindow.h"
+#include "SkCanvas.h"
+#include "SkOSMenu.h"
+#include "SkSystemEventTypes.h"
+#include "SkTime.h"
+
+#define SK_EventDelayInval "\xd" "n" "\xa" "l"
+
+#define TEST_BOUNDERx
+
+#include "SkBounder.h"
+class test_bounder : public SkBounder {
+public:
+ test_bounder(const SkBitmap& bm) : fCanvas(bm) {}
+protected:
+ virtual bool onIRect(const SkIRect& r)
+ {
+ SkRect rr;
+
+ rr.set(SkIntToScalar(r.fLeft), SkIntToScalar(r.fTop),
+ SkIntToScalar(r.fRight), SkIntToScalar(r.fBottom));
+
+ SkPaint p;
+
+ p.setStyle(SkPaint::kStroke_Style);
+ p.setColor(SK_ColorYELLOW);
+
+#if 0
+ rr.inset(SK_ScalarHalf, SK_ScalarHalf);
+#else
+ rr.inset(-SK_ScalarHalf, -SK_ScalarHalf);
+#endif
+
+ fCanvas.drawRect(rr, p);
+ return true;
+ }
+private:
+ SkCanvas fCanvas;
+};
+
+SkWindow::SkWindow() : fFocusView(NULL)
+{
+ fClick = NULL;
+ fWaitingOnInval = false;
+
+#ifdef SK_BUILD_FOR_WINCE
+ fConfig = SkBitmap::kRGB_565_Config;
+#else
+ fConfig = SkBitmap::kARGB_8888_Config;
+#endif
+}
+
+SkWindow::~SkWindow()
+{
+ delete fClick;
+
+ fMenus.deleteAll();
+}
+
+void SkWindow::setConfig(SkBitmap::Config config)
+{
+ this->resize(fBitmap.width(), fBitmap.height(), config);
+}
+
+#include "SkImageDecoder.h"
+
+void SkWindow::resize(int width, int height, SkBitmap::Config config)
+{
+ if (config == SkBitmap::kNo_Config)
+ config = fConfig;
+
+ if (width != fBitmap.width() || height != fBitmap.height() || config != fConfig)
+ {
+ fConfig = config;
+ fBitmap.setConfig(config, width, height);
+ fBitmap.allocPixels();
+
+ this->setSize(SkIntToScalar(width), SkIntToScalar(height));
+ this->inval(NULL);
+ }
+
+ SkImageDecoder::SetDeviceConfig(fConfig);
+}
+
+void SkWindow::eraseARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b)
+{
+ fBitmap.eraseARGB(a, r, g, b);
+}
+
+void SkWindow::eraseRGB(U8CPU r, U8CPU g, U8CPU b)
+{
+ fBitmap.eraseRGB(r, g, b);
+}
+
+bool SkWindow::handleInval(const SkRect& r)
+{
+ SkIRect ir;
+
+ r.round(&ir);
+ fDirtyRgn.op(ir, SkRegion::kUnion_Op);
+
+#ifdef SK_BUILD_FOR_WIN32
+ if (!fWaitingOnInval)
+ {
+ fWaitingOnInval = true;
+ (new SkEvent(SK_EventDelayInval))->post(this->getSinkID(), 10);
+ }
+#else
+ this->onHandleInval(ir);
+#endif
+ return true;
+}
+
+#if defined(SK_BUILD_FOR_WINCE) && defined(USE_GX_SCREEN)
+ #include <windows.h>
+ #include <gx.h>
+ extern GXDisplayProperties gDisplayProps;
+#endif
+
+#ifdef SK_SIMULATE_FAILED_MALLOC
+extern bool gEnableControlledThrow;
+#endif
+
+bool SkWindow::update(SkIRect* updateArea)
+{
+ if (!fDirtyRgn.isEmpty())
+ {
+ SkBitmap bm = this->getBitmap();
+
+#if defined(SK_BUILD_FOR_WINCE) && defined(USE_GX_SCREEN)
+ char* buffer = (char*)GXBeginDraw();
+ SkASSERT(buffer);
+
+ RECT rect;
+ GetWindowRect((HWND)((SkOSWindow*)this)->getHWND(), &rect);
+ buffer += rect.top * gDisplayProps.cbyPitch + rect.left * gDisplayProps.cbxPitch;
+
+ bm.setPixels(buffer);
+#endif
+
+ SkCanvas canvas(bm);
+
+ canvas.clipRegion(fDirtyRgn);
+ if (updateArea)
+ *updateArea = fDirtyRgn.getBounds();
+
+ // empty this now, so we can correctly record any inval calls that
+ // might be made during the draw call.
+ fDirtyRgn.setEmpty();
+
+#ifdef TEST_BOUNDER
+ test_bounder b(bm);
+ canvas.setBounder(&b);
+#endif
+#ifdef SK_SIMULATE_FAILED_MALLOC
+ gEnableControlledThrow = true;
+#endif
+#ifdef SK_BUILD_FOR_WIN32
+ try {
+ this->draw(&canvas);
+ }
+ catch (...) {
+ }
+#else
+ this->draw(&canvas);
+#endif
+#ifdef SK_SIMULATE_FAILED_MALLOC
+ gEnableControlledThrow = false;
+#endif
+#ifdef TEST_BOUNDER
+ canvas.setBounder(NULL);
+#endif
+
+#if defined(SK_BUILD_FOR_WINCE) && defined(USE_GX_SCREEN)
+ GXEndDraw();
+#endif
+
+ return true;
+ }
+ return false;
+}
+
+bool SkWindow::handleChar(SkUnichar uni)
+{
+ if (this->onHandleChar(uni))
+ return true;
+
+ SkView* focus = this->getFocusView();
+ if (focus == NULL)
+ focus = this;
+
+ SkEvent evt(SK_EventType_Unichar);
+ evt.setFast32(uni);
+ return focus->doEvent(evt);
+}
+
+bool SkWindow::handleKey(SkKey key)
+{
+ if (key == kNONE_SkKey)
+ return false;
+
+ if (this->onHandleKey(key))
+ return true;
+
+ // send an event to the focus-view
+ {
+ SkView* focus = this->getFocusView();
+ if (focus == NULL)
+ focus = this;
+
+ SkEvent evt(SK_EventType_Key);
+ evt.setFast32(key);
+ if (focus->doEvent(evt))
+ return true;
+ }
+
+ if (key == kUp_SkKey || key == kDown_SkKey)
+ {
+ if (this->moveFocus(key == kUp_SkKey ? kPrev_FocusDirection : kNext_FocusDirection) == NULL)
+ this->onSetFocusView(NULL);
+ return true;
+ }
+ return false;
+}
+
+bool SkWindow::handleKeyUp(SkKey key)
+{
+ if (key == kNONE_SkKey)
+ return false;
+
+ if (this->onHandleKeyUp(key))
+ return true;
+
+ //send an event to the focus-view
+ {
+ SkView* focus = this->getFocusView();
+ if (focus == NULL)
+ focus = this;
+
+ //should this one be the same?
+ SkEvent evt(SK_EventType_KeyUp);
+ evt.setFast32(key);
+ if (focus->doEvent(evt))
+ return true;
+ }
+ return false;
+}
+
+void SkWindow::addMenu(SkOSMenu* menu)
+{
+ *fMenus.append() = menu;
+ this->onAddMenu(menu);
+}
+
+void SkWindow::setTitle(const char title[])
+{
+ if (NULL == title)
+ title = "";
+ this->onSetTitle(title);
+}
+
+bool SkWindow::handleMenu(uint32_t cmd)
+{
+ for (int i = 0; i < fMenus.count(); i++)
+ {
+ SkEvent* evt = fMenus[i]->createEvent(cmd);
+ if (evt)
+ {
+ evt->post(this->getSinkID());
+ return true;
+ }
+ }
+ return false;
+}
+
+//////////////////////////////////////////////////////////////////////
+
+bool SkWindow::onEvent(const SkEvent& evt)
+{
+ if (evt.isType(SK_EventDelayInval))
+ {
+ SkRegion::Iterator iter(fDirtyRgn);
+
+ for (; !iter.done(); iter.next())
+ this->onHandleInval(iter.rect());
+ fWaitingOnInval = false;
+ return true;
+ }
+ return this->INHERITED::onEvent(evt);
+}
+
+bool SkWindow::onGetFocusView(SkView** focus) const
+{
+ if (focus)
+ *focus = fFocusView;
+ return true;
+}
+
+bool SkWindow::onSetFocusView(SkView* focus)
+{
+ if (fFocusView != focus)
+ {
+ if (fFocusView)
+ fFocusView->onFocusChange(false);
+ fFocusView = focus;
+ if (focus)
+ focus->onFocusChange(true);
+ }
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////
+
+void SkWindow::onHandleInval(const SkIRect&)
+{
+}
+
+bool SkWindow::onHandleChar(SkUnichar)
+{
+ return false;
+}
+
+bool SkWindow::onHandleKey(SkKey key)
+{
+ return false;
+}
+
+bool SkWindow::onHandleKeyUp(SkKey key)
+{
+ return false;
+}
+
+bool SkWindow::handleClick(int x, int y, Click::State state)
+{
+ bool handled = false;
+
+ switch (state) {
+ case Click::kDown_State:
+ if (fClick)
+ delete fClick;
+ fClick = this->findClickHandler(SkIntToScalar(x), SkIntToScalar(y));
+ if (fClick)
+ {
+ SkView::DoClickDown(fClick, x, y);
+ handled = true;
+ }
+ break;
+ case Click::kMoved_State:
+ if (fClick)
+ {
+ SkView::DoClickMoved(fClick, x, y);
+ handled = true;
+ }
+ break;
+ case Click::kUp_State:
+ if (fClick)
+ {
+ SkView::DoClickUp(fClick, x, y);
+ delete fClick;
+ fClick = NULL;
+ handled = true;
+ }
+ break;
+ }
+ return handled;
+}
+
diff --git a/src/xml/SkBML_Verbs.h b/src/xml/SkBML_Verbs.h
new file mode 100644
index 0000000..86bfede
--- /dev/null
+++ b/src/xml/SkBML_Verbs.h
@@ -0,0 +1,33 @@
+/* libs/graphics/xml/SkBML_Verbs.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef 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/src/xml/SkBML_XMLParser.cpp b/src/xml/SkBML_XMLParser.cpp
new file mode 100644
index 0000000..53b7f61
--- /dev/null
+++ b/src/xml/SkBML_XMLParser.cpp
@@ -0,0 +1,192 @@
+/* libs/graphics/xml/SkBML_XMLParser.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/xml/SkDOM.cpp b/src/xml/SkDOM.cpp
new file mode 100644
index 0000000..a9fc31e
--- /dev/null
+++ b/src/xml/SkDOM.cpp
@@ -0,0 +1,512 @@
+/* libs/graphics/xml/SkDOM.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/xml/SkJS.cpp b/src/xml/SkJS.cpp
new file mode 100644
index 0000000..03ccba6
--- /dev/null
+++ b/src/xml/SkJS.cpp
@@ -0,0 +1,237 @@
+/* libs/graphics/xml/SkJS.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include <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/src/xml/SkJSDisplayable.cpp b/src/xml/SkJSDisplayable.cpp
new file mode 100644
index 0000000..d52a7c9
--- /dev/null
+++ b/src/xml/SkJSDisplayable.cpp
@@ -0,0 +1,472 @@
+/* libs/graphics/xml/SkJSDisplayable.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include <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/src/xml/SkParse.cpp b/src/xml/SkParse.cpp
new file mode 100644
index 0000000..e740280
--- /dev/null
+++ b/src/xml/SkParse.cpp
@@ -0,0 +1,336 @@
+/* libs/graphics/xml/SkParse.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/xml/SkParseColor.cpp b/src/xml/SkParseColor.cpp
new file mode 100644
index 0000000..eca2e38
--- /dev/null
+++ b/src/xml/SkParseColor.cpp
@@ -0,0 +1,546 @@
+/* libs/graphics/xml/SkParseColor.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/xml/SkXMLParser.cpp b/src/xml/SkXMLParser.cpp
new file mode 100644
index 0000000..da33307
--- /dev/null
+++ b/src/xml/SkXMLParser.cpp
@@ -0,0 +1,95 @@
+/* libs/graphics/xml/SkXMLParser.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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/src/xml/SkXMLPullParser.cpp b/src/xml/SkXMLPullParser.cpp
new file mode 100644
index 0000000..5df75b7
--- /dev/null
+++ b/src/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/src/xml/SkXMLWriter.cpp b/src/xml/SkXMLWriter.cpp
new file mode 100644
index 0000000..4685d81
--- /dev/null
+++ b/src/xml/SkXMLWriter.cpp
@@ -0,0 +1,342 @@
+/* libs/graphics/xml/SkXMLWriter.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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
+