diff options
488 files changed, 79697 insertions, 0 deletions
diff --git a/include/corecg/Sk64.h b/include/corecg/Sk64.h new file mode 100644 index 0000000000..ed67b1f8f5 --- /dev/null +++ b/include/corecg/Sk64.h @@ -0,0 +1,219 @@ +#ifndef Sk64_DEFINED +#define Sk64_DEFINED + +#include "SkMath.h" + +/** \class Sk64 + + Sk64 is a 64-bit math package that does not require long long support from the compiler. +*/ +struct Sk64 { + int32_t fHi; //!< the high 32 bits of the number (including sign) + uint32_t fLo; //!< the low 32 bits of the number + + /** Returns non-zero if the Sk64 can be represented as a signed 32 bit integer + */ + SkBool is32() const { return fHi == ((int32_t)fLo >> 31); } + /** Returns non-zero if the Sk64 cannot be represented as a signed 32 bit integer + */ + SkBool is64() const { return fHi != ((int32_t)fLo >> 31); } + /** Returns non-zero if the Sk64 can be represented as a signed 48 bit integer. Used to know + if we can shift the value down by 16 to treat it as a SkFixed. + */ + SkBool isFixed() const; + + /** Return the signed 32 bit integer equivalent. Asserts that is32() returns non-zero. + */ + int32_t get32() const { SkASSERT(this->is32()); return (int32_t)fLo; } + /** Return the number >> 16. Asserts that this does not loose any significant high bits. + */ + SkFixed getFixed() const + { + SkASSERT(this->isFixed()); + + uint32_t sum = fLo + (1 << 15); + int32_t hi = fHi; + if (sum < fLo) + hi += 1; + + return (hi << 16) | (sum >> 16); + } + /** Return the number >> 30. Asserts that this does not loose any significant high bits. + */ + SkFract getFract() const; + + /** Returns the square-root of the number as a signed 32 bit value. + */ + int32_t getSqrt() const; + + /** Returns the number of leading zeros of the absolute value of this. + Will return in the range [0..64] + */ + int getClzAbs() const; + + /** Returns non-zero if the number is zero + */ + SkBool isZero() const { return (fHi | fLo) == 0; } + /** Returns non-zero if the number is non-zero + */ + SkBool nonZero() const { return fHi | fLo; } + /** Returns non-zero if the number is negative (number < 0) + */ + SkBool isNeg() const { return (uint32_t)fHi >> 31; } + /** Returns non-zero if the number is positive (number > 0) + */ + SkBool isPos() const { return ~(fHi >> 31) & (fHi | fLo); } + /** Returns -1,0,+1 based on the sign of the number + */ + int sign() const { return (fHi >> 31) | Sk32ToBool(fHi | fLo); } + /** Negate the number + */ + void negate(); + + /** If the number < 0, negate the number + */ + void abs(); + + /** Returns the number of bits needed to shift the Sk64 to the right + in order to make it fit in a signed 32 bit integer. + */ + int shiftToMake32() const; + + /** Set the number to zero + */ + void setZero() { fHi = fLo = 0; } + /** Set the high and low 32 bit values of the number + */ + void set(int32_t hi, uint32_t lo) { fHi = hi; fLo = lo; } + /** Set the number to the specified 32 bit integer + */ + void set(int32_t a) { fHi = a >> 31; fLo = a; } + /** Set the number to the product of the two 32 bit integers + */ + void setMul(int32_t a, int32_t b); + + /** extract 32bits after shifting right by bitCount. + Note: itCount must be [0..63]. + Asserts that no significant high bits were lost. + */ + int32_t getShiftRight(unsigned bitCount) const; + /** Shift the number left by the specified number of bits. + @param bits How far to shift left, must be [0..63] + */ + void shiftLeft(unsigned bits); + /** Shift the number right by the specified number of bits. + @param bits How far to shift right, must be [0..63]. This + performs an arithmetic right-shift (sign extending). + */ + void shiftRight(unsigned bits); + /** Shift the number right by the specified number of bits, but + round the result. + @param bits How far to shift right, must be [0..63]. This + performs an arithmetic right-shift (sign extending). + */ + void roundRight(unsigned bits); + + /** Add the specified 32 bit integer to the number + */ + void add(int32_t lo) + { + int32_t hi = lo >> 31; // 0 or -1 + uint32_t sum = fLo + (uint32_t)lo; + + fHi = fHi + hi + (sum < fLo); + fLo = sum; + } + /** Add the specified Sk64 to the number + */ + void add(int32_t hi, uint32_t lo) + { + uint32_t sum = fLo + lo; + + fHi = fHi + hi + (sum < fLo); + fLo = sum; + } + /** Add the specified Sk64 to the number + */ + void add(const Sk64& other) { this->add(other.fHi, other.fLo); } + /** Subtract the specified Sk64 from the number. (*this) = (*this) - num + */ + void sub(const Sk64& num); + /** Subtract the number from the specified Sk64. (*this) = num - (*this) + */ + void rsub(const Sk64& num); + /** Multiply the number by the specified 32 bit integer + */ + void mul(int32_t); + + enum DivOptions { + kTrunc_DivOption, //!< truncate the result when calling div() + kRound_DivOption //!< round the result when calling div() + }; + /** Divide the number by the specified 32 bit integer, using the specified + divide option (either truncate or round). + */ + void div(int32_t, DivOptions); + + SkFixed addGetFixed(const Sk64& other) const + { + return this->addGetFixed(other.fHi, other.fLo); + } + SkFixed addGetFixed(int32_t hi, uint32_t lo) const + { +#ifdef SK_DEBUG + Sk64 tmp(*this); + tmp.add(hi, lo); +#endif + uint32_t sum = fLo + lo + (1 << 15); + + hi = fHi + hi + (sum < fLo); + hi = (hi << 16) | (sum >> 16); + + SkASSERT(hi == tmp.getFixed()); + return hi; + } + + /** Return the result of dividing the number by denom, treating the answer + as a SkFixed. (*this) << 16 / denom. It is an error for denom to be 0. + */ + SkFixed getFixedDiv(const Sk64& denom) const; + + friend bool operator==(const Sk64& a, const Sk64& b) + { + return a.fHi == b.fHi && a.fLo == b.fLo; + } + friend bool operator!=(const Sk64& a, const Sk64& b) + { + return a.fHi != b.fHi || a.fLo != b.fLo; + } + friend bool operator<(const Sk64& a, const Sk64& b) + { + return a.fHi < b.fHi || a.fHi == b.fHi && a.fLo < b.fLo; + } + friend bool operator<=(const Sk64& a, const Sk64& b) + { + return a.fHi < b.fHi || a.fHi == b.fHi && a.fLo <= b.fLo; + } + friend bool operator>(const Sk64& a, const Sk64& b) + { + return a.fHi > b.fHi || a.fHi == b.fHi && a.fLo > b.fLo; + } + friend bool operator>=(const Sk64& a, const Sk64& b) + { + return a.fHi > b.fHi || a.fHi == b.fHi && a.fLo >= b.fLo; + } + +#ifdef SK_CAN_USE_LONGLONG + SkLONGLONG getLongLong() const; +#endif + +#ifdef SK_DEBUG + /** @cond UNIT_TEST */ + static void UnitTest(); + /** @endcond */ +#endif +}; + +#endif + + diff --git a/include/corecg/SkBuffer.h b/include/corecg/SkBuffer.h new file mode 100644 index 0000000000..cd670fc67b --- /dev/null +++ b/include/corecg/SkBuffer.h @@ -0,0 +1,135 @@ +#ifndef SkBuffer_DEFINED +#define SkBuffer_DEFINED + +#include "SkScalar.h" + +/** \class SkRBuffer + + Light weight class for reading data from a memory block. + The RBuffer is given the buffer to read from, with either a specified size + or no size (in which case no range checking is performed). It is iillegal + to attempt to read a value from an empty RBuffer (data == null). +*/ +class SkRBuffer { +public: + SkRBuffer() : fData(0), fPos(0), fStop(0) {} + /** Initialize RBuffer with a data pointer, but no specified length. + This signals the RBuffer to not perform range checks during reading. + */ + SkRBuffer(const void* data) + { + fData = (const char*)data; + fPos = (const char*)data; + fStop = 0; // no bounds checking + } + /** Initialize RBuffer with a data point and length. + */ + SkRBuffer(const void* data, size_t size) + { + SkASSERT(data != 0 || size == 0); + fData = (const char*)data; + fPos = (const char*)data; + fStop = (const char*)data + size; + } + + /** Return the number of bytes that have been read from the beginning + of the data pointer. + */ + size_t pos() const { return fPos - fData; } + /** Return the total size of the data pointer. Only defined if the length was + specified in the constructor or in a call to reset(). + */ + size_t size() const { return fStop - fData; } + /** Return true if the buffer has read to the end of the data pointer. + Only defined if the length was specified in the constructor or in a call + to reset(). Always returns true if the length was not specified. + */ + bool eof() const { return fPos >= fStop; } + + /** Read the specified number of bytes from the data pointer. If buffer is not + null, copy those bytes into buffer. + */ + void read(void* buffer, size_t size) { if (size) this->readNoSizeCheck(buffer, size); } + size_t skipToAlign4(); + + void* readPtr() { void* ptr; read(&ptr, sizeof(ptr)); return ptr; } + SkScalar readScalar() { SkScalar x; read(&x, 4); return x; } + uint32_t readU32() { uint32_t x; read(&x, 4); return x; } + int32_t readS32() { int32_t x; read(&x, 4); return x; } + uint16_t readU16() { uint16_t x; read(&x, 2); return x; } + int16_t readS16() { int16_t x; read(&x, 2); return x; } + uint8_t readU8() { uint8_t x; read(&x, 1); return x; } + bool readBool() { return this->readU8() != 0; } + +private: + void readNoSizeCheck(void* buffer, size_t size); + + const char* fData; + const char* fPos; + const char* fStop; +}; + +/** \class SkWBuffer + + Light weight class for writing data to a memory block. + The WBuffer is given the buffer to write into, with either a specified size + or no size, in which case no range checking is performed. An empty WBuffer + is legal, in which case no data is ever written, but the relative pos() + is updated. +*/ +class SkWBuffer { +public: + SkWBuffer() : fData(0), fPos(0), fStop(0) {} + SkWBuffer(void* data) + { + fData = (char*)data; + fPos = (char*)data; + fStop = 0; // no bounds checking + } + SkWBuffer(void* data, size_t size) + { + SkASSERT(data != 0 || size == 0); + fData = (char*)data; + fPos = (char*)data; + fStop = (char*)data + size; + } + + void reset(void* data) + { + fData = (char*)data; + fPos = (char*)data; + fStop = 0; // no bounds checking + } + void reset(void* data, size_t size) + { + SkASSERT(data != 0 || size == 0); + fData = (char*)data; + fPos = (char*)data; + fStop = (char*)data + size; + } + + void* data() const { return fData; } + size_t pos() const { return fPos - fData; } + size_t size() const { return fStop - fData; } + bool eof() const { return fPos >= fStop; } + + void write(const void* buffer, size_t size) { if (size) this->writeNoSizeCheck(buffer, size); } + size_t padToAlign4(); + + void writePtr(const void* x) { this->writeNoSizeCheck(&x, sizeof(x)); } + void writeScalar(SkScalar x) { this->writeNoSizeCheck(&x, 4); } + void write32(int32_t x) { this->writeNoSizeCheck(&x, 4); } + void write16(int16_t x) { this->writeNoSizeCheck(&x, 2); } + void write8(int8_t x) { this->writeNoSizeCheck(&x, 1); } + void writeBool(bool x) { this->write8(x); } + +private: + void writeNoSizeCheck(const void* buffer, size_t size); + + char* fData; + char* fPos; + char* fStop; +}; + +#endif + diff --git a/include/corecg/SkChunkAlloc.h b/include/corecg/SkChunkAlloc.h new file mode 100644 index 0000000000..3070af875a --- /dev/null +++ b/include/corecg/SkChunkAlloc.h @@ -0,0 +1,30 @@ +#ifndef SkChunkAlloc_DEFINED +#define SkChunkAlloc_DEFINED + +#include "SkTypes.h" + +class SkChunkAlloc { +public: + SkChunkAlloc(size_t minSize) : fBlock(nil), fMinSize(SkAlign4(minSize)) {} + ~SkChunkAlloc(); + + void reset(); + + enum AllocFailType { + kReturnNil_AllocFailType, + kThrow_AllocFailType + }; + void* alloc(size_t bytes, AllocFailType); + +private: + struct Block { + Block* fNext; + size_t fFreeSize; + char* fFreePtr; + // data[] follows + }; + Block* fBlock; + size_t fMinSize; +}; + +#endif diff --git a/include/corecg/SkEndian.h b/include/corecg/SkEndian.h new file mode 100644 index 0000000000..845037c656 --- /dev/null +++ b/include/corecg/SkEndian.h @@ -0,0 +1,82 @@ +#ifndef SkEndian_DEFINED +#define SkEndian_DEFINED + +#include "SkTypes.h" + +/** \file SkEndian.h + + Macros and helper functions for handling 16 and 32 bit values in + big and little endian formats. +*/ + +#if defined(SK_CPU_LENDIAN) && defined(SK_CPU_BENDIAN) + #error "can't have both LENDIAN and BENDIAN defined" +#endif + +#if !defined(SK_CPU_LENDIAN) && !defined(SK_CPU_BENDIAN) + #error "need either LENDIAN or BENDIAN defined" +#endif + +/** Swap the two bytes in the low 16bits of the parameters. + e.g. 0x1234 -> 0x3412 +*/ +inline uint16_t SkEndianSwap16(U16CPU value) +{ + SkASSERT(value == (uint16_t)value); + return (uint16_t)((value >> 8) | (value << 8)); +} + +/** Vector version of SkEndianSwap16(), which swaps the + low two bytes of each value in the array. +*/ +inline void SkEndianSwap16s(uint16_t array[], int count) +{ + SkASSERT(count == 0 || array != nil); + + while (--count >= 0) + { + *array = SkEndianSwap16(*array); + array += 1; + } +} + +/** Reverse all 4 bytes in a 32bit value. + e.g. 0x12345678 -> 0x78563412 +*/ +inline uint32_t SkEndianSwap32(uint32_t value) +{ + return ((value & 0xFF) << 24) | + ((value & 0xFF00) << 8) | + ((value & 0xFF0000) >> 8) | + (value >> 24); +} + +/** Vector version of SkEndianSwap16(), which swaps the + bytes of each value in the array. +*/ +inline void SkEndianSwap32s(uint32_t array[], int count) +{ + SkASSERT(count == 0 || array != nil); + + while (--count >= 0) + { + *array = SkEndianSwap32(*array); + array += 1; + } +} + +#ifdef SK_CPU_LENDIAN + #define SkEndian_SwapBE16(n) SkEndianSwap16(n) + #define SkEndian_SwapBE32(n) SkEndianSwap32(n) + #define SkEndian_SwapLE16(n) (n) + #define SkEndian_SwapLE32(n) (n) +#else // SK_CPU_BENDIAN + #define SkEndian_SwapBE16(n) (n) + #define SkEndian_SwapBE32(n) (n) + #define SkEndian_SwapLE16(n) SkEndianSwap16(n) + #define SkEndian_SwapLE32(n) SkEndianSwap32(n) +#endif + + +#endif + diff --git a/include/corecg/SkFDot6.h b/include/corecg/SkFDot6.h new file mode 100644 index 0000000000..838c3ba126 --- /dev/null +++ b/include/corecg/SkFDot6.h @@ -0,0 +1,51 @@ +#ifndef SkFDot6_DEFINED +#define SkFDot6_DEFINED + +#include "SkMath.h" + +typedef int32_t SkFDot6; + +#define SK_FDot61 (64) +#define SK_FDot6Half (32) + +#ifdef SK_DEBUG + inline SkFDot6 SkIntToFDot6(S16CPU x) + { + SkASSERT(SkToS16(x) == x); + return x << 6; + } +#else + #define SkIntToFDot6(x) ((x) << 6) +#endif + +#define SkFDot6Floor(x) ((x) >> 6) +#define SkFDot6Ceil(x) (((x) + 63) >> 6) +#define SkFDot6Round(x) (((x) + 32) >> 6) + +#define SkFixedToFDot6(x) ((x) >> 10) + +inline SkFixed SkFDot6ToFixed(SkFDot6 x) +{ + SkASSERT((x << 10 >> 10) == x); + + return x << 10; +} + +#ifdef SK_SCALAR_IS_FLOAT + #define SkScalarToFDot6(x) (SkFDot6)((x) * 64) +#else + #define SkScalarToFDot6(x) ((x) >> 10) +#endif + +inline SkFixed SkFDot6Div(SkFDot6 a, SkFDot6 b) +{ + SkASSERT(b != 0); + + if (a == (int16_t)a) + return (a << 16) / b; + else + return SkFixedDiv(a, b); +} + +#endif + diff --git a/include/corecg/SkFixed.h b/include/corecg/SkFixed.h new file mode 100644 index 0000000000..ddb4a6f716 --- /dev/null +++ b/include/corecg/SkFixed.h @@ -0,0 +1,159 @@ +#ifndef SkFixed_DEFINED +#define SkFixed_DEFINED + +/** \file SkFixed.h + + Types and macros for 16.16 fixed point +*/ + +/** 32 bit signed integer used to represent fractions values with 16 bits to the right of the decimal point +*/ +typedef int32_t SkFixed; +#define SK_Fixed1 (1 << 16) +#define SK_FixedHalf (1 << 15) +#define SK_FixedMax (0x7FFFFFFF) +#define SK_FixedMin (0x1) +#define SK_FixedNaN ((int) 0x80000000) +#define SK_FixedPI (0x3243F) +#define SK_FixedSqrt2 (92682) +#define SK_FixedTanPIOver8 (0x6A0A) +#define SK_FixedRoot2Over2 (0xB505) + +#ifdef SK_CAN_USE_FLOAT + #define SkFixedToFloat(x) ((x) * 1.5258789e-5f) + #define SkFloatToFixed(x) ((SkFixed)((x) * SK_Fixed1)) +#endif + +/** 32 bit signed integer used to represent fractions values with 30 bits to the right of the decimal point +*/ +typedef int32_t SkFract; +#define SK_Fract1 (1 << 30) +#define Sk_FracHalf (1 << 29) +#define SK_FractPIOver180 (0x11DF46A) + +#ifdef SK_CAN_USE_FLOAT + #define SkFractToFloat(x) ((float)(x) * 0.00000000093132257f) + #define SkFloatToFract(x) ((SkFract)((x) * SK_Fract1)) +#endif + +/** Converts an integer to a SkFixed, asserting that the result does not overflow + a 32 bit signed integer +*/ +#ifdef SK_DEBUG + inline SkFixed SkIntToFixed(int n) + { + SkASSERT(n >= -32768 && n <= 32767); + return n << 16; + } +#else + // force the cast to SkFixed to ensure that the answer is signed (like the debug version) + #define SkIntToFixed(n) (SkFixed)((n) << 16) +#endif + +/** Converts a SkFixed to a SkFract, asserting that the result does not overflow + a 32 bit signed integer +*/ +#ifdef SK_DEBUG + inline SkFract SkFixedToFract(SkFixed x) + { + SkASSERT(x >= (-2 << 16) && x <= (2 << 16) - 1); + return x << 14; + } +#else + #define SkFixedToFract(x) ((x) << 14) +#endif + +/** Returns the signed fraction of a SkFixed +*/ +inline SkFixed SkFixedFraction(SkFixed x) +{ + SkFixed mask = x >> 31 << 16; + return x & 0xFFFF | mask; +} + +/** Converts a SkFract to a SkFixed +*/ +#define SkFractToFixed(x) ((x) >> 14) +/** Round a SkFixed to an integer +*/ +#define SkFixedRound(x) (((x) + SK_FixedHalf) >> 16) +#define SkFixedCeil(x) (((x) + SK_Fixed1 - 1) >> 16) +#define SkFixedFloor(x) ((x) >> 16) +#define SkFixedAbs(x) SkAbs32(x) +#define SkFixedAve(a, b) (((a) + (b)) >> 1) + +#if defined(SK_BUILD_FOR_BREW) && !defined(AEE_SIMULATOR) + inline SkFixed SkFixedSquare(SkFixed a) + { + SkFixed answer; + asm volatile ( "SMULL r6, r7, %0, %0" : : "r"(a) : "r6", "r7" ); + asm volatile ( "MOV r6, r6, LSR #16" ); + asm volatile ( "ORR r6, r6, r7, LSL #16" ); + asm volatile ( "STR r6, %0" : "=m"(answer) ); + return answer; + } + inline SkFixed SkFixedMul(SkFixed a, SkFixed b) + { + SkFixed answer; + asm volatile ( "SMULL r6, r7, %0, %1" : : "r"(a), "r"(b) : "r6", "r7" ); + asm volatile ( "MOV r6, r6, LSR #16" ); + asm volatile ( "ORR r6, r6, r7, LSL #16" ); + asm volatile ( "STR r6, %0" : "=m"(answer) ); + return answer; + } + inline SkFract SkFractMul(SkFract a, SkFract b) + { + SkFract answer; + asm volatile ( "SMULL r6, r7, %0, %1" : : "r"(a), "r"(b) : "r6", "r7" ); + asm volatile ( "MOV r6, r6, LSR #30" ); + asm volatile ( "ORR r6, r6, r7, LSL #2" ); + asm volatile ( "STR r6, %0" : "=m"(answer) ); + return answer; + } +#else + inline SkFixed SkFixedSquare(SkFixed value) + { + uint32_t a = SkAbs32(value); + uint32_t ah = a >> 16; + uint32_t al = a & 0xFFFF; + return ah * a + al * ah + (al * al >> 16); + } + SkFixed SkFixedMul(SkFixed, SkFixed); + SkFract SkFractMul(SkFract, SkFract); +#endif +#define SkFixedDiv(numer, denom) SkDivBits(numer, denom, 16) +SkFixed SkFixedDivInt(int32_t numer, int32_t denom); +SkFixed SkFixedMod(SkFixed numer, SkFixed denom); +#define SkFixedInvert(n) SkDivBits(SK_Fixed1, n, 16) +#define SkFixedSqrt(n) SkSqrtBits(n, 23) +SkFixed SkFixedMean(SkFixed a, SkFixed b); //*< returns sqrt(x*y) +int SkFixedMulCommon(SkFixed, int , int bias); // internal used by SkFixedMulFloor, SkFixedMulCeil, SkFixedMulRound + +#define SkFractDiv(numer, denom) SkDivBits(numer, denom, 30) +#define SkFractSqrt(n) SkSqrtBits(n, 30) + +SkFixed SkFixedSinCos(SkFixed radians, SkFixed* cosValueOrNil); +#define SkFixedSin(radians) SkFixedSinCos(radians, nil) +inline SkFixed SkFixedCos(SkFixed radians) +{ + SkFixed cosValue; + (void)SkFixedSinCos(radians, &cosValue); + return cosValue; +} +SkFixed SkFixedTan(SkFixed radians); +SkFixed SkFixedASin(SkFixed); +SkFixed SkFixedACos(SkFixed); +SkFixed SkFixedATan2(SkFixed y, SkFixed x); +SkFixed SkFixedExp(SkFixed); +SkFixed SkFixedLog(SkFixed); + +#define SK_FixedNearlyZero (SK_Fixed1 >> 12) + +inline bool SkFixedNearlyZero(SkFixed x, SkFixed tolerance = SK_FixedNearlyZero) +{ + SkASSERT(tolerance > 0); + return SkAbs32(x) < tolerance; +} + +#endif + diff --git a/include/corecg/SkFloatingPoint.h b/include/corecg/SkFloatingPoint.h new file mode 100644 index 0000000000..984108620e --- /dev/null +++ b/include/corecg/SkFloatingPoint.h @@ -0,0 +1,44 @@ +#ifndef SkFloatingPoint_DEFINED +#define SkFloatingPoint_DEFINED + +#include "SkTypes.h" + +#ifdef SK_CAN_USE_FLOAT + +#include <math.h> +#include <float.h> + +#ifdef SK_BUILD_FOR_WINCE + #define sk_float_sqrt(x) (float)::sqrt(x) + #define sk_float_sin(x) (float)::sin(x) + #define sk_float_cos(x) (float)::cos(x) + #define sk_float_tan(x) (float)::tan(x) + #define sk_float_acos(x) (float)::acos(x) + #define sk_float_asin(x) (float)::asin(x) + #define sk_float_atan2(y,x) (float)::atan2(y,x) + #define sk_float_abs(x) (float)::fabs(x) + #define sk_float_mod(x,y) (float)::fmod(x,y) + #define sk_float_exp(x) (float)::exp(x) + #define sk_float_log(x) (float)::log(x) +#else + #define sk_float_sqrt(x) sqrtf(x) + #define sk_float_sin(x) sinf(x) + #define sk_float_cos(x) cosf(x) + #define sk_float_tan(x) tanf(x) +#ifdef SK_BUILD_FOR_MAC + #define sk_float_acos(x) acos(x) + #define sk_float_asin(x) asin(x) +#else + #define sk_float_acos(x) acosf(x) + #define sk_float_asin(x) asinf(x) +#endif + #define sk_float_atan2(y,x) atan2f(y,x) + #define sk_float_abs(x) fabsf(x) + #define sk_float_mod(x,y) fmodf(x,y) + #define sk_float_exp(x) expf(x) + #define sk_float_log(x) logf(x) + #define sk_float_isNaN(x) _isnan(x) +#endif + +#endif +#endif diff --git a/include/corecg/SkMath.h b/include/corecg/SkMath.h new file mode 100644 index 0000000000..9657f7120c --- /dev/null +++ b/include/corecg/SkMath.h @@ -0,0 +1,132 @@ +#ifndef SkMath_DEFINED +#define SkMath_DEFINED + +#include "SkTypes.h" + +/** \file SkMath.h + + This file defines various math types and functions. It also introduces + SkScalar, the type used to describe fractional values and coordinates. + SkScalar is defined at compile time to be either an IEEE float, or a + 16.16 fixed point integer. Various macros and functions in SkMath.h + allow arithmetic operations to be performed on SkScalars without known + which representation is being used. e.g. SkScalarMul(a, b) multiplies + two SkScalar values, and returns a SkScalar, and this works with either + float or fixed implementations. +*/ + +//#if defined(SK_BUILD_FOR_BREW) && !defined(AEE_SIMULATOR) +#if 0 + inline int SkCLZ(uint32_t value) + { + int answer; + asm volatile ( "CLZ r6, %0" : : "r"(value) : "r6" ); + asm volatile ( "STR r6, %0" : "=m"(answer) ); + return answer; + } +#else + int SkCLZ(uint32_t); //<! Returns the number of leading zero bits (0...32) +#endif + +/** Computes the 64bit product of a * b, and then shifts the answer down by + shift bits, returning the low 32bits. shift must be [0..63] + e.g. to perform a fixedmul, call SkMulShift(a, b, 16) +*/ +int32_t SkMulShift(int32_t a, int32_t b, unsigned shift); +/** Computes numer1 * numer2 / denom in full 64 intermediate precision. + It is an error for denom to be 0. There is no special handling if + the result overflows 32bits. +*/ +int32_t SkMulDiv(int32_t numer1, int32_t numer2, int32_t denom); +/** Computes (numer1 << shift) / denom in full 64 intermediate precision. + It is an error for denom to be 0. There is no special handling if + the result overflows 32bits. +*/ +int32_t SkDivBits(int32_t numer, int32_t denom, int shift); +int32_t SkSqrtBits(int32_t value, int bits); +#define SkSqrt32(n) SkSqrtBits(n, 15) +int32_t SkCubeRootBits(int32_t value, int bits); + +/** Returns -1 if n < 0, else returns 0 +*/ +#define SkExtractSign(n) ((int32_t)(n) >> 31) + +/** If sign == -1, returns -n, else sign must be 0, and returns n. + Typically used in conjunction with SkExtractSign(). +*/ +inline int32_t SkApplySign(int32_t n, int32_t sign) +{ + SkASSERT(sign == 0 || sign == -1); + return (n ^ sign) - sign; +} + +/** Returns max(value, 0) +*/ +inline int SkClampPos(int value) +{ + return value & ~(value >> 31); +} + +/** Given an integer and a positive (max) integer, return the value + pinned against 0 and max, inclusive. + Note: only works as long as max - value doesn't wrap around + @param value The value we want returned pinned between [0...max] + @param max The positive max value + @return 0 if value < 0, max if value > max, else value +*/ +inline int SkClampMax(int value, int max) +{ + // ensure that max is positive + SkASSERT(max >= 0); + // ensure that if value is negative, max - value doesn't wrap around + SkASSERT(value >= 0 || max - value > 0); + +#ifdef SK_CPU_HAS_CONDITIONAL_INSTR + if (value < 0) + value = 0; + if (value > max) + value = max; + return value; +#else + + int diff = max - value; + // clear diff if diff is positive + diff &= diff >> 31; + + // clear the result if value < 0 + return (value + diff) & ~(value >> 31); +#endif +} + +/** Given a positive value and a positive max, return the value + pinned against max. + Note: only works as long as max - value doesn't wrap around + @return max if value >= max, else value +*/ +inline unsigned SkClampUMax(unsigned value, unsigned max) +{ +#ifdef SK_CPU_HAS_CONDITIONAL_INSTR + if (value > max) + value = max; + return value; +#else + int diff = max - value; + // clear diff if diff is positive + diff &= diff >> 31; + + return value + diff; +#endif +} + +#include "SkFixed.h" +#include "SkScalar.h" + +#ifdef SK_DEBUG + class SkMath { + public: + static void UnitTest(); + }; +#endif + +#endif + diff --git a/include/corecg/SkMatrix.h b/include/corecg/SkMatrix.h new file mode 100644 index 0000000000..26fdb294cf --- /dev/null +++ b/include/corecg/SkMatrix.h @@ -0,0 +1,298 @@ +#ifndef SkMatrix_DEFINED +#define SkMatrix_DEFINED + +#include "SkRect.h" + +/** \class SkMatrix + + The SkMatrix class holds a 3x3 matrix for transforming coordinates. + SkMatrix does not have a constructor, so it must be explicitly initialized + using either reset() - to construct an identity matrix, or one of the set...() + functions (e.g. setTranslate, setRotate, etc.). +*/ +class SkMatrix { +public: + /** Bit fields used to identify the characteristics of the matrix. + See TypeMask for the corresponding mask values. + */ + enum TypeShift { + kTranslate_Shift, + kScale_Shift, + kAffine_Shift, + kPerspective_Shift, + + kShiftCount + }; + + /** Enum of bit fields for the mask return by getType(). + Use this to identify the complexity of the matrix. + */ + enum TypeMask { + kIdentity_Mask = 0, //!< type is 0 iff the matrix is the identiy + kTranslate_Mask = 1 << kTranslate_Shift, //!< set if the matrix has non-zero translation + kScale_Mask = 1 << kScale_Shift, //!< set if the matrix has X or Y scale different from 1.0 + kAffine_Mask = 1 << kAffine_Shift, //!< set if the matrix skews or rotates + kPerspective_Mask = 1 << kPerspective_Shift //!< set if the matrix is in perspective + }; + + /** Returns true if the mask represents a matrix that will only scale + or translate (i.e., will map a rectangle into another rectangle). + */ + static bool RectStaysRect(TypeMask mask) + { + return (mask & (kAffine_Mask | kPerspective_Mask)) == 0; + } + + /** Returns a mask bitfield describing the types of transformations + that the matrix will perform. This information is used by routines + like mapPoints, to optimize its inner loops to only perform as much + arithmetic as is necessary. + */ + TypeMask getType() const; + + /** Returns true if the matrix is identity. + This is faster than testing if (getType() == kIdentity_Mask) + */ + bool isIdentity() const; + + /** Returns true if the matrix that will only scale + or translate (i.e., will map a rectangle into another rectangle). + */ + bool rectStaysRect() const { return RectStaysRect(this->getType()); } + + SkScalar getScaleX() const { return fMat[0]; } + SkScalar getScaleY() const { return fMat[4]; } + SkScalar getSkewY() const { return fMat[3]; } + SkScalar getSkewX() const { return fMat[1]; } + SkScalar getTranslateX() const { return fMat[2]; } + SkScalar getTranslateY() const { return fMat[5]; } + SkScalar getPerspX() const { return fMat[6]; } + SkScalar getPerspY() const { return fMat[7]; } + + void setScaleX(SkScalar v) { fMat[0] = v; } + void setScaleY(SkScalar v) { fMat[4] = v; } + void setSkewY(SkScalar v) { fMat[3] = v; } + void setSkewX(SkScalar v) { fMat[1] = v; } + void setTranslateX(SkScalar v) { fMat[2] = v; } + void setTranslateY(SkScalar v) { fMat[5] = v; } +#ifdef SK_SCALAR_IS_FIXED + void setPerspX(SkFract v) { fMat[6] = v; } + void setPerspY(SkFract v) { fMat[7] = v; } +#else + void setPerspX(SkScalar v) { fMat[6] = v; } + void setPerspY(SkScalar v) { fMat[7] = v; } +#endif + /** Set the matrix to identity + */ + void reset(); + + void set(const SkMatrix& other) { *this = other; } + + /** Set the matrix to translate by (dx, dy). + */ + void setTranslate(SkScalar dx, SkScalar dy); + /** Set the matrix to scale by sx and sy, with a pivot point at (px, py). + The pivot point is the coordinate that should remain unchanged by the + specified transformation. + */ + void setScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py); + /** Set the matrix to rotate by the specified number of degrees, with a pivot point at (px, py). + The pivot point is the coordinate that should remain unchanged by the + specified transformation. + */ + void setRotate(SkScalar degrees, SkScalar px, SkScalar py); + /** Set the matrix to rotate by the specified sine and cosine values, with a pivot point at (px, py). + The pivot point is the coordinate that should remain unchanged by the + specified transformation. + */ + void setSinCos(SkScalar sinValue, SkScalar cosValue, SkScalar px, SkScalar py); + /** Set the matrix to skew by sx and sy, with a pivot point at (px, py). + The pivot point is the coordinate that should remain unchanged by the + specified transformation. + */ + void setSkew(SkScalar kx, SkScalar ky, SkScalar px, SkScalar py); + /** Set the matrix to the concatenation of the two specified matrices, returning + true if the the result can be represented. Either of the two matrices may + also be the target matrix. *this = a * b; + */ + bool setConcat(const SkMatrix& a, const SkMatrix& b); + + /** Preconcats the matrix with the specified translation. + M' = M * T(dx, dy) + */ + bool preTranslate(SkScalar dx, SkScalar dy); + /** Preconcats the matrix with the specified scale. + M' = M * S(sx, sy, px, py) + */ + bool preScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py); + /** Preconcats the matrix with the specified rotation. + M' = M * R(degrees, px, py) + */ + bool preRotate(SkScalar degrees, SkScalar px, SkScalar py); + /** Preconcats the matrix with the specified skew. + M' = M * K(kx, ky, px, py) + */ + bool preSkew(SkScalar kx, SkScalar ky, SkScalar px, SkScalar py); + /** Preconcats the matrix with the specified matrix. + M' = M * other + */ + bool preConcat(const SkMatrix& other); + + /** Postconcats the matrix with the specified translation. + M' = T(dx, dy) * M + */ + bool postTranslate(SkScalar dx, SkScalar dy); + /** Postconcats the matrix with the specified scale. + M' = S(sx, sy, px, py) * M + */ + bool postScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py); + /** Postconcats the matrix with the specified rotation. + M' = R(degrees, px, py) * M + */ + bool postRotate(SkScalar degrees, SkScalar px, SkScalar py); + /** Postconcats the matrix with the specified skew. + M' = K(kx, ky, px, py) * M + */ + bool postSkew(SkScalar kx, SkScalar ky, SkScalar px, SkScalar py); + /** Postconcats the matrix with the specified matrix. + M' = other * M + */ + bool postConcat(const SkMatrix& other); + + enum ScaleToFit { + kFill_ScaleToFit, //!< scale in X and Y independently + kStart_ScaleToFit, //!< uniform scale in X/Y, align along left/top + kCenter_ScaleToFit, //!< uniform scale in X/Y, align along center + kEnd_ScaleToFit //!< uniform scale in X/Y, align along right/bottom + }; + /** Set the matrix to the scale and translate values that map the source rectangle + to the destination rectangle, returning true if the the result can be represented. + @param src the source rectangle to map from. + @param dst the destination rectangle to map to. + @param stf the ScaleToFit option + @return true if the matrix can be represented by the rectangle mapping. + */ + bool setRectToRect(const SkRect& src, const SkRect& dst, ScaleToFit stf = kFill_ScaleToFit); + /** Set the matrix such that the specified src points would map to the + specified dst points. count must be withing [0..4]. + */ + bool setPolyToPoly(const SkPoint dst[], const SkPoint src[], int count); + + + /** If this matrix can be inverted, return true and if inverse is not nil, set inverse + to be the inverse of this matrix. If this matrix cannot be inverted, ignore inverse + and return false + */ + bool invert(SkMatrix* inverse) const; + + /** Apply this matrix to the array of points specified by src, and write the transformed + points into the array of points specified by dst. + dst[] = M * src[] + @param dst Where the transformed coordinates are written. It must contain at least count entries + @param src The original coordinates that are to be transformed. It must contain at least count entries + @param count The number of points in src to read, and then transform into dst. + @param typeMask The mask bits returned by getType() for this matrix. + */ + bool mapPoints(SkPoint dst[], const SkPoint src[], int count, TypeMask typeMask) const; + /** Apply this matrix to the array of vectors specified by src, and write the transformed + vectors into the array of points specified by dst. This is similar to mapPoints, but + ignores any translation in the matrix. + @param dst Where the transformed coordinates are written. It must contain at least count entries + @param src The original coordinates that are to be transformed. It must contain at least count entries + @param count The number of vectors in src to read, and then transform into dst. + @param typeMask The mask bits returned by getType() for this matrix. + */ + bool mapVectors(SkVector dst[], const SkVector src[], int count, TypeMask typeMask) const; + /** Apply this matrix to the src rectangle, and write the transformed rectangle into + dst. This is accomplished by transforming the 4 corners of src, and then setting + dst to the bounds of those points. + @param dst Where the transformed rectangle is written. + @param src The original rectangle to be transformed. + @param typeMask The mask bits returned by getType() for this matrix. + */ + bool mapRect(SkRect* dst, const SkRect& src, TypeMask typeMask) const; + + /** Helper method for mapPoints() where the TypeMask needs to be computed. + */ + bool mapPoints(SkPoint dst[], const SkPoint src[], int count) const + { + return this->mapPoints(dst, src, count, this->getType()); + } + /** Helper method for mapPoints() where the src and dst arrays are the + same, and the TypeMask needs to be computed. + */ + bool mapPoints(SkPoint pts[], int count) const + { + return this->mapPoints(pts, pts, count, this->getType()); + } + /** Helper method for mapVectors() where the TypeMask needs to be computed. + */ + bool mapVectors(SkVector dst[], const SkVector src[], int count) const + { + return this->mapVectors(dst, src, count, this->getType()); + } + /** Helper method for mapVectors() where the src and dst arrays are the + same, and the TypeMask needs to be computed. + */ + bool mapVectors(SkVector vecs[], int count) const + { + return this->mapVectors(vecs, vecs, count, this->getType()); + } + /** Helper method for mapRect() where the TypeMask needs to be computed. + */ + bool mapRect(SkRect* dst, const SkRect& src) const + { + return this->mapRect(dst, src, this->getType()); + } + /** Helper method for mapRect() where the TypeMask needs to be computed + and the src and dst rects are the same (i.e. map in place) + */ + bool mapRect(SkRect* rect) const + { + return this->mapRect(rect, *rect, this->getType()); + } + + /** Return the mean radius of a circle after it has been mapped by + this matrix. NOTE: in perspective this value assumes the circle + has its center at the origin. + */ + SkScalar mapRadius(SkScalar radius) const; + + typedef void (*MapPtProc)(const SkMatrix& mat, SkScalar x, SkScalar y, SkPoint* result); + MapPtProc getMapPtProc() const; + + /** If the matrix can be stepped in X (not complex perspective) + then return true and if step[XY] is not nil, return the step[XY] value. + If it cannot, return false and ignore step. + */ + bool fixedStepInX(SkScalar y, SkFixed* stepX, SkFixed* stepY) const; + + friend bool operator==(const SkMatrix& a, const SkMatrix& b) + { + return memcmp(a.fMat, b.fMat, sizeof(a)) == 0; + } + +#ifdef SK_DEBUG + /** @cond UNIT_TEST */ + void dump() const; + + static void UnitTest(); + /** @endcond */ +#endif + +private: + SkScalar fMat[9]; + + static void Map2Pt(const SkPoint srcPt[], SkMatrix* dst, SkScalar scale); + static void Map3Pt(const SkPoint srcPt[], SkMatrix* dst, SkScalar scaleX, SkScalar scaleY); + static void Map4Pt(const SkPoint srcPt[], SkMatrix* dst, SkScalar scaleX, SkScalar scaleY); + + static void Perspective_ptProc(const SkMatrix&, SkScalar, SkScalar, SkPoint*); + static void Affine_ptProc(const SkMatrix&, SkScalar, SkScalar, SkPoint*); + static void Scale_ptProc(const SkMatrix&, SkScalar, SkScalar, SkPoint*); + static void Translate_ptProc(const SkMatrix&, SkScalar, SkScalar, SkPoint*); + static void Identity_ptProc(const SkMatrix&, SkScalar, SkScalar, SkPoint*); +}; + +#endif + diff --git a/include/corecg/SkPoint.h b/include/corecg/SkPoint.h new file mode 100644 index 0000000000..5c9e16545f --- /dev/null +++ b/include/corecg/SkPoint.h @@ -0,0 +1,244 @@ +#ifndef SkPoint_DEFINED +#define SkPoint_DEFINED + +#include "SkMath.h" + +/** \struct SkPoint16 + + SkPoint16 holds two 16 bit integer coordinates +*/ +struct SkPoint16 { + int16_t fX, fY; + + void set(S16CPU x, S16CPU y) { fX = SkToS16(x); fY = SkToS16(y); } + + /** Rotate the point clockwise, writing the new point into dst + It is legal for dst == this + */ + void rotateCW(SkPoint16* dst) const; + /** Rotate the point clockwise, writing the new point back into the point + */ + void rotateCW() { this->rotateCW(this); } + /** Rotate the point counter-clockwise, writing the new point into dst. + It is legal for dst == this + */ + void rotateCCW(SkPoint16* dst) const; + /** Rotate the point counter-clockwise, writing the new point back into the point + */ + void rotateCCW() { this->rotateCCW(this); } + /** Negate the X and Y coordinates of the point. + */ + void negate() { fX = -fX; fY = -fY; } + /** Return a new point whose X and Y coordinates are the negative of the original point's + */ + SkPoint16 operator-() const + { + SkPoint16 neg; + neg.fX = -fX; + neg.fY = -fY; + return neg; + } + /** Add v's coordinates to this point's + */ + void operator+=(const SkPoint16& v) + { + fX = SkToS16(fX + v.fX); + fY = SkToS16(fY + v.fY); + } + /** Subtract v's coordinates from this point's + */ + void operator-=(const SkPoint16& v) + { + fX = SkToS16(fX - v.fX); + fY = SkToS16(fY - v.fY); + } + /** Returns true if the point's coordinates equal (x,y) + */ + bool equals(S16CPU x, S16CPU y) const { return fX == x && fY == y; } + friend bool operator==(const SkPoint16& a, const SkPoint16& b) + { + return a.fX == b.fX && a.fY == b.fY; + } + friend bool operator!=(const SkPoint16& a, const SkPoint16& b) + { + return a.fX != b.fX || a.fY != b.fY; + } + /** Returns a new point whose coordinates are the difference between a and b (a - b) + */ + friend SkPoint16 operator-(const SkPoint16& a, const SkPoint16& b) + { + SkPoint16 v; + v.set(a.fX - b.fX, a.fY - b.fY); + return v; + } + /** Returns a new point whose coordinates are the sum of a and b (a + b) + */ + friend SkPoint16 operator+(const SkPoint16& a, const SkPoint16& b) + { + SkPoint16 v; + v.set(a.fX + b.fX, a.fY + b.fY); + return v; + } + /** Returns the dot product of a and b, treating them as 2D vectors + */ + static int32_t DotProduct(const SkPoint16& a, const SkPoint16& b) + { + return a.fX * b.fX + a.fY * b.fY; + } + /** Returns the cross product of a and b, treating them as 2D vectors + */ + static int32_t CrossProduct(const SkPoint16& a, const SkPoint16& b) + { + return a.fX * b.fY - a.fY * b.fX; + } +}; + +struct SkPoint32 { + int32_t fX, fY; + + void set(int x, int y) { fX = x; fY = y; } +}; + +struct SkPoint { + SkScalar fX, fY; + + /** Set the point's X and Y coordinates + */ + void set(SkScalar x, SkScalar y) { fX = x; fY = y; } + /** Set the point's X and Y coordinates by automatically promoting (x,y) to SkScalar values. + */ + void iset(S16CPU x, S16CPU y) { fX = SkIntToScalar(x); fY = SkIntToScalar(y); } + /** Set the point's X and Y coordinates by automatically promoting p's coordinates to SkScalar values. + */ + void iset(const SkPoint16& p) { fX = SkIntToScalar(p.fX); fY = SkIntToScalar(p.fY); } + + /** Return the euclidian distance from (0,0) to the point + */ + SkScalar length() const { return SkPoint::Length(fX, fY); } + + /** Set the point (vector) to be unit-length in the same direction as it + currently is, and return its old length. If the old length is + degenerately small (nearly zero), do nothing and return 0. + */ + bool normalize(); + /** Set the point (vector) to be unit-length in the same direction as the + x,y params, and return their old length. If the old length is + degenerately small (nearly zero), do nothing and return 0. + */ + bool setUnit(SkScalar x, SkScalar y); + /** Scale the point to have the specified length, and return that + length. If the original length is + degenerately small (nearly zero), do nothing and return 0. + */ + bool setLength(SkScalar length); + /** Set the point to have the specified length in the same direction as (x,y), + and return the old length of (x,y). If that old length is + degenerately small (nearly zero), do nothing and return 0. + */ + bool setLength(SkScalar x, SkScalar y, SkScalar length); + + /** Scale the point's coordinates by scale, writing the answer into dst. + It is legal for dst == this. + */ + void scale(SkScalar scale, SkPoint* dst) const; + /** Scale the point's coordinates by scale, writing the answer back into the point. + */ + void scale(SkScalar scale) { this->scale(scale, this); } + + /** Rotate the point clockwise by 90 degrees, writing the answer into dst. + It is legal for dst == this. + */ + void rotateCW(SkPoint* dst) const; + /** Rotate the point clockwise by 90 degrees, writing the answer back into the point. + */ + void rotateCW() { this->rotateCW(this); } + /** Rotate the point counter-clockwise by 90 degrees, writing the answer into dst. + It is legal for dst == this. + */ + void rotateCCW(SkPoint* dst) const; + /** Rotate the point counter-clockwise by 90 degrees, writing the answer back into the point. + */ + void rotateCCW() { this->rotateCCW(this); } + /** Negate the point's coordinates + */ + void negate() { fX = -fX; fY = -fY; } + /** Returns a new point whose coordinates are the negative of the point's + */ + SkPoint operator-() const + { + SkPoint neg; + neg.fX = -fX; + neg.fY = -fY; + return neg; + } + + /** Add v's coordinates to the point's + */ + void operator+=(const SkPoint& v) + { + fX += v.fX; + fY += v.fY; + } + /** Subtract v's coordinates from the point's + */ + void operator-=(const SkPoint& v) + { + fX -= v.fX; + fY -= v.fY; + } + + /** Returns true if the point's coordinates equal (x,y) + */ + bool equals(SkScalar x, SkScalar y) const { return fX == x && fY == y; } + friend bool operator==(const SkPoint& a, const SkPoint& b) + { + return a.fX == b.fX && a.fY == b.fY; + } + friend bool operator!=(const SkPoint& a, const SkPoint& b) + { + return a.fX != b.fX || a.fY != b.fY; + } + + /** Returns a new point whose coordinates are the difference between a's and b's (a - b) + */ + friend SkPoint operator-(const SkPoint& a, const SkPoint& b) + { + SkPoint v; + v.set(a.fX - b.fX, a.fY - b.fY); + return v; + } + /** Returns a new point whose coordinates are the sum of a's and b's (a + b) + */ + friend SkPoint operator+(const SkPoint& a, const SkPoint& b) + { + SkPoint v; + v.set(a.fX + b.fX, a.fY + b.fY); + return v; + } + /** Returns the euclidian distance from (0,0) to (x,y) + */ + static SkScalar Length(SkScalar x, SkScalar y); + /** Returns the euclidian distance between a and b + */ + static SkScalar Distance(const SkPoint& a, const SkPoint& b) + { + return Length(a.fX - b.fX, a.fY - b.fY); + } + /** Returns the dot product of a and b, treating them as 2D vectors + */ + static SkScalar DotProduct(const SkPoint& a, const SkPoint& b) + { + return SkScalarMul(a.fX, b.fX) + SkScalarMul(a.fY, b.fY); + } + /** Returns the cross product of a and b, treating them as 2D vectors + */ + static SkScalar CrossProduct(const SkPoint& a, const SkPoint& b) + { + return SkScalarMul(a.fX, b.fY) - SkScalarMul(a.fY, b.fX); + } +}; + +typedef SkPoint SkVector; + +#endif + diff --git a/include/corecg/SkPostConfig.h b/include/corecg/SkPostConfig.h new file mode 100644 index 0000000000..18f1517fd2 --- /dev/null +++ b/include/corecg/SkPostConfig.h @@ -0,0 +1,169 @@ +#ifndef SkPostConfig_DEFINED +#define SkPostConfig_DEFINED + +#if defined(SK_BUILD_FOR_WIN32) || defined(SK_BUILD_FOR_WINCE) + #define SK_BUILD_FOR_WIN +#endif + +#if defined(SK_DEBUG) && defined(SK_RELEASE) + #error "cannot define both SK_DEBUG and SK_RELEASE" +#elif !defined(SK_DEBUG) && !defined(SK_RELEASE) + #error "must define either SK_DEBUG or SK_RELEASE" +#endif + +#if defined SK_SUPPORT_UNITTEST && !defined(SK_DEBUG) + #error "can't have unittests without debug" +#endif + +#if defined(SK_SCALAR_IS_FIXED) && defined(SK_SCALAR_IS_FLOAT) + #error "cannot define both SK_SCALAR_IS_FIXED and SK_SCALAR_IS_FLOAT" +#elif !defined(SK_SCALAR_IS_FIXED) && !defined(SK_SCALAR_IS_FLOAT) + #ifdef SK_CAN_USE_FLOAT + #define SK_SCALAR_IS_FLOAT + #else + #define SK_SCALAR_IS_FIXED + #endif +#endif + +#if defined(SK_SCALAR_IS_FLOAT) && !defined(SK_CAN_USE_FLOAT) + #define SK_CAN_USE_FLOAT + // we do nothing in the else case: fixed-scalars can have floats or not +#endif + +#if defined(SK_CPU_LENDIAN) && defined(SK_CPU_BENDIAN) + #error "cannot define both SK_CPU_LENDIAN and SK_CPU_BENDIAN" +#elif !defined(SK_CPU_LENDIAN) && !defined(SK_CPU_BENDIAN) + #error "must define either SK_CPU_LENDIAN or SK_CPU_BENDIAN" +#endif + +#ifndef SkNEW + #define SkNEW(type_name) new type_name + #define SkNEW_ARGS(type_name, args) new type_name args + #define SkDELETE(obj) delete obj +#endif + +/////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_BUILD_FOR_WIN + #define WIN32_LEAN_AND_MEAN + #include <windows.h> + #undef WIN32_LEAN_AND_MEAN + + #ifndef SK_DEBUGBREAK + #define SK_DEBUGBREAK(cond) do { if (!(cond)) DebugBreak(); } while (false) + #endif + + #ifdef SK_BUILD_FOR_WIN32 + #define strcasecmp(a, b) stricmp(a, b) + #define strncasecmp(a, b, c) strnicmp(a, b, c) + #elif defined(SK_BUILD_FOR_WINCE) + #define strcasecmp(a, b) _stricmp(a, b) + #define strncasecmp(a, b, c) _strnicmp(a, b, c) + #endif +#elif defined(SK_BUILD_FOR_MAC) + #include <carbon/carbon.h> + #ifndef SK_DEBUGBREAK + #define SK_DEBUGBREAK(cond) do { if (!(cond)) sk_throw(); } while (false) + #endif +#else + #ifdef SK_DEBUG + #include <assert.h> + #ifndef SK_DEBUGBREAK + #define SK_DEBUGBREAK(cond) assert(cond) + #endif + #endif +#endif + +// stdlib macros + +#if 0 +#if !defined(strlen) && defined(SK_DEBUG) + extern size_t sk_strlen(const char*); + #define strlen(s) sk_strlen(s) +#endif +#ifndef sk_strcpy + #define sk_strcpy(dst, src) strcpy(dst, src) +#endif +#ifndef sk_strchr + #define sk_strchr(s, c) strchr(s, c) +#endif +#ifndef sk_strrchr + #define sk_strrchr(s, c) strrchr(s, c) +#endif +#ifndef sk_strcmp + #define sk_strcmp(s, t) strcmp(s, t) +#endif +#ifndef sk_strncmp + #define sk_strncmp(s, t, n) strncmp(s, t, n) +#endif +#ifndef sk_memcpy + #define sk_memcpy(dst, src, n) memcpy(dst, src, n) +#endif +#ifndef memmove + #define memmove(dst, src, n) memmove(dst, src, n) +#endif +#ifndef sk_memset + #define sk_memset(dst, val, n) memset(dst, val, n) +#endif +#ifndef sk_memcmp + #define sk_memcmp(s, t, n) memcmp(s, t, n) +#endif + +#define sk_strequal(s, t) (!sk_strcmp(s, t)) +#define sk_strnequal(s, t, n) (!sk_strncmp(s, t, n)) +#endif + +////////////////////////////////////////////////////////////////////////////////////////////// +#ifndef SK_BUILD_FOR_WINCE +#include <string.h> +#include <stdlib.h> +#else +#define _CMNINTRIN_DECLARE_ONLY +#include "cmnintrin.h" +#endif + +#if defined SK_DEBUG && defined SK_BUILD_FOR_WIN32 +//#define _CRTDBG_MAP_ALLOC +#ifdef free +#undef free +#endif +#include <crtdbg.h> +#undef free + +#ifdef SK_DEBUG +#if defined(SK_SIMULATE_FAILED_MALLOC) && defined(__cplusplus) + void * operator new( + size_t cb, + int nBlockUse, + const char * szFileName, + int nLine, + int foo + ); + void * operator new[]( + size_t cb, + int nBlockUse, + const char * szFileName, + int nLine, + int foo + ); + void operator delete( + void *pUserData, + int, const char*, int, int + ); + void operator delete( + void *pUserData + ); + void operator delete[]( void * p ); + #define DEBUG_CLIENTBLOCK new( _CLIENT_BLOCK, __FILE__, __LINE__, 0) +#else + #define DEBUG_CLIENTBLOCK new( _CLIENT_BLOCK, __FILE__, __LINE__) +#endif + #define new DEBUG_CLIENTBLOCK +#else +#define DEBUG_CLIENTBLOCK +#endif // _DEBUG + +#endif + +#endif + diff --git a/include/corecg/SkPreConfig.h b/include/corecg/SkPreConfig.h new file mode 100644 index 0000000000..7e142872d5 --- /dev/null +++ b/include/corecg/SkPreConfig.h @@ -0,0 +1,101 @@ +#ifndef SkPreConfig_DEFINED +#define SkPreConfig_DEFINED + +#ifdef ANDROID + #define SK_BUILD_FOR_UNIX + #define SK_SCALAR_IS_FIXED + #define SK_CAN_USE_FLOAT +#endif + +#if !defined(SK_CPU_BENDIAN) && !defined(SK_CPU_LENDIAN) + #if defined(__APPLE__) || defined(__MC68K__) + #define SK_CPU_BENDIAN + #else + #define SK_CPU_LENDIAN + #endif +#endif + +////////////////////////////////////////////////////////////////////// + +#if !defined(SK_BUILD_FOR_PALM) && !defined(SK_BUILD_FOR_WINCE) && !defined(SK_BUILD_FOR_WIN32) && !defined(SK_BUILD_FOR_SYMBIAN) && !defined(SK_BUILD_FOR_UNIX) && !defined(SK_BUILD_FOR_MAC) + + #if defined(PALMOS_SDK_VERSION) + #define SK_BUILD_FOR_PALM + #elif defined(UNDER_CE) + #define SK_BUILD_FOR_WINCE + #elif defined(WIN32) + #define SK_BUILD_FOR_WIN32 + #elif defined(__SYMBIAN32__) + #define SK_BUILD_FOR_WIN32 + #elif defined(linux) + #define SK_BUILD_FOR_UNIX + #else + #define SK_BUILD_FOR_MAC + #endif + +#endif + +////////////////////////////////////////////////////////////////////// + +#if !defined(SK_DEBUG) && !defined(SK_RELEASE) + #ifdef NDEBUG + #define SK_RELEASE + #else + #define SK_DEBUG + #endif +#endif + +////////////////////////////////////////////////////////////////////// + +#ifdef SK_BUILD_FOR_WIN32 + #define SK_SCALAR_IS_FLOAT +#endif + +#if defined(SK_BUILD_FOR_WIN32) || defined(SK_BUILD_FOR_MAC) + #define SK_CAN_USE_FLOAT + #define SK_SCALAR_IS_FIXED + #define SK_CAN_USE_LONGLONG +#endif + +////////////////////////////////////////////////////////////////////// + +#ifdef SK_CAN_USE_LONGLONG + #ifdef SK_BUILD_FOR_WIN32 + #define SkLONGLONG __int64 + #else + #define SkLONGLONG long long + #endif +#endif + +////////////////////////////////////////////////////////////////////// + +#if !defined(SK_CPU_BENDIAN) && !defined(SK_CPU_LENDIAN) + +#ifdef SK_BUILD_FOR_MAC + #define SK_CPU_BENDIAN +#else + #define SK_CPU_LENDIAN +#endif + +#endif + +#if defined(SK_BUILD_FOR_BREW) || defined(SK_BUILD_FOR_WINCE) || (defined(SK_BUILD_FOR_SYMBIAN) && !defined(__MARM_THUMB__)) + /* e.g. the ARM instructions have conditional execution, making tiny branches cheap */ + #define SK_CPU_HAS_CONDITIONAL_INSTR +#endif + +////////////////////////////////////////////////////////////////////// +// Conditional features based on build target + +#if defined(SK_BUILD_FOR_WIN32) || defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_UNIX) + #ifndef SK_BUILD_NO_IMAGE_ENCODE + #define SK_SUPPORT_IMAGE_ENCODE + #endif +#endif + +#ifdef SK_BUILD_FOR_SYMBIAN + #define SK_USE_RUNTIME_GLOBALS +#endif + +#endif + diff --git a/include/corecg/SkRandom.h b/include/corecg/SkRandom.h new file mode 100644 index 0000000000..362117e2bd --- /dev/null +++ b/include/corecg/SkRandom.h @@ -0,0 +1,85 @@ +#ifndef SkRandom_DEFINED +#define SkRandom_DEFINED + +#include "Sk64.h" + +/** \class SkRandom + + Utility class that implements pseudo random 32bit numbers using a fast + linear equation. Unlike rand(), this class holds its own seed (initially + set to 0), so that multiple instances can be used with no side-effects. +*/ +class SkRandom { +public: + SkRandom() : fSeed(0) {} + SkRandom(uint32_t seed) : fSeed(seed) {} + + /** Return the next pseudo random number as an unsigned 32bit value. + */ + uint32_t nextU() { uint32_t r = fSeed * kMul + kAdd; fSeed = r; return r; } + /** Return the next pseudo random number as a signed 32bit value. + */ + int32_t nextS() { return (int32_t)this->nextU(); } + /** Return the next pseudo random number as an unsigned 16bit value. + */ + U16CPU nextU16() { return this->nextU() >> 16; } + /** Return the next pseudo random number as a signed 16bit value. + */ + S16CPU nextS16() { return this->nextS() >> 16; } + + /** Return the next pseudo random number, as an unsigned value of + at most bitCount bits. + @param bitCount The maximum number of bits to be returned + */ + uint32_t nextBits(unsigned bitCount) + { + SkASSERT(bitCount > 0 && bitCount <= 32); + return this->nextU() >> (32 - bitCount); + } + /** Return the next pseudo random unsigned number, mapped to lie within + [min, max] inclusive. + */ + uint32_t nextRangeU(uint32_t min, uint32_t max) + { + SkASSERT(min <= max); + return min + this->nextU() % (max - min + 1); + } + + /** Return the next pseudo random number expressed as an unsigned SkFixed + in the range [0..SK_Fixed1). + */ + SkFixed nextUFixed1() { return this->nextU() >> 16; } + /** Return the next pseudo random number expressed as a signed SkFixed + in the range (-SK_Fixed1..SK_Fixed1). + */ + SkFixed nextSFixed1() { return this->nextS() >> 15; } + + /** Return the next pseudo random number expressed as a SkScalar + in the range [0..SK_Scalar1). + */ + SkScalar nextUScalar1() { return SkFixedToScalar(this->nextUFixed1()); } + /** Return the next pseudo random number expressed as a SkScalar + in the range (-SK_Scalar1..SK_Scalar1). + */ + SkScalar nextSScalar1() { return SkFixedToScalar(this->nextSFixed1()); } + + /** Return the next pseudo random number as a signed 64bit value. + */ + void next64(Sk64* a) { SkASSERT(a); a->set(this->nextS(), this->nextU()); } + /** Set the seed of the random object. The seed is initialized to 0 when the + object is first created, and is updated each time the next pseudo random + number is requested. + */ + void setSeed(int32_t seed) { fSeed = (uint32_t)seed; } + +private: + // "Numerical Recipes in C", 1992 page 284 + enum { + kMul = 1664525, + kAdd = 1013904223 + }; + uint32_t fSeed; +}; + +#endif + diff --git a/include/corecg/SkRect.h b/include/corecg/SkRect.h new file mode 100644 index 0000000000..ec48e38af8 --- /dev/null +++ b/include/corecg/SkRect.h @@ -0,0 +1,266 @@ +#ifndef SkRect_DEFINED +#define SkRect_DEFINED + +#include "SkPoint.h" + +/** \struct SkRect16 + + SkRect16 holds four 16 bit integer coordinates for a rectangle +*/ +struct SkRect16 { + S16 fLeft, fTop, fRight, fBottom; + + /** Returns true if the rectangle is empty (e.g. left >= right or top >= bottom) + */ + bool isEmpty() const { return fLeft >= fRight || fTop >= fBottom; } + /** Returns the rectangle's width. This does not check for a valid rectangle (i.e. left <= right) + so the result may be negative. + */ + int width() const { return fRight - fLeft; } + /** Returns the rectangle's height. This does not check for a valid rectangle (i.e. top <= bottom) + so the result may be negative. + */ + int height() const { return fBottom - fTop; } + + friend int operator==(const SkRect16& a, const SkRect16& b) + { + return !memcmp(&a, &b, sizeof(a)); + } + friend int operator!=(const SkRect16& a, const SkRect16& b) + { + return memcmp(&a, &b, sizeof(a)); + } + + /** Set the rectangle to (0,0,0,0) + */ + void setEmpty() { memset(this, 0, sizeof(*this)); } + + void set(S16CPU left, S16CPU top, S16CPU right, S16CPU bottom) + { + fLeft = SkToS16(left); + fTop = SkToS16(top); + fRight = SkToS16(right); + fBottom = SkToS16(bottom); + } + /** Offset set the rectangle by adding dx to its left and right, + and adding dy to its top and bottom. + */ + void offset(S16CPU dx, S16CPU dy) + { + fLeft = SkToS16(fLeft + dx); + fTop = SkToS16(fTop + dy); + fRight = SkToS16(fRight + dx); + fBottom = SkToS16(fBottom + dy); + } + /** Inset the rectangle by (dx,dy). If dx is positive, then the sides are moved inwards, + making the rectangle narrower. If dx is negative, then the sides are moved outwards, + making the rectangle wider. The same hods true for dy and the top and bottom. + */ + void inset(S16CPU dx, S16CPU dy) + { + fLeft = SkToS16(fLeft + dx); + fTop = SkToS16(fTop + dy); + fRight = SkToS16(fRight - dx); + fBottom = SkToS16(fBottom - dy); + } + /** Returns true if (x,y) is inside the rectangle. The left and top are considered to be + inside, while the right and bottom are not. Thus for the rectangle (0, 0, 5, 10), the + points (0,0) and (0,9) are inside, while (-1,0) and (5,9) are not. + */ + bool contains(S16CPU x, S16CPU y) const + { + return (unsigned)(x - fLeft) < (unsigned)(fRight - fLeft) && + (unsigned)(y - fTop) < (unsigned)(fBottom - fTop); + } + /** Returns true if the 4 specified sides of a rectangle are inside or equal to this rectangle. + */ + bool contains(S16CPU left, S16CPU top, S16CPU right, S16CPU bottom) const + { + return fLeft <= left && fTop <= top && + fRight >= right && fBottom >= bottom; + } + /** Returns true if the specified rectangle r is inside or equal to this rectangle. + */ + bool contains(const SkRect16& r) const + { + return fLeft <= r.fLeft && fTop <= r.fTop && + fRight >= r.fRight && fBottom >= r.fBottom; + } + /** If r intersects this rectangle, return true and set this rectangle to that + intersection, otherwise return false and do not change this rectangle. + */ + bool intersect(const SkRect16& r); + /** If rectangles a and b intersect, return true and set this rectangle to that + intersection, otherwise return false and do not change this rectangle. + */ + bool intersect(const SkRect16& a, const SkRect16& b); + /** If the rectangle specified by left,top,right,bottom intersects this rectangle, + return true and set this rectangle to that intersection, + otherwise return false and do not change this rectangle. + */ + bool intersect(S16CPU left, S16CPU top, S16CPU right, S16CPU bottom); + /** Returns true if a and b intersect + */ + static bool Intersects(const SkRect16& a, const SkRect16& b) + { + return a.fLeft < b.fRight && b.fLeft < a.fRight && + a.fTop < b.fBottom && b.fTop < a.fBottom; + } + void join(const SkRect16& r) + { + fLeft = SkToS16(SkMin32(fLeft, r.fLeft)); + fTop = SkToS16(SkMin32(fTop, r.fTop)); + fRight = SkToS16(SkMax32(fRight, r.fRight)); + fBottom = SkToS16(SkMax32(fBottom, r.fBottom)); + } + + /** Swap top/bottom or left/right if there are flipped. + This can be called if the edges are computed separately, + and may have crossed over each other. + When this returns, left <= right && top <= bottom + */ + void sort(); +}; + +/** \struct SkRect +*/ +struct SkRect { + SkScalar fLeft, fTop, fRight, fBottom; + + bool isEmpty() const { return fLeft >= fRight || fTop >= fBottom; } + SkScalar width() const { return fRight - fLeft; } + SkScalar height() const { return fBottom - fTop; } + SkScalar centerX() const { return SkScalarHalf(fLeft + fRight); } + SkScalar centerY() const { return SkScalarHalf(fTop + fBottom); } + + friend int operator==(const SkRect& a, const SkRect& b) + { + return !memcmp(&a, &b, sizeof(a)); + } + friend int operator!=(const SkRect& a, const SkRect& b) + { + return memcmp(&a, &b, sizeof(a)); + } + + /** return the 4 points that enclose the rectangle + */ + void toQuad(SkPoint quad[4]) const; + + /** Set this rectangle to the empty rectangle (0,0,0,0) + */ + void setEmpty() { memset(this, 0, sizeof(*this)); } + + void set(const SkRect16& src) + { + fLeft = SkIntToScalar(src.fLeft); + fTop = SkIntToScalar(src.fTop); + fRight = SkIntToScalar(src.fRight); + fBottom = SkIntToScalar(src.fBottom); + } + + void set(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) + { + fLeft = left; + fTop = top; + fRight = right; + fBottom = bottom; + } + /** Set this rectangle to be the bounds of the array of points. + If the array is empty (count == 0), then set this rectangle + to the empty rectangle (0,0,0,0) + */ + void set(const SkPoint pts[], int count); + + /** Offset set the rectangle by adding dx to its left and right, + and adding dy to its top and bottom. + */ + void offset(SkScalar dx, SkScalar dy) + { + fLeft += dx; + fTop += dy; + fRight += dx; + fBottom += dy; + } + /** Inset the rectangle by (dx,dy). If dx is positive, then the sides are moved inwards, + making the rectangle narrower. If dx is negative, then the sides are moved outwards, + making the rectangle wider. The same hods true for dy and the top and bottom. + */ + void inset(SkScalar dx, SkScalar dy) + { + fLeft += dx; + fTop += dy; + fRight -= dx; + fBottom -= dy; + } + + /** If this rectangle intersects r, return true and set this rectangle to that + intersection, otherwise return false and do not change this rectangle. + */ + bool intersect(const SkRect& r); + /** If this rectangle intersects the rectangle specified by left, top, right, bottom, + return true and set this rectangle to that + intersection, otherwise return false and do not change this rectangle. + */ + bool intersect(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom); + /** Return true if rectangles a and b intersect. + */ + static bool Intersects(const SkRect& a, const SkRect& b) + { + return a.fLeft < b.fRight && b.fLeft < a.fRight && + a.fTop < b.fBottom && b.fTop < a.fBottom; + } + + /** Returns true if (p.fX,p.fY) is inside the rectangle. The left and top coordinates of + the rectangle are considered to be inside, while the right and bottom coordinates + are not. Thus for the rectangle (0, 0, 5, 10), the points (0,0) and (0,9) are inside, + while (-1,0) and (5,9) are not. + */ + bool contains(const SkPoint& p) const + { + return fLeft <= p.fX && p.fX < fRight && + fTop <= p.fY && p.fY < fBottom; + } + /** Returns true if (x,y) is inside the rectangle. The left and top coordinates of + the rectangle are considered to be inside, while the right and bottom coordinates + are not. Thus for the rectangle (0, 0, 5, 10), the points (0,0) and (0,9) are inside, + while (-1,0) and (5,9) are not. + */ + bool contains(SkScalar x, SkScalar y) const + { + return fLeft <= x && x < fRight && + fTop <= y && y < fBottom; + } + /** Return true if this rectangle contains r + */ + bool contains(const SkRect& r) const + { + return fLeft <= r.fLeft && fTop <= r.fTop && + fRight >= r.fRight && fBottom >= r.fBottom; + } + /** Set the dst integer rectangle by rounding this rectangle's coordinates + to their nearest integer values. + */ + void round(SkRect16* dst) const + { + SkASSERT(dst); + dst->set(SkScalarRound(fLeft), SkScalarRound(fTop), SkScalarRound(fRight), SkScalarRound(fBottom)); + } + /** Set the dst integer rectangle by rounding "out" this rectangle, choosing the floor of top and left, + and the ceiling of right and bototm. + */ + void roundOut(SkRect16* dst) const + { + SkASSERT(dst); + dst->set(SkScalarFloor(fLeft), SkScalarFloor(fTop), SkScalarCeil(fRight), SkScalarCeil(fBottom)); + } + + /** Swap top/bottom or left/right if there are flipped. + This can be called if the edges are computed separately, + and may have crossed over each other. + When this returns, left <= right && top <= bottom + */ + void sort(); +}; + +#endif + diff --git a/include/corecg/SkRegion.h b/include/corecg/SkRegion.h new file mode 100644 index 0000000000..73daa9ce68 --- /dev/null +++ b/include/corecg/SkRegion.h @@ -0,0 +1,249 @@ +#ifndef SkRegion_DEFINED +#define SkRegion_DEFINED + +#include "SkRect.h" + +class SkPath; +class SkRgnBuilder; + +namespace android { + class Region; +} + +#define SkRegion_gEmptyRunHeadPtr ((SkRegion::RunHead*)-1) +#define SkRegion_gRectRunHeadPtr 0 + +/** \class SkRegion + + The SkRegion class encapsulates the geometric region used to specify + clipping areas for drawing. +*/ +class SkRegion { +public: + typedef int16_t RunType; + + SkRegion(); + explicit SkRegion(const SkRegion&); + explicit SkRegion(const SkRect16&); + ~SkRegion(); + + SkRegion& operator=(const SkRegion&); + + friend int operator==(const SkRegion& a, const SkRegion& b); + friend int operator!=(const SkRegion& a, const SkRegion& b) + { + return !(a == b); + } + + // provide explicitly, so we'll have a java equivalent + void set(const SkRegion& src) + { + SkASSERT(&src); + *this = src; + } + /** Swap the contents of this and the specified region. This operation + is gauarenteed to never fail. + */ + void swap(SkRegion&); + + /** Return true if this region is empty */ + bool isEmpty() const { return fRunHead == SkRegion_gEmptyRunHeadPtr; } + /** Return true if this region is a single, non-empty rectangle */ + bool isRect() const { return fRunHead == SkRegion_gRectRunHeadPtr; } + /** Return true if this region consists of more than 1 rectangular area */ + bool isComplex() const { return !this->isEmpty() && !this->isRect(); } + /** Return the bounds of this region. If the region is empty, returns an + empty rectangle. + */ + const SkRect16& getBounds() const { return fBounds; } + + /** Returns true if the region is non-empty, and if so, sets the specified path to the + boundary(s) of the region. + */ + bool getBoundaryPath(SkPath* path) const; + + /** Set the region to be empty, and return false */ + bool setEmpty(); + /** If rect is non-empty, set this region to that rectangle and return true, + otherwise set this region to empty and return false. + */ + bool setRect(const SkRect16&); + /** If left < right and top < bottom, set this region to that rectangle and + return true, otherwise set this region to empty and return false. + */ + bool setRect(S16CPU left, S16CPU top, S16CPU right, S16CPU bottom); + /** Set this region to the specified region, and return true if it is non-empty. */ + bool setRegion(const SkRegion&); + /** Set this region to the area described by the path, optionally clipped (if clip is + not nil). Return true if the resulting region is non-empty. This produces a region + that is identical to the pixels that would be drawn by the path (with no antialiasing). + */ + bool setPath(const SkPath&, const SkRegion* clip = nil); + /** Return true if the specified x,y coordinate is inside the region. + */ + bool contains(S16CPU x, S16CPU y) const; + /** Return true if this region is a single rectangle (not complex) and the specified rectangle + is contained by this region. Returning false is not a guarantee that the rectangle is not contained + by this region, but return true is a guarantee that the rectangle is contained by this region. + */ + bool quickContains(const SkRect16& r) const + { + return this->isRect() && fBounds.contains(r); + } + /** Return true if this region is a single rectangle (not complex) and the specified rectangle + is contained by this region. Returning false is not a guarantee that the rectangle is not contained + by this region, but return true is a guarantee that the rectangle is contained by this region. + */ + bool quickContains(S16CPU left, S16CPU top, S16CPU right, S16CPU bottom) const + { + return this->isRect() && fBounds.contains(left, top, right, bottom); + } + /** Return true if this region is empty, or if the specified rectangle does not intersect + the region. Returning false is not a guarantee that they intersect, but returning + true is a guarantee that they do not. + */ + bool quickReject(const SkRect16& rect) const + { + return this->isEmpty() || !SkRect16::Intersects(fBounds, rect); + } + /** Return true if this region, or rgn, is empty, or if their bounds do not intersect. + Returning false is not a guarantee that they intersect, but returning true is a guarantee + that they do not. + */ + bool quickReject(const SkRegion& rgn) const + { + return this->isEmpty() || rgn.isEmpty() || !SkRect16::Intersects(fBounds, rgn.fBounds); + } + + void translate(int dx, int dy) + { + this->translate(dx, dy, this); + } + void translate(int dx, int dy, SkRegion* dst) const; + + enum Op { + kDifference_Op, + kIntersect_Op, + kUnion_Op, + kXOR_Op, + + kOpCount + }; + /** Set this region to the result of applying the Opereation to this region and the specified + rectangle. Return true if the resulting region is non-empty. + */ + bool op(const SkRect16&, Op); + // helper for java, so it doesn't have to create a Rect object + bool op(S16CPU left, S16CPU top, S16CPU right, S16CPU bottom, Op op) + { + SkRect16 r; + r.set(left, top, right, bottom); + return this->op(r, op); + } + /** Set this region to the result of applying the Opereation to this region and the specified + region. Return true if the resulting region is non-empty. + */ + bool op(const SkRegion& rgn, Op op) { return this->op(*this, rgn, op); } + /** Set this region to the result of applying the Opereation to the specified rectangle and region. + Return true if the resulting region is non-empty. + */ + bool op(const SkRect16&, const SkRegion&, Op); + /** Set this region to the result of applying the Opereation to the specified regions. + Return true if the resulting region is non-empty. + */ + bool op(const SkRegion&, const SkRegion&, Op); + + /** Helper class that returns the sequence of rectangles that make up this region. + */ + class Iterator { + public: + Iterator(); + Iterator(const SkRegion&); + void reset(const SkRegion&); + bool done() { return fDone; } + void next(); + const SkRect16& rect() const { return fRect; } + + private: + const RunType* fRuns; + SkRect16 fRect; + bool fDone; + }; + + /** Helper class that returns the sequence of rectangles that make up this region, + intersected with the clip rectangle. + */ + class Cliperator { + public: + Cliperator(const SkRegion&, const SkRect16& clip); + bool done() { return fDone; } + void next(); + const SkRect16& rect() const { return fRect; } + + private: + Iterator fIter; + SkRect16 fClip; + SkRect16 fRect; + bool fDone; + }; + + /** Helper class that returns the sequence of scanline runs that make up this region. + */ + class Spanerator { + public: + Spanerator(const SkRegion&, int y, int left, int right); + bool next(int* left, int* right); + + private: + const SkRegion::RunType* fRuns; + int fLeft, fRight; + bool fDone; + }; + + /** Return the number of bytes need to write this region to a buffer. + */ + size_t computeBufferSize() const; + /** Write the region to the buffer, and return the number of bytes written. + */ + size_t writeToBuffer(void* buffer) const; + /** Initialized the region from the buffer, returning the number + of bytes actually read. + */ + size_t readFromBuffer(const void* buffer); + + SkDEBUGCODE(void dump() const;) + SkDEBUGCODE(void validate() const;) + SkDEBUGCODE(static void UnitTest();) + +private: + enum { + kRectRegionRuns = 6, // need to store a region of a rect [T B L R S S] + kRunTypeSentinel = 0x7FFF + }; + + friend class android::Region; // needed for marshalling efficiently + void allocateRuns(int count); // allocate space for count runs + + struct RunHead; + + SkRect16 fBounds; + RunHead* fRunHead; + + void freeRuns(); + const RunType* getRuns(RunType tmpStorage[], int* count) const; + bool setRuns(RunType runs[], int count); + + int count_runtype_values(int* itop, int* ibot) const; + + static void build_rect_runs(const SkRect16& bounds, RunType runs[kRectRegionRuns]); + static bool compute_run_bounds(const RunType runs[], int count, SkRect16* bounds); + + friend struct RunHead; + friend class Iterator; + friend class Spanerator; + friend class SkRgnBuilder; +}; + + +#endif + diff --git a/include/corecg/SkScalar.h b/include/corecg/SkScalar.h new file mode 100644 index 0000000000..271f1d3915 --- /dev/null +++ b/include/corecg/SkScalar.h @@ -0,0 +1,235 @@ +#ifndef SkScalar_DEFINED +#define SkScalar_DEFINED + +#include "SkTypes.h" + +/** \file SkScalar.h + + Types and macros for the data type SkScalar. This is the fractional numeric type + that, depending on the compile-time flag SK_SCALAR_IS_FLOAT, may be implemented + either as an IEEE float, or as a 16.16 SkFixed. The macros in this file are written + to allow the calling code to manipulate SkScalar values without knowing which representation + is in effect. +*/ + +#ifdef SK_SCALAR_IS_FLOAT + #include "SkFloatingPoint.h" + + /** SkScalar is our type for fractional values and coordinates. Depending on + compile configurations, it is either represented as an IEEE float, or + as a 16.16 fixed point integer. + */ + typedef float SkScalar; + extern const uint32_t gIEEENotANumber; + extern const uint32_t gIEEEInfinity; + + /** SK_Scalar1 is defined to be 1.0 represented as an SkScalar + */ + #define SK_Scalar1 (1.0f) + /** SK_Scalar1 is defined to be 1/2 represented as an SkScalar + */ + #define SK_ScalarHalf (0.5f) + /** SK_ScalarInfinity is defined to be infinity as an SkScalar + */ + #define SK_ScalarInfinity (*(const float*)&gIEEEInfinity) + /** SK_ScalarMax is defined to be the largest value representable as an SkScalar + */ + #define SK_ScalarMax (3.4028235e+38f) + /** SK_ScalarMin is defined to be the smallest value representable as an SkScalar + */ + #define SK_ScalarMin (1.1754944e-38f) + /** SK_ScalarNaN is defined to be 'Not a Number' as an SkScalar + */ + #define SK_ScalarNaN (*(const float*)&gIEEENotANumber) + /** SkScalarIsNaN(n) returns true if argument is not a number + */ + static inline bool SkScalarIsNaN(float x) { return x != x; } + /** SkIntToScalar(n) returns its integer argument as an SkScalar + */ + #define SkIntToScalar(n) ((float)(n)) + /** SkFixedToScalar(n) returns its SkFixed argument as an SkScalar + */ + #define SkFixedToScalar(x) SkFixedToFloat(x) + /** SkFixedToScalar(n) returns its SkScalar argument as an SkFixed + */ + #define SkScalarToFixed(x) (SkFixed)((x) * SK_Fixed1) + + #define SkScalarToFloat(n) (n) + #define SkFloatToScalar(n) (n) + + /** SkScalarFraction(x) returns the signed fractional part of the argument + */ + #define SkScalarFraction(x) sk_float_mod(x, 1.0f) + /** Rounds the SkScalar to the nearest integer value + */ + inline int SkScalarRound(SkScalar x) + { + if (x < 0) + x -= SK_ScalarHalf; + else + x += SK_ScalarHalf; + return (int)x; + } + /** Returns the smallest integer that is >= the specified SkScalar + */ + #define SkScalarCeil(x) (int)ceil(x) + /** Returns the largest integer that is <= the specified SkScalar + */ + #define SkScalarFloor(x) (int)floor(x) + /** Returns the absolute value of the specified SkScalar + */ + #define SkScalarAbs(x) sk_float_abs(x) + /** Returns the value pinned between 0 and max inclusive + */ + inline SkScalar SkScalarClampMax(SkScalar x, SkScalar max) { + return x < 0 ? 0 : x > max ? max : x; + } + /** Returns the value pinned between min and max inclusive + */ + inline SkScalar SkScalarPin(SkScalar x, SkScalar min, SkScalar max) { + return x < min ? min : x > max ? max : x; + } + /** Returns the specified SkScalar squared (x*x) + */ + inline SkScalar SkScalarSquare(SkScalar x) { return x * x; } + /** Returns the product of two SkScalars + */ + #define SkScalarMul(a, b) ((a) * (b)) + /** Returns the product of a SkScalar and an int rounded to the nearest integer value + */ + #define SkScalarMulRound(a, b) SkScalarRound((a) * (b)) + /** Returns the product of a SkScalar and an int promoted to the next larger int + */ + #define SkScalarMulCeil(a, b) SkScalarCeil((a) * (b)) + /** Returns the product of a SkScalar and an int truncated to the next smaller int + */ + #define SkScalarMulFloor(a, b) SkScalarFloor((a) * (b)) + /** Returns the quotient of two SkScalars (a/b) + */ + #define SkScalarDiv(a, b) ((a) / (b)) + /** Returns the mod of two SkScalars (a mod b) + */ + #define SkScalarMod(x,y) sk_float_mod(x,y) + /** Returns the product of the first two arguments, divided by the third argument + */ + #define SkScalarMulDiv(a, b, c) ((a) * (b) / (c)) + /** Returns the multiplicative inverse of the SkScalar (1/x) + */ + #define SkScalarInvert(x) (SK_Scalar1 / (x)) + /** Returns the square root of the SkScalar + */ + #define SkScalarSqrt(x) sk_float_sqrt(x) + /** Returns the average of two SkScalars (a+b)/2 + */ + #define SkScalarAve(a, b) (((a) + (b)) * 0.5f) + /** Returns the geometric mean of two SkScalars + */ + #define SkScalarMean(a, b) sk_float_sqrt((a) * (b)) + /** Returns one half of the specified SkScalar + */ + #define SkScalarHalf(a) ((a) * 0.5f) + + #define SK_ScalarSqrt2 1.41421356f + #define SK_ScalarPI 3.14159265f + #define SK_ScalarTanPIOver8 0.414213562f + #define SK_ScalarRoot2Over2 0.707106781f + + #define SkDegreesToRadians(degrees) ((degrees) * (SK_ScalarPI / 180)) + float SkScalarSinCos(SkScalar radians, SkScalar* cosValue); + #define SkScalarSin(radians) (float)sk_float_sin(radians) + #define SkScalarCos(radians) (float)sk_float_cos(radians) + #define SkScalarTan(radians) (float)sk_float_tan(radians) + #define SkScalarASin(val) (float)sk_float_asin(val) + #define SkScalarACos(val) (float)sk_float_acos(val) + #define SkScalarATan2(y, x) (float)sk_float_atan2(y,x) + #define SkScalarExp(x) (float)sk_float_exp(x) + #define SkScalarLog(x) (float)sk_float_log(x) + + inline SkScalar SkMaxScalar(SkScalar a, SkScalar b) { return a > b ? a : b; } + inline SkScalar SkMinScalar(SkScalar a, SkScalar b) { return a < b ? a : b; } + +#else + #include "SkFixed.h" + + typedef SkFixed SkScalar; + + #define SK_Scalar1 SK_Fixed1 + #define SK_ScalarHalf SK_FixedHalf + #define SK_ScalarInfinity SK_FixedMax + #define SK_ScalarMax SK_FixedMax + #define SK_ScalarMin SK_FixedMin + #define SK_ScalarNaN SK_FixedNaN + #define SkScalarIsNaN(x) ((x) == SK_FixedNaN) + #define SkIntToScalar(n) SkIntToFixed(n) + #define SkFixedToScalar(x) (x) + #define SkScalarToFixed(x) (x) + #ifdef SK_CAN_USE_FLOAT + #define SkScalarToFloat(n) SkFixedToFloat(n) + #define SkFloatToScalar(n) SkFloatToFixed(n) + #endif + #define SkScalarFraction(x) SkFixedFraction(x) + #define SkScalarRound(x) SkFixedRound(x) + #define SkScalarCeil(x) SkFixedCeil(x) + #define SkScalarFloor(x) SkFixedFloor(x) + #define SkScalarAbs(x) SkFixedAbs(x) + #define SkScalarClampMax(x, max) SkClampMax(x, max) + #define SkScalarPin(x, min, max) SkPin32(x, min, max) + #define SkScalarSquare(x) SkFixedSquare(x) + #define SkScalarMul(a, b) SkFixedMul(a, b) + #define SkScalarMulRound(a, b) SkFixedMulCommon(a, b, SK_FixedHalf) + #define SkScalarMulCeil(a, b) SkFixedMulCommon(a, b, SK_Fixed1 - 1) + #define SkScalarMulFloor(a, b) SkFixedMulCommon(a, b, 0) + #define SkScalarDiv(a, b) SkFixedDiv(a, b) + #define SkScalarMod(a, b) SkFixedMod(a, b) + #define SkScalarMulDiv(a, b, c) SkMulDiv(a, b, c) + #define SkScalarInvert(x) SkFixedInvert(x) + #define SkScalarSqrt(x) SkFixedSqrt(x) + #define SkScalarAve(a, b) SkFixedAve(a, b) + #define SkScalarMean(a, b) SkFixedMean(a, b) + #define SkScalarHalf(a) ((a) >> 1) + + #define SK_ScalarSqrt2 SK_FixedSqrt2 + #define SK_ScalarPI SK_FixedPI + #define SK_ScalarTanPIOver8 SK_FixedTanPIOver8 + #define SK_ScalarRoot2Over2 SK_FixedRoot2Over2 + + #define SkDegreesToRadians(degrees) SkFractMul(degrees, SK_FractPIOver180) + #define SkScalarSinCos(radians, cosPtr) SkFixedSinCos(radians, cosPtr) + #define SkScalarSin(radians) SkFixedSin(radians) + #define SkScalarCos(radians) SkFixedCos(radians) + #define SkScalarTan(val) SkFixedTan(val) + #define SkScalarASin(val) SkFixedASin(val) + #define SkScalarACos(val) SkFixedACos(val) + #define SkScalarATan2(y, x) SkFixedATan2(y,x) + #define SkScalarExp(x) SkFixedExp(x) + #define SkScalarLog(x) SkFixedLog(x) + + #define SkMaxScalar(a, b) SkMax32(a, b) + #define SkMinScalar(a, b) SkMin32(a, b) +#endif + +#define SK_ScalarNearlyZero (SK_Scalar1 / (1 << 12)) + +/* <= is slower than < for floats, so we use < for our tolerance test +*/ + +inline bool SkScalarNearlyZero(SkScalar x, SkScalar tolerance = SK_ScalarNearlyZero) +{ + SkASSERT(tolerance > 0); + return SkScalarAbs(x) < tolerance; +} + +/** Linearly interpolate between A and B, based on t. + If t is 0, return A + If t is 1, return B + else interpolate. + t must be [0..SK_Scalar1] +*/ +inline SkScalar SkScalarInterp(SkScalar A, SkScalar B, SkScalar t) +{ + SkASSERT(t >= 0 && t <= SK_Scalar1); + return A + SkScalarMul(B - A, t); +} + +#endif + diff --git a/include/corecg/SkTemplates.h b/include/corecg/SkTemplates.h new file mode 100644 index 0000000000..448638d0a1 --- /dev/null +++ b/include/corecg/SkTemplates.h @@ -0,0 +1,161 @@ +#ifndef SkTemplates_DEFINED +#define SkTemplates_DEFINED + +#include "SkTypes.h" + +/** \file SkTemplates.h + + This file contains light-weight template classes for type-safe and exception-safe + resource management. +*/ + +/** \class SkAutoTCallProc + + Similar to SkAutoTDelete, this class is used to auto delete an object + when leaving the scope of the object. This is mostly useful when + errors occur and objects need to be cleaned up. The template uses two + parameters, the object, and a function that is to be called in the destructor. + If detach() is called then the function is not called when SkAutoTCallProc goes out + of scope. This also happens is the passed in object is nil. + +*/ +template <typename T, void (*P)(T*)> class SkAutoTCallProc { +public: + SkAutoTCallProc(T* obj): fObj(obj) {} + ~SkAutoTCallProc() + { + if (fObj) + P(fObj); + } + T* detach() { T* obj = fObj; fObj = nil; return obj; } +private: + T* fObj; +}; + +template <typename T> class SkAutoTDelete { +public: + SkAutoTDelete(T* obj) : fObj(obj) {} + ~SkAutoTDelete() { delete fObj; } + + void free() { delete fObj; fObj = nil; } + T* detach() { T* obj = fObj; fObj = nil; return obj; } + +private: + T* fObj; +}; + +template <typename T> class SkAutoTDeleteArray { +public: + SkAutoTDeleteArray(T array[]) : fArray(array) {} + ~SkAutoTDeleteArray() { delete[] fArray; } + + void free() { delete[] fArray; fArray = nil; } + T* detach() { T* array = fArray; fArray = nil; return array; } + +private: + T* fArray; +}; + +template <typename T> class SkAutoTArray { +public: + SkAutoTArray(size_t count) + { + fArray = nil; // init first in case we throw + if (count) + fArray = new T[count]; +#ifdef SK_DEBUG + fCount = count; +#endif + } + ~SkAutoTArray() + { + delete[] fArray; + } + + T* get() const { return fArray; } + T& operator[](int index) const { SkASSERT((unsigned)index < fCount); return fArray[index]; } + + void reset() + { + if (fArray) + { + delete[] fArray; + fArray = nil; + } + } + + void replace(T* array) + { + if (fArray != array) + { + delete[] fArray; + fArray = array; + } + } + + /** Call swap to exchange your pointer to an array of T with the SkAutoTArray object. + After this call, the SkAutoTArray object will be responsible for deleting your + array, and you will be responsible for deleting its. + */ + void swap(T*& other) + { + T* tmp = fArray; + fArray = other; + other = tmp; + } + +private: +#ifdef SK_DEBUG + size_t fCount; +#endif + T* fArray; +}; + +/** Allocate a temp array on the stack/heap. + Does NOT call any constructors/destructors on T (i.e. T must be POD) +*/ +template <typename T> class SkAutoTMalloc { +public: + SkAutoTMalloc(size_t count) + { + fPtr = (T*)sk_malloc_flags(count * sizeof(T), SK_MALLOC_THROW | SK_MALLOC_TEMP); + } + ~SkAutoTMalloc() + { + sk_free(fPtr); + } + T* get() const { return fPtr; } + +private: + T* fPtr; + // illegal + SkAutoTMalloc(const SkAutoTMalloc&); + SkAutoTMalloc& operator=(const SkAutoTMalloc&); +}; + +template <size_t N, typename T> class SkAutoSTMalloc { +public: + SkAutoSTMalloc(size_t count) + { + if (count <= N) + fPtr = (T*)fStorage; + else + fPtr = (T*)sk_malloc_flags(count * sizeof(T), SK_MALLOC_THROW | SK_MALLOC_TEMP); + } + ~SkAutoSTMalloc() + { + if (fPtr != (T*)fStorage) + sk_free(fPtr); + } + T* get() const { return fPtr; } + +private: + T* fPtr; + uint32_t fStorage[(N*sizeof(T) + 3) >> 2]; + // illegal + SkAutoSTMalloc(const SkAutoSTMalloc&); + SkAutoSTMalloc& operator=(const SkAutoSTMalloc&); +}; + +#endif + diff --git a/include/corecg/SkThread.h b/include/corecg/SkThread.h new file mode 100644 index 0000000000..ac548c476c --- /dev/null +++ b/include/corecg/SkThread.h @@ -0,0 +1,40 @@ +#ifndef SkThread_DEFINED +#define SkThread_DEFINED + +#include "SkTypes.h" +#include "SkThread_platform.h" + +/****** SkThread_platform needs to define the following... + +int32_t sk_atomic_inc(int32_t*); +int32_t sk_atomic_dec(int32_t*); + +class SkMutex { +public: + SkMutex(); + ~SkMutex(); + + void acquire(); + void release(); +}; + +****************/ + +class SkAutoMutexAcquire { +public: + explicit SkAutoMutexAcquire(SkMutex& mutex) : fMutex(mutex) + { + mutex.acquire(); + } + ~SkAutoMutexAcquire() + { + fMutex.release(); + } +private: + SkMutex& fMutex; + + // illegal + SkAutoMutexAcquire& operator=(SkAutoMutexAcquire&); +}; + +#endif diff --git a/include/corecg/SkThread_platform.h b/include/corecg/SkThread_platform.h new file mode 100644 index 0000000000..bfa3899c6b --- /dev/null +++ b/include/corecg/SkThread_platform.h @@ -0,0 +1,37 @@ +#ifndef SkThread_platform_DEFINED +#define SkThread_platform_DEFINED + +#ifdef ANDROID + +#include <utils/threads.h> +#include <utils/Atomic.h> + +#define sk_atomic_inc(addr) android_atomic_inc(addr) +#define sk_atomic_dec(addr) android_atomic_dec(addr) + +class SkMutex : android::Mutex { +public: + SkMutex() {} + ~SkMutex() {} + + void acquire() { this->lock(); } + void release() { this->unlock(); } +}; + +#else /* SkThread_empty.cpp */ + +int32_t sk_atomic_inc(int32_t* addr); +int32_t sk_atomic_dec(int32_t* addr); + +class SkMutex { +public: + SkMutex(); + ~SkMutex(); + + void acquire(); + void release(); +}; + +#endif + +#endif diff --git a/include/corecg/SkTypes.h b/include/corecg/SkTypes.h new file mode 100644 index 0000000000..fe0cc38a64 --- /dev/null +++ b/include/corecg/SkTypes.h @@ -0,0 +1,299 @@ +#ifndef SkTypes_DEFINED +#define SkTypes_DEFINED + +#include "SkPreConfig.h" +#include "SkUserConfig.h" +#include "SkPostConfig.h" + +#include <stdint.h> +#include <stdio.h> + +/** \file SkTypes.h +*/ + +/* + memory wrappers +*/ + +extern void sk_out_of_memory(void); // platform specific, does not return +extern void sk_throw(void); // platform specific, does not return +enum { + SK_MALLOC_TEMP = 0x01, //!< hint to sk_malloc that the requested memory will be freed in the scope of the stack frame + SK_MALLOC_THROW = 0x02 //!< instructs sk_malloc to call sk_throw if the memory cannot be allocated. +}; +/** Return a block of memory (at least 4-byte aligned) of at least the + specified size. If the requested memory cannot be returned, either + return nil (if SK_MALLOC_TEMP bit is clear) or call sk_throw() + (if SK_MALLOC_TEMP bit is set). To free the memory, call sk_free(). +*/ +extern void* sk_malloc_flags(size_t size, unsigned flags); +/** Same as sk_malloc(), but hard coded to pass SK_MALLOC_THROW as the flag +*/ +extern void* sk_malloc_throw(size_t size); +/** Same as standard realloc(), but this one never returns nil on failure. It will throw + an exception if it fails. +*/ +extern void* sk_realloc_throw(void* buffer, size_t size); +/** Free memory returned by sk_malloc(). It is safe to pass nil. +*/ +extern void sk_free(void*); + +/////////////////////////////////////////////////////////////////////// + +#define SK_INIT_TO_AVOID_WARNING = 0 + +#ifdef SK_DEBUG + #define SkASSERT(cond) SK_DEBUGBREAK(cond) + #define SkDEBUGCODE(code) code + #define SkDECLAREPARAM(type, var) , type var + #define SkPARAM(var) , var +// #define SkDEBUGF(args ) SkDebugf##args + #define SkDEBUGF(args ) SkDebugf args + void SkDebugf(const char format[], ...); + + #define SkAssertResult(cond) SkASSERT(cond) +#else + #define SkASSERT(cond) + #define SkDEBUGCODE(code) + #define SkDEBUGF(args) + #define SkDECLAREPARAM(type, var) + #define SkPARAM(var) + + // unlike SkASSERT, this guy executes its condition in the non-debug build + #define SkAssertResult(cond) cond +#endif + +/////////////////////////////////////////////////////////////////////// + +#ifndef nil + #define nil 0 +#endif + +// legacy defines. will be removed before shipping +typedef int8_t S8; +typedef uint8_t U8; +typedef int16_t S16; +typedef uint16_t U16; +typedef int32_t S32; +typedef uint32_t U32; + +/** Fast type for signed 8 bits. Use for parameter passing and local variables, not for storage +*/ +typedef int S8CPU; +/** Fast type for unsigned 8 bits. Use for parameter passing and local variables, not for storage +*/ +typedef int S16CPU; +/** Fast type for signed 16 bits. Use for parameter passing and local variables, not for storage +*/ +typedef unsigned U8CPU; +/** Fast type for unsigned 16 bits. Use for parameter passing and local variables, not for storage +*/ +typedef unsigned U16CPU; + +/** Meant to be faster than bool (doesn't promise to be 0 or 1, just 0 or non-zero +*/ +typedef int SkBool; +/** Meant to be a small version of bool, for storage purposes. Will be 0 or 1 +*/ +typedef uint8_t SkBool8; + +#ifdef SK_DEBUG + int8_t SkToS8(long); + uint8_t SkToU8(size_t); + int16_t SkToS16(long); + uint16_t SkToU16(size_t); + int32_t SkToS32(long); + uint32_t SkToU32(size_t); +#else + #define SkToS8(x) ((int8_t)(x)) + #define SkToU8(x) ((uint8_t)(x)) + #define SkToS16(x) ((int16_t)(x)) + #define SkToU16(x) ((uint16_t)(x)) + #define SkToS32(x) ((int32_t)(x)) + #define SkToU32(x) ((uint32_t)(x)) +#endif + +/** Returns 0 or 1 based on the condition +*/ +#define SkToBool(cond) ((cond) != 0) + +#define SK_MaxS16 32767 +#define SK_MinS16 -32767 +#define SK_MaxU16 0xFFFF +#define SK_MinU16 0 +#define SK_MaxS32 0x7FFFFFFF +#define SK_MinS32 0x80000001 +#define SK_MaxU32 0xFFFFFFFF +#define SK_MinU32 0 +#define SK_NaN32 0x80000000 + +#ifndef SK_OFFSETOF + #define SK_OFFSETOF(type, field) ((char*)&(((type*)1)->field) - (char*)1) +#endif + +/** Returns the number of entries in an array (not a pointer) +*/ +#define SK_ARRAY_COUNT(array) (sizeof(array) / sizeof(array[0])) + +/** Returns x rounded up to a multiple of 2 +*/ +#define SkAlign2(x) (((x) + 1) >> 1 << 1) +/** Returns x rounded up to a multiple of 4 +*/ +#define SkAlign4(x) (((x) + 3) >> 2 << 2) + +typedef uint32_t SkFourByteTag; +#define SkSetFourByteTag(a, b, c, d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d)) + +/** 32 bit integer to hold a unicode value +*/ +typedef int32_t SkUnichar; +/** 32 bit value to hold a millisecond count +*/ +typedef uint32_t SkMSec; +/** 1 second measured in milliseconds +*/ +#define SK_MSec1 1000 +/** maximum representable milliseconds +*/ +#define SK_MSecMax 0x7FFFFFFF +/** Returns a < b for milliseconds, correctly handling wrap-around from 0xFFFFFFFF to 0 +*/ +#define SkMSec_LT(a, b) ((int32_t)(a) - (int32_t)(b) < 0) +/** Returns a <= b for milliseconds, correctly handling wrap-around from 0xFFFFFFFF to 0 +*/ +#define SkMSec_LE(a, b) ((int32_t)(a) - (int32_t)(b) <= 0) + + +/**************************************************************************** + The rest of these only build with C++ +*/ +#ifdef __cplusplus + +/** Faster than SkToBool for integral conditions. Returns 0 or 1 +*/ +inline int Sk32ToBool(uint32_t n) +{ + return (n | (0-n)) >> 31; +} + +template <typename T> inline void SkTSwap(T& a, T& b) +{ + T c(a); + a = b; + b = c; +} + +inline int32_t SkAbs32(int32_t value) +{ +#ifdef SK_CPU_HAS_CONDITIONAL_INSTR + if (value < 0) + value = -value; + return value; +#else + int32_t mask = value >> 31; + return (value ^ mask) - mask; +#endif +} + +inline int32_t SkMax32(int32_t a, int32_t b) +{ + if (a < b) + a = b; + return a; +} + +inline int32_t SkMin32(int32_t a, int32_t b) +{ + if (a > b) + a = b; + return a; +} + +inline int32_t SkSign32(int32_t a) +{ + return (a >> 31) | ((unsigned) -a >> 31); +} + +inline int32_t SkFastMin32(int32_t value, int32_t max) +{ +#ifdef SK_CPU_HAS_CONDITIONAL_INSTR + if (value > max) + value = max; + return value; +#else + int diff = max - value; + // clear diff if it is negative (clear if value > max) + diff &= (diff >> 31); + return value + diff; +#endif +} + +/** Returns signed 32 bit value pinned between min and max, inclusively +*/ +inline int32_t SkPin32(int32_t value, int32_t min, int32_t max) +{ +#ifdef SK_CPU_HAS_CONDITIONAL_INSTR + if (value < min) + value = min; + if (value > max) + value = max; +#else + if (value < min) + value = min; + else if (value > max) + value = max; +#endif + return value; +} + +inline uint32_t SkSetClear32(uint32_t flags, bool cond, unsigned shift) +{ + return flags & ~(1 << shift) | ((int)cond << shift); +} + +class SkAutoMalloc { +public: + SkAutoMalloc(size_t size) + { + fPtr = sk_malloc_flags(size, SK_MALLOC_THROW | SK_MALLOC_TEMP); + } + ~SkAutoMalloc() + { + sk_free(fPtr); + } + void* get() const { return fPtr; } +private: + void* fPtr; + // illegal + SkAutoMalloc(const SkAutoMalloc&); + SkAutoMalloc& operator=(const SkAutoMalloc&); +}; + +template <size_t kSize> class SkAutoSMalloc { +public: + SkAutoSMalloc(size_t size) + { + if (size <= kSize) + fPtr = fStorage; + else + fPtr = sk_malloc_flags(size, SK_MALLOC_THROW | SK_MALLOC_TEMP); + } + ~SkAutoSMalloc() + { + if (fPtr != (void*)fStorage) + sk_free(fPtr); + } + void* get() const { return fPtr; } +private: + void* fPtr; + uint32_t fStorage[(kSize + 3) >> 2]; + // illegal + SkAutoSMalloc(const SkAutoSMalloc&); + SkAutoSMalloc& operator=(const SkAutoSMalloc&); +}; + +#endif /* C++ */ + +#endif + diff --git a/include/corecg/SkUserConfig.h b/include/corecg/SkUserConfig.h new file mode 100644 index 0000000000..21f1604846 --- /dev/null +++ b/include/corecg/SkUserConfig.h @@ -0,0 +1,59 @@ +#ifndef SkUserConfig_DEFINED +#define SkUserConfig_DEFINED + +/* This file is included before all other headers, except for SkPreConfig.h. + That file uses various heuristics to make a "best guess" at settings for + the following build defines. + + However, in this file you can override any of those decisions by either + defining new symbols, or #undef symbols that were already set. +*/ + +// experimental for now +#define SK_SUPPORT_MIPMAP + +// android specific defines and tests + +#ifdef SK_FORCE_SCALARFIXED + #define SK_SCALAR_IS_FIXED + #undef SK_SCALAR_IS_FLOAT + #undef SK_CAN_USE_FLOAT +#endif + +#ifdef SK_FORCE_SCALARFLOAT + #define SK_SCALAR_IS_FLOAT + #define SK_CAN_USE_FLOAT + #undef SK_SCALAR_IS_FIXED +#endif + +#ifdef ANDROID + #include <utils/misc.h> + + #if __BYTE_ORDER == __BIG_ENDIAN + #define SK_CPU_BENDIAN + #undef SK_CPU_LENDIAN + #else + #define SK_CPU_LENDIAN + #undef SK_CPU_BENDIAN + #endif +#endif + +#ifdef SK_DEBUG + #define SK_SUPPORT_UNITTEST + /* Define SK_SIMULATE_FAILED_MALLOC to have + * sk_malloc throw an exception. Use this to + * detect unhandled memory leaks. */ + //#define SK_SIMULATE_FAILED_MALLOC + //#define SK_FIND_MEMORY_LEAKS +#endif + +#ifdef SK_BUILD_FOR_BREW + #include "SkBrewUserConfig.h" +#endif + +#ifdef SK_BUILD_FOR_MAC + #define SK_CAN_USE_FLOAT +#endif + +#endif + diff --git a/include/graphics/DoxygenMain.dox b/include/graphics/DoxygenMain.dox new file mode 100644 index 0000000000..975186200a --- /dev/null +++ b/include/graphics/DoxygenMain.dox @@ -0,0 +1,3 @@ +/** \mainpage notitle
+* \htmlinclude "SGL Spec. rev 9.htm"
+*/
\ No newline at end of file diff --git a/include/graphics/Sk1DPathEffect.h b/include/graphics/Sk1DPathEffect.h new file mode 100644 index 0000000000..3fd28859ed --- /dev/null +++ b/include/graphics/Sk1DPathEffect.h @@ -0,0 +1,65 @@ +#ifndef Sk1DPathEffect_DEFINED +#define Sk1DPathEffect_DEFINED + +#include "SkPathEffect.h" +#include "SkPath.h" + +class SkPathMeasure; + +// This class is not exported to java. +class Sk1DPathEffect : public SkPathEffect { +public: + Sk1DPathEffect() {} + + virtual bool filterPath(SkPath* dst, const SkPath& src, SkScalar* width); + +protected: + /** Called at the start of each contour, returns the initial offset + into that contour. + */ + virtual SkScalar begin(SkScalar contourLength); + /** Called with the current distance along the path, with the current matrix + for the point/tangent at the specified distance. + Return the distance to travel for the next call. If return <= 0, then that + contour is done. + */ + virtual SkScalar next(SkPath* dst, SkScalar distance, SkPathMeasure&); + + Sk1DPathEffect(SkRBuffer& buffer) : SkPathEffect(buffer) {} + +private: + // illegal + Sk1DPathEffect(const Sk1DPathEffect&); + Sk1DPathEffect& operator=(const Sk1DPathEffect&); + + typedef SkPathEffect INHERITED; +}; + +class SkPath1DPathEffect : public Sk1DPathEffect { +public: + enum Style { + kTranslate_Style, // translate the shape to each position + kRotate_Style, // rotate the shape about its center + kMorph_Style, // transform each point, and turn lines into curves + + kStyleCount + }; + SkPath1DPathEffect(const SkPath& path, SkScalar advance, SkScalar phase, Style); + + // This method is not exported to java. + virtual bool filterPath(SkPath* dst, const SkPath& src, SkScalar* width); + +protected: + virtual SkScalar begin(SkScalar contourLength); + virtual SkScalar next(SkPath* dst, SkScalar distance, SkPathMeasure&); + +private: + SkPath fPath; + SkScalar fAdvance, fPhase; + Style fStyle; + + typedef Sk1DPathEffect INHERITED; +}; + + +#endif diff --git a/include/graphics/Sk2DPathEffect.h b/include/graphics/Sk2DPathEffect.h new file mode 100644 index 0000000000..502d4a3b0b --- /dev/null +++ b/include/graphics/Sk2DPathEffect.h @@ -0,0 +1,53 @@ +#ifndef Sk2DPathEffect_DEFINED +#define Sk2DPathEffect_DEFINED + +#include "SkPathEffect.h" +#include "SkMatrix.h" + +// This class is not exported to java. +class Sk2DPathEffect : public SkPathEffect { +public: + Sk2DPathEffect(const SkMatrix& mat); + + // overrides + virtual bool filterPath(SkPath* dst, const SkPath& src, SkScalar* width); + + // overrides from SkFlattenable + virtual void flatten(SkWBuffer&); + virtual Factory getFactory(); + +protected: + /** New virtual, to be overridden by subclasses. + This is called once from filterPath, and provides the + uv parameter bounds for the path. Subsequent calls to + next() will receive u and v values within these bounds, + and then a call to end() will signal the end of processing. + */ + virtual void begin(const SkRect16& uvBounds, SkPath* dst); + virtual void next(const SkPoint& loc, int u, int v, SkPath* dst); + virtual void end(SkPath* dst); + + /** Low-level virtual called per span of locations in the u-direction. + The default implementation calls next() repeatedly with each + location. + */ + virtual void nextSpan(int u, int v, int ucount, SkPath* dst); + + const SkMatrix& getMatrix() const { return fMatrix; } + + // protected so that subclasses can call this during unflattening + Sk2DPathEffect(SkRBuffer&); + +private: + SkMatrix fMatrix, fInverse; + // illegal + Sk2DPathEffect(const Sk2DPathEffect&); + Sk2DPathEffect& operator=(const Sk2DPathEffect&); + + static SkFlattenable* CreateProc(SkRBuffer&); + + friend class Sk2DPathEffectBlitter; + typedef SkPathEffect INHERITED; +}; + +#endif diff --git a/include/graphics/SkAnimator.h b/include/graphics/SkAnimator.h new file mode 100644 index 0000000000..7a70c107f3 --- /dev/null +++ b/include/graphics/SkAnimator.h @@ -0,0 +1,492 @@ +#ifndef SkAnimator_DEFINED +#define SkAnimator_DEFINED + +#include "SkScalar.h" +#include "SkKey.h" +#include "SkEventSink.h" + +class SkAnimateMaker; +class SkCanvas; +class SkDisplayable; +class SkEvent; +class SkExtras; +struct SkMemberInfo; +class SkPaint; +struct SkRect; +class SkStream; +class SkTypedArray; +class SkXMLParserError; +class SkDOM; +struct SkDOMNode; + +/** SkElementType is the type of element: a rectangle, a color, an animator, and so on. + This enum is incomplete and will be fleshed out in a future release */ +enum SkElementType { + kElementDummyType +}; +/** SkFieldType is the type of field: a scalar, a string, an integer, a boolean, and so on. + This enum is incomplete and will be fleshed out in a future release */ +enum SkFieldType { + kFieldDummyType +}; + +/** \class SkAnimator + + The SkAnimator class decodes an XML stream into a display list. The + display list can be drawn statically as a picture, or can drawn + different elements at different times to form a moving animation. + + SkAnimator does not read the system time on its own; it relies on the + caller to pass the current time. The caller can pause, speed up, or + reverse the animation by varying the time passed in. + + The XML describing the display list must conform to the schema + described by SkAnimateSchema.xsd. + + The XML must contain an <event> element to draw. Usually, it contains + an <event kind="onload" /> block to add some drawing elements to the + display list when the document is first decoded. + + Here's an "Hello World" XML sample: + + <screenplay> + <event kind="onload" > + <text text="Hello World" y="20" /> + </event> + </screenplay> + + To read and draw this sample: + + // choose one of these two + SkAnimator animator; // declare an animator instance on the stack + // SkAnimator* animator = new SkAnimator() // or one could instantiate the class + + // choose one of these three + animator.decodeMemory(buffer, size); // to read from RAM + animator.decodeStream(stream); // to read from a user-defined stream (e.g., a zip file) + animator.decodeURI(filename); // to read from a web location, or from a local text file + + // to draw to the current window: + SkCanvas canvas(getBitmap()); // create a canvas + animator.draw(canvas, &paint, 0); // draw the scene +*/ +class SkAnimator : public SkEventSink { +public: + SkAnimator(); + virtual ~SkAnimator(); + + /** Add a drawable extension to the graphics engine. Experimental. + @param extras A derived class that implements methods that identify and instantiate the class + */ + void addExtras(SkExtras* extras); + + /** Read in XML from a stream, and append it to the current + animator. Returns false if an error was encountered. + Error diagnostics are stored in fErrorCode and fLineNumber. + @param stream The stream to append. + @return true if the XML was parsed successfully. + */ + bool appendStream(SkStream* stream); + + /** Read in XML from memory. Returns true if the file can be + read without error. Returns false if an error was encountered. + Error diagnostics are stored in fErrorCode and fLineNumber. + @param buffer The XML text as UTF-8 characters. + @param size The XML text length in bytes. + @return true if the XML was parsed successfully. + */ + bool decodeMemory(const void* buffer, size_t size); + + /** Read in XML from a stream. Returns true if the file can be + read without error. Returns false if an error was encountered. + Error diagnostics are stored in fErrorCode and fLineNumber. + @param stream The stream containg the XML text as UTF-8 characters. + @return true if the XML was parsed successfully. + */ + virtual bool decodeStream(SkStream* stream); + + /** Parse the DOM tree starting at the specified node. Returns true if it can be + parsed without error. Returns false if an error was encountered. + Error diagnostics are stored in fErrorCode and fLineNumber. + @return true if the DOM was parsed successfully. + */ + virtual bool decodeDOM(const SkDOM&, const SkDOMNode*); + + /** Read in XML from a URI. Returns true if the file can be + read without error. Returns false if an error was encountered. + Error diagnostics are stored in fErrorCode and fLineNumber. + @param uri The complete url path to be read (either ftp, http or https). + @return true if the XML was parsed successfully. + */ + bool decodeURI(const char uri[]); + + /** Pass a char event, usually a keyboard symbol, to the animator. + This triggers events of the form <event kind="keyChar" key="... /> + @param ch The character to match against <event> element "key" + attributes. + @return true if the event was dispatched successfully. + */ + bool doCharEvent(SkUnichar ch); + + /** Experimental: + Pass a mouse click event along with the mouse coordinates to + the animator. This triggers events of the form <event kind="mouseDown" ... /> + and other mouse events. + @param state The mouse state, described by SkView::Click::State : values are + down == 0, moved == 1, up == 2 + @param x The x-position of the mouse + @param y The y-position of the mouse + @return true if the event was dispatched successfully. + */ + bool doClickEvent(int state, SkScalar x, SkScalar y); + + /** Pass a meta-key event, such as an arrow , to the animator. + This triggers events of the form <event kind="keyPress" code="... /> + @param code The key to match against <event> element "code" + attributes. + @return true if the event was dispatched successfully. + */ + bool doKeyEvent(SkKey code); + bool doKeyUpEvent(SkKey code); + + /** Send an event to the animator. The animator's clock is set + relative to the current time. + @return true if the event was dispatched successfully. + */ + bool doUserEvent(const SkEvent& evt); + + /** The possible results from the draw function. + */ + enum DifferenceType { + kNotDifferent, + kDifferent, + kPartiallyDifferent + }; + /** Draws one frame of the animation. The first call to draw always + draws the initial frame of the animation. Subsequent calls draw + the offset into the animation by + subtracting the initial time from the current time. + @param canvas The canvas to draw into. + @param paint The paint to draw with. + @param time The offset into the current animation. + @return kNotDifferent if there are no active animations; kDifferent if there are active animations; and + kPartiallyDifferent if the document contains an active <bounds> element that specifies a minimal + redraw area. + */ + DifferenceType draw(SkCanvas* canvas, SkPaint* paint, SkMSec time); + + /** Draws one frame of the animation, using a new Paint each time. + The first call to draw always + draws the initial frame of the animation. Subsequent calls draw + the offset into the animation by + subtracting the initial time from the current time. + @param canvas The canvas to draw into. + @param time The offset into the current animation. + @return kNotDifferent if there are no active animations; kDifferent if there are active animations; and + kPartiallyDifferent if the document contains an active <bounds> element that specifies a minimal + redraw area. + */ + DifferenceType draw(SkCanvas* canvas, SkMSec time); + + /** Experimental: + Helper to choose whether to return a SkView::Click handler. + @param x ignored + @param y ignored + @return true if a mouseDown event handler is enabled. + */ + bool findClickEvent(SkScalar x, SkScalar y); + + + /** Get the nested animator associated with this element, if any. + Use this to access a movie's event sink, to send events to movies. + @param element the value returned by getElement + @return the internal animator. + */ + const SkAnimator* getAnimator(const SkDisplayable* element) const; + + /** Returns the scalar value of the specified element's attribute[index] + @param element the value returned by getElement + @param field the value returned by getField + @param index the array entry + @return the integer value to retrieve, or SK_NaN32 if unsuccessful + */ + int32_t getArrayInt(const SkDisplayable* element, const SkMemberInfo* field, int index); + + /** Returns the scalar value of the specified element's attribute[index] + @param elementID is the value of the id attribute in the XML of this element + @param fieldName specifies the name of the attribute + @param index the array entry + @return the integer value to retrieve, or SK_NaN32 if unsuccessful + */ + int32_t getArrayInt(const char* elementID, const char* fieldName, int index); + + /** Returns the scalar value of the specified element's attribute[index] + @param element the value returned by getElement + @param field the value returned by getField + @param index the array entry + @return the scalar value to retrieve, or SK_ScalarNaN if unsuccessful + */ + SkScalar getArrayScalar(const SkDisplayable* element, const SkMemberInfo* field, int index); + + /** Returns the scalar value of the specified element's attribute[index] + @param elementID is the value of the id attribute in the XML of this element + @param fieldName specifies the name of the attribute + @param index the array entry + @return the scalar value to retrieve, or SK_ScalarNaN if unsuccessful + */ + SkScalar getArrayScalar(const char* elementID, const char* fieldName, int index); + + /** Returns the string value of the specified element's attribute[index] + @param element is a value returned by getElement + @param field is a value returned by getField + @param index the array entry + @return the string value to retrieve, or null if unsuccessful + */ + const char* getArrayString(const SkDisplayable* element, const SkMemberInfo* field, int index); + + /** Returns the string value of the specified element's attribute[index] + @param elementID is the value of the id attribute in the XML of this element + @param fieldName specifies the name of the attribute + @param index the array entry + @return the string value to retrieve, or null if unsuccessful + */ + const char* getArrayString(const char* elementID, const char* fieldName, int index); + + /** Returns the XML element corresponding to the given ID. + @param elementID is the value of the id attribute in the XML of this element + @return the element matching the ID, or nil if the element can't be found + */ + const SkDisplayable* getElement(const char* elementID); + + /** Returns the element type corresponding to the XML element. + The element type matches the element name; for instance, <line> returns kElement_LineType + @param element is a value returned by getElement + @return element type, or 0 if the element can't be found + */ + SkElementType getElementType(const SkDisplayable* element); + + /** Returns the element type corresponding to the given ID. + @param elementID is the value of the id attribute in the XML of this element + @return element type, or 0 if the element can't be found + */ + SkElementType getElementType(const char* elementID); + + /** Returns the XML field of the named attribute in the XML element. + @param element is a value returned by getElement + @param fieldName is the attribute to return + @return the attribute matching the fieldName, or nil if the element can't be found + */ + const SkMemberInfo* getField(const SkDisplayable* element, const char* fieldName); + + /** Returns the XML field of the named attribute in the XML element matching the elementID. + @param elementID is the value of the id attribute in the XML of this element + @param fieldName is the attribute to return + @return the attribute matching the fieldName, or nil if the element can't be found + */ + const SkMemberInfo* getField(const char* elementID, const char* fieldName); + + /** Returns the value type coresponding to the element's attribute. + The value type matches the XML schema: and may be kField_BooleanType, kField_ScalarType, etc. + @param field is a value returned by getField + @return the attribute type, or 0 if the element can't be found + */ + SkFieldType getFieldType(const SkMemberInfo* field); + + /** Returns the value type coresponding to the element's attribute. + @param elementID is the value of the id attribute in the XML of this element + @param fieldName specifies the name of the attribute + @return the attribute type, or 0 if the element can't be found + */ + SkFieldType getFieldType(const char* elementID, const char* fieldName); + + /** Returns the recommended animation interval. Returns zero if no + interval is specified. + */ + SkMSec getInterval(); + + /** Returns the partial rectangle to invalidate after drawing. Call after draw() returns + kIsPartiallyDifferent to do a mimimal inval(). */ + void getInvalBounds(SkRect* inval); + + /** Returns the details of any error encountered while parsing the XML. + */ + const SkXMLParserError* getParserError(); + + /** Returns the details of any error encountered while parsing the XML as string. + */ + const char* getParserErrorString(); + + /** Returns the scalar value of the specified element's attribute + @param element is a value returned by getElement + @param field is a value returned by getField + @return the integer value to retrieve, or SK_NaN32 if not found + */ + int32_t getInt(const SkDisplayable* element, const SkMemberInfo* field); + + /** Returns the scalar value of the specified element's attribute + @param elementID is the value of the id attribute in the XML of this element + @param fieldName specifies the name of the attribute + @return the integer value to retrieve, or SK_NaN32 if not found + */ + int32_t getInt(const char* elementID, const char* fieldName); + + /** Returns the scalar value of the specified element's attribute + @param element is a value returned by getElement + @param field is a value returned by getField + @return the scalar value to retrieve, or SK_ScalarNaN if not found + */ + SkScalar getScalar(const SkDisplayable* element, const SkMemberInfo* field); + + /** Returns the scalar value of the specified element's attribute + @param elementID is the value of the id attribute in the XML of this element + @param fieldName specifies the name of the attribute + @return the scalar value to retrieve, or SK_ScalarNaN if not found + */ + SkScalar getScalar(const char* elementID, const char* fieldName); + + /** Returns the string value of the specified element's attribute + @param element is a value returned by getElement + @param field is a value returned by getField + @return the string value to retrieve, or null if not found + */ + const char* getString(const SkDisplayable* element, const SkMemberInfo* field); + + /** Returns the string value of the specified element's attribute + @param elementID is the value of the id attribute in the XML of this element + @param fieldName specifies the name of the attribute + @return the string value to retrieve, or null if not found + */ + const char* getString(const char* elementID, const char* fieldName); + + /** Gets the file default directory of the URL base path set explicitly or by reading the last URL. */ + const char* getURIBase(); + + /** Resets the animator to a newly created state with no animation data. */ + void initialize(); + + /** Experimental. Resets any active animations so that the next time passed is treated as + time zero. */ + void reset(); + + /** Sets the scalar value of the specified element's attribute + @param elementID is the value of the id attribute in the XML of this element + @param fieldName specifies the name of the attribute + @param array is the c-style array of integers + @param count is the length of the array + @return true if the value was set successfully + */ + bool setArrayInt(const char* elementID, const char* fieldName, const int* array, int count); + + /** Sets the scalar value of the specified element's attribute + @param elementID is the value of the id attribute in the XML of this element + @param fieldName specifies the name of the attribute + @param array is the c-style array of strings + @param count is the length of the array + @return true if the value was set successfully + */ + bool setArrayString(const char* elementID, const char* fieldName, const char** array, int count); + + /** Sets the scalar value of the specified element's attribute + @param elementID is the value of the id attribute in the XML of this element + @param fieldName specifies the name of the attribute + @param data the integer value to set + @return true if the value was set successfully + */ + bool setInt(const char* elementID, const char* fieldName, int32_t data); + + /** Sets the scalar value of the specified element's attribute + @param elementID is the value of the id attribute in the XML of this element + @param fieldName specifies the name of the attribute + @param data the scalar value to set + @return true if the value was set successfully + */ + bool setScalar(const char* elementID, const char* fieldName, SkScalar data); + + /** Sets the string value of the specified element's attribute + @param elementID is the value of the id attribute in the XML of this element + @param fieldName specifies the name of the attribute + @param data the string value to set + @return true if the value was set successfully + */ + bool setString(const char* elementID, const char* fieldName, const char* data); + + /** Sets the file default directory of the URL base path + @param path the directory path + */ + void setURIBase(const char* path); + + typedef void* Handler; + // This guy needs to be exported to java, so don't make it virtual + void setHostHandler(Handler handler) { + this->onSetHostHandler(handler); + } + + /** \class Timeline + Returns current time to animator. To return a custom timeline, create a child + class and override the getMSecs method. + */ + class Timeline { + public: + virtual ~Timeline() {} + + /** Returns the current time in milliseconds */ + virtual SkMSec getMSecs() const = 0; + }; + + /** Sets a user class to return the current time to the animator. + Optional; if not called, the system clock will be used by calling SkTime::GetMSecs instead. + @param callBack the time function + */ + void setTimeline(const Timeline& ); + + static void Init(bool runUnitTests); + static void Term(); + + /** The event sink events generated by the animation are posted to. + Screenplay also posts an inval event to this event sink after processing an + event to force a redraw. + @param target the event sink id + */ + void setHostEventSinkID(SkEventSinkID hostID); + SkEventSinkID getHostEventSinkID() const; + + // helper + void setHostEventSink(SkEventSink* sink) { + this->setHostEventSinkID(sink ? sink->getSinkID() : 0); + } + + virtual void setJavaOwner(Handler owner); + +#ifdef SK_DEBUG + virtual void eventDone(const SkEvent& evt); + virtual bool isTrackingEvents(); + static bool NoLeaks(); +#endif + +protected: + virtual void onSetHostHandler(Handler handler); + virtual void onEventPost(SkEvent*, SkEventSinkID); + virtual void onEventPostTime(SkEvent*, SkEventSinkID, SkMSec time); + +private: +// helper functions for setters + bool setArray(SkDisplayable* element, const SkMemberInfo* field, SkTypedArray array); + bool setArray(const char* elementID, const char* fieldName, SkTypedArray array); + bool setInt(SkDisplayable* element, const SkMemberInfo* field, int32_t data); + bool setScalar(SkDisplayable* element, const SkMemberInfo* field, SkScalar data); + bool setString(SkDisplayable* element, const SkMemberInfo* field, const char* data); + + virtual bool onEvent(const SkEvent&); + SkAnimateMaker* fMaker; + friend class SkAnimateMaker; + friend class SkAnimatorScript; + friend class SkAnimatorScript2; + friend class SkApply; + friend class SkDisplayMovie; + friend class SkDisplayType; + friend class SkPost; + friend class SkXMLAnimatorWriter; +}; + +#endif + diff --git a/include/graphics/SkAnimatorView.h b/include/graphics/SkAnimatorView.h new file mode 100644 index 0000000000..2fa765760c --- /dev/null +++ b/include/graphics/SkAnimatorView.h @@ -0,0 +1,31 @@ +#ifndef SkAnimatorView_DEFINED +#define SkAnimatorView_DEFINED + +#include "SkView.h" +#include "SkAnimator.h" + +class SkAnimatorView : public SkView { +public: + SkAnimatorView(); + virtual ~SkAnimatorView(); + + SkAnimator* getAnimator() const { return fAnimator; } + + bool decodeFile(const char path[]); + bool decodeMemory(const void* buffer, size_t size); + bool decodeStream(SkStream* stream); + +protected: + // overrides + virtual bool onEvent(const SkEvent&); + virtual void onDraw(SkCanvas*); + virtual void onInflate(const SkDOM&, const SkDOM::Node*); + +private: + SkAnimator* fAnimator; + + typedef SkView INHERITED; +}; + +#endif + diff --git a/include/graphics/SkApplication.h b/include/graphics/SkApplication.h new file mode 100644 index 0000000000..fbeea4753f --- /dev/null +++ b/include/graphics/SkApplication.h @@ -0,0 +1,10 @@ +#ifndef SkApplication_DEFINED +#define SkApplication_DEFINED + +class SkOSWindow; + +extern SkOSWindow* create_sk_window(void* hwnd); +extern void application_init(); +extern void application_term(); + +#endif // SkApplication_DEFINED diff --git a/include/graphics/SkAvoidXfermode.h b/include/graphics/SkAvoidXfermode.h new file mode 100644 index 0000000000..574568b414 --- /dev/null +++ b/include/graphics/SkAvoidXfermode.h @@ -0,0 +1,34 @@ +#ifndef SkAvoidXfermode_DEFINED +#define SkAvoidXfermode_DEFINED + +#include "SkXfermode.h" + +/** \class SkAvoidXfermode + + This xfermode will draw the src everywhere except on top of the specified + color. +*/ +class SkAvoidXfermode : public SkXfermode { +public: + /** This xfermode will draw the src everywhere except on top of the specified + color. + @param opColor the color to avoid (or to target if reverse is true); + @param tolerance How closely we compare a pixel to the opColor. + 0 - we only avoid on an exact match + 255 - maximum gradation (blending) based on how similar + the pixel is to our opColor. + @param reverse true means we target the opColor rather than avoid it. + */ + SkAvoidXfermode(SkColor opColor, U8CPU tolerance, bool reverse); + + virtual void xfer32(SkPMColor dst[], const SkPMColor src[], int count, const SkAlpha aa[]); + virtual void xfer16(uint16_t dst[], const SkPMColor src[], int count, const SkAlpha aa[]); + virtual void xferA8(SkAlpha dst[], const SkPMColor src[], int count, const SkAlpha aa[]); + +private: + SkColor fOpColor; + uint32_t fDistMul; // x.14 + bool fReverse; +}; + +#endif diff --git a/include/graphics/SkBGViewArtist.h b/include/graphics/SkBGViewArtist.h new file mode 100644 index 0000000000..dd6e2633d2 --- /dev/null +++ b/include/graphics/SkBGViewArtist.h @@ -0,0 +1,25 @@ +#ifndef SkBGViewArtist_DEFINED +#define SkBGViewArtist_DEFINED + +#include "SkView.h" +#include "SkPaint.h" + +class SkBGViewArtist : public SkView::Artist { +public: + SkBGViewArtist(SkColor c = SK_ColorWHITE); + virtual ~SkBGViewArtist(); + + const SkPaint& paint() const { return fPaint; } + SkPaint& paint() { return fPaint; } + +protected: + // overrides + virtual void onDraw(SkView*, SkCanvas*); + virtual void onInflate(const SkDOM&, const SkDOM::Node*); + +private: + SkPaint fPaint; +}; + +#endif + diff --git a/include/graphics/SkBML_WXMLParser.h b/include/graphics/SkBML_WXMLParser.h new file mode 100644 index 0000000000..706b6b1788 --- /dev/null +++ b/include/graphics/SkBML_WXMLParser.h @@ -0,0 +1,38 @@ +#ifndef SkBML_WXMLParser_DEFINED +#define SkBML_WXMLParser_DEFINED + +#include "SkString.h" +#include "SkXMLParser.h" + +class SkStream; +class SkWStream; + +class BML_WXMLParser : public SkXMLParser { +public: + BML_WXMLParser(SkWStream& writer); + virtual ~BML_WXMLParser(); + static void Write(SkStream& s, const char filename[]); + + /** @cond UNIT_TEST */ + SkDEBUGCODE(static void UnitTest();) + /** @endcond */ +private: + virtual bool onAddAttribute(const char name[], const char value[]); + virtual bool onEndElement(const char name[]); + virtual bool onStartElement(const char name[]); + BML_WXMLParser& operator=(const BML_WXMLParser& src); +#ifdef SK_DEBUG + int fElemsCount, fElemsReused; + int fAttrsCount, fNamesReused, fValuesReused; +#endif + SkWStream& fWriter; + char* fElems[256]; + char* fAttrNames[256]; + char* fAttrValues[256]; + + // important that these are U8, so we get automatic wrap-around + U8 fNextElem, fNextAttrName, fNextAttrValue; +}; + +#endif // SkBML_WXMLParser_DEFINED + diff --git a/include/graphics/SkBML_XMLParser.h b/include/graphics/SkBML_XMLParser.h new file mode 100644 index 0000000000..29e7f4e7d6 --- /dev/null +++ b/include/graphics/SkBML_XMLParser.h @@ -0,0 +1,23 @@ +#ifndef SkBML_XMLParser_DEFINED +#define SkBML_XMLParser_DEFINED + +class SkStream; +class SkWStream; +class SkXMLParser; +class SkXMLWriter; + +class BML_XMLParser { +public: + /** Read the byte XML stream and write the decompressed XML. + */ + static void Read(SkStream& s, SkXMLWriter& writer); + /** Read the byte XML stream and write the decompressed XML into a writable stream. + */ + static void Read(SkStream& s, SkWStream& output); + /** Read the byte XML stream and write the decompressed XML into an XML parser. + */ + static void Read(SkStream& s, SkXMLParser& output); +}; + +#endif // SkBML_XMLParser_DEFINED + diff --git a/include/graphics/SkBitmap.h b/include/graphics/SkBitmap.h new file mode 100644 index 0000000000..a7de542ff3 --- /dev/null +++ b/include/graphics/SkBitmap.h @@ -0,0 +1,374 @@ +#ifndef SkBitmap_DEFINED +#define SkBitmap_DEFINED + +#include "SkColor.h" +#include "SkRefCnt.h" + +// Android - we need to run as an embedded product, not X11 +//#ifdef SK_BUILD_FOR_UNIX +//#include <X11/Xlib.h> +//#endif + + +class SkColorTable; + +/** \class SkBitmap + + The SkBitmap class specifies a raster bitmap. A bitmap has an integer width + and height, and a format (config), and a pointer to the actual pixels. + Bitmaps can be drawn into a SkCanvas, but they are also used to specify the target + of a SkCanvas' drawing operations. +*/ +class SkBitmap { +public: + enum Config { + kNo_Config, //!< bitmap has not been configured + kA1_Config, //!< 1-bit per pixel, (0 is transparent, 1 is opaque) + kA8_Config, //!< 8-bits per pixel, with only alpha specified (0 is transparent, 0xFF is opaque) + kIndex8_Config, //!< 8-bits per pixel, using SkColorTable to specify the colors + kRGB_565_Config, //!< 16-bits per pixel, (see SkColorPriv.h for packing) + kARGB_8888_Config, //!< 32-bits per pixel, (see SkColorPriv.h for packing) + + kConfigCount + }; + + /** Default construct creates a bitmap with zero width and height, and no pixels. + Its config is set to kNo_Config. + */ + SkBitmap(); + /** Constructor initializes the new bitmap by copying the src bitmap. All fields are copied, + but ownership of the pixels remains with the src bitmap. + */ + // This method is not exported to java. + SkBitmap(const SkBitmap& src); + /** Destructor that, if getOwnsPixels() returns true, will delete the pixel's memory. + */ + ~SkBitmap(); + + /** Copies the src bitmap into this bitmap. Ownership of the src bitmap's pixels remains + with the src bitmap. + */ + SkBitmap& operator=(const SkBitmap& src); + /** Swap the fields of the two bitmaps. This routine is guaranteed to never fail or throw. + */ + // This method is not exported to java. + void swap(SkBitmap& other); + + /** Return the config for the bitmap. + */ + Config getConfig() const { return (Config)fConfig; } + /** Return the bitmap's width, in pixels. + */ + unsigned width() const { return fWidth; } + /** Return the bitmap's height, in pixels. + */ + unsigned height() const { return fHeight; } + /** Return the number of bytes between subsequent rows of the bitmap. + */ + unsigned rowBytes() const { return fRowBytes; } + /** Return the address of the pixels for this SkBitmap. This can be set either with + setPixels(), where the caller owns the buffer, or with allocPixels() or resizeAlloc(), + which marks the pixel memory to be owned by the SkBitmap (e.g. will be freed automatically + when the bitmap is destroyed). + */ + void* getPixels() const { return fPixels; } + /** Return the byte size of the pixels, based on the height and rowBytes + */ + size_t getSize() const { return fHeight * fRowBytes; } + + /** Returns true if the bitmap is opaque (has no translucent/transparent pixels). + */ + bool isOpaque() const; + /** Specify if this bitmap's pixels are all opaque or not. Is only meaningful for configs + that support per-pixel alpha (RGB32, A1, A8). + */ + void setIsOpaque(bool); + + /** Reset the bitmap to its initial state (see default constructor). If getOwnsPixels() returned + true, then the memory for the pixels is freed. + */ + void reset(); + /** Set the bitmap's config and dimensions. If rowBytes is 0, then an appropriate value + is computed based on the bitmap's config and width. If getOwnsPixels() returned true, + then the pixel's memory is freed. + */ + void setConfig(Config, U16CPU width, U16CPU height, U16CPU rowBytes = 0); + /** Use this to assign a new pixel address for an existing bitmap. If getOwnsPixels() returned + true, then the previous pixel's memory is freed. The new address is "owned" by the called, + and getOwnsPixels() will now return false. This method is not exported to java. + */ + void setPixels(void* p); + /** If this is called, then the bitmap will dynamically allocate memory for its pixels + based on rowBytes and height. The SkBitmap will remember that it allocated + this, and will automatically free it as needed, thus getOwnsPixels() will now return true. + */ + void allocPixels(); + /** Realloc the memory for the pixels based on the specified width and height. This + keeps the old value for config, and computes a rowBytes based on the config and the width. + This is similar, but more efficient than calling setConfig() followed by allocPixels(). + */ +// not implemented +// void resizeAlloc(U16CPU width, U16CPU height); + + /** Returns true if the current pixels have been allocated via allocPixels() + or resizeAlloc(). This method is not exported to java. + */ + bool getOwnsPixels() const; + /** Call this to explicitly change the ownership rule for the pixels. This may be called + after one bitmap is copied into another, to specify which bitmap should handle freeing + the memory. This method is not exported to java. + */ + void setOwnsPixels(bool ownsPixels); + + /** Get the bitmap's colortable object. + + Return the bitmap's colortable (if any). Does not affect the colortable's + reference count. + */ + SkColorTable* getColorTable() const { return fColorTable; } + /** Assign ctable to be the colortable for the bitmap, replacing any existing reference. + The reference count of ctable (if it is not nil) is incremented, and any existing + reference has its reference count decremented. NOTE: colortable's may be assigned + to any bitmap, but are only interpreted for kIndex8_Config bitmaps, where they + are required. + @return the ctable argument + */ + SkColorTable* setColorTable(SkColorTable* ctable); + + /** Initialize the bitmap's pixels with the specified color+alpha, automatically converting into the correct format + for the bitmap's config. If the config is kRGB_565_Config, then the alpha value is ignored. + If the config is kA8_Config, then the r,g,b parameters are ignored. + */ + void eraseARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b); + /** Initialize the bitmap's pixels with the specified color+alpha, automatically converting into the correct format + for the bitmap's config. If the config is kRGB_565_Config, then the alpha value is presumed + to be 0xFF. If the config is kA8_Config, then the r,g,b parameters are ignored and the + pixels are all set to 0xFF. + */ + void eraseRGB(U8CPU r, U8CPU g, U8CPU b) + { + this->eraseARGB(0xFF, r, g, b); + } + /** Initialize the bitmap's pixels with the specified color, automatically converting into the correct format + for the bitmap's config. If the config is kRGB_565_Config, then the color's alpha value is presumed + to be 0xFF. If the config is kA8_Config, then only the color's alpha value is used. + */ + void eraseColor(SkColor c) + { + this->eraseARGB(SkColorGetA(c), SkColorGetR(c), SkColorGetG(c), SkColorGetB(c)); + } + + /** Returns the address of the pixel specified by x,y. + Asserts that x,y are in range, and that the bitmap's config is either kARGB_8888_Config. + */ + // This method is not exported to java. + inline uint32_t* getAddr32(int x, int y) const; + /** Returns the address of the pixel specified by x,y. + Asserts that x,y are in range, and that the bitmap's config is kRGB_565_Config. + */ + // This method is not exported to java. + inline uint16_t* getAddr16(int x, int y) const; + /** Returns the address of the pixel specified by x,y. + Asserts that x,y are in range, and that the bitmap's config is either kA8_Config or kIndex8_Config. + */ + // This method is not exported to java. + inline uint8_t* getAddr8(int x, int y) const; + /** Returns the color corresponding to the pixel specified by x,y. + Asserts that x,y are in range, and that the bitmap's config is kIndex8_Config. + */ + // This method is not exported to java. + inline SkPMColor getIndex8Color(int x, int y) const; + /** Returns the address of the byte containing the pixel specified by x,y. + Asserts that x,y are in range, and that the bitmap's config is kA1_Config. + */ + // This method is not exported to java. + inline uint8_t* getAddr1(int x, int y) const; + + // OS-specific helpers +#ifndef SK_USE_WXWIDGETS +#ifdef SK_BUILD_FOR_WIN + /** On Windows and PocketPC builds, this will draw the SkBitmap onto the + specified HDC + */ + void drawToHDC(HDC, int left, int top) const; +#elif defined(SK_BUILD_FOR_MAC) + /** On Mac OS X and Carbon builds, this will draw the SkBitmap onto the + specified WindowRef + */ + void drawToPort(WindowRef) const; +#endif +#endif + + void buildMipMap(bool forceRebuild); + unsigned countMipLevels() const; + +private: + SkColorTable* fColorTable; // only meaningful for kIndex8 + +#ifdef SK_SUPPORT_MIPMAP + struct MipLevel { + void* fPixels; + uint16_t fWidth, fHeight, fRowBytes; + uint8_t fConfig, fShift; + }; + enum { + kMaxMipLevels = 5 + }; + struct MipMap { + MipLevel fLevel[kMaxMipLevels]; + }; + MipMap* fMipMap; +#endif + + enum Flags { + kWeOwnThePixels_Flag = 0x01, + kWeOwnTheMipMap_Flag = 0x02, + kImageIsOpaque_Flag = 0x04 + }; + + void* fPixels; + uint16_t fWidth, fHeight, fRowBytes; + uint8_t fConfig; + uint8_t fFlags; + + const MipLevel* getMipLevel(unsigned level) const; + + void freePixels(); + + friend class SkBitmapShader; +}; + +/** \class SkColorTable + + SkColorTable holds an array SkPMColors (premultiplied 32-bit colors) used by + 8-bit bitmaps, where the bitmap bytes are interpreted as indices into the colortable. +*/ +class SkColorTable : public SkRefCnt { +public: + /** Constructs an empty color table (zero colors). + */ + SkColorTable(); + virtual ~SkColorTable(); + + enum Flags { + kColorsAreOpaque_Flag = 0x01 //!< if set, all of the colors in the table are opaque (alpha==0xFF) + }; + /** Returns the flag bits for the color table. These can be changed with setFlags(). + */ + unsigned getFlags() const { return fFlags; } + /** Set the flags for the color table. See the Flags enum for possible values. + */ + void setFlags(unsigned flags); + + /** Returns the number of colors in the table. + */ + int count() const { return fCount; } + + /** Returns the specified color from the table. In the debug build, this asserts that + the index is in range (0 <= index < count). + */ + SkPMColor operator[](int index) const + { + SkASSERT(fColors != nil && (unsigned)index < fCount); + return fColors[index]; + } + + /** Specify the number of colors in the color table. This does not initialize the colors + to any value, just allocates memory for them. To initialize the values, either call + setColors(array, count), or follow setCount(count) with a call to + lockColors()/{set the values}/unlockColors(true). + */ + void setColors(int count) { this->setColors(nil, count); } + void setColors(const SkPMColor[], int count); + + /** Return the array of colors for reading and/or writing. This must be + balanced by a call to unlockColors(changed?), telling the colortable if + the colors were changed during the lock. + */ + SkPMColor* lockColors() + { + SkDEBUGCODE(fColorLockCount += 1;) + return fColors; + } + /** Balancing call to lockColors(). If the colors have been changed, pass true. + */ + void unlockColors(bool changed) + { + SkASSERT(fColorLockCount != 0); + SkDEBUGCODE(fColorLockCount -= 1;) + } + + /** Similar to lockColors(), lock16BitCache() returns the array of + RGB16 colors that mirror the 32bit colors. However, this function + will return nil if kColorsAreOpaque_Flag is not set. + Also, unlike lockColors(), the returned array here cannot be modified. + */ + const uint16_t* lock16BitCache(); + /** Balancing call to lock16BitCache(). + */ + void unlock16BitCache() + { + SkASSERT(f16BitCacheLockCount > 0); + SkDEBUGCODE(f16BitCacheLockCount -= 1); + } + +private: + SkPMColor* fColors; + uint16_t* f16BitCache; + uint16_t fCount; + uint8_t fFlags; + SkDEBUGCODE(int fColorLockCount;) + SkDEBUGCODE(int f16BitCacheLockCount;) + + void inval16BitCache(); +}; + +////////////////////////////////////////////////////////////////////////////////// + +inline uint32_t* SkBitmap::getAddr32(int x, int y) const +{ + SkASSERT(fPixels); + SkASSERT(fConfig == kARGB_8888_Config); + SkASSERT((unsigned)x < fWidth && (unsigned)y < fHeight); + + return (uint32_t*)((char*)fPixels + y * fRowBytes + (x << 2)); +} + +inline uint16_t* SkBitmap::getAddr16(int x, int y) const +{ + SkASSERT(fPixels); + SkASSERT(fConfig == kRGB_565_Config); + SkASSERT((unsigned)x < fWidth && (unsigned)y < fHeight); + + return (uint16_t*)((char*)fPixels + y * fRowBytes + (x << 1)); +} + +inline uint8_t* SkBitmap::getAddr8(int x, int y) const +{ + SkASSERT(fPixels); + SkASSERT(fConfig == kA8_Config || fConfig == kIndex8_Config); + SkASSERT((unsigned)x < fWidth && (unsigned)y < fHeight); + return (uint8_t*)fPixels + y * fRowBytes + x; +} + +inline SkPMColor SkBitmap::getIndex8Color(int x, int y) const +{ + SkASSERT(fPixels); + SkASSERT(fConfig == kIndex8_Config); + SkASSERT((unsigned)x < fWidth && (unsigned)y < fHeight); + SkASSERT(fColorTable); + return (*fColorTable)[*((const uint8_t*)fPixels + y * fRowBytes + x)]; +} + +// returns the address of the byte that contains the x coordinate +inline uint8_t* SkBitmap::getAddr1(int x, int y) const +{ + SkASSERT(fPixels); + SkASSERT(fConfig == kA1_Config); + SkASSERT((unsigned)x < fWidth && (unsigned)y < fHeight); + return (uint8_t*)fPixels + y * fRowBytes + (x >> 3); +} + + +#endif + diff --git a/include/graphics/SkBitmapRef.h b/include/graphics/SkBitmapRef.h new file mode 100644 index 0000000000..ffecfb6ea8 --- /dev/null +++ b/include/graphics/SkBitmapRef.h @@ -0,0 +1,58 @@ +#ifndef SkBitmapRef_DEFINED +#define SkBitmapRef_DEFINED + +#include "SkBitmap.h" + +class SkStream; + +/** Helper class to manage a cache of decoded images from the file system +*/ +class SkBitmapRef : public SkRefCnt { +public: + /** Create a non-cached bitmap, trasfering ownership of pixels if needed + */ + SkBitmapRef(const SkBitmap& src, bool transferOwnsPixels); + virtual ~SkBitmapRef(); + + const SkBitmap& bitmap(); + + static SkBitmapRef* create(const SkBitmap& src, bool transferOwnsPixels); + static SkBitmapRef* DecodeFile(const char file[], bool forceDecode); + static SkBitmapRef* DecodeMemory(const void* bytes, size_t len); + static SkBitmapRef* DecodeStream(SkStream* stream); + + /** Frees all cached images, asserting that all references have been removed + */ + static void PurgeCacheAll(); + + /** frees one cached image, returning true, or returns false if none could be freed + */ + static bool PurgeCacheOne(); + +private: + struct Rec; + Rec* fRec; + + SkBitmapRef(Rec*); + + friend class SkBitmapRef_Globals; +}; + +class SkAutoBitmapRef { +public: + SkAutoBitmapRef(const char file[], bool forceDecode) + { + fRef = SkBitmapRef::DecodeFile(file, forceDecode); + } + ~SkAutoBitmapRef() { delete fRef; } + + const SkBitmap* bitmap() const + { + return fRef ? &fRef->bitmap() : nil; + } +private: + SkBitmapRef* fRef; +}; + + +#endif diff --git a/include/graphics/SkBlurMaskFilter.h b/include/graphics/SkBlurMaskFilter.h new file mode 100644 index 0000000000..9bbba182c6 --- /dev/null +++ b/include/graphics/SkBlurMaskFilter.h @@ -0,0 +1,37 @@ +#ifndef SkBlurMaskFilter_DEFINED +#define SkBlurMaskFilter_DEFINED + +#include "SkMaskFilter.h" + +class SkBlurMaskFilter : public SkMaskFilter { +public: + enum BlurStyle { + kNormal_BlurStyle, //!< fuzzy inside and outside + kSolid_BlurStyle, //!< solid inside, fuzzy outside + kOuter_BlurStyle, //!< nothing inside, fuzzy outside + kInner_BlurStyle, //!< fuzzy inside, nothing outside + + kBlurStyleCount + }; + + /** Create a blur maskfilter. + @param radius The radius to extend the blur from the original mask. Must be > 0. + @param style The BlurStyle to use + @return The new blur maskfilter + */ + static SkMaskFilter* Create(SkScalar radius, BlurStyle style); + + /** Create an emboss maskfilter + @param direction array of 3 scalars [x, y, z] specifying the direction of the light source + @param ambient 0...1 amount of ambient light + @param specular coefficient for specular highlights (e.g. 8) + @param blurRadius amount to blur before applying lighting (e.g. 3) + @return the emboss maskfilter + */ + static SkMaskFilter* CreateEmboss( const SkScalar direction[3], + SkScalar ambient, SkScalar specular, + SkScalar blurRadius); +}; + +#endif + diff --git a/include/graphics/SkBorderView.h b/include/graphics/SkBorderView.h new file mode 100644 index 0000000000..307a91d997 --- /dev/null +++ b/include/graphics/SkBorderView.h @@ -0,0 +1,31 @@ +#ifndef SkBorderView_DEFINED +#define SkBorderView_DEFINED + +#include "SkView.h" +#include "SkWidgetViews.h" +#include "SkAnimator.h" + +class SkBorderView : public SkWidgetView { +public: + SkBorderView(); + ~SkBorderView(); + void setSkin(const char skin[]); + SkScalar getLeft() const { return fLeft; } + SkScalar getRight() const { return fRight; } + SkScalar getTop() const { return fTop; } + SkScalar getBottom() const { return fBottom; } +protected: + //overrides + virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node); + virtual void onSizeChange(); + virtual void onDraw(SkCanvas* canvas); + virtual bool onEvent(const SkEvent& evt); +private: + SkAnimator fAnim; + SkScalar fLeft, fRight, fTop, fBottom; //margin on each side + SkRect fMargin; + + typedef SkWidgetView INHERITED; +}; + +#endif
\ No newline at end of file diff --git a/include/graphics/SkBounder.h b/include/graphics/SkBounder.h new file mode 100644 index 0000000000..8bf9dfac70 --- /dev/null +++ b/include/graphics/SkBounder.h @@ -0,0 +1,45 @@ +#ifndef SkBounder_DEFINED +#define SkBounder_DEFINED + +#include "SkTypes.h" +#include "SkRefCnt.h" + +struct SkRect16; +struct SkPoint; +struct SkRect; +class SkPaint; +class SkPath; +class SkRegion; + +/** \class SkBounder + + Base class for intercepting the device bounds of shapes before they are drawn. + Install a subclass of this in your canvas. +*/ +class SkBounder : public SkRefCnt { +public: + bool doIRect(const SkRect16&, const SkRegion&); + bool doHairline(const SkPoint&, const SkPoint&, const SkPaint&, const SkRegion&); + bool doRect(const SkRect&, const SkPaint&, const SkRegion&); + bool doPath(const SkPath&, const SkPaint&, const SkRegion&, bool doFill); + +protected: + /** Override in your subclass. This is called with the device bounds of an + object (text, geometry, image) just before it is drawn. If your method + returns false, the drawing for that shape is aborted. If your method + returns true, drawing continues. The bounds your method receives have already + been transformed in to device coordinates, and clipped to the current clip. + */ + virtual bool onIRect(const SkRect16&) = 0; + + /** Called after each shape has been drawn. The default implementation does + nothing, but your override could use this notification to signal itself + that the offscreen being rendered into needs to be updated to the screen. + */ + virtual void commit(); + + friend class SkAutoBounderCommit; +}; + +#endif + diff --git a/include/graphics/SkCamera.h b/include/graphics/SkCamera.h new file mode 100644 index 0000000000..d39fa89582 --- /dev/null +++ b/include/graphics/SkCamera.h @@ -0,0 +1,80 @@ +#ifndef SkCamera_DEFINED +#define SkCamera_DEFINED + +#include "SkMatrix.h" +#include "Sk64.h" + +#ifdef SK_SCALAR_IS_FIXED + typedef SkFract SkUnitScalar; + #define SK_UnitScalar1 SK_Fract1 + #define SkUnitScalarMul(a, b) SkFractMul(a, b) + #define SkUnitScalarDiv(a, b) SkFractDiv(a, b) +#else + typedef float SkUnitScalar; + #define SK_UnitScalar1 SK_Scalar1 + #define SkUnitScalarMul(a, b) SkScalarMul(a, b) + #define SkUnitScalarDiv(a, b) SkScalarDiv(a, b) +#endif + +// Taken from Rob Johnson's most excellent QuickDraw GX library + +struct SkUnit3D { + SkUnitScalar fX, fY, fZ; + + void set(SkUnitScalar x, SkUnitScalar y, SkUnitScalar z) + { + fX = x; fY = y; fZ = z; + } + static SkUnitScalar Dot(const SkUnit3D&, const SkUnit3D&); + static void Cross(const SkUnit3D&, const SkUnit3D&, SkUnit3D* cross); +}; + +struct SkPoint3D { + SkScalar fX, fY, fZ; + + void set(SkScalar x, SkScalar y, SkScalar z) + { + fX = x; fY = y; fZ = z; + } + SkScalar normalize(SkUnit3D*) const; +}; + +class SkPatch3D { +public: + SkPatch3D(); + + void reset(); + void rotate(SkScalar radX, SkScalar radY, SkScalar radZ); + void rotateDegrees(SkScalar degX, SkScalar degY, SkScalar degZ) + { + this->rotate(SkDegreesToRadians(degX), + SkDegreesToRadians(degY), + SkDegreesToRadians(degZ)); + } + + // dot a unit vector with the patch's normal + SkScalar dotWith(SkScalar dx, SkScalar dy, SkScalar dz) const; + + SkPoint3D fU, fV, fOrigin; +private: + friend class SkCamera3D; +}; + +class SkCamera3D { +public: + SkCamera3D(); + + void update(); + void computeMatrix(const SkPatch3D&, SkMatrix* matrix) const; + + SkPoint3D fLocation; + SkPoint3D fAxis; + SkPoint3D fZenith; + SkPoint3D fObserver; + +private: + SkMatrix fOrientation; +}; + +#endif + diff --git a/include/graphics/SkCanvas.h b/include/graphics/SkCanvas.h new file mode 100644 index 0000000000..11dfb99972 --- /dev/null +++ b/include/graphics/SkCanvas.h @@ -0,0 +1,470 @@ +#ifndef SkCanvas_DEFINED +#define SkCanvas_DEFINED + +#include "SkBitmap.h" +#include "SkDeque.h" +#include "SkPaint.h" +#include "SkPorterDuff.h" +#include "SkPath.h" +#include "SkRegion.h" + +class SkBounder; + +/** \class SkCanvas + + The SkCanvas class holds the "draw" calls. To draw something, you need + 4 basic components: A SkBitmap to hold the pixels, a SkCanvas to host + the draw calls (writing into the bitmap), a drawing primitive (e.g. SkRect, SkPath, + text, SkBitmap), and a paint (to describe the colors and styles for the drawing). +*/ +class SkCanvas { +public: + /** Construct an empty canvas. Use setPixels() to specify a bitmap to draw into. + */ + SkCanvas(); + /** Construct a canvas with the specified bitmap to draw into. + @param bitmap Specifies a bitmap for the canvas to draw into. Its contents are copied to the canvas. + */ + SkCanvas(const SkBitmap& bitmap); + ~SkCanvas(); + + /** Return a copy of the bitmap that the canvas draws into. This does not make a copy + of the bitmap's pixels, but just returns the pixel's address. + @param bitmap The bitmap, allocated by the caller, that receives a copy of the canvas' bitmap + (the one specified in the setPixels() call, or in the constructor). + */ + void getPixels(SkBitmap* bitmap) const; + /** Specify a bitmap for the canvas to draw into. This routine makes a copy of the bitmap, + but does not copy the actual pixels. Ownership of the bitmap's pixels stays with the caller's + bitmap. + @param bitmap Specifies a new bitmap for the canvas to draw into. Its contents are copied to the canvas. + */ + void setPixels(const SkBitmap& bitmap); + + /** Return true if the bitmap that the current layer draws into is always opaque + (i.e. does not support per-pixel alpha). e.g. kARGB_8888_Config returns false, + kARGB_565_Config returns true. + @return true if the bitmap that the current layer draws into is always opaque + */ + bool isBitmapOpaque() const; + + /** Return the width of the bitmap that the current layer draws into + @return the width of the bitmap that the current layer draws into + */ + int getBitmapWidth() const { return this->getCurrBitmap().width(); } + /** Return the height of the bitmap that the current layer draws into + @return the height of the bitmap that the current layer draws into + */ + int getBitmapHeight() const { return this->getCurrBitmap().height(); } + + /** This call saves the current matrix and clip information, and pushes a copy onto a + private stack. Subsequent calls to translate,scale,rotate,skew,concat or clipRect,clipPath + all operate on this copy. When the balancing call to restore() is made, this copy is deleted + and the previous matrix/clip state is restored. + @return The value to pass to restoreToCount() to balance this save() + */ + int save(); + /** This behaves the same as save(), but in addition it allocates an offscreen bitmap. + All drawing calls are directed there, and only when the balancing call to restore() is made + is that offscreen transfered to the canvas (or the previous layer). + Subsequent calls to translate,scale,rotate,skew,concat or clipRect,clipPath + all operate on this copy. When the balancing call to restore() is made, this copy is deleted + and the previous matrix/clip state is restored. + @param bounds The maximum size the offscreen bitmap needs to be (in local coordinates) + @param paint This is copied, and is applied to the offscreen when restore() is called. + @return The value to pass to restoreToCount() to balance this save() + */ + int saveLayer(const SkRect& bounds, const SkPaint& paint); + /** This call balances a previous call to save(), and is used to remove all modifications to + the matrix/clip state since the last save call. It is an error to call restore() more times + than save() was called. + */ + void restore(); + /** Returns the number of matrix/clip states on the SkCanvas' private stack. This will equal + # save() calls - # restore() calls. + */ + int getSaveCount() const; + /** Efficient way to pop any calls to save() that happened after the save count reached saveCount. + It is an error for saveCount to be less than getSaveCount() + @param saveCount The number of save() levels to restore from + */ + void restoreToCount(int saveCount); + + /** Preconcat the current matrix with the specified translation + @param dx The distance to translate in X + @param dy The distance to translate in Y + returns true if the operation succeeded (e.g. did not overflow) + */ + bool translate(SkScalar dx, SkScalar dy); + /** Preconcat the current matrix with the specified scale and pivot point. + The pivot is the point that will remain unchanged after the scale is applied. + @param sx The amount to scale in X, about the pivot point (px,py) + @param sy The amount to scale in Y, about the pivot point (px,py) + @param px The pivot's X coordinate + @param py The pivot's Y coordinate + returns true if the operation succeeded (e.g. did not overflow) + */ + bool scale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py); + /** Preconcat the current matrix with the specified rotation and pivot point. + The pivot is the point that will remain unchanged after the rotation is applied. + @param degrees The amount to rotate, in degrees + @param px The pivot's X coordinate + @param py The pivot's Y coordinate + returns true if the operation succeeded (e.g. did not overflow) + */ + bool rotate(SkScalar degrees, SkScalar px, SkScalar py); + /** Preconcat the current matrix with the specified skew and pivot point. + The pivot is the point that will remain unchanged after the skew is applied. + @param sx The amount to skew in X, about the pivot point (px,py) + @param sy The amount to skew in Y, about the pivot point (px,py) + @param px The pivot's X coordinate + @param py The pivot's Y coordinate + returns true if the operation succeeded (e.g. did not overflow) + */ + bool skew(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py); + /** Preconcat the current matrix with the specified matrix. + @param matrix The matrix to preconcatenate with the current matrix + @return true if the operation succeeded (e.g. did not overflow) + */ + bool concat(const SkMatrix& matrix); + + /** Intersect the current clip with the specified rectangle. + @param rect The rect to intersect with the current clip + */ + void clipRect(const SkRect& rect); + /** Intersect the current clip with the specified rectangle. + @param left The left side of the rectangle to intersect with the current clip + @param top The top side of the rectangle to intersect with the current clip + @param right The right side of the rectangle to intersect with the current clip + @param bottom The bottom side of the rectangle to intersect with the current clip + */ + void clipRect(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom); + /** Intersect the current clip with the specified path. + @param path The path to intersect with the current clip + */ + void clipPath(const SkPath& path); + /** Intersect the current clip with the specified region. Note that unlike clipRect() + and clipPath() which transform their arguments by the current matrix, clipDeviceRgn() + assumes its argument is already in the coordinate system of the current layer's bitmap, + and so not transformation is performed. + @param deviceRgn The region to intersect with the current clip + */ + void clipDeviceRgn(const SkRegion& deviceRgn); + + /** Return true if the specified rectangle, after being transformed by the current + matrix, would lie completely outside of the current clip. Call this to check + if an area you intend to draw into is clipped out (and therefore you can skip + making the draw calls). + @param rect the rect to compare with the current clip + @param antialiased true if the rect should be considered antialiased, since that means it may + affect a larger area (more pixels) than non-antialiased. + @return true if the rect (transformed by the canvas' matrix) does not intersect with the canvas' clip + */ + bool quickReject(const SkRect& rect, bool antialiased = false) const; + /** Return true if the specified path, after being transformed by the current + matrix, would lie completely outside of the current clip. Call this to check + if an area you intend to draw into is clipped out (and therefore you can skip + making the draw calls). + Note, for speed it may return false even if the path itself might not intersect + the clip (i.e. the bounds of the path intersects, but the path doesnot). + @param path The path to compare with the current clip + @param antialiased true if the path should be considered antialiased, since that means it may + affect a larger area (more pixels) than non-antialiased. + @return true if the path (transformed by the canvas' matrix) does not intersect with the canvas' clip + */ + bool quickReject(const SkPath& path, bool antialiased = false) const; + /** Return true if the specified rectangle, after being transformed by the current + matrix, would lie completely outside of the current clip. Call this to check + if an area you intend to draw into is clipped out (and therefore you can skip + making the draw calls). + @param left The left side of the rectangle to compare with the current clip + @param top The top side of the rectangle to compare with the current clip + @param right The right side of the rectangle to compare with the current clip + @param bottom The bottom side of the rectangle to compare with the current clip + @param antialiased true if the rect should be considered antialiased, since that means it may + affect a larger area (more pixels) than non-antialiased. + @return true if the rect (transformed by the canvas' matrix) does not intersect with the canvas' clip + */ + bool quickReject(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom, bool antialiased = false) const + { + SkRect r; + r.set(left, top, right, bottom); + return this->quickReject(r, antialiased); + } + + /** Fill the entire canvas' bitmap (restricted to the current clip) with the + specified RGB color, using srcover porterduff mode. + @param r the red component (0..255) of the color used to draw onto the canvas + @param g the green component (0..255) of the color used to draw onto the canvas + @param b the blue component (0..255) of the color used to draw onto the canvas + */ + void drawRGB(U8CPU r, U8CPU g, U8CPU b); + /** Fill the entire canvas' bitmap (restricted to the current clip) with the + specified ARGB color, using srcover porterduff mode. + @param a the alpha component (0..255) of the color used to draw onto the canvas + @param r the red component (0..255) of the color used to draw onto the canvas + @param g the green component (0..255) of the color used to draw onto the canvas + @param b the blue component (0..255) of the color used to draw onto the canvas + */ + void drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b); + /** Fill the entire canvas' bitmap (restricted to the current clip) with the + specified color, using srcover porterduff mode. + @param color the color to draw onto the canvas + */ + void drawColor(SkColor color) + { + this->drawColor(color, SkPorterDuff::kSrcOver_Mode); + } + /** Fill the entire canvas' bitmap (restricted to the current clip) with the + specified color and porter-duff xfermode. + @param color the color to draw with + @param mode the porter-duff mode to apply to the color + */ + void drawColor(SkColor color, SkPorterDuff::Mode mode); + + /** Fill the entire canvas' bitmap (restricted to the current clip) with the + specified paint. This is equivalent (but faster) to drawing an infinitely + large rectangle with the specified paint. + @param paint The paint used to draw onto the canvas + */ + void drawPaint(const SkPaint& paint); + /** Draw a line segment with the specified start and stop points, using the specified + paint. NOTE: since a line is always "framed", the Style is ignored in + the paint. + @param start The start point of the line + @param stop The stop point of the line + @param paint The paint used to draw the line + */ + void drawLine(const SkPoint& start, const SkPoint& stop, const SkPaint& paint); + /** Draw a line segment with the specified start and stop x,y coordinates, using the specified + paint. NOTE: since a line is always "framed", the Style is ignored in + the paint. + @param startX The x-coordinate of the start point of the line + @param startY The y-coordinate of the start point of the line + @param endX The x-coordinate of the end point of the line + @param endY The y-coordinate of the end point of the line + @param paint The paint used to draw the line + */ + void drawLine(SkScalar startX, SkScalar startY, SkScalar stopX, SkScalar stopY, const SkPaint& paint); + /** Draw the specified SkRect using the specified paint. The rectangle will be filled + or framed based on the Style in the paint. + @param rect The rect to be drawn + @param paint The paint used to draw the rect + */ + void drawRect(const SkRect& rect, const SkPaint& paint); + /** Draw the specified SkRect using the specified paint. The rectangle will be filled + or framed based on the Style in the paint. + @param left The left side of the rectangle to be drawn + @param top The top side of the rectangle to be drawn + @param right The right side of the rectangle to be drawn + @param bottom The bottom side of the rectangle to be drawn + @param paint The paint used to draw the rect + */ + void drawRect(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom, const SkPaint& paint); + /** Draw the specified oval using the specified paint. The oval will be filled + or framed based on the Style in the paint. + @param oval The rectangle bounds of the oval to be drawn + @param paint The paint used to draw the oval + */ + void drawOval(const SkRect& oval, const SkPaint&); + /** Draw the specified circle using the specified paint. If radius is <= 0, then + nothing will be drawn. The circle will be filled + or framed based on the Style in the paint. + @param cx The x-coordinate of the center of the cirle to be drawn + @param cy The y-coordinate of the center of the cirle to be drawn + @param radius The radius of the cirle to be drawn + @param paint The paint used to draw the circle + */ + void drawCircle(SkScalar cx, SkScalar cy, SkScalar radius, const SkPaint& paint); + /** Draw the specified round-rect using the specified paint. The round-rect will be filled + or framed based on the Style in the paint. + @param rect The rectangular bounds of the roundRect to be drawn + @param rx The x-radius of the oval used to round the corners + @param ry The y-radius of the oval used to round the corners + @param paint The paint used to draw the roundRect + */ + void drawRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry, const SkPaint& paint); + /** Draw the specified path using the specified paint. The path will be filled + or framed based on the Style in the paint. + @param path The path to be drawn + @param paint The paint used to draw the path + */ + void drawPath(const SkPath& path, const SkPaint& paint); + /** Draw the specified bitmap, with its top/left corner at (x,y), using the specified paint, + transformed by the current matrix. + @param bitmap The bitmap to be drawn + @param left The position of the left side of the bitmap being drawn + @param top The position of the top side of the bitmap being drawn + @param paint The paint used to draw the bitmap + */ + void drawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top, const SkPaint& paint); + /** Draw the specified bitmap, with its top/left corner at (x,y), transformed + by the current matrix. Since no paint is specified, the bitmap is drawn with no overriding + alpha or colorfilter, and in srcover porterduff mode. + @param bitmap The bitmap to be drawn + @param left The position of the left side of the bitmap being drawn + @param top The position of the top side of the bitmap being drawn + */ + void drawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top); + /** Draw the specified bitmap, with its top/left corner at (x,y), NOT transformed + by the current matrix. This method is not exported to java. + @param bitmap The bitmap to be drawn + @param left The position of the left side of the bitmap being drawn + @param top The position of the top side of the bitmap being drawn + @param paint The paint used to draw the bitmap + */ + void drawSprite(const SkBitmap& bitmap, int left, int top, const SkPaint& paint); + /** Draw the utf8-text, with origin at (x,y), using the specified paint. The origin is interpreted + based on the Align setting in the paint. + @param text The UTF8 text to be drawn + @param byteLength The number of bytes to read from the text parameter + @param x The x-coordinate of the origin of the text being drawn + @param y The y-coordinate of the origin of the text being drawn + @param paint The paint used for the text (e.g. color, size, style) + */ + void drawText(const char text[], size_t byteLength, SkScalar x, SkScalar y, const SkPaint& paint); + /** Draw the utf8-text, with origin at (x,y), using the specified paint. The origin is interpreted + based on the Align setting in the paint. + @param text The UTF16 text to be drawn + @param numberOf16BitValues The number of 16bit values to read from the text parameter + @param x The x-coordinate of the origin of the text being drawn + @param y The y-coordinate of the origin of the text being drawn + @param paint The paint used for the text (e.g. color, size, style) + */ + void drawText16(const uint16_t text[], size_t numberOf16BitValues, SkScalar x, SkScalar y, const SkPaint& paint); + /** Draw the utf8-text, with origin at (x,y), using the specified paint. The origin is interpreted + based on the Align setting in the paint. + @param text The UTF8 text to be drawn + @param byteLength The number of bytes to read from the text parameter + @param pos Array of positions, used to position each character + @param paint The paint used for the text (e.g. color, size, style) + */ + void drawPosText(const char text[], size_t byteLength, const SkPoint pos[], const SkPaint& paint); + /** Draw the utf8-text, with origin at (x,y), using the specified paint. The origin is interpreted + based on the Align setting in the paint. + @param text The UTF16 text to be drawn + @param numberOf16BitValues The number of 16bit values to read from the text parameter + @param pos Array of positions, used to position each character + @param paint The paint used for the text (e.g. color, size, style) + */ + void drawPosText16(const uint16_t text[], size_t numberOf16BitValues, const SkPoint pos[], const SkPaint& paint); + /** Draw the utf8-text, with origin at (x,y), using the specified paint, along the specified path. + The paint's Align setting determins where along the path to start the text. + @param text The UTF8 text to be drawn + @param byteLength The number of bytes to read from the text parameter + @param path The path the text should follow for its baseline + @param distance The distance along the path to add to the text's starting position + @param paint The paint used for the text (e.g. color, size, style) + */ + void drawTextOnPath(const char text[], size_t byteLength, const SkPath& path, SkScalar distance, const SkPaint& paint); + /** Draw the utf8-text, with origin at (x,y), using the specified paint, along the specified path. + The paint's Align setting determins where along the path to start the text. + @param text The UTF16 text to be drawn + @param numberOf16BitValues The number of 16bit values to read from the text parameter + @param path The path the text should follow for its baseline + @param offset The distance along the path to add to the text's starting position + @param paint The paint used for the text (e.g. color, size, style) + */ + void drawText16OnPath(const uint16_t text[], size_t numberOf16BitValues, const SkPath& path, SkScalar distance, const SkPaint& paint); + + /** Return the current set mask, used to temporarily modify the paint's flags + when something is being drawin. + This method is not exported to java. + */ + uint32_t getPaintSetBits() const; + /** Return the current clear mask, used to temporarily modify the paint's flags + when something is being drawin. + This method is not exported to java. + */ + uint32_t getPaintClearBits() const; + /** Set the current set and clear masks, used to temporarily modify the paint's flags + when something is being drawin. The setBits are applied before the clrBits. + This method is not exported to java. + @param setBits A mask of bits to be OR'd into the paint's flag bits + @param clrBits A mask of bits to be cleared from the paint's flag bits + */ + void setPaintSetClearBits(uint32_t setBits, uint32_t clrBits); + /** Helper for getPaintSetClearBits/setPaintSetClearBits. The parameters are OR'd into + the current values, rather than replacing them as with setPaintSetClearBits. + This method is not exported to java. + @param setBits A mask of bits to be OR'd with the existing setBits on the canvas + @param clearBits A mask of bits to be OR'd with the existing clearBits on the canvas + */ + void orPaintSetClearBits(uint32_t setBits, uint32_t clearBits); + + /** Get the current bounder object. + <p /> + The bounder's reference count is not affected. + @return the canva's bounder (or NULL). + */ + SkBounder* getBounder() const { return fBounder; } + /** Set a new bounder (or NULL). + <p /> + Pass NULL to clear any previous bounder. + As a convenience, the parameter passed is also returned. + If a previous bounder exists, its reference count is decremented. + If bounder is not NULL, its reference count is incremented. + @param bounder the new bounder (or NULL) to be installed in the canvas + @return the set bounder object + */ + SkBounder* setBounder(SkBounder*); + + /** Return a reference to the bitmap that the current layer draws into. + This method is not exported to java. + @return The a reference to the bitmap that the current layer draws into. + */ + const SkBitmap& getCurrBitmap() const; + /** Return the MapPtProc for the current matrix on the canvas. + This method is not exported to java. + @return the MapPtProc for the current matrix on the canvas. + */ + SkMatrix::MapPtProc getCurrMapPtProc() const; + /** Return the current matrix on the canvas. + This method is not exported to java. + @return The current matrix on the canvas. + */ + const SkMatrix& getTotalMatrix() const; + /** Return the current device clip (concatenation of all clip calls). + This method is not exported to java. + @return the current device clip (concatenation of all clip calls). + */ + const SkRegion& getTotalClip() const; + +private: + struct MCRec; + + SkDeque fMCStack; + MCRec* fMCRec; // points to top of stack + uint32_t fMCRecStorage[32]; // the first N recs that can fit here mean we won't call malloc + + SkBitmap fBitmap; + SkBounder* fBounder; + + friend class SkDraw; +}; + +/** Stack helper class to automatically call restoreToCount() on the canvas + when this object goes out of scope. Use this to guarantee that the canvas + is restored to a known state. +*/ +class SkAutoCanvasRestore { +public: + SkAutoCanvasRestore(SkCanvas* canvas, bool doSave) : fCanvas(canvas) + { + SkASSERT(canvas); + fSaveCount = canvas->getSaveCount(); + if (doSave) + canvas->save(); + } + ~SkAutoCanvasRestore() + { + fCanvas->restoreToCount(fSaveCount); + } + +private: + SkCanvas* fCanvas; + int fSaveCount; +}; + +#endif + diff --git a/include/graphics/SkColor.h b/include/graphics/SkColor.h new file mode 100644 index 0000000000..6df71ea406 --- /dev/null +++ b/include/graphics/SkColor.h @@ -0,0 +1,126 @@ +#ifndef SkColor_DEFINED +#define SkColor_DEFINED + +#include "SkScalar.h" + +/** \file SkColor.h + + Types and macros for colors +*/ + +/** 8-bit type for an alpha value. 0xFF is 100% opaque, 0x00 is 100% transparent. +*/ +typedef uint8_t SkAlpha; +/** 32 bit ARGB color value, not premultiplied. The color components are always in + a known order. This is different from SkPMColor, which has its bytes in a configuration + dependent order, to match the format of kARGB32 bitmaps. SkColor is the type used to + specify colors in SkPaint and in gradients. +*/ +typedef uint32_t SkColor; + +/** Return a SkColor value from 8 bit component values +*/ +static inline SkColor SkColorSetARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) +{ + SkASSERT(a <= 255 && r <= 255 && g <= 255 && b <= 255); + + return (a << 24) | (r << 16) | (g << 8) | (b << 0); +} + +/** Return a SkColor value from 8 bit component values, with an implied value + of 0xFF for alpha (fully opaque) +*/ +#define SkColorSetRGB(r, g, b) SkColorSetARGB(0xFF, r, g, b) + +#define SkColorGetA(color) ((color) >> 24) //!< return the alpha byte from a SkColor value +#define SkColorGetR(color) ((color) << 8 >> 24) //!< return the red byte from a SkColor value +#define SkColorGetG(color) ((color) << 16 >> 24) //!< return the green byte from a SkColor value +#define SkColorGetB(color) ((color) << 24 >> 24) //!< return the blue byte from a SkColor value + +// common colors + +#define SK_ColorBLACK 0xFF000000 //!< black SkColor value +#define SK_ColorDKGRAY 0xFF444444 //!< dark gray SkColor value +#define SK_ColorGRAY 0xFF888888 //!< gray SkColor value +#define SK_ColorLTGRAY 0xFFCCCCCC //!< light gray SkColor value +#define SK_ColorWHITE 0xFFFFFFFF //!< white SkColor value + +#define SK_ColorRED 0xFFFF0000 //!< red SkColor value +#define SK_ColorGREEN 0xFF00FF00 //!< green SkColor value +#define SK_ColorBLUE 0xFF0000FF //!< blue SkColor value +#define SK_ColorYELLOW 0xFFFFFF00 //!< yellow SkColor value +#define SK_ColorCYAN 0xFF00FFFF //!< cyan SkColor value +#define SK_ColorMAGENTA 0xFFFF00FF //!< magenta SkColor value + +//////////////////////////////////////////////////////////////////////// + +/** Convert RGB components to HSV. + hsv[0] is Hue [0 .. 360) + hsv[1] is Saturation [0...1] + hsv[2] is Value [0...1] + @param red red component value [0..255] + @param green green component value [0..255] + @param blue blue component value [0..255] + @param hsv 3 element array which holds the resulting HSV components. +*/ +void SkRGBToHSV(U8CPU red, U8CPU green, U8CPU blue, SkScalar hsv[3]); + +/** Convert the argb color to its HSV components. + hsv[0] is Hue [0 .. 360) + hsv[1] is Saturation [0...1] + hsv[2] is Value [0...1] + @param color the argb color to convert. Note: the alpha component is ignored. + @param hsv 3 element array which holds the resulting HSV components. +*/ +static inline void SkColorToHSV(SkColor color, SkScalar hsv[3]) +{ + SkRGBToHSV(SkColorGetR(color), SkColorGetG(color), SkColorGetB(color), hsv); +} + +/** Convert HSV components to an ARGB color. The alpha component is passed through unchanged. + hsv[0] is Hue [0 .. 360) + hsv[1] is Saturation [0...1] + hsv[2] is Value [0...1] + If hsv values are out of range, they are pinned. + @param alpha the alpha component of the returned argb color. + @param hsv 3 element array which holds the input HSV components. + @return the resulting argb color +*/ +SkColor SkHSVToColor(U8CPU alpha, const SkScalar hsv[3]); + +/** Convert HSV components to an ARGB color. The alpha component set to 0xFF. + hsv[0] is Hue [0 .. 360) + hsv[1] is Saturation [0...1] + hsv[2] is Value [0...1] + If hsv values are out of range, they are pinned. + @param hsv 3 element array which holds the input HSV components. + @return the resulting argb color +*/ +static inline SkColor SkHSVToColor(const SkScalar hsv[3]) +{ + return SkHSVToColor(0xFF, hsv); +} + +//////////////////////////////////////////////////////////////////////// + +/** 32 bit ARGB color value, premultiplied. The byte order for this value is + configuration dependent, matching the format of kARGB32 bitmaps. This is different + from SkColor, which is nonpremultiplied, and is always in the same byte order. +*/ +typedef uint32_t SkPMColor; + +/** Return a SkPMColor value from unpremultiplied 8 bit component values +*/ +SkPMColor SkPreMultiplyARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b); +/** Return a SkPMColor value from a SkColor value. This is done by multiplying the color + components by the color's alpha, and by arranging the bytes in a configuration + dependent order, to match the format of kARGB32 bitmaps. +*/ +SkPMColor SkPreMultiplyColor(SkColor c); + +/** Define a function pointer type for combining two premultiplied colors +*/ +typedef SkPMColor (*SkXfermodeProc)(SkPMColor src, SkPMColor dst); + +#endif + diff --git a/include/graphics/SkColorFilter.h b/include/graphics/SkColorFilter.h new file mode 100644 index 0000000000..91879aeebe --- /dev/null +++ b/include/graphics/SkColorFilter.h @@ -0,0 +1,56 @@ +#ifndef SkColorFilter_DEFINED +#define SkColorFilter_DEFINED + +#include "SkRefCnt.h" +#include "SkColor.h" +#include "SkPorterDuff.h" + +class SkColorFilter : public SkRefCnt { +public: + /** Called with a scanline of colors, as if there was a shader installed. + The implementation writes out its filtered version into result[]. + @param shader array of colors, possibly generated by a shader + @param count the number of entries in the shader[] and result[] arrays + @param result written by the filter, these are the colors that are used to draw + */ + virtual void filterSpan(const SkPMColor shader[], int count, SkPMColor result[]); + + /** Create a colorfilter that uses the specified color and xfermode proc. + @param srcColor The source color passed to the xfermode proc + @param proc The xfermode proc that is applied to each color in the colorfilter's filterSpan method + @return colorfilter object that applies the src color and xfermode proc, or NULL if proc is NULL + */ + static SkColorFilter* CreatXfermodeFilter(SkColor srcColor, SkXfermodeProc proc); + /** Create a colorfilter that uses the specified color and porter-duff mode. + @param srcColor The source color used with the specified porter-duff mode + @param porterDuffMode The porter-duff mode that is applied to each color in the colorfilter's filterSpan method + @return colorfilter object that applies the src color and porter-duff mode, or NULL is mode is out of range + */ + static SkColorFilter* CreatePorterDuffFilter(SkColor srcColor, SkPorterDuff::Mode porterDuffMode); + + /** Create a colorfilter that multiplies the RGB channels by one color, and then adds a second color, + pinning the result for each component to [0..255]. The alpha components of the mul and add arguments + are ignored. + */ + static SkColorFilter* CreateLightingFilter(SkColor mul, SkColor add); +}; + +#include "SkShader.h" + +class SkFilterShader : public SkShader { +public: + SkFilterShader(SkShader* shader, SkColorFilter* filter); + virtual ~SkFilterShader(); + + // override + virtual bool setContext(const SkBitmap& device, const SkPaint& paint, const SkMatrix& matrix); + virtual void shadeSpan(int x, int y, SkPMColor result[], int count); + +private: + SkShader* fShader; + SkColorFilter* fFilter; + + typedef SkShader INHERITED; +}; + +#endif diff --git a/include/graphics/SkColorPriv.h b/include/graphics/SkColorPriv.h new file mode 100644 index 0000000000..65b5851d5c --- /dev/null +++ b/include/graphics/SkColorPriv.h @@ -0,0 +1,218 @@ +#ifndef SkColorPriv_DEFINED +#define SkColorPriv_DEFINED + +#include "SkColor.h" + +inline unsigned SkAlpha255To256(U8CPU alpha) +{ + SkASSERT(SkToU8(alpha) == alpha); + return alpha + (alpha >> 7); +} + +#define SkAlphaMul(value, alpha256) ((value) * (alpha256) >> 8) + +// The caller may want negative values, so keep all params signed (int) +// so we don't accidentally slip into unsigned math and lose the sign +// extension when we shift (in SkAlphaMul) +inline int SkAlphaBlend(int src, int dst, int scale256) +{ + SkASSERT((unsigned)scale256 <= 256); + return dst + SkAlphaMul(src - dst, scale256); +} + +#define SK_R16_BITS 5 +#define SK_G16_BITS 6 +#define SK_B16_BITS 5 + +#define SK_R16_SHIFT (SK_B16_BITS + SK_G16_BITS) +#define SK_G16_SHIFT (SK_B16_BITS) +#define SK_B16_SHIFT 0 + +#define SK_R16_MASK ((1 << SK_R16_BITS) - 1) +#define SK_G16_MASK ((1 << SK_G16_BITS) - 1) +#define SK_B16_MASK ((1 << SK_B16_BITS) - 1) + +#define SkGetPackedR16(color) (((unsigned)(color) >> SK_R16_SHIFT) & SK_R16_MASK) +#define SkGetPackedG16(color) (((unsigned)(color) >> SK_G16_SHIFT) & SK_G16_MASK) +#define SkGetPackedB16(color) (((unsigned)(color) >> SK_B16_SHIFT) & SK_B16_MASK) + +inline uint16_t SkPackRGB16(unsigned r, unsigned g, unsigned b) +{ + SkASSERT(r <= SK_R16_MASK); + SkASSERT(g <= SK_G16_MASK); + SkASSERT(b <= SK_B16_MASK); + + return SkToU16((r << SK_R16_SHIFT) | (g << SK_G16_SHIFT) | (b << SK_B16_SHIFT)); +} + +inline int SkShouldDitherXY(int x, int y) +{ + return (x ^ y) & 1; +} + +inline uint16_t SkDitherPack888ToRGB16(U8CPU r, U8CPU g, U8CPU b) +{ + r = ((r << 1) - ((r >> (8 - SK_R16_BITS) << (8 - SK_R16_BITS)) | (r >> SK_R16_BITS))) >> (8 - SK_R16_BITS); + g = ((g << 1) - ((g >> (8 - SK_G16_BITS) << (8 - SK_G16_BITS)) | (g >> SK_G16_BITS))) >> (8 - SK_G16_BITS); + b = ((b << 1) - ((b >> (8 - SK_B16_BITS) << (8 - SK_B16_BITS)) | (b >> SK_B16_BITS))) >> (8 - SK_B16_BITS); + + return SkPackRGB16(r, g, b); +} + +#define SK_R16_MASK_IN_PLACE (SK_R16_MASK << SK_R16_SHIFT) +#define SK_G16_MASK_IN_PLACE (SK_G16_MASK << SK_G16_SHIFT) +#define SK_B16_MASK_IN_PLACE (SK_B16_MASK << SK_B16_SHIFT) + +#define SK_R16B16_MASK_IN_PLACE (SK_R16_MASK_IN_PLACE | SK_B16_MASK_IN_PLACE) + +inline U16CPU SkAlphaMulRGB16(U16CPU c, unsigned scale) +{ +#if SK_G16_MASK_IN_PLACE != 0x07E0 + return SkPackRGB16( SkAlphaMul(SkGetPackedR16(c), scale), + SkAlphaMul(SkGetPackedG16(c), scale), + SkAlphaMul(SkGetPackedB16(c), scale)); +#else + scale >>= (8 - SK_G16_BITS); + uint32_t rb = (c & SK_R16B16_MASK_IN_PLACE) * scale >> SK_G16_BITS; + uint32_t g = (c & SK_G16_MASK_IN_PLACE) * scale >> SK_G16_BITS; + return (g & SK_G16_MASK_IN_PLACE) | (rb & SK_R16B16_MASK_IN_PLACE); +#endif +} + +inline U16CPU SkBlendRGB16(U16CPU src, U16CPU dst, unsigned scale) +{ + SkASSERT(scale <= 256); + + return SkPackRGB16( SkAlphaBlend(SkGetPackedR16(src), SkGetPackedR16(dst), scale), + SkAlphaBlend(SkGetPackedG16(src), SkGetPackedG16(dst), scale), + SkAlphaBlend(SkGetPackedB16(src), SkGetPackedB16(dst), scale)); +} + +///////////////////////////////////////////////////////////////////////////////////////////// + +#define SK_A32_BITS 8 +#define SK_R32_BITS 8 +#define SK_G32_BITS 8 +#define SK_B32_BITS 8 + +#ifdef TEST_INTEL_MAC + +#define SK_A32_SHIFT 0 +#define SK_R32_SHIFT 8 +#define SK_G32_SHIFT 16 +#define SK_B32_SHIFT 24 + +#else + +#define SK_A32_SHIFT 24 +#define SK_R32_SHIFT 16 +#define SK_G32_SHIFT 8 +#define SK_B32_SHIFT 0 + +#endif + +#define SK_A32_MASK ((1 << SK_A32_BITS) - 1) +#define SK_R32_MASK ((1 << SK_R32_BITS) - 1) +#define SK_G32_MASK ((1 << SK_G32_BITS) - 1) +#define SK_B32_MASK ((1 << SK_B32_BITS) - 1) + +#define SkGetPackedA32(packed) ((uint32_t)((packed) << (24 - SK_A32_SHIFT)) >> 24) +#define SkGetPackedR32(packed) ((uint32_t)((packed) << (24 - SK_R32_SHIFT)) >> 24) +#define SkGetPackedG32(packed) ((uint32_t)((packed) << (24 - SK_G32_SHIFT)) >> 24) +#define SkGetPackedB32(packed) ((uint32_t)((packed) << (24 - SK_B32_SHIFT)) >> 24) + +inline SkPMColor SkPackARGB32(U8CPU a, U8CPU r, U8CPU g, U8CPU b) +{ + SkASSERT(a <= SK_A32_MASK); + SkASSERT(r <= a); + SkASSERT(g <= a); + SkASSERT(b <= a); + + return (a << SK_A32_SHIFT) | (r << SK_R32_SHIFT) | (g << SK_G32_SHIFT) | (b << SK_B32_SHIFT); +} + +inline uint32_t SkAlphaMulQ(uint32_t c, unsigned scale) +{ + uint32_t rb = ((c & 0xFF00FF) * scale) >> 8; + uint32_t ag = ((c >> 8) & 0xFF00FF) * scale; + return (rb & 0xFF00FF) | (ag & ~0xFF00FF); +} + +inline SkPMColor SkPMSrcOver(SkPMColor src, SkPMColor dst) +{ + return src + SkAlphaMulQ(dst, SkAlpha255To256(255 - SkGetPackedA32(src))); +} + +inline SkPMColor SkBlendARGB32(SkPMColor src, SkPMColor dst, U8CPU aa) +{ + SkASSERT((unsigned)aa <= 255); + + unsigned src_scale = SkAlpha255To256(aa); + unsigned dst_scale = SkAlpha255To256(255 - SkAlphaMul(SkGetPackedA32(src), src_scale)); + + return SkAlphaMulQ(src, src_scale) + SkAlphaMulQ(dst, dst_scale); +} + +//////////////////////////////////////////////////////////////////////////////////////////// +// Convert a 32bit pixel to a 16bit pixel (no dither) + +#define SkR32ToR16(r) ((unsigned)(r) >> (SK_R32_BITS - SK_R16_BITS)) +#define SkG32ToG16(g) ((unsigned)(g) >> (SK_G32_BITS - SK_G16_BITS)) +#define SkB32ToB16(b) ((unsigned)(b) >> (SK_B32_BITS - SK_B16_BITS)) + +#define SkPacked32ToR16(c) (((unsigned)(c) >> (SK_R32_SHIFT + SK_R32_BITS - SK_R16_BITS)) & SK_R16_MASK) +#define SkPacked32ToG16(c) (((unsigned)(c) >> (SK_G32_SHIFT + SK_G32_BITS - SK_G16_BITS)) & SK_G16_MASK) +#define SkPacked32ToB16(c) (((unsigned)(c) >> (SK_B32_SHIFT + SK_B32_BITS - SK_B16_BITS)) & SK_B16_MASK) + +inline U16CPU SkPixel32ToPixel16(SkPMColor src) +{ +#if 0 + return (SkPacked32ToR16(src) << SK_R16_SHIFT) | + (SkPacked32ToG16(src) << SK_G16_SHIFT) | + (SkPacked32ToB16(src) << SK_B16_SHIFT); +#else // only works if the components are in the same order in both formats (i.e. foo32_shift >= foo16_shift) + return ((src >> (SK_R32_SHIFT + SK_R32_BITS - SK_R16_BITS - SK_R16_SHIFT)) & (SK_R16_MASK << SK_R16_SHIFT)) | + ((src >> (SK_G32_SHIFT + SK_G32_BITS - SK_G16_BITS - SK_G16_SHIFT)) & (SK_G16_MASK << SK_G16_SHIFT)) | + ((src >> (SK_B32_SHIFT + SK_B32_BITS - SK_B16_BITS - SK_B16_SHIFT)) & (SK_B16_MASK << SK_B16_SHIFT)); +#endif +} + +#define SkPixel32ToPixel16_ToU16(src) SkToU16(SkPixel32ToPixel16(src)) + +//////////////////////////////////////////////////////////////////////////////////////////// +// Convert a 16bit pixel to a 32bit pixel + +inline unsigned SkR16ToR32(unsigned r) +{ + return (r << (8 - SK_R16_BITS)) | (r >> (2 * SK_R16_BITS - 8)); +} +inline unsigned SkG16ToG32(unsigned g) +{ + return (g << (8 - SK_G16_BITS)) | (g >> (2 * SK_G16_BITS - 8)); +} +inline unsigned SkB16ToB32(unsigned b) +{ + return (b << (8 - SK_B16_BITS)) | (b >> (2 * SK_B16_BITS - 8)); +} + +#define SkPacked16ToR32(c) SkR16ToR32(SkGetPackedR16(c)) +#define SkPacked16ToG32(c) SkG16ToG32(SkGetPackedG16(c)) +#define SkPacked16ToB32(c) SkB16ToB32(SkGetPackedB16(c)) + +inline SkPMColor SkPixel16ToPixel32(U16CPU src) +{ + SkASSERT(src == SkToU16(src)); + + unsigned r = SkPacked16ToR32(src); + unsigned g = SkPacked16ToG32(src); + unsigned b = SkPacked16ToB32(src); + + SkASSERT((r >> (8 - SK_R16_BITS)) == SkGetPackedR16(src)); + SkASSERT((g >> (8 - SK_G16_BITS)) == SkGetPackedG16(src)); + SkASSERT((b >> (8 - SK_B16_BITS)) == SkGetPackedB16(src)); + + return SkPackARGB32(0xFF, r, g, b); +} + +#endif + diff --git a/include/graphics/SkCornerPathEffect.h b/include/graphics/SkCornerPathEffect.h new file mode 100644 index 0000000000..4307c707b4 --- /dev/null +++ b/include/graphics/SkCornerPathEffect.h @@ -0,0 +1,45 @@ +#ifndef SkCornerPathEffect_DEFINED +#define SkCornerPathEffect_DEFINED + +#include "SkPathEffect.h" + +/** \class SkCornerPathEffect + + SkCornerPathEffect is a subclass of SkPathEffect that can turn sharp corners + into various treatments (e.g. rounded corners) +*/ +class SkCornerPathEffect : public SkPathEffect { +public: + /** radius must be > 0 to have an effect. It specifies the distance from each corner + that should be "rounded". + */ + SkCornerPathEffect(SkScalar radius); + virtual ~SkCornerPathEffect(); + + // overrides for SkPathEffect + // This method is not exported to java. + virtual bool filterPath(SkPath* dst, const SkPath& src, SkScalar* width); + + // overrides for SkFlattenable + // This method is not exported to java. + virtual Factory getFactory(); + // This method is not exported to java. + virtual void flatten(SkWBuffer&); + +protected: + SkCornerPathEffect(SkRBuffer&); + +private: + SkScalar fRadius; + + static SkFlattenable* CreateProc(SkRBuffer&); + + // illegal + SkCornerPathEffect(const SkCornerPathEffect&); + SkCornerPathEffect& operator=(const SkCornerPathEffect&); + + typedef SkPathEffect INHERITED; +}; + +#endif + diff --git a/include/graphics/SkCullPoints.h b/include/graphics/SkCullPoints.h new file mode 100644 index 0000000000..def938d7ae --- /dev/null +++ b/include/graphics/SkCullPoints.h @@ -0,0 +1,58 @@ +#ifndef SkCullPoints_DEFINED +#define SkCullPoints_DEFINED + +#include "SkRect.h" + +class SkCullPoints { +public: + SkCullPoints(); + SkCullPoints(const SkRect16& r); + + void reset(const SkRect16& r); + + /** Start a contour at (x,y). Follow this with call(s) to lineTo(...) + */ + void moveTo(int x, int y); + + enum LineToResult { + kNo_Result, //!< line segment was completely clipped out + kLineTo_Result, //!< path.lineTo(pts[1]); + kMoveToLineTo_Result //!< path.moveTo(pts[0]); path.lineTo(pts[1]); + }; + /** Connect a line to the previous call to lineTo (or moveTo). + */ + LineToResult lineTo(int x, int y, SkPoint16 pts[2]); + +private: + SkRect16 fR; + SkPoint16 fAsQuad[4]; + SkPoint32 fPrevPt; // local state + + bool sect_test(int x0, int y0, int x1, int y1) const; +}; + +///////////////////////////////////////////////////////////////////////////////// + +class SkPath; + +/** \class SkCullPointsPath + + Similar to SkCullPoints, but this class handles the return values + from lineTo, and automatically builds a SkPath with the result(s). +*/ +class SkCullPointsPath { +public: + SkCullPointsPath(); + SkCullPointsPath(const SkRect16& r, SkPath* dst); + + void reset(const SkRect16& r, SkPath* dst); + + void moveTo(int x, int y); + void lineTo(int x, int y); + +private: + SkCullPoints fCP; + SkPath* fPath; +}; + +#endif diff --git a/include/graphics/SkDOM.h b/include/graphics/SkDOM.h new file mode 100644 index 0000000000..6567a89562 --- /dev/null +++ b/include/graphics/SkDOM.h @@ -0,0 +1,83 @@ +#ifndef SkDOM_DEFINED +#define SkDOM_DEFINED + +#include "SkChunkAlloc.h" +#include "SkMath.h" +#include "SkTemplates.h" + +struct SkDOMNode; +struct SkDOMAttr; + +class SkDOM { +public: + SkDOM(); + ~SkDOM(); + + typedef SkDOMNode Node; + typedef SkDOMAttr Attr; + + /** Returns nil on failure + */ + const Node* build(const char doc[], size_t len); + const Node* copy(const SkDOM& dom, const Node* node); + + const Node* getRootNode() const; + + enum Type { + kElement_Type, + kText_Type + }; + Type getType(const Node*) const; + + const char* getName(const Node*) const; + const Node* getFirstChild(const Node*, const char elem[] = nil) const; + const Node* getNextSibling(const Node*, const char elem[] = nil) const; + + const char* findAttr(const Node*, const char attrName[]) const; + const Attr* getFirstAttr(const Node*) const; + const Attr* getNextAttr(const Node*, const Attr*) const; + const char* getAttrName(const Node*, const Attr*) const; + const char* getAttrValue(const Node*, const Attr*) const; + + // helpers for walking children + int countChildren(const Node* node, const char elem[] = nil) const; + + // helpers for calling SkParse + bool findS32(const Node*, const char name[], int32_t* value) const; + bool findScalars(const Node*, const char name[], SkScalar value[], int count) const; + bool findHex(const Node*, const char name[], uint32_t* value) const; + bool findBool(const Node*, const char name[], bool*) const; + int findList(const Node*, const char name[], const char list[]) const; + + bool findScalar(const Node* node, const char name[], SkScalar value[]) const + { + return this->findScalars(node, name, value, 1); + } + + bool hasAttr(const Node*, const char name[], const char value[]) const; + bool hasS32(const Node*, const char name[], int32_t value) const; + bool hasScalar(const Node*, const char name[], SkScalar value) const; + bool hasHex(const Node*, const char name[], uint32_t value) const; + bool hasBool(const Node*, const char name[], bool value) const; + + class AttrIter { + public: + AttrIter(const class SkDOM&, const Node*); + const char* next(const char** value); + private: + const Attr* fAttr; + const Attr* fStop; + }; + + SkDEBUGCODE(void dump(const Node* node = nil, int tabLevel = 0) const;) + SkDEBUGCODE(static void UnitTest();) + +private: + SkChunkAlloc fAlloc; + Node* fRoot; + friend class AttrIter; + friend class SkDOMParser; +}; + +#endif + diff --git a/include/graphics/SkDashPathEffect.h b/include/graphics/SkDashPathEffect.h new file mode 100644 index 0000000000..0e567ef414 --- /dev/null +++ b/include/graphics/SkDashPathEffect.h @@ -0,0 +1,49 @@ +#ifndef SkDashPathEffect_DEFINED +#define SkDashPathEffect_DEFINED + +#include "SkPathEffect.h" + +/** \class SkDashPathEffect + + SkDashPathEffect is a subclass of SkPathEffect that implements dashing +*/ +class SkDashPathEffect : public SkPathEffect { +public: + /** The intervals array must contain an even number of entries (>=2), with the even + indices specifying the "on" intervals, and the odd indices specifying the "off" + intervals. phase is an offset into the intervals array (mod the sum of all of the + intervals). + Note: only affects framed paths + */ + SkDashPathEffect(const SkScalar intervals[], int count, SkScalar phase, bool scaleToFit = false); + virtual ~SkDashPathEffect(); + + // overrides for SkPathEffect + // This method is not exported to java. + virtual bool filterPath(SkPath* dst, const SkPath& src, SkScalar* width); + + // overrides for SkFlattenable + // This method is not exported to java. + virtual Factory getFactory(); + // This method is not exported to java. + virtual void flatten(SkWBuffer&); + +protected: + SkDashPathEffect(SkRBuffer&); + +private: + SkScalar* fIntervals; + int32_t fCount; + // computed from phase + SkScalar fInitialDashLength; + int32_t fInitialDashIndex; + SkScalar fIntervalLength; + bool fScaleToFit; + + static SkFlattenable* CreateProc(SkRBuffer&); + + typedef SkPathEffect INHERITED; +}; + +#endif + diff --git a/include/graphics/SkDeque.h b/include/graphics/SkDeque.h new file mode 100644 index 0000000000..09677020aa --- /dev/null +++ b/include/graphics/SkDeque.h @@ -0,0 +1,191 @@ +#ifndef SkTDeque_DEFINED +#define SkTDeque_DEFINED + +#include "SkTypes.h" + +template <typename T> struct sk_trait_trivial_constructor { enum { value = false }; }; +template <typename T> struct sk_trait_trivial_destructor { enum { value = false }; }; +template <typename T> struct sk_trait_trivial_copy { enum { value = false }; }; +template <typename T> struct sk_trait_trivial_assign { enum { value = false }; }; + +template <typename T> struct sk_traits { + enum { + has_trivial_constructor = sk_trait_trivial_constructor<T>::value, + has_trivial_destructor = sk_trait_trivial_destructor<T>::value, + has_trivial_copy = sk_trait_trivial_copy<T>::value, + has_trivial_assign = sk_trait_trivial_assign<T>::value + }; +}; + +#define SK_SET_BASIC_TRAITS(T) \ + template <> struct sk_trait_trivial_constructor<T> { enum { value = true }; }; \ + template <> struct sk_trait_trivial_destructor<T> { enum { value = true }; }; \ + template <> struct sk_trait_trivial_copy<T> { enum { value = true }; }; \ + template <> struct sk_trait_trivial_assign<T> { enum { value = true }; } + +#define SK_SET_TYPE_TRAITS(T, ctor, dtor, copy, asgn) \ + template <> struct sk_trait_trivial_constructor<T> { enum { value = ctor }; }; \ + template <> struct sk_trait_trivial_destructor<T> { enum { value = dtor }; }; \ + template <> struct sk_trait_trivial_copy<T> { enum { value = copy }; }; \ + template <> struct sk_trait_trivial_assign<T> { enum { value = asgn }; } + +#include <new> + +class SkDeque { +public: + SkDeque(size_t elemSize); + SkDeque(size_t elemSize, void* storage, size_t storageSize); + ~SkDeque(); + + bool empty() const { return fCount == 0; } + int count() const { return fCount; } + + const void* front() const; + const void* back() const; + + void* front() + { + return (void*)((const SkDeque*)this)->front(); + } + void* back() + { + return (void*)((const SkDeque*)this)->back(); + } + + void* push_front(); + void* push_back(); + + void pop_front(); + void pop_back(); + + SkDEBUGCODE(static void UnitTest();) + +private: + struct Head; + +public: + class Iter { + public: + Iter(const SkDeque& d); + void* next(); + + private: + SkDeque::Head* fHead; + char* fPos; + size_t fElemSize; + }; + +private: + Head* fFront; + Head* fBack; + size_t fElemSize; + void* fInitialStorage; + int fCount; + + friend class Iter; +}; + +template <typename T> class SkTDeque { +public: + SkTDeque() : fD(sizeof(T)) {} + SkTDeque(T storage[], int count) : fD(sizeof(T), storage, count * sizeof(T)) {} + inline ~SkTDeque(); + + bool empty() const { return fD.empty(); } + int count() const { return fD.count(); } + + T* front() { return (T*)fD.front(); } + const T* front() const { return (const T*)fD.front(); } + T* back() { return (T*)fD.back(); } + const T* back() const { return (const T*)fD.back(); } + + T* push_front() + { + T* front = (T*)fD.push_front(); + if (!sk_traits<T>::has_trivial_constructor) { + new(front) T(); + } + return front; + } + T* push_back() + { + T* back = (T*)fD.push_back(); + if (!sk_traits<T>::has_trivial_constructor) { + new(back) T(); + } + return back; + } + + T* push_front(const T& value) + { + T* front = (T*)fD.push_front(); + if (sk_traits<T>::has_trivial_copy) { + *front = value; + } + else { + new(front) T(value); + } + return front; + } + T* push_back(const T& value) + { + T* back = (T*)fD.push_back(); + if (sk_traits<T>::has_trivial_copy) { + *back = value; + } + else { + new(back) T(value); + } + return back; + } + + void pop_front() + { + if (!sk_traits<T>::has_trivial_destructor) { + this->front()->~T(); + } + fD.pop_front(); + } + void pop_back() + { + if (!sk_traits<T>::has_trivial_destructor) { + this->back()->~T(); + } + fD.pop_back(); + } + + class Iter : private SkDeque::Iter { + public: + Iter(const SkTDeque<T>& d) : SkDeque::Iter(d.fD) {} + T* next() { return (T*)SkDeque::Iter::next(); } + }; + +private: + SkDeque fD; + + friend class Iter; +}; + +template <size_t COUNT, typename T> class SkSTDeque : public SkTDeque<T> { +public: + SkSTDeque() : SkTDeque<T>((T*)fStorage, COUNT) {} + +private: + uint32_t fStorage[SkAlign4(COUNT * sizeof(T))]; +}; + +//////////////////////////////////////////////////////////////////////////////////// + +template <typename T> inline SkTDeque<T>::~SkTDeque() +{ + if (!sk_traits<T>::has_trivial_destructor) + { + Iter iter(*this); + T* t; + while ((t = iter.next()) != nil) { + t->~T(); + } + } +} + +#endif diff --git a/include/graphics/SkDescriptor.h b/include/graphics/SkDescriptor.h new file mode 100644 index 0000000000..b43114489c --- /dev/null +++ b/include/graphics/SkDescriptor.h @@ -0,0 +1,158 @@ +#ifndef SkDescriptor_DEFINED +#define SkDescriptor_DEFINED + +#include "SkTypes.h" + +class SkDescriptor { +public: + static size_t ComputeOverhead(int entryCount) + { + SkASSERT(entryCount >= 0); + return sizeof(SkDescriptor) + entryCount * sizeof(Entry); + } + + static SkDescriptor* Alloc(size_t length) + { + SkASSERT(SkAlign4(length) == length); + SkDescriptor* desc = (SkDescriptor*)sk_malloc_throw(length); + return desc; + } + + static void Free(SkDescriptor* desc) + { + sk_free(desc); + } + + void init() + { + fLength = sizeof(SkDescriptor); + fCount = 0; + } + + U32 getLength() const { return fLength; } + + void* addEntry(U32 tag, U32 length, const void* data = nil) + { + SkASSERT(tag); + SkASSERT(SkAlign4(length) == length); + SkASSERT(this->findEntry(tag, nil) == nil); + + Entry* entry = (Entry*)((char*)this + fLength); + entry->fTag = tag; + entry->fLen = length; + if (data) + memcpy(entry + 1, data, length); + + fCount += 1; + fLength += sizeof(Entry) + length; + return (entry + 1); // return its data + } + + void computeChecksum() + { + fChecksum = SkDescriptor::ComputeChecksum(this); + } + +#ifdef SK_DEBUG + void assertChecksum() const + { + SkASSERT(fChecksum == SkDescriptor::ComputeChecksum(this)); + } +#endif + + const void* findEntry(U32 tag, U32* length) const + { + const Entry* entry = (const Entry*)(this + 1); + int count = fCount; + + while (--count >= 0) + { + if (entry->fTag == tag) + { + if (length) + *length = entry->fLen; + return entry + 1; + } + entry = (const Entry*)((const char*)(entry + 1) + entry->fLen); + } + return nil; + } + + SkDescriptor* copy() const + { + SkDescriptor* desc = SkDescriptor::Alloc(fLength); + memcpy(desc, this, fLength); + return desc; + } + + friend bool operator==(const SkDescriptor& a, const SkDescriptor& b) + { + return a.fChecksum == b.fChecksum && + a.fLength == b.fLength && + // this assumes that fCount is the beginning of the rest of the descriptor + // (after fCheckSum and fLength) + memcmp(&a.fCount, &b.fCount, a.fLength - 2*sizeof(U32)) == 0; + } + + struct Entry { + U32 fTag; + U32 fLen; + }; + +#ifdef SK_DEBUG + U32 getChecksum() const { return fChecksum; } + U32 getCount() const { return fCount; } +#endif + +private: + U32 fChecksum; // must be first + U32 fLength; // must be second + U32 fCount; + + static U32 ComputeChecksum(const SkDescriptor* desc) + { + const U32* ptr = (const U32*)desc + 1; // skip the checksum field + const U32* stop = (const U32*)((const char*)desc + desc->fLength); + U32 sum = 0; + + SkASSERT(ptr < stop); + do { + sum = (sum << 1) | (sum >> 31); + sum += *ptr++; + } while (ptr < stop); + + return sum; + } +}; + +#include "SkScalerContext.h" + +class SkAutoDescriptor { +public: + SkAutoDescriptor(size_t size) + { + if (size <= kStorageSize) + fDesc = (SkDescriptor*)fStorage; + else + fDesc = SkDescriptor::Alloc(size); + } + ~SkAutoDescriptor() + { + if (fDesc != (SkDescriptor*)fStorage) + SkDescriptor::Free(fDesc); + } + SkDescriptor* getDesc() const { return fDesc; } +private: + enum { + kStorageSize = sizeof(SkDescriptor) + + sizeof(SkDescriptor::Entry) + sizeof(SkScalerContext::Rec) // for rec + + sizeof(SkDescriptor::Entry) + sizeof(void*) // for typeface + + 32 // slop for occational small extras + }; + SkDescriptor* fDesc; + U32 fStorage[kStorageSize >> 2]; +}; + + +#endif + diff --git a/include/graphics/SkDiscretePathEffect.h b/include/graphics/SkDiscretePathEffect.h new file mode 100644 index 0000000000..d3586243f6 --- /dev/null +++ b/include/graphics/SkDiscretePathEffect.h @@ -0,0 +1,40 @@ +#ifndef SkDiscretePathEffect_DEFINED +#define SkDiscretePathEffect_DEFINED + +#include "SkPathEffect.h" + +/** \class SkDiscretePathEffect + + This path effect chops a path into discrete segments, and randomly displaces them. +*/ +class SkDiscretePathEffect : public SkPathEffect { +public: + /** Break the path into segments of segLength length, and randomly move the endpoints + away from the original path by a maximum of deviation. + Note: works on filled or framed paths + */ + SkDiscretePathEffect(SkScalar segLength, SkScalar deviation); + + // overrides for SkPathEffect + // This method is not exported to java. + virtual bool filterPath(SkPath* dst, const SkPath& src, SkScalar* width); + + // overrides for SkFlattenable + // This method is not exported to java. + virtual Factory getFactory(); + // This method is not exported to java. + virtual void flatten(SkWBuffer&); + +protected: + SkDiscretePathEffect(SkRBuffer&); + +private: + SkScalar fSegLength, fPerterb; + + static SkFlattenable* CreateProc(SkRBuffer&); + + typedef SkPathEffect INHERITED; +}; + +#endif + diff --git a/include/graphics/SkDrawExtraPathEffect.h b/include/graphics/SkDrawExtraPathEffect.h new file mode 100755 index 0000000000..50a43c618c --- /dev/null +++ b/include/graphics/SkDrawExtraPathEffect.h @@ -0,0 +1,6 @@ +#ifndef SK_DRAW_EXTRA_PATH_EFFECT_H +#define SK_DRAW_EXTRA_PATH_EFFECT_H +class SkAnimator; +void InitializeSkExtraPathEffects(SkAnimator* animator); +#endif + diff --git a/include/graphics/SkEmbossMaskFilter.h b/include/graphics/SkEmbossMaskFilter.h new file mode 100644 index 0000000000..5cfa145a06 --- /dev/null +++ b/include/graphics/SkEmbossMaskFilter.h @@ -0,0 +1,47 @@ +#ifndef SkEmbossMaskFilter_DEFINED +#define SkEmbossMaskFilter_DEFINED + +#include "SkMaskFilter.h" + +/** \class SkEmbossMaskFilter + + This mask filter creates a 3D emboss look, by specifying a light and blur amount. +*/ +class SkEmbossMaskFilter : public SkMaskFilter { +public: + struct Light { + SkScalar fDirection[3]; // x,y,z + U16 fPad; + U8 fAmbient; + U8 fSpecular; // exponent, 4.4 right now + }; + + SkEmbossMaskFilter(const Light& light, SkScalar blurRadius); + + // overrides from SkMaskFilter + // This method is not exported to java. + virtual SkMask::Format getFormat(); + // This method is not exported to java. + virtual bool filterMask(SkMask* dst, const SkMask& src, const SkMatrix& matrix, SkPoint16* margin); + + // overrides from SkFlattenable + + // This method is not exported to java. + virtual Factory getFactory(); + // This method is not exported to java. + virtual void flatten(SkWBuffer&); + +protected: + SkEmbossMaskFilter(SkRBuffer&); + +private: + Light fLight; + SkScalar fBlurRadius; + + static SkFlattenable* CreateProc(SkRBuffer&); + + typedef SkMaskFilter INHERITED; +}; + +#endif + diff --git a/include/graphics/SkEvent.h b/include/graphics/SkEvent.h new file mode 100644 index 0000000000..d171015ff9 --- /dev/null +++ b/include/graphics/SkEvent.h @@ -0,0 +1,228 @@ +#ifndef SkEvent_DEFINED +#define SkEvent_DEFINED + +#include "SkDOM.h" +#include "SkMetaData.h" +#include "SkString.h" + +//class SkOSWindow; + +/** Unique 32bit id used to identify an instance of SkEventSink. When events are + posted, they are posted to a specific sinkID. When it is time to dispatch the + event, the sinkID is used to find the specific SkEventSink object. If it is found, + its doEvent() method is called with the event. +*/ +typedef U32 SkEventSinkID; + +/** \class SkEvent + + SkEvents are used to communicate type-safe information to SkEventSinks. + SkEventSinks (including SkViews) each have a unique ID, which is stored + in an event. This ID is used to target the event once it has been "posted". +*/ +class SkEvent { +public: + /** Default construct, creating an empty event. + */ + SkEvent(); + /** Construct a new event with the specified type. + */ + explicit SkEvent(const SkString& type); + /** Construct a new event with the specified type. + */ + explicit SkEvent(const char type[]); + /** Construct a new event by copying the fields from the src event. + */ + SkEvent(const SkEvent& src); + ~SkEvent(); + +// /** Return the event's type (will never be nil) */ +// const char* getType() const; + /** Copy the event's type into the specified SkString parameter */ + void getType(SkString* str) const; + /** Returns true if the event's type matches exactly the specified type (case sensitive) */ + bool isType(const SkString& str) const; + /** Returns true if the event's type matches exactly the specified type (case sensitive) */ + bool isType(const char type[], size_t len = 0) const; + /** Set the event's type to the specified string. + In XML, use the "type" attribute. + */ + void setType(const SkString&); + /** Set the event's type to the specified string. + In XML, use the "type" attribute. + */ + void setType(const char type[], size_t len = 0); + + /** Return the event's unnamed 32bit field. Default value is 0 */ + U32 getFast32() const { return f32; } + /** Set the event's unnamed 32bit field. In XML, use + the subelement <data fast32=... /> + */ + void setFast32(uint32_t x) { f32 = x; } + + /** Return true if the event contains the named 32bit field, and return the field + in value (if value is non-nil). If there is no matching named field, return false + and ignore the value parameter. + */ + bool findS32(const char name[], int32_t* value = nil) const { return fMeta.findS32(name, value); } + /** Return true if the event contains the named SkScalar field, and return the field + in value (if value is non-nil). If there is no matching named field, return false + and ignore the value parameter. + */ + bool findScalar(const char name[], SkScalar* value = nil) const { return fMeta.findScalar(name, value); } + /** Return true if the event contains the named SkScalar field, and return the fields + in value[] (if value is non-nil), and return the number of SkScalars in count (if count is non-nil). + If there is no matching named field, return false and ignore the value and count parameters. + */ + const SkScalar* findScalars(const char name[], int* count, SkScalar values[] = nil) const { return fMeta.findScalars(name, count, values); } + /** Return the value of the named string field, or if no matching named field exists, return nil. + */ + const char* findString(const char name[]) const { return fMeta.findString(name); } + /** Return true if the event contains the named pointer field, and return the field + in value (if value is non-nil). If there is no matching named field, return false + and ignore the value parameter. + */ + bool findPtr(const char name[], void** value) const { return fMeta.findPtr(name, value); } + bool findBool(const char name[], bool* value) const { return fMeta.findBool(name, value); } + + /** Returns true if ethe event contains the named 32bit field, and if it equals the specified value */ + bool hasS32(const char name[], int32_t value) const { return fMeta.hasS32(name, value); } + /** Returns true if ethe event contains the named SkScalar field, and if it equals the specified value */ + bool hasScalar(const char name[], SkScalar value) const { return fMeta.hasScalar(name, value); } + /** Returns true if ethe event contains the named string field, and if it equals (using strcmp) the specified value */ + bool hasString(const char name[], const char value[]) const { return fMeta.hasString(name, value); } + /** Returns true if ethe event contains the named pointer field, and if it equals the specified value */ + bool hasPtr(const char name[], void* value) const { return fMeta.hasPtr(name, value); } + bool hasBool(const char name[], bool value) const { return fMeta.hasBool(name, value); } + + /** Add/replace the named 32bit field to the event. In XML use the subelement <data name=... s32=... /> */ + void setS32(const char name[], int32_t value) { fMeta.setS32(name, value); } + /** Add/replace the named SkScalar field to the event. In XML use the subelement <data name=... scalar=... /> */ + void setScalar(const char name[], SkScalar value) { fMeta.setScalar(name, value); } + /** Add/replace the named SkScalar[] field to the event. */ + SkScalar* setScalars(const char name[], int count, const SkScalar values[] = nil) { return fMeta.setScalars(name, count, values); } + /** Add/replace the named string field to the event. In XML use the subelement <data name=... string=... */ + void setString(const char name[], const SkString& value) { fMeta.setString(name, value.c_str()); } + /** Add/replace the named string field to the event. In XML use the subelement <data name=... string=... */ + void setString(const char name[], const char value[]) { fMeta.setString(name, value); } + /** Add/replace the named pointer field to the event. There is no XML equivalent for this call */ + void setPtr(const char name[], void* value) { fMeta.setPtr(name, value); } + void setBool(const char name[], bool value) { fMeta.setBool(name, value); } + + /** Return the underlying metadata object */ + SkMetaData& getMetaData() { return fMeta; } + /** Return the underlying metadata object */ + const SkMetaData& getMetaData() const { return fMeta; } + + void tron() { SkDEBUGCODE(fDebugTrace = true;) } + void troff() { SkDEBUGCODE(fDebugTrace = false;) } + bool isDebugTrace() const + { +#ifdef SK_DEBUG + return fDebugTrace; +#else + return false; +#endif + } + + /** Call this to initialize the event from the specified XML node */ + void inflate(const SkDOM&, const SkDOM::Node*); + + SkDEBUGCODE(void dump(const char title[] = nil);) + + /** Post the specified event to the event queue, targeting the specified eventsink, with an optional + delay. The event must be dynamically allocated for this. It cannot be a global or on the stack. + After this call, ownership is transfered to the system, so the caller must not retain + the event's ptr. Returns false if the event could not be posted (which means it will have been deleted). + */ + static bool Post(SkEvent* evt, SkEventSinkID targetID, SkMSec delay = 0); + /** Post the specified event to the event queue, targeting the specified eventsink, to be delivered on/after the + specified millisecond time. The event must be dynamically allocated for this. It cannot be a global or on the stack. + After this call, ownership is transfered to the system, so the caller must not retain + the event's ptr. Returns false if the event could not be posted (which means it will have been deleted). + */ + static bool PostTime(SkEvent* evt, SkEventSinkID targetID, SkMSec time); + + /** Helper method for calling SkEvent::PostTime(this, ...), where the caller specifies a delay. + The real "time" will be computed automatically by sampling the clock and adding its value + to delay. + */ + bool post(SkEventSinkID sinkID, SkMSec delay = 0) + { + return SkEvent::Post(this, sinkID, delay); + } + + void postTime(SkEventSinkID sinkID, SkMSec time) + { + SkEvent::PostTime(this, sinkID, time); + } + + /////////////////////////////////////////////// + /** Porting layer must call these functions **/ + /////////////////////////////////////////////// + + /** Global initialization function for the SkEvent system. Should be called exactly + once before any other event method is called, and should be called after the + call to SkGraphics::Init(). + */ + static void Init(); + /** Global cleanup function for the SkEvent system. Should be called exactly once after + all event methods have been called, and should be called before calling SkGraphics::Term(). + */ + static void Term(); + + /** Call this to process one event from the queue. If it returns true, there are more events + to process. + */ + static bool ProcessEvent(); + /** Call this whenever the requested timer has expired (requested by a call to SetQueueTimer). + It will post any delayed events whose time as "expired" onto the event queue. + It may also call SignalQueueTimer() and SignalNonEmptyQueue(). + */ + static void ServiceQueueTimer(); + + //////////////////////////////////////////////////// + /** Porting layer must implement these functions **/ + //////////////////////////////////////////////////// + + /** Called whenever an SkEvent is posted to an empty queue, so that the OS + can be told to later call Dequeue(). + */ + static void SignalNonEmptyQueue(); + /** Called whenever the delay until the next delayed event changes. If zero is + passed, then there are no more queued delay events. + */ + static void SignalQueueTimer(SkMSec delay); + +#ifndef SK_USE_WXWIDGETS +#ifdef SK_BUILD_FOR_WIN + static bool WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); +#elif defined(SK_BUILD_FOR_UNIXx) + static U32 HandleTimer(U32, void*); + static bool WndProc(Display*, Window, XEvent&); +#endif +#else + // Don't know yet what this will be + //static bool CustomEvent(); +#endif + +private: + SkMetaData fMeta; + mutable char* fType; // may be characters with low bit set to know that it is not a pointer + U32 f32; + SkDEBUGCODE(bool fDebugTrace;) + + // these are for our implementation of the event queue + SkEventSinkID fTargetID; + SkMSec fTime; + SkEvent* fNextEvent; // either in the delay or normal event queue + void initialize(const char* type, size_t typeLen); + + static bool Enqueue(SkEvent* evt); + static SkMSec EnqueueTime(SkEvent* evt, SkMSec time); + static SkEvent* Dequeue(SkEventSinkID* targetID); + static bool QHasEvents(); +}; + +#endif + diff --git a/include/graphics/SkEventSink.h b/include/graphics/SkEventSink.h new file mode 100644 index 0000000000..6772f034c9 --- /dev/null +++ b/include/graphics/SkEventSink.h @@ -0,0 +1,87 @@ +#ifndef SkEventSink_DEFINED +#define SkEventSink_DEFINED + +#include "SkRefCnt.h" +#include "SkEvent.h" + +struct SkTagList; + +/** \class SkEventSink + + SkEventSink is the base class for all objects that receive SkEvents. +*/ +class SkEventSink : public SkRefCnt { +public: + SkEventSink(); + virtual ~SkEventSink(); + + /** Returns this eventsink's unique ID. Use this to post SkEvents to + this eventsink. + */ + SkEventSinkID getSinkID() const { return fID; } + + /** Call this to pass an event to this object for processing. Returns true if the + event was handled. + */ + bool doEvent(const SkEvent&); + /** Returns true if the sink (or one of its subclasses) understands the event as a query. + If so, the sink may modify the event to communicate its "answer". + */ + bool doQuery(SkEvent* query); + + /** Add sinkID to the list of listeners, to receive events from calls to sendToListeners() + and postToListeners(). If sinkID already exists in the listener list, no change is made. + */ + void addListenerID(SkEventSinkID sinkID); + /** Copy listeners from one event sink to another, typically from parent to child. + @param from the event sink to copy the listeners from + */ + void copyListeners(const SkEventSink& from); + /** Remove sinkID from the list of listeners. If sinkID does not appear in the list, + no change is made. + */ + void removeListenerID(SkEventSinkID); + /** Returns true if there are 1 or more listeners attached to this eventsink + */ + bool hasListeners() const; + /** Posts a copy of evt to each of the eventsinks in the lisener list. + */ + void postToListeners(const SkEvent& evt, SkMSec delay = 0); + + enum EventResult { + kHandled_EventResult, //!< the eventsink returned true from its doEvent method + kNotHandled_EventResult, //!< the eventsink returned false from its doEvent method + kSinkNotFound_EventResult //!< no matching eventsink was found for the event's getSink(). + }; + /** DoEvent handles searching for an eventsink object that matches the targetID. + If one is found, it calls the sink's doEvent method, returning + either kHandled_EventResult or kNotHandled_EventResult. If no matching + eventsink is found, kSinkNotFound_EventResult is returned. + */ + static EventResult DoEvent(const SkEvent&, SkEventSinkID targetID); + + /** Returns the matching eventsink, or nil if not found + */ + static SkEventSink* FindSink(SkEventSinkID); + +protected: + /** Override this to handle events in your subclass. Be sure to call the inherited version + for events that you don't handle. + */ + virtual bool onEvent(const SkEvent&); + virtual bool onQuery(SkEvent*); + + SkTagList* findTagList(U8CPU tag) const; + void addTagList(SkTagList*); + void removeTagList(U8CPU tag); + +private: + SkEventSinkID fID; + SkTagList* fTagHead; + + // for our private link-list + SkEventSink* fNextSink; +}; + +#endif + diff --git a/include/graphics/SkFlattenable.h b/include/graphics/SkFlattenable.h new file mode 100644 index 0000000000..b68a3613a4 --- /dev/null +++ b/include/graphics/SkFlattenable.h @@ -0,0 +1,23 @@ +#ifndef SkFlattenable_DEFINED +#define SkFlattenable_DEFINED + +#include "SkRefCnt.h" +#include "SkBuffer.h" + +/** \class SkFlattenable + + SkFlattenable is the base class for objects that need to be flattened + into a data stream for either transport or as part of the key to the + font cache. +*/ +// This class is not exported to java. +class SkFlattenable : public SkRefCnt { +public: + typedef SkFlattenable* (*Factory)(SkRBuffer&); + + virtual Factory getFactory(); + virtual void flatten(SkWBuffer&); +}; + +#endif + diff --git a/include/graphics/SkFontCodec.h b/include/graphics/SkFontCodec.h new file mode 100644 index 0000000000..5dc873868e --- /dev/null +++ b/include/graphics/SkFontCodec.h @@ -0,0 +1,20 @@ +#ifndef SkFontCodec_DEFINED +#define SkFontCodec_DEFINED + +#include "SkSFNT.h" + +class SkFontCodec { +public: + static void Compress(SkSFNT& font, const char fileName[]); + + /* Format is [count] + [instruction, bitcount] * count + Allocated with sk_malloc() + */ + static U8* BuildInstrHuffmanTable(SkSFNT&); + static U8* BuildOutlineHuffmanTable(SkSFNT& font); + + SkDEBUGCODE(static void UnitTest();) +}; + +#endif + diff --git a/include/graphics/SkFontHost.h b/include/graphics/SkFontHost.h new file mode 100644 index 0000000000..3b3eacda88 --- /dev/null +++ b/include/graphics/SkFontHost.h @@ -0,0 +1,43 @@ +#ifndef SkFontHost_DEFINED +#define SkFontHost_DEFINED + +#include "SkScalerContext.h" +#include "SkTypeface.h" + +class SkDescriptor; + +/** \class SkFontHost + + This class is ported to each environment. It is responsible for bridging the gap + between SkTypeface and the resulting platform-specific instance of SkScalerContext. +*/ +class SkFontHost { +public: + /** Return a subclass of SkTypeface, one that can be used by your scalaracontext + (returned by SkFontHost::CreateScalarContext). + 1) If family is nil, use name. + 2) If name is nil, use family. + 3) If both are nil, use default family. + */ + static SkTypeface* CreateTypeface(const SkTypeface* family, const char name[], SkTypeface::Style); + /** Given a typeface (or nil), return the number of bytes needed to flatten it + into a buffer, for the purpose of communicating information to the + scalercontext. If buffer is nil, then ignore it but still return the number + of bytes that would be written. + */ + static uint32_t FlattenTypeface(const SkTypeface* face, void* buffer); + /** Return a subclass of SkScalarContext + */ + static SkScalerContext* CreateScalerContext(const SkDescriptor* desc); + + enum ScalerContextID { + kMissing_ScalerContextID = SK_UnknownAuxScalerContextID, + kMax_ScalerContextID = SK_MaxAuxScalerContextID + }; + static ScalerContextID FindScalerContextIDForUnichar(int32_t unichar); + + static SkScalerContext* CreateScalerContextFromID(ScalerContextID, const SkScalerContext::Rec&); +}; + +#endif + diff --git a/include/graphics/SkGlobals.h b/include/graphics/SkGlobals.h new file mode 100644 index 0000000000..8ac0d31079 --- /dev/null +++ b/include/graphics/SkGlobals.h @@ -0,0 +1,51 @@ +#ifndef SkGlobals_DEFINED +#define SkGlobals_DEFINED + +#include "SkThread.h" + +class SkGlobals { +public: + class Rec { + public: + virtual ~Rec(); + private: + Rec* fNext; + U32 fTag; + + friend class SkGlobals; + }; + + /** Look for a matching Rec for the specified tag. If one is found, return it. + If one is not found, if create_proc is nil, return nil, else + call the proc, and if it returns a Rec, add it to the global list + and return it. + + create_proc can NOT call back into SkGlobals::Find (it would deadlock) + */ + static Rec* Find(U32 tag, Rec* (*create_proc)()); + /** Helper for Find, when you want to assert that the Rec is already in the list + */ + static Rec* Get(U32 tag) + { + Rec* rec = SkGlobals::Find(tag, nil); + SkASSERT(rec); + return rec; + } + + // used by porting layer + struct BootStrap { + SkMutex fMutex; + Rec* fHead; + }; + +private: + static void Init(); + static void Term(); + friend class SkGraphics; + + // This last function is implemented in the porting layer + static BootStrap& GetBootStrap(); +}; + +#endif + diff --git a/include/graphics/SkGradientShader.h b/include/graphics/SkGradientShader.h new file mode 100644 index 0000000000..7e5a722f2a --- /dev/null +++ b/include/graphics/SkGradientShader.h @@ -0,0 +1,57 @@ +#ifndef SkGradientShader_DEFINED +#define SkGradientShader_DEFINED + +#include "SkShader.h" + +class SkUnitMapper; + +/** \class SkGradientShader + + SkGradientShader hosts factories for creating subclasses of SkShader that + render linear and radial gradients. +*/ +class SkGradientShader : public SkShader { +public: + /** Returns a shader that generates a linear gradient between the two + specified points. + <p /> + CreateLinear returns a shader with a reference count of 1. + The caller should decrement the shader's reference count when done with the shader. + It is an error for count to be < 2. + @param pts The start and end points for the gradient. + @param colors The array[count] of colors, to be distributed between the two points + @param pos May be NULL. array[count] of SkScalars, or NULL, of the relative position of + each corresponding color in the colors array. If this is NULL, + the the colors are distributed evenly between the start and end point. + @param count Must be >=2. The number of colors (and pos if not NULL) entries. + @param mode The tiling mode + @param mapper May be NULL. Callback to modify the spread of the colors. + */ + static SkShader* CreateLinear( const SkPoint pts[2], + const SkColor colors[], const SkScalar pos[], int count, + TileMode mode, + SkUnitMapper* mapper = NULL); + + /** Returns a shader that generates a radial gradient given the center and radius. + <p /> + CreateRadial returns a shader with a reference count of 1. + The caller should decrement the shader's reference count when done with the shader. + It is an error for colorCount to be < 2, or for radius to be <= 0. + @param center The center of the circle for this gradient + @param radius Must be positive. The radius of the circle for this gradient + @param colors The array[count] of colors, to be distributed between the center and edge of the circle + @param pos May be NULL. The array[count] of SkScalars, or NULL, of the relative position of + each corresponding color in the colors array. If this is NULL, + the the colors are distributed evenly between the center and edge of the circle. + @param count Must be >= 2. The number of colors (and pos if not NULL) entries + @param mode The tiling mode + @param mapper May be NULL. Callback to modify the spread of the colors. + */ + static SkShader* CreateRadial( const SkPoint& center, SkScalar radius, + const SkColor colors[], const SkScalar pos[], int count, + TileMode mode, + SkUnitMapper* mapper = NULL); +}; + +#endif + diff --git a/include/graphics/SkGraphics.h b/include/graphics/SkGraphics.h new file mode 100644 index 0000000000..96c3f11539 --- /dev/null +++ b/include/graphics/SkGraphics.h @@ -0,0 +1,30 @@ +#ifndef SkGraphics_DEFINED +#define SkGraphics_DEFINED + +#include "SkTypes.h" + +class SkGraphics { +public: + static void Init(bool runUnitTests); + static void Term(); + + /** Call this if the heap that the graphics engine uses is low on memory. + It will attempt to free some of its caches. Returns true if it was + able to, or false if it could do nothing. + + This may be called from any thread, and guarantees not to call + new or sk_malloc (though it will hopefully call delete and/or sk_free). + It also will never throw an exception. + */ + static bool FreeCaches(size_t bytesNeeded); + +private: + /** This is automatically called by SkGraphics::Init(), and must be + implemented by the host OS. This allows the host OS to register a callback + with the C++ runtime to call SkGraphics::FreeCaches() + */ + static void InstallNewHandler(); +}; + +#endif + diff --git a/include/graphics/SkImageDecoder.h b/include/graphics/SkImageDecoder.h new file mode 100644 index 0000000000..e5c7f3cb02 --- /dev/null +++ b/include/graphics/SkImageDecoder.h @@ -0,0 +1,130 @@ +#ifndef SkImageDecoder_DEFINED +#define SkImageDecoder_DEFINED + +#include "SkBitmap.h" +#include "SkBitmapRef.h" + +class SkStream; + +/** \class SkImageDecoder + + Base class for decoding compressed images into a SkBitmap +*/ +class SkImageDecoder { +public: + virtual ~SkImageDecoder(); + + /** Decode the image stored in the specified file, and store the result + in bitmap. Return true for success or false on failure. + + If pref is kNo_Config, then the decoder is free to choose the most natural + config given the image data. If pref something other than kNo_Config, + the decoder will attempt to decode the image into that format, unless + there is a conflict (e.g. the image has per-pixel alpha and the bitmap's + config does not support that), in which case the decoder will choose a + closest match configuration. + */ + static bool DecodeFile(const char file[], SkBitmap* bitmap, + SkBitmap::Config pref = SkBitmap::kNo_Config); + /** Decode the image stored in the specified memory buffer, and store the result + in bitmap. Return true for success or false on failure. + + If pref is kNo_Config, then the decoder is free to choose the most natural + config given the image data. If pref something other than kNo_Config, + the decoder will attempt to decode the image into that format, unless + there is a conflict (e.g. the image has per-pixel alpha and the bitmap's + config does not support that), in which case the decoder will choose a + closest match configuration. + */ + static bool DecodeMemory(const void* buffer, size_t size, SkBitmap* bitmap, + SkBitmap::Config pref = SkBitmap::kNo_Config); + /** Decode the image stored in the specified SkStream, and store the result + in bitmap. Return true for success or false on failure. + + If pref is kNo_Config, then the decoder is free to choose the most natural + config given the image data. If pref something other than kNo_Config, + the decoder will attempt to decode the image into that format, unless + there is a conflict (e.g. the image has per-pixel alpha and the bitmap's + config does not support that), in which case the decoder will choose a + closest match configuration. + */ + static bool DecodeStream(SkStream*, SkBitmap* bitmap, + SkBitmap::Config pref = SkBitmap::kNo_Config); + + /** Decode the image stored at the specified URL, and store the result + in bitmap. Return true for success or false on failure. The URL restrictions + are device dependent. On Win32 and WinCE, the URL may be ftp, http or + https. + + If pref is kNo_Config, then the decoder is free to choose the most natural + config given the image data. If pref something other than kNo_Config, + the decoder will attempt to decode the image into that format, unless + there is a conflict (e.g. the image has per-pixel alpha and the bitmap's + config does not support that), in which case the decoder will choose a + closest match configuration. + */ + static bool DecodeURL(const char url[], SkBitmap* bitmap, + SkBitmap::Config pref = SkBitmap::kNo_Config); + + /** Return the default config for the running device. + Currently this used as a suggestion to image decoders that need to guess + what config they should decode into. + Default is kNo_Config, but this can be changed with SetDeviceConfig() + */ + static SkBitmap::Config GetDeviceConfig(); + /** Set the default config for the running device. + Currently this used as a suggestion to image decoders that need to guess + what config they should decode into. + Default is kNo_Config. + This can be queried with GetDeviceConfig() + */ + static void SetDeviceConfig(SkBitmap::Config); + + /** @cond UNIT_TEST */ + SkDEBUGCODE(static void UnitTest();) + /** @endcond */ + +protected: + SkImageDecoder(); + + /** Given a stream, decode it into the specified bitmap. + If the decoder can decompress the image, it should call setConfig() on the bitmap, + and then call allocPixels(), which will allocated offscreen memory for the pixels. + It can then set the pixels with the decompressed image. If the image cannot be + decompressed, return false and leave the bitmap unchanged. + */ + virtual bool onDecode(SkStream*, SkBitmap* bitmap, SkBitmap::Config pref) = 0; + +private: + static SkImageDecoder* Factory(SkStream*); +}; + +#ifdef SK_SUPPORT_IMAGE_ENCODE + +class SkWStream; + +class SkImageEncoder { +public: + enum Type { + kJPEG_Type, + kPNG_Type + }; + static SkImageEncoder* Create(Type); + + virtual ~SkImageEncoder(); + + /* Quality ranges from 0..100 */ + + bool encodeFile(const char file[], const SkBitmap&, int quality = 80); + bool encodeStream(SkWStream*, const SkBitmap&, int quality = 80); + +protected: + virtual bool onEncode(SkWStream*, const SkBitmap&, int quality) = 0; +}; + +#endif /* SK_SUPPORT_IMAGE_ENCODE */ + +/////////////////////////////////////////////////////////////////////// + +#endif + diff --git a/include/graphics/SkImageView.h b/include/graphics/SkImageView.h new file mode 100644 index 0000000000..78ee5b79d1 --- /dev/null +++ b/include/graphics/SkImageView.h @@ -0,0 +1,59 @@ +#ifndef SkImageView_DEFINED +#define SkImageView_DEFINED + +#include "SkView.h" +#include "SkString.h" + +class SkAnimator; +class SkBitmap; +struct SkMatrix; + +class SkImageView : public SkView { +public: + SkImageView(); + virtual ~SkImageView(); + + void getUri(SkString*) const; + void setUri(const char []); + void setUri(const SkString&); + + + enum ScaleType { + kMatrix_ScaleType, + kFitXY_ScaleType, + kFitStart_ScaleType, + kFitCenter_ScaleType, + kFitEnd_ScaleType + }; + ScaleType getScaleType() const { return (ScaleType)fScaleType; } + void setScaleType(ScaleType); + + bool getImageMatrix(SkMatrix*) const; + void setImageMatrix(const SkMatrix*); + +protected: + // overrides + virtual bool onEvent(const SkEvent&); + virtual void onDraw(SkCanvas*); + virtual void onInflate(const SkDOM&, const SkDOMNode*); + +private: + SkString fUri; + SkMatrix* fMatrix; // nil or copy of caller's matrix ,,,,, + union { + SkAnimator* fAnim; + SkBitmap* fBitmap; + } fData; + U8 fScaleType; + SkBool8 fDataIsAnim; // as opposed to bitmap + SkBool8 fUriIsValid; + + void onUriChange(); + bool getDataBounds(SkRect* bounds); + bool freeData(); + bool ensureUriIsLoaded(); + + typedef SkView INHERITED; +}; + +#endif diff --git a/include/graphics/SkInterpolator.h b/include/graphics/SkInterpolator.h new file mode 100644 index 0000000000..c224c8cd66 --- /dev/null +++ b/include/graphics/SkInterpolator.h @@ -0,0 +1,98 @@ +#ifndef SkInterpolator_DEFINED +#define SkInterpolator_DEFINED + +#include "SkMath.h" + +class SkInterpolatorBase { +public: + enum Result { + kNormal_Result, + kFreezeStart_Result, + kFreezeEnd_Result + }; + static SkScalar Blend(SkScalar t, SkScalar blend); +protected: + SkInterpolatorBase(); + ~SkInterpolatorBase(); +public: + void reset(int elemCount, int frameCount); + + /** Return the start and end time for this interpolator. + If there are no key frames, return false. + @param startTime If no nil, returns the time (in milliseconds) of the + first keyframe. If there are no keyframes, this parameter + is ignored (left unchanged). + @param endTime If no nil, returns the time (in milliseconds) of the + last keyframe. If there are no keyframes, this parameter + is ignored (left unchanged). + @return True if there are key frames, or false if there are none. + */ + bool getDuration(SkMSec* startTime, SkMSec* endTime) const; + + + /** Set the whether the repeat is mirrored. + @param If true, the odd repeats interpolate from the last key frame and the first. + */ + void setMirror(bool mirror) { fFlags = SkToU8(fFlags & ~kMirror | (int) mirror); } + + /** Set the repeat count. The repeat count may be fractional. + @param repeatCount Multiplies the total time by this scalar. + */ + void setRepeatCount(SkScalar repeatCount) { fRepeat = repeatCount; } + + /** Set the whether the repeat is mirrored. + @param If true, the odd repeats interpolate from the last key frame and the first. + */ + void setReset(bool reset) { fFlags = SkToU8(fFlags & ~kReset | (int) reset); } + + Result timeToT(SkMSec time, SkScalar* T, int* index, SkBool* exact) const; +protected: + enum Flags { + kMirror = 1, + kReset = 2 + }; + static SkScalar ComputeRelativeT(SkMSec time, SkMSec prevTime, SkMSec nextTime, SkScalar blend); + S16 fFrameCount; + U8 fElemCount; + U8 fFlags; + SkScalar fRepeat; + struct SkTimeCode { + SkMSec fTime; + SkScalar fBlend; + }; + SkTimeCode* fTimes; // pointer into fStorage + void* fStorage; +#ifdef SK_DEBUG + SkTimeCode(* fTimesArray)[10]; +#endif +}; + +class SkInterpolator : public SkInterpolatorBase { +public: + SkInterpolator(); + SkInterpolator(int elemCount, int frameCount); + void reset(int elemCount, int frameCount); + + /** Add or replace a key frame, copying the values[] data into the interpolator. + @param index The index of this frame (frames must be ordered by time) + @param time The millisecond time for this frame + @param values The array of values [elemCount] for this frame. The data is copied + into the interpolator. + @param blend A positive scalar specifying how to blend between this and the next key frame. + [0...1) is a cubic lag/log/lag blend (slow to change at the beginning and end) + 1 is a linear blend (default) + */ + bool setKeyFrame(int index, SkMSec time, const SkScalar values[], SkScalar blend = SK_Scalar1); + Result timeToValues(SkMSec time, SkScalar values[]) const; + SkDEBUGCODE(static void UnitTest();) +private: + SkScalar* fValues; // pointer into fStorage +#ifdef SK_DEBUG + SkScalar(* fScalarsArray)[10]; +#endif + typedef SkInterpolatorBase INHERITED; +}; + + +#endif + diff --git a/include/graphics/SkJS.h b/include/graphics/SkJS.h new file mode 100644 index 0000000000..89fa4d747a --- /dev/null +++ b/include/graphics/SkJS.h @@ -0,0 +1,31 @@ +#include "SkTypes.h" +#include "SkWindow.h" + +extern "C" { + typedef long JSWord; + typedef JSWord jsword; + typedef jsword jsval; + typedef struct JSRuntime JSRuntime; + typedef struct JSContext JSContext; + typedef struct JSObject JSObject; +} + +class SkString; + +class SkJS : public SkOSWindow { +public: + SkJS(void* hwnd); + ~SkJS(); + SkBool EvaluateScript(const char* script, jsval* rVal); + SkBool ValueToString(jsval value, SkString* string); +#ifdef SK_DEBUG + static void Test(void* hwnd); +#endif +protected: + void InitializeDisplayables(const SkBitmap& , JSContext *, JSObject *, JSObject *); + void DisposeDisplayables(); + JSRuntime *fRuntime; + JSContext *fContext; + JSObject *fGlobal; +}; + diff --git a/include/graphics/SkKey.h b/include/graphics/SkKey.h new file mode 100644 index 0000000000..4a1297c9a6 --- /dev/null +++ b/include/graphics/SkKey.h @@ -0,0 +1,47 @@ +#ifndef SkKey_DEFINED +#define SkKey_DEFINED + +#include "SkTypes.h" + +enum SkKey { + //reordering these to match android.app.KeyEvent + kNONE_SkKey, //corresponds to android's UNKNOWN + + kLeftSoftKey_SkKey, + kRightSoftKey_SkKey, + + kHome_SkKey, //!< the home key - added to match android + kBack_SkKey, //!< (CLR) + kSend_SkKey, //!< the green (talk) key + kEnd_SkKey, //!< the red key + + k0_SkKey, + k1_SkKey, + k2_SkKey, + k3_SkKey, + k4_SkKey, + k5_SkKey, + k6_SkKey, + k7_SkKey, + k8_SkKey, + k9_SkKey, + kStar_SkKey, //!< the * key + kHash_SkKey, //!< the # key + + kUp_SkKey, + kDown_SkKey, + kLeft_SkKey, + kRight_SkKey, + + kOK_SkKey, //!< the center key + + kVolUp_SkKey, //!< volume up - match android + kVolDown_SkKey, //!< volume down - same + kPower_SkKey, //!< power button - same + kCamera_SkKey, //!< camera - same + + kSkKeyCount +}; + +#endif + diff --git a/include/graphics/SkLayerRasterizer.h b/include/graphics/SkLayerRasterizer.h new file mode 100644 index 0000000000..548968a02a --- /dev/null +++ b/include/graphics/SkLayerRasterizer.h @@ -0,0 +1,41 @@ +#ifndef SkLayerRasterizer_DEFINED +#define SkLayerRasterizer_DEFINED + +#include "SkRasterizer.h" +#include "SkDeque.h" +#include "SkScalar.h" + +class SkPaint; + +class SkLayerRasterizer : public SkRasterizer { +public: + SkLayerRasterizer(); + virtual ~SkLayerRasterizer(); + + void addLayer(const SkPaint& paint) + { + this->addLayer(paint, 0, 0); + } + void addLayer(const SkPaint& paint, SkScalar dx, SkScalar dy); + + // overrides from SkFlattenable + virtual Factory getFactory(); + virtual void flatten(SkWBuffer&); + +protected: + SkLayerRasterizer(SkRBuffer&); + + // override from SkRasterizer + virtual bool onRasterize(const SkPath& path, const SkMatrix& matrix, + const SkRect16* clipBounds, + SkMask* mask, SkMask::CreateMode mode); + +private: + SkDeque fLayers; + + static SkFlattenable* CreateProc(SkRBuffer&); + + typedef SkRasterizer INHERITED; +}; + +#endif diff --git a/include/graphics/SkMask.h b/include/graphics/SkMask.h new file mode 100644 index 0000000000..8fdf4614f9 --- /dev/null +++ b/include/graphics/SkMask.h @@ -0,0 +1,65 @@ +#ifndef SkMask_DEFINED +#define SkMask_DEFINED + +#include "SkRect.h" + +/** \class SkMask + SkMask is used to describe alpha bitmaps, either 1bit, 8bit, or + the 3-channel 3D format. These are passed to SkMaskFilter objects. +*/ +struct SkMask { + enum Format { + kBW_Format, //!< 1bit per pixel mask (e.g. monochrome) + kA8_Format, //!< 8bits per pixel mask (e.g. antialiasing) + k3D_Format //!< 3 8bit per pixl planes: alpha, mul, add + }; + + uint8_t* fImage; + SkRect16 fBounds; + uint16_t fRowBytes; + uint8_t fFormat; // Format + + /** Return the byte size of the mask, assuming only 1 plane. + Does not account for k3D_Format. For that, use computeFormatImageSize() + */ + size_t computeImageSize() const; + /** Return the byte size of the mask, taking into account + any extra planes (e.g. k3D_Format). + */ + size_t computeTotalImageSize() const; + + /** Returns the address of the byte that holds the specified bit. + Asserts that the mask is kBW_Format, and that x,y are in range. + x,y are in the same coordiate space as fBounds. + */ + uint8_t* getAddr1(int x, int y) const + { + SkASSERT(fFormat == kBW_Format); + SkASSERT(fBounds.contains(x, y)); + SkASSERT(fImage != nil); + return fImage + ((x - fBounds.fLeft) >> 3) + (y - fBounds.fTop) * fRowBytes; + } + /** Returns the address of the specified byte. + Asserts that the mask is kA8_Format, and that x,y are in range. + x,y are in the same coordiate space as fBounds. + */ + uint8_t* getAddr(int x, int y) const + { + SkASSERT(fFormat != kBW_Format); + SkASSERT(fBounds.contains(x, y)); + SkASSERT(fImage != nil); + return fImage + x - fBounds.fLeft + (y - fBounds.fTop) * fRowBytes; + } + + static uint8_t* AllocImage(size_t bytes); + static void FreeImage(uint8_t* image); + + enum CreateMode { + kJustComputeBounds_CreateMode, //!< compute bounds and return + kJustRenderImage_CreateMode, //!< render into preallocate mask + kComputeBoundsAndRenderImage_CreateMode //!< compute bounds, alloc image and render into it + }; +}; + +#endif + diff --git a/include/graphics/SkMaskFilter.h b/include/graphics/SkMaskFilter.h new file mode 100644 index 0000000000..f844e36250 --- /dev/null +++ b/include/graphics/SkMaskFilter.h @@ -0,0 +1,83 @@ +#ifndef SkMaskFilter_DEFINED +#define SkMaskFilter_DEFINED + +#include "SkFlattenable.h" +#include "SkMask.h" + +class SkBlitter; +class SkBounder; +class SkMatrix; +class SkPath; +class SkRegion; + +/** \class SkMaskFilter + + SkMaskFilter is the base class for object that perform transformations on + an alpha-channel mask before drawing it. A subclass of SkMaskFilter may be + installed into a SkPaint. Once there, each time a primitive is drawn, it + is first scan converted into a SkMask::kA8_Format mask, and handed to the + filter, calling its filterMask() method. If this returns true, then the + new mask is used to render into the device. + + Blur and emboss are implemented as subclasses of SkMaskFilter. +*/ +class SkMaskFilter : public SkFlattenable { +public: + SkMaskFilter() {} + + /** Returns the format of the resulting mask that this subclass will return + when its filterMask() method is called. + */ + virtual SkMask::Format getFormat() = 0; + + /** Create a new mask by filter the src mask. + If src.fImage == nil, then do not allocate or create the dst image + but do fill out the other fields in dstMask. + If you do allocate a dst image, use SkMask::AllocImage() + If this returns false, dst mask is ignored. + @param dst the result of the filter. If src.fImage == nil, dst should not allocate its image + @param src the original image to be filtered. + @param matrix the CTM + @param margin if not nil, return the buffer dx/dy need when calculating the effect. Used when + drawing a clipped object to know how much larger to allocate the src before + applying the filter. + @return true if the dst mask was correctly created. + */ + virtual bool filterMask(SkMask* dst, const SkMask& src, const SkMatrix&, SkPoint16* margin); + + /** Helper method that, given a path in device space, will rasterize it into a kA8_Format mask + and then call filterMask(). If this returns true, the specified blitter will be called + to render that mask. Returns false if filterMask() returned false. + This method is not exported to java. + */ + bool filterPath(const SkPath& devPath, const SkMatrix& devMatrix, + const SkRegion& devClip, SkBounder*, SkBlitter* blitter); + +protected: + // empty for now, but lets get our subclass to remember to init us for the future + SkMaskFilter(SkRBuffer&) {} +}; + +/** \class SkAutoMaskImage + + Stack class used to manage the fImage buffer in a SkMask. + When this object loses scope, the buffer is freed with SkMask::FreeImage(). +*/ +class SkAutoMaskImage { +public: + SkAutoMaskImage(SkMask* mask, bool alloc) + { + if (alloc) + mask->fImage = SkMask::AllocImage(mask->computeImageSize()); + fImage = mask->fImage; + } + ~SkAutoMaskImage() + { + SkMask::FreeImage(fImage); + } +private: + uint8_t* fImage; +}; + +#endif + diff --git a/include/graphics/SkMetaData.h b/include/graphics/SkMetaData.h new file mode 100644 index 0000000000..b3b80eb787 --- /dev/null +++ b/include/graphics/SkMetaData.h @@ -0,0 +1,138 @@ +#ifndef SkMetaData_DEFINED +#define SkMetaData_DEFINED + +#include "SkMath.h" + +class SkMetaData { +public: + SkMetaData(); + SkMetaData(const SkMetaData& src); + ~SkMetaData(); + + SkMetaData& operator=(const SkMetaData& src); + + void reset(); + + bool findS32(const char name[], int32_t* value = nil) const; + bool findScalar(const char name[], SkScalar* value = nil) const; + const SkScalar* findScalars(const char name[], int* count, SkScalar values[] = nil) const; + const char* findString(const char name[]) const; + bool findPtr(const char name[], void** value = nil) const; + bool findBool(const char name[], bool* value = nil) const; + + bool hasS32(const char name[], int32_t value) const + { + int32_t v; + return this->findS32(name, &v) && v == value; + } + bool hasScalar(const char name[], SkScalar value) const + { + SkScalar v; + return this->findScalar(name, &v) && v == value; + } + bool hasString(const char name[], const char value[]) const + { + const char* v = this->findString(name); + return v == nil && value == nil || + v != nil && value != nil && !strcmp(v, value); + } + bool hasPtr(const char name[], void* value) const + { + void* v; + return this->findPtr(name, &v) && v == value; + } + bool hasBool(const char name[], bool value) const + { + bool v; + return this->findBool(name, &v) && v == value; + } + + void setS32(const char name[], int32_t value); + void setScalar(const char name[], SkScalar value); + SkScalar* setScalars(const char name[], int count, const SkScalar values[] = nil); + void setString(const char name[], const char value[]); + void setPtr(const char name[], void* value); + void setBool(const char name[], bool value); + + bool removeS32(const char name[]); + bool removeScalar(const char name[]); + bool removeString(const char name[]); + bool removePtr(const char name[]); + bool removeBool(const char name[]); + + SkDEBUGCODE(static void UnitTest();) + + enum Type { + kS32_Type, + kScalar_Type, + kString_Type, + kPtr_Type, + kBool_Type, + + kTypeCount + }; + + struct Rec; + class Iter; + friend class Iter; + + class Iter { + public: + Iter() : fRec(nil) {} + Iter(const SkMetaData&); + + /** Reset the iterator, so that calling next() will return the first + data element. This is done implicitly in the constructor. + */ + void reset(const SkMetaData&); + + /** Each time next is called, it returns the name of the next data element, + or nil when there are no more elements. If non-nil is returned, then the + element's type is returned (if not nil), and the number of data values + is returned in count (if not nil). + */ + const char* next(Type*, int* count); + + private: + Rec* fRec; + }; + +public: + struct Rec { + Rec* fNext; + uint16_t fDataCount; // number of elements + uint8_t fDataLen; // sizeof a single element +#ifdef SK_DEBUG + Type fType; +#else + uint8_t fType; +#endif + +#ifdef SK_DEBUG + const char* fName; + union { + int32_t fS32; + SkScalar fScalar; + const char* fString; + void* fPtr; + bool fBool; + } fData; +#endif + + const void* data() const { return (this + 1); } + void* data() { return (this + 1); } + const char* name() const { return (const char*)this->data() + fDataLen * fDataCount; } + char* name() { return (char*)this->data() + fDataLen * fDataCount; } + + static Rec* Alloc(size_t); + static void Free(Rec*); + }; + Rec* fRec; + + const Rec* find(const char name[], Type) const; + void* set(const char name[], const void* data, size_t len, Type, int count); + bool remove(const char name[], Type); +}; + +#endif + diff --git a/include/graphics/SkNinePatch.h b/include/graphics/SkNinePatch.h new file mode 100644 index 0000000000..5cc48c15ab --- /dev/null +++ b/include/graphics/SkNinePatch.h @@ -0,0 +1,21 @@ +#ifndef SkNinePatch_DEFINED +#define SkNinePatch_DEFINED + +#include "SkRect.h" + +class SkBitmap; +class SkCanvas; +class SkPaint; + +class SkNinePatch { +public: + static void Draw(SkCanvas* canvas, const SkRect& dst, + const SkBitmap& bitmap, const SkRect16& margin, + const SkPaint* paint = NULL); + + static void Draw(SkCanvas* canvas, const SkRect& dst, + const SkBitmap& bitmap, int cx, int cy, + const SkPaint* paint = NULL); +}; + +#endif diff --git a/include/graphics/SkOSFile.h b/include/graphics/SkOSFile.h new file mode 100644 index 0000000000..6baef7d0dc --- /dev/null +++ b/include/graphics/SkOSFile.h @@ -0,0 +1,70 @@ +// Copyright Skia Inc. 2004 - 2005 +// +#ifndef SkOSFile_DEFINED +#define SkOSFile_DEFINED + +#include "SkString.h" + +#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_UNIX) + #include <dirent.h> +#endif + +struct SkFILE; + +enum SkFILE_Flags { + kRead_SkFILE_Flag = 0x01, + kWrite_SkFILE_Flag = 0x02 +}; + +SkFILE* sk_fopen(const char path[], SkFILE_Flags); +void sk_fclose(SkFILE*); + +size_t sk_fgetsize(SkFILE*); +/** Return true if the file could seek back to the beginning +*/ +bool sk_frewind(SkFILE*); + +size_t sk_fread(void* buffer, size_t byteCount, SkFILE*); +size_t sk_fwrite(const void* buffer, size_t byteCount, SkFILE*); +void sk_fflush(SkFILE*); + +int sk_fseek( SkFILE*, size_t, int ); +size_t sk_ftell( SkFILE* ); + +class SkOSFile { +public: + class Iter { + public: + Iter(); + Iter(const char path[], const char suffix[] = nil); + ~Iter(); + + void reset(const char path[], const char suffix[] = nil); + bool next(SkString* name, bool getDir = false); + + private: +#ifdef SK_BUILD_FOR_WIN + HANDLE fHandle; + U16* fPath16; +#elif defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_UNIX) + DIR* fDIR; + SkString fPath, fSuffix; +#endif + }; +}; + +class SkUTF16_Str { +public: + SkUTF16_Str(const char src[]); + ~SkUTF16_Str() + { + sk_free(fStr); + } + const U16* get() const { return fStr; } + +private: + U16* fStr; +}; + +#endif + diff --git a/include/graphics/SkOSMenu.h b/include/graphics/SkOSMenu.h new file mode 100644 index 0000000000..4cf52e7697 --- /dev/null +++ b/include/graphics/SkOSMenu.h @@ -0,0 +1,39 @@ +#ifndef SkOSMenu_DEFINED +#define SkOSMenu_DEFINED + +#include "SkEvent.h" +#include "SkTDArray.h" + +class SkOSMenu { +public: + explicit SkOSMenu(const char title[]); + ~SkOSMenu(); + + const char* getTitle() const { return fTitle; } + + void appendItem(const char title[], const char eventType[], S32 eventData); + + // called by SkOSWindow when it receives an OS menu event + int countItems() const; + const char* getItem(int index, U32* cmdID) const; + + SkEvent* createEvent(U32 os_cmd); + +private: + const char* fTitle; + + struct Item { + const char* fTitle; + const char* fEventType; + U32 fEventData; + U32 fOSCmd; // internal + }; + SkTDArray<Item> fItems; + + // illegal + SkOSMenu(const SkOSMenu&); + SkOSMenu& operator=(const SkOSMenu&); +}; + +#endif + diff --git a/include/graphics/SkOSSound.h b/include/graphics/SkOSSound.h new file mode 100644 index 0000000000..2e4262be66 --- /dev/null +++ b/include/graphics/SkOSSound.h @@ -0,0 +1,20 @@ +#ifndef SkOSSound_DEFINED +#define SkOSSound_DEFINED + +#include "SkTypes.h" + +class SkOSSound { +public: + static void Play(const char path[]); + static void Pause(); + static void Resume(); + static bool TogglePause(); // returns true if we are now playing, or false if we're now paused + static void Stop(); + + // volume runs from 0 (silent) to 0xFF (max-volume) + static U8 GetVolume(); + static void SetVolume(U8CPU volume); +}; + +#endif + diff --git a/include/graphics/SkOSWindow_Mac.h b/include/graphics/SkOSWindow_Mac.h new file mode 100644 index 0000000000..b37b652063 --- /dev/null +++ b/include/graphics/SkOSWindow_Mac.h @@ -0,0 +1,32 @@ +#ifndef SkOSWindow_Mac_DEFINED +#define SkOSWindow_Mac_DEFINED + +#include "SkWindow.h" + +class SkOSWindow : public SkWindow { +public: + SkOSWindow(void* hwnd); + + void* getHWND() const { return fHWND; } + void updateSize(); + + static bool PostEvent(SkEvent* evt, SkEventSinkID, SkMSec delay); + + static pascal OSStatus SkOSWindow::EventHandler( EventHandlerCallRef inHandler, EventRef inEvent, void* userData ); + +protected: + // overrides from SkWindow + virtual void onHandleInval(const SkRect16&); + // overrides from SkView + virtual void onAddMenu(const SkOSMenu*); + +private: + void* fHWND; + + void doPaint(void* ctx); + + typedef SkWindow INHERITED; +}; + +#endif + diff --git a/include/graphics/SkOSWindow_Unix.h b/include/graphics/SkOSWindow_Unix.h new file mode 100644 index 0000000000..5c1b1eb0e8 --- /dev/null +++ b/include/graphics/SkOSWindow_Unix.h @@ -0,0 +1,44 @@ +#ifndef SkOSWindow_Unix_DEFINED +#define SkOSWindow_Unix_DEFINED + +#include "SkWindow.h" +#include <X11/Xlib.h> + +struct SkUnixWindow { + Display* fDisplay; + Window fWin; + size_t fOSWin; +}; + +class SkOSWindow : public SkWindow { +public: + SkOSWindow(Display* display, Window win); + + void* getHWND() const { return (void*)fUnixWindow.fWin; } + void* getDisplay() const { return (void*)fUnixWindow.fDisplay; } + void* getUnixWindow() const { return (void*)&fUnixWindow; } + void setSize(int width, int height); + void updateSize(); + + static bool PostEvent(SkEvent* evt, SkEventSinkID, SkMSec delay); + + static bool WndProc(SkUnixWindow* w, XEvent &e); + +protected: + // overrides from SkWindow + virtual void onHandleInval(const SkRect16&); + // overrides from SkView + virtual void onAddMenu(const SkOSMenu*); + +private: + SkUnixWindow fUnixWindow; + + void doPaint(); + + void* fMBar; + + typedef SkWindow INHERITED; +}; + +#endif + diff --git a/include/graphics/SkOSWindow_Win.h b/include/graphics/SkOSWindow_Win.h new file mode 100644 index 0000000000..b213a057a3 --- /dev/null +++ b/include/graphics/SkOSWindow_Win.h @@ -0,0 +1,43 @@ +#ifndef SkOSWindow_Win_DEFINED +#define SkOSWindow_Win_DEFINED + +#include "SkWindow.h" + +class SkOSWindow : public SkWindow { +public: + SkOSWindow(void* hwnd); + + void* getHWND() const { return fHWND; } + void setSize(int width, int height); + void updateSize(); + + static bool PostEvent(SkEvent* evt, SkEventSinkID, SkMSec delay); + + static bool WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); + static bool SkOSWindow::QuitOnDeactivate(HWND hWnd); + + enum { + SK_WM_SkEvent = WM_APP + 1000, + SK_WM_SkTimerID = 0xFFFF // just need a non-zero value + }; + +protected: + virtual bool quitOnDeactivate() { return true; } + + // overrides from SkWindow + virtual void onHandleInval(const SkRect16&); + // overrides from SkView + virtual void onAddMenu(const SkOSMenu*); + +private: + void* fHWND; + + void doPaint(void* ctx); + + HMENU fMBar; + + typedef SkWindow INHERITED; +}; + +#endif + diff --git a/include/graphics/SkOSWindow_wxwidgets.h b/include/graphics/SkOSWindow_wxwidgets.h new file mode 100644 index 0000000000..1f526c0076 --- /dev/null +++ b/include/graphics/SkOSWindow_wxwidgets.h @@ -0,0 +1,37 @@ +/* + * SkOSWindow_wxwidgets.h + * wxwidgets + * + * Created by phanna on 12/14/05. + * Copyright 2005 __MyCompanyName__. All rights reserved. + * + */ + +#ifndef SkOSWindow_wxwidgets_DEFINED +#define SkOSWindow_wxwidgets_DEFINED + +#include "SkWindow.h" +#include "wx/frame.h" + +class SkOSWindow: public SkWindow +{ +public: + SkOSWindow(); + SkOSWindow(const wxString& title, int x, int y, int width, int height); + ~SkOSWindow(); + + wxFrame* getWXFrame() const { return fFrame; } + + void updateSize(); + +protected: + virtual void onHandleInval(const SkRect16&); + virtual void onAddMenu(const SkOSMenu*); + +private: + wxFrame* fFrame; + typedef SkWindow INHERITED; + +}; + +#endif
\ No newline at end of file diff --git a/include/graphics/SkPaint.h b/include/graphics/SkPaint.h new file mode 100644 index 0000000000..5c1a0364a0 --- /dev/null +++ b/include/graphics/SkPaint.h @@ -0,0 +1,630 @@ +#ifndef SkPaint_DEFINED +#define SkPaint_DEFINED + +#include "SkColor.h" +#include "SkMath.h" +#include "SkPorterDuff.h" + +class SkColorFilter; +class SkGlyphCache; +class SkMaskFilter; +class SkMatrix; +class SkPath; +class SkPathEffect; +class SkRasterizer; +class SkShader; +class SkTextLayout; +class SkTypeface; +class SkXfermode; + +typedef SkUnichar (*SkUnicodeWalkerProc)(const char** text); + +/** \class SkPaint + + The SkPaint class holds the style and color information about how to draw geometries, text and bitmaps. +*/ +class SkPaint { +public: + SkPaint(); + SkPaint(const SkPaint& paint); + ~SkPaint(); + + SkPaint& operator=(const SkPaint&); + + friend int operator==(const SkPaint& a, const SkPaint& b); + friend int operator!=(const SkPaint& a, const SkPaint& b) { return !(a == b); } + + /** Restores the paint to its initial settings. + */ + void reset(); + + /** FlagShift enum specifies the amount to bit-shift for a given flag setting. + Can be used to slide a boolean value into the correct position (e.g. + flags |= isAntiAlias << kAntiAlias_Shift; + */ + enum FlagShift { + kAntiAlias_Shift, //!< bit position for the flag enabling antialiasing + kLinearText_Shift , //!< bit position for the flag enabling linear-text (no gridding) + kUnderlineText_Shift, //!< bit position for the flag enabling underline text + kStrikeThruText_Shift, //!< bit position for the flag enabling strike-thru text + kFakeBoldText_Shift, //!< bit position for the flag enabling fake-bold text + + kFlagShiftCount + }; + + /** FlagMask enum specifies the bit values that are stored in the paint's flags. + */ + enum FlagMask { + kAntiAlias_Mask = 1 << kAntiAlias_Shift, //!< bit mask for the flag enabling antialiasing + kLinearText_Mask = 1 << kLinearText_Shift, //!< bit mask for the flag enabling linear-text (no gridding) + kUnderlineText_Mask = 1 << kUnderlineText_Shift, //!< bit mask for the flag enabling underline text + kStrikeThruText_Mask= 1 << kStrikeThruText_Shift, //!< bit mask for the flag enabling strike-thru text + kFakeBoldText_Mask = 1 << kFakeBoldText_Shift, //!< bit mask for the flag enabling fake-bold text + + kAllFlagMasks = (1 << kFlagShiftCount) - 1 + }; + + /** Return the paint's flags. Use the FlagMask enum to test flag values. + @return the paint's flags (see enums ending in _Mask for bit masks) + */ + uint32_t getFlags() const { return fFlags; } + /** Set the paint's flags. Use the FlagMask enum to specific flag values. + @param flags The new flag bits for the paint (see enums ending in _Mask for bit masks) + */ + void setFlags(uint32_t flags); + + /** Helper for getFlags(), returning true if kAntiAlias_Mask bit is set + @return true if the antialias bit is set in the paint's flags. + */ + bool isAntiAliasOn() const { return SkToBool(this->getFlags() & kAntiAlias_Mask); } + /** Helper for setFlags(), setting or clearing the kAntiAlias_Mask bit + @param aa true to set the antialias bit in the flags, false to clear it + */ + void setAntiAliasOn(bool aa); + /** Helper for getFlags(), returning true if kLinearText_Mask bit is set + @return true if the lineartext bit is set in the paint's flags + */ + bool isLinearTextOn() const { return SkToBool(this->getFlags() & kLinearText_Mask); } + /** Helper for setFlags(), setting or clearing the kLinearText_Mask bit + @param linearText true to set the linearText bit in the paint's flags, false to clear it. + */ + void setLinearTextOn(bool linearText); + /** Helper for getFlags(), returning true if kUnderlineText_Mask bit is set + @return true if the underlineText bit is set in the paint's flags. + */ + bool isUnderlineTextOn() const { return SkToBool(this->getFlags() & kUnderlineText_Mask); } + /** Helper for setFlags(), setting or clearing the kUnderlineText_Mask bit + @param underlineText true to set the underlineText bit in the paint's flags, false to clear it. + */ + void setUnderlineTextOn(bool underlineText); + /** Helper for getFlags(), returning true if kStrikeThruText_Mask bit is set + @return true if the strikeThruText bit is set in the paint's flags. + */ + bool isStrikeThruTextOn() const { return SkToBool(this->getFlags() & kStrikeThruText_Mask); } + /** Helper for setFlags(), setting or clearing the kStrikeThruText_Mask bit + @param strikeThruText true to set the strikeThruText bit in the paint's flags, false to clear it. + */ + void setStrikeThruTextOn(bool strikeThruText); + /** Helper for getFlags(), returning true if kFakeBoldText_Mask bit is set + @return true if the fakeBoldText bit is set in the paint's flags. + */ + bool isFakeBoldTextOn() const { return SkToBool(this->getFlags() & kFakeBoldText_Mask); } + /** Helper for setFlags(), setting or clearing the kStrikeThruText_Mask bit + @param fakeBoldText true to set the fakeBoldText bit in the paint's flags, false to clear it. + */ + void setFakeBoldTextOn(bool fakeBoldText); + + /** Styles apply to rect, oval, path, and text. + Bitmaps are always drawn in "fill", and lines are always drawn in "stroke" + */ + enum Style { + kFill_Style, //!< fill with the paint's color + kStroke_Style, //!< stroke with the paint's color + kStrokeAndFill_Style, //!< fill and stroke with the paint's color + + kStyleCount, + kDefault_Style = kFill_Style, //!< the default style setting in the paint + }; + /** Return the paint's style, used for controlling how primitives' + geometries are interpreted (except for drawBitmap, which always assumes + kFill_Style). + @return the paint's style setting (Fill, Stroke, StrokeAndFill) + */ + Style getStyle() const { return (Style)fStyle; } + /** Set the paint's style, used for controlling how primitives' + geometries are interpreted (except for drawBitmap, which always assumes + Fill). + @param style The new style to set in the paint (Fill, Stroke, StrokeAndFill) + */ + void setStyle(Style style); + + /** Return the paint's color. Note that the color is a 32bit value containing alpha + as well as r,g,b. This 32bit value is not premultiplied, meaning that + its alpha can be any value, regardless of the values of r,g,b. + @return the paint's color (and alpha). + */ + SkColor getColor() const { return fColor; } + /** Helper to getColor() that just returns the color's alpha value. + @return the alpha component of the paint's color. + */ + uint8_t getAlpha() const { return SkToU8(SkColorGetA(fColor)); } + /** Set the paint's color. Note that the color is a 32bit value containing alpha + as well as r,g,b. This 32bit value is not premultiplied, meaning that + its alpha can be any value, regardless of the values of r,g,b. + @param color The new color (including alpha) to set in the paint. + */ + void setColor(SkColor color); + /** Helper to setColor(), that only assigns the color's alpha value, leaving its + r,g,b values unchanged. + @param a set the alpha component (0..255) of the paint's color. + */ + void setAlpha(U8CPU a); + /** Helper to setColor(), that takes a,r,g,b and constructs the color value using SkColorSetARGB() + @param a The new alpha component (0..255) of the paint's color. + @param r The new red component (0..255) of the paint's color. + @param g The new green component (0..255) of the paint's color. + @param b The new blue component (0..255) of the paint's color. + */ + void setARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b); + + /** Return the width for stroking. + <p /> + A value of 0 strokes in hairline mode. + Hairlines always draws a single pixel independent of the canva's matrix. + @return the paint's stroke width, used whenever the paint's style is Stroke or StrokeAndFill. + */ + SkScalar getStrokeWidth() const { return fWidth; } + /** Set the width for stroking. + Pass 0 to stroke in hairline mode. + Hairlines always draws a single pixel independent of the canva's matrix. + @param width set the paint's stroke width, used whenever the paint's style is Stroke or StrokeAndFill. + */ + void setStrokeWidth(SkScalar width); + + /** Return the paint's stroke miter value. This is used to control the behavior + of miter joins when the joins angle is sharp. + @return the paint's miter limit, used whenever the paint's style is Stroke or StrokeAndFill. + */ + SkScalar getStrokeMiter() const { return fMiterLimit; } + /** Set the paint's stroke miter value. This is used to control the behavior + of miter joins when the joins angle is sharp. This value must be >= 0. + @param miter set the miter limit on the paint, used whenever the paint's style is Stroke or StrokeAndFill. + */ + void setStrokeMiter(SkScalar miter); + + /** Cap enum specifies the settings for the paint's strokecap. This is the treatment + that is applied to the beginning and end of each non-closed contour (e.g. lines). + */ + enum Cap { + kButt_Cap, //!< begin and end a contour with no extension + kRound_Cap, //!< begin and end a contour with a semi-circle extension + kSquare_Cap, //!< begin and end a contour with a half square extension + + kCapCount, + kDefault_Cap = kButt_Cap + }; + + /** Join enum specifies the settings for the paint's strokejoin. This is the treatment + that is applied to corners in paths and rectangles. + */ + enum Join { + kMiter_Join, //!< connect path segments with a sharp join (respects miter-limit) + kRound_Join, //!< connect path segments with a round join + kBevel_Join, //!< connect path segments with a flat bevel join + + kJoinCount, + kDefault_Join = kMiter_Join + }; + + /** Return the paint's stroke cap type, controlling how the start and end of stroked lines and paths + are treated. + @return the line cap style for the paint, used whenever the paint's style is Stroke or StrokeAndFill. + */ + Cap getStrokeCap() const { return (Cap)fCapType; } + /** Set the paint's stroke cap type. + @param cap set the paint's line cap style, used whenever the paint's style is Stroke or StrokeAndFill. + */ + void setStrokeCap(Cap cap); + + /** Return the paint's stroke join type. + @return the paint's line join style, used whenever the paint's style is Stroke or StrokeAndFill. + */ + Join getStrokeJoin() const { return (Join)fJoinType; } + /** Set the paint's stroke join type. + @param join set the paint's line join style, used whenever the paint's style is Stroke or StrokeAndFill. + */ + void setStrokeJoin(Join join); + + enum FilterType { + kNo_FilterType, //!< draw bitmaps using nearest-neighbor sampling + kBilinear_FilterType, //!< draw bitmaps using bilinear sampling + + kFilterTypeCount + }; + /** Return the paint's bitmap filter type. This setting affects drawBitmap() and bitmaps + that appear inside a bitmap shader. + @return the paint's filter type, used when drawing bitmaps. + */ + FilterType getFilterType() const { return (FilterType)fFilterType; } + /** Set the paint's bitmap filter type. This setting affects drawBitmap() and bitmaps + that appear inside a bitmap shader. + @param filterType set the new filter type on the paint, used when drawing a bitmap + */ + void setFilterType(FilterType filterType); + + /** Get the paint's shader object. + <p /> + The shader's reference count is not affected. + @return the paint's shader (or NULL) + */ + SkShader* getShader() const { return fShader; } + /** Set or clear the shader object. + <p /> + Pass NULL to clear any previous shader. + As a convenience, the parameter passed is also returned. + If a previous shader exists, its reference count is decremented. + If shader is not NULL, its reference count is incremented. + @param shader May be NULL. the new shader to be installed in the paint + @return shader + */ + SkShader* setShader(SkShader* shader); + + /** Get the paint's colorfilter (or NULL). If there is a colorfilter, its reference + count is not changed. + @return the paint's colorfilter (or NULL) + */ + SkColorFilter* getColorFilter() const { return fColorFilter; } + /** Set or clear the paint's colorfilter, returning the parameter. + <p /> + If the paint already has a filter, its reference count is decremented. + If filter is not NULL, its reference count is incremented. + @param filter May be NULL. The new filter to be installed in the paint + @return filter + */ + SkColorFilter* setColorFilter(SkColorFilter* filter); + + /** Get the paint's xfermode object. + <p /> + The xfermode's reference count is not affected. + @return the paint's xfermode (or NULL) + */ + SkXfermode* getXfermode() const { return fXfermode; } + /** Set or clear the xfermode object. + <p /> + Pass NULL to clear any previous xfermode. + As a convenience, the parameter passed is also returned. + If a previous xfermode exists, its reference count is decremented. + If xfermode is not NULL, its reference count is incremented. + @param xfermode May be NULL. The new xfermode to be installed in the paint + @return xfermode + */ + SkXfermode* setXfermode(SkXfermode* xfermode); + + /** Helper for setXfermode, passing the corresponding xfermode object returned from the + PorterDuff factory. + @param mode The porter-duff mode used to create an xfermode for the paint. + @return the resulting xfermode object (or NULL if the mode is SrcOver) + */ + SkXfermode* setPorterDuffXfermode(SkPorterDuff::Mode mode); + + /** Get the paint's patheffect object. + <p /> + The patheffect reference count is not affected. + @return the paint's patheffect (or NULL) + */ + SkPathEffect* getPathEffect() const { return fPathEffect; } + /** Set or clear the patheffect object. + <p /> + Pass NULL to clear any previous patheffect. + As a convenience, the parameter passed is also returned. + If a previous patheffect exists, its reference count is decremented. + If patheffect is not NULL, its reference count is incremented. + @param effect May be NULL. The new patheffect to be installed in the paint + @return effect + */ + SkPathEffect* setPathEffect(SkPathEffect* effect); + + /** Get the paint's maskfilter object. + <p /> + The maskfilter reference count is not affected. + @return the paint's maskfilter (or NULL) + */ + SkMaskFilter* getMaskFilter() const { return fMaskFilter; } + /** Set or clear the maskfilter object. + <p /> + Pass NULL to clear any previous maskfilter. + As a convenience, the parameter passed is also returned. + If a previous maskfilter exists, its reference count is decremented. + If maskfilter is not NULL, its reference count is incremented. + @param maskfilter May be NULL. The new maskfilter to be installed in the paint + @return maskfilter + */ + SkMaskFilter* setMaskFilter(SkMaskFilter* maskfilter); + + // These attributes are for text/fonts + + /** Get the paint's typeface object. + <p /> + The typeface object identifies which font to use when drawing or measuring text. + The typeface reference count is not affected. + @return the paint's typeface (or NULL) + */ + SkTypeface* getTypeface() const { return fTypeface; } + /** Set or clear the typeface object. + <p /> + Pass NULL to clear any previous typeface. + As a convenience, the parameter passed is also returned. + If a previous typeface exists, its reference count is decremented. + If typeface is not NULL, its reference count is incremented. + @param typeface May be NULL. The new typeface to be installed in the paint + @return typeface + */ + SkTypeface* setTypeface(SkTypeface* typeface); + + /** Get the paint's textlayout (or NULL). + <p /> + The textlayout can modify the spacing between letters and words when measured/drawn. + The textlayout reference count is not affected. + @return the paint's textlayout (or NULL) + */ + SkTextLayout* getTextLayout() const { return fTextLayout; } + /** Set or clear the textlayout object. + <p /> + Pass NULL to clear any previous textlayout. + As a convenience, the parameter passed is also returned. + If a previous layout exists in the paint, its reference count is decremented. + If layout is not NULL, its reference count is incremented. + @param layout May be NULL. The new layout to be installed in the paint. + @return layout + */ + SkTextLayout* setTextLayout(SkTextLayout* layout); + + /** Get the paint's rasterizer (or NULL). + <p /> + The raster controls/modifies how paths/text are turned into alpha masks. + @return the paint's rasterizer (or NULL) + */ + SkRasterizer* getRasterizer() const { return fRasterizer; } + /** Set or clear the rasterizer object. + <p /> + Pass NULL to clear any previous rasterizer. + As a convenience, the parameter passed is also returned. + If a previous rasterizer exists in the paint, its reference count is decremented. + If r is not NULL, its reference count is incremented. + @param rasterizer May be NULL. The new rasterizer to be installed in the paint. + @return rasterizer + */ + SkRasterizer* setRasterizer(SkRasterizer* rasterizer); + + enum Align { + kLeft_Align, + kCenter_Align, + kRight_Align, + + kAlignCount + }; + /** Return the paint's Align value for drawing text. + @return the paint's Align value for drawing text. + */ + Align getTextAlign() const { return (Align)fTextAlign; } + /** Set the paint's text alignment. + @param align set the paint's Align value for drawing text. + */ + void setTextAlign(Align align); + + /** Return the paint's text size. + @return the paint's text size. + */ + SkScalar getTextSize() const { return fTextSize; } + /** Set the paint's text size. This value must be > 0 + @param textSize set the paint's text size. + */ + void setTextSize(SkScalar textSize); + + /** Return the paint's horizontal scale factor for text. The default value + is 1.0. + @return the paint's scale factor in X for drawing/measuring text + */ + SkScalar getTextScaleX() const { return fTextScaleX; } + /** Set the paint's horizontal scale factor for text. The default value + is 1.0. Values > 1.0 will stretch the text wider. Values < 1.0 will + stretch the text narrower. + @param scaleX set the paint's scale factor in X for drawing/measuring text. + */ + void setTextScaleX(SkScalar scaleX); + + /** Return the paint's horizontal skew factor for text. The default value + is 0. + @return the paint's skew factor in X for drawing text. + */ + SkScalar getTextSkewX() const { return fTextSkewX; } + /** Set the paint's horizontal skew factor for text. The default value + is 0. For approximating oblique text, use values around -0.25. + @param skewX set the paint's skew factor in X for drawing text. + */ + void setTextSkewX(SkScalar skewX); + + /** Return the width of the utf8 text. + @param utf8 Address of the utf8 text + @param length Number of bytes of utf8 text to measure + @param above If not NULL, returns the distance above the baseline (ascent) + @param below If not NULL, returns the distance below the baseline (descent) + @return The width of the utf8 text + */ + SkScalar measureText(const char utf8[], size_t length, + SkScalar* above, SkScalar* below) const; + /** Return the width of the utf16 text. + @param utf16 Address of the utf16 text + @param numberOf16BitValues Number of 16bit values to measure + @param above May be NULL. If not NULL, returns the distance above the baseline (ascent) + @param below May be NULL. If not NULL, returns the distance below the baseline (descent) + @return The width of the text + */ + SkScalar measureText16(const uint16_t utf16[], size_t numberOf16BitValues, + SkScalar* above, SkScalar* below) const; + /** Return the distance above (negative) the baseline (ascent) based on the current typeface and text size. + @return the distance above (negative) the baseline (ascent) based on the current typeface and text size. + */ + SkScalar ascent() const; + /** Return the distance below (positive) the baseline (descent) based on the current typeface and text size. + @return the distance below (positive) the baseline (descent) based on the current typeface and text size. + */ + SkScalar descent() const; + + /** Return the width of the utf8 text. + @param text The utf8 text to measure + @param byteLength The number of bytes of text to process + @return the measured width of the specified text. + */ + SkScalar measureText(const char text[], size_t byteLength) const + { + return this->measureText(text, byteLength, NULL, NULL); + } + /** Return the width of the utf16 text. + @param text The utf16 text to measure + @param numberOf16BitValues The number of 16bit values in text to process + @return the measured width of the specified text. + */ + SkScalar measureText16(const uint16_t text[], size_t numberOf16BitValues) const + { + return this->measureText16(text, numberOf16BitValues, NULL, NULL); + } + + /** Return the advance widths for the characters in the string. + @param text UTF8 text + @param byteLength number of bytes to read from the UTF8 text parameter + @param widths array of SkScalars to receive the advance widths of the characters. + May be NULL. If not NULL, must be at least a large as the number + of unichars in the specified text. + @return the number of unichars in the specified text. + */ + int getTextWidths(const char text[], size_t byteLength, SkScalar widths[]) const; + /** Return the advance widths for the characters in the string. + @param text UTF16 text + @param numberOf16BitValues number of 16bit values to read from the UTF16 text parameter + @param widths array of SkScalars to receive the advance widths of the characters. + May be NULL. If not NULL, must be at least a large as the number + of unichars in the specified text. + @return the number of unichars in the specified text. + */ + int getTextWidths16(const uint16_t text[], size_t numberOf16BitValues, SkScalar widths[]) const; + + /** Return the path (outline) for the specified text. + Note: just like SkCanvas::drawText, this will respect the Align setting in the paint. + */ + void getTextPath(const char text[], size_t length, SkScalar x, SkScalar y, SkPath* path) const; + + /** Return the path (outline) for the specified text. + Note: just like SkCanvas::drawText, this will respect the Align setting in the paint. + */ + void getText16Path(const uint16_t text[], size_t numberOf16BitValues, SkScalar x, SkScalar y, SkPath* path) const; + + /** Applies any/all effects (patheffect, stroking) to src, returning the result in dst. + The result is that drawing src with this paint will be the same as drawing dst + with a default paint (at least from the geometric perspective). + @param src input path + @param dst output path (may be the same as src) + @return true if the path should be filled, or false if it should be drawn with a hairline (width == 0) + */ + bool getFillPath(const SkPath& src, SkPath* dst) const; + +private: + SkTypeface* fTypeface; + SkScalar fTextSize; + SkScalar fTextScaleX; + SkScalar fTextSkewX; + + SkPathEffect* fPathEffect; + SkShader* fShader; + SkXfermode* fXfermode; + SkMaskFilter* fMaskFilter; + SkColorFilter* fColorFilter; + SkTextLayout* fTextLayout; + SkRasterizer* fRasterizer; + + SkColor fColor; + SkScalar fWidth; + SkScalar fMiterLimit; + unsigned fFlags : 5; + unsigned fFilterType : 2; + unsigned fTextAlign : 2; + unsigned fCapType : 2; + unsigned fJoinType : 2; + unsigned fStyle : 2; + + SkScalar privateMeasureText(SkUnicodeWalkerProc, const char text[], size_t byteLength, + SkScalar* above, SkScalar* below) const; + void privateGetTextPath(SkUnicodeWalkerProc, const char text[], size_t length, + SkScalar x, SkScalar y, SkPath* path) const; + int privateGetTextWidths(const char text[], size_t byteLength, + SkScalar widths[], SkUnicodeWalkerProc textProc) const; + + SkGlyphCache* detachCache(const SkMatrix*) const; + + friend class SkGlyphCache; + enum { + kCanonicalTextSizeForPaths = 64 + }; + friend class SkDraw; + friend class SkTextToPathIter; +}; + +class SkAutoRestorePaintFlags { +public: + SkAutoRestorePaintFlags(const SkPaint& paint, uint32_t newFlags) + { + SkASSERT(&paint); + fPaint = (SkPaint*)&paint; // remove constness + fOldFlags = paint.getFlags(); + fPaint->setFlags(newFlags); + } + ~SkAutoRestorePaintFlags() + { + fPaint->setFlags(fOldFlags); + } +private: + SkPaint* fPaint; + uint32_t fOldFlags; +}; + +////////////////////////////////////////////////////////////////////////// + +#include "SkPathEffect.h" + +/** \class SkStrokePathEffect + + SkStrokePathEffect simulates stroking inside a patheffect, allowing the caller to have explicit + control of when to stroke a path. Typically this is used if the caller wants to stroke before + another patheffect is applied (using SkComposePathEffect or SkSumPathEffect). +*/ +class SkStrokePathEffect : public SkPathEffect { +public: + SkStrokePathEffect(const SkPaint&); + SkStrokePathEffect(SkScalar width, SkPaint::Style, SkPaint::Join, SkPaint::Cap, SkScalar miterLimit = -1); + + // overrides + // This method is not exported to java. + virtual bool filterPath(SkPath* dst, const SkPath& src, SkScalar* width); + + // overrides for SkFlattenable + // This method is not exported to java. + virtual void flatten(SkWBuffer&); + // This method is not exported to java. + virtual Factory getFactory(); + +private: + SkScalar fWidth, fMiter; + uint8_t fStyle, fJoin, fCap; + + static SkFlattenable* CreateProc(SkRBuffer&); + SkStrokePathEffect(SkRBuffer&); + + typedef SkPathEffect INHERITED; + + // illegal + SkStrokePathEffect(const SkStrokePathEffect&); + SkStrokePathEffect& operator=(const SkStrokePathEffect&); +}; + +#endif + diff --git a/include/graphics/SkParse.h b/include/graphics/SkParse.h new file mode 100644 index 0000000000..9e513fe298 --- /dev/null +++ b/include/graphics/SkParse.h @@ -0,0 +1,29 @@ +#ifndef SkParse_DEFINED +#define SkParse_DEFINED + +#include "SkColor.h" +#include "SkMath.h" + +class SkParse { +public: + static int Count(const char str[]); // number of scalars or int values + static int Count(const char str[], char separator); + static const char* FindColor(const char str[], SkColor* value); + static const char* FindHex(const char str[], uint32_t* value); + static const char* FindMSec(const char str[], SkMSec* value); + static const char* FindNamedColor(const char str[], size_t len, SkColor* color); + static const char* FindS32(const char str[], int32_t* value); + static const char* FindScalar(const char str[], SkScalar* value); + static const char* FindScalars(const char str[], SkScalar value[], int count); + + static bool FindBool(const char str[], bool* value); + // return the index of str in list[], or -1 if not found + static int FindList(const char str[], const char list[]); +#ifdef SK_SUPPORT_UNITTEST + static void TestColor(); + static void UnitTest(); +#endif +}; + +#endif + diff --git a/include/graphics/SkParsePaint.h b/include/graphics/SkParsePaint.h new file mode 100644 index 0000000000..d5f7f68a82 --- /dev/null +++ b/include/graphics/SkParsePaint.h @@ -0,0 +1,18 @@ +#ifndef SkParsePaint_DEFINED +#define SkParsePaint_DEFINED + +#include "SkPaint.h" +#include "SkDOM.h" + +/** "color" color + "opacity" scalar [0..1] + "stroke-width" scalar (0...inf) + "text-size" scalar (0..inf) + "is-stroke" bool + "is-antialias" bool + "is-lineartext" bool +*/ +void SkPaint_Inflate(SkPaint*, const SkDOM&, const SkDOM::Node*); + +#endif + diff --git a/include/graphics/SkPath.h b/include/graphics/SkPath.h new file mode 100644 index 0000000000..b0a538d137 --- /dev/null +++ b/include/graphics/SkPath.h @@ -0,0 +1,373 @@ +#ifndef SkPath_DEFINED +#define SkPath_DEFINED + +#include "SkMatrix.h" +#include "SkTDArray.h" + +class SkString; + +/** \class SkPath + + The SkPath class encapsulates compound (multiple contour) geometric paths consisting + of straight line segments, quadratic curves, and cubic curves. +*/ +class SkPath { +public: + SkPath(); + SkPath(const SkPath&); + ~SkPath(); + + SkPath& operator=(const SkPath&); + + enum FillType { + kWinding_FillType, //!< Specifies that "inside" is computed by a non-zero sum of signed edge crossings + kEvenOdd_FillType //!< Specifies that "inside" is computed by an odd number of edge crossings + }; + /** Return the path's fill type. This is used to define how "inside" is computed. + The default value is kWinding_FillType. + @return the path's fill type + */ + FillType getFillType() const { return (FillType)fFillType; } + /** Set the path's fill type. This is used to define how "inside" is computed. + The default value is kWinding_FillType. + @param ft The new fill type for this path + */ + void setFillType(FillType ft) { fFillType = SkToU8(ft); } + + /** Clear any lines and curves from the path, making it empty. + This does NOT change the fill-type setting. + */ + void reset(); + /** Returns true if the path is empty (contains no lines or curves) + @return true if the path is empty (contains no lines or curves) + */ + bool isEmpty() const; + /** Returns true if the path specifies a rectangle. If so, and if rect is not nil, + set rect to the bounds of the path. If the path does not specify a rectangle, + return false and ignore rect. + @param rect If not nil, returns the bounds of the path if it specifies a rectangle + @return true if the path specifies a rectangle + */ + bool isRect(SkRect* rect) const; + /** Returns the number of points in the path. Up to max points are copied. + @param points If not null, receives up to max points + @param max The maximum number of points to copy into points + @return the actual number of points in the path + */ + int getPoints(SkPoint points[], int max) const; + //! Swap contents of this and other. Guaranteed not to throw + void swap(SkPath& other); + + enum BoundsType { + kFast_BoundsType, //!< compute the bounds of the path's control points, may be larger than with kExact_BoundsType, but may be faster to compute + kExact_BoundsType //!< compute the exact bounds of the path, may be smaller than with kFast_BoundsType, but may be slower to compute + }; + /** Compute the bounds of the path, and write the answer into bounds. If the path contains 0 or 1 points, + the bounds is set to (0,0,0,0) + @param bounds Returns the computed bounds of the path + @param btype Specifies if the computed bounds should be exact (slower) or approximate (faster) + */ + void computeBounds(SkRect* bounds, BoundsType btype) const; + + // Construction methods + + /** Hint to the path to prepare for adding more points. This can allow the path to more efficiently grow its storage. + @param extraPtCount The number of extra points that may be added to this path + */ + void incReserve(unsigned extraPtCount); + + /** Set the beginning of the next contour to the point (x,y). + @param x The x-coordinate of the start of a new contour + @param y The y-coordinate of the start of a new contour + */ + void moveTo(SkScalar x, SkScalar y); + /** Set the beginning of the next contour to the point + @param p The start of a new contour + */ + void moveTo(const SkPoint& p) + { + this->moveTo(p.fX, p.fY); + } + /** Set the beginning of the next contour relative to the last point on the previous + contour. If there is no previous contour, this is treated the same as moveTo(). + @param dx The amount to add to the x-coordinate of the end of the previous contour, to specify the start of a new contour + @param dy The amount to add to the y-coordinate of the end of the previous contour, to specify the start of a new contour + */ + void rMoveTo(SkScalar dx, SkScalar dy); + /** Add a line from the last point to the specified point (x,y). + If no moveTo() call has been made for this contour, the first point is automatically set to (0,0). + @param x The x-coordinate of the end of a line + @param y The y-coordinate of the end of a line + */ + void lineTo(SkScalar x, SkScalar y); + /** Add a line from the last point to the specified point. + If no moveTo() call has been made for this contour, the first point is automatically set to (0,0). + @param p The end of a line + */ + void lineTo(const SkPoint& p) + { + this->lineTo(p.fX, p.fY); + } + /** Same as lineTo, but the coordinates are considered relative to the last point on this + contour. If there is no previous point, then a moveTo(0,0) is inserted automatically. + @param dx The amount to add to the x-coordinate of the previous point on this contour, to specify a line + @param dy The amount to add to the y-coordinate of the previous point on this contour, to specify a line + */ + void rLineTo(SkScalar dx, SkScalar dy); + /** Add a quadratic bezier from the last point, approaching control point (x1,y1), and ending at (x2,y2). + If no moveTo() call has been made for this contour, the first point is automatically set to (0,0). + @param x1 The x-coordinate of the control point on a quadratic curve + @param y1 The y-coordinate of the control point on a quadratic curve + @param x2 The x-coordinate of the end point on a quadratic curve + @param y2 The y-coordinate of the end point on a quadratic curve + */ + void quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2); + /** Add a quadratic bezier from the last point, approaching control point p1, and ending at p2. + If no moveTo() call has been made for this contour, the first point is automatically set to (0,0). + @param p1 The control point on a quadratic curve + @param p2 The end point on a quadratic curve + */ + void quadTo(const SkPoint& p1, const SkPoint& p2) + { + this->quadTo(p1.fX, p1.fY, p2.fX, p2.fY); + } + /** Same as quadTo, but the coordinates are considered relative to the last point on this + contour. If there is no previous point, then a moveTo(0,0) is inserted automatically. + @param dx1 The amount to add to the x-coordinate of the last point on this contour, to specify the control point of a quadratic curve + @param dy1 The amount to add to the y-coordinate of the last point on this contour, to specify the control point of a quadratic curve + @param dx2 The amount to add to the x-coordinate of the last point on this contour, to specify the end point of a quadratic curve + @param dy2 The amount to add to the y-coordinate of the last point on this contour, to specify the end point of a quadratic curve + */ + void rQuadTo(SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2); + /** Add a cubic bezier from the last point, approaching control points (x1,y1) and (x2,y2), and ending at (x3,y3). + If no moveTo() call has been made for this contour, the first point is automatically set to (0,0). + @param x1 The x-coordinate of the 1st control point on a cubic curve + @param y1 The y-coordinate of the 1st control point on a cubic curve + @param x2 The x-coordinate of the 2nd control point on a cubic curve + @param y2 The y-coordinate of the 2nd control point on a cubic curve + @param x3 The x-coordinate of the end point on a cubic curve + @param y3 The y-coordinate of the end point on a cubic curve + */ + void cubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, SkScalar x3, SkScalar y3); + /** Add a cubic bezier from the last point, approaching control points p1 and p2, and ending at p3. + If no moveTo() call has been made for this contour, the first point is automatically set to (0,0). + @param p1 The 1st control point on a cubic curve + @param p2 The 2nd control point on a cubic curve + @param p3 The end point on a cubic curve + */ + void cubicTo(const SkPoint& p1, const SkPoint& p2, const SkPoint& p3) + { + this->cubicTo(p1.fX, p1.fY, p2.fX, p2.fY, p3.fX, p3.fY); + } + /** Same as cubicTo, but the coordinates are considered relative to the current point on this + contour. If there is no previous point, then a moveTo(0,0) is inserted automatically. + @param dx1 The amount to add to the x-coordinate of the last point on this contour, to specify the 1st control point of a cubic curve + @param dy1 The amount to add to the y-coordinate of the last point on this contour, to specify the 1st control point of a cubic curve + @param dx2 The amount to add to the x-coordinate of the last point on this contour, to specify the 2nd control point of a cubic curve + @param dy2 The amount to add to the y-coordinate of the last point on this contour, to specify the 2nd control point of a cubic curve + @param dx3 The amount to add to the x-coordinate of the last point on this contour, to specify the end point of a cubic curve + @param dy3 The amount to add to the y-coordinate of the last point on this contour, to specify the end point of a cubic curve + */ + void rCubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, SkScalar x3, SkScalar y3); + /** Close the current contour. If the current point is not equal to the first point of the contour, + a line segment is automatically added. + */ + void close(); + + enum Direction { + kCW_Direction, //!< clockwise direction for adding closed contours + kCCW_Direction //!< counter-clockwise direction for adding closed contours + }; + /** Add a closed rectangle contour to the path + @param rect The rectangle to add as a closed contour to the path + @param dir The direction to wind the rectangle's contour + */ + void addRect(const SkRect& rect, Direction dir = kCW_Direction); + /** Add a closed rectangle contour to the path + @param left The left side of a rectangle to add as a closed contour to the path + @param top The top of a rectangle to add as a closed contour to the path + @param right The right side of a rectangle to add as a closed contour to the path + @param bottom The bottom of a rectangle to add as a closed contour to the path + @param dir The direction to wind the rectangle's contour + */ + void addRect(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom, Direction dir = kCW_Direction); + /** Add a closed oval contour to the path + @param oval The bounds of the oval to add as a closed contour to the path + @param dir The direction to wind the oval's contour + */ + void addOval(const SkRect& oval, Direction dir = kCW_Direction); + /** Add a closed circle contour to the path + @param x The x-coordinate of the center of a circle to add as a closed contour to the path + @param y The y-coordinate of the center of a circle to add as a closed contour to the path + @param radius The radius of a circle to add as a closed contour to the path + @param dir The direction to wind the circle's contour + */ + void addCircle(SkScalar x, SkScalar y, SkScalar radius, Direction dir = kCW_Direction); + /** Add a closed round-rectangle contour to the path + @param rect The bounds of a round-rectangle to add as a closed contour to the path + @param rx The x-radius of the rounded corners on the round-rectangle + @param ry The y-radius of the rounded corners on the round-rectangle + @param dir The direction to wind the round-rectangle's contour + */ + void addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry, Direction dir = kCW_Direction); + /** Add a copy of src to the path, offset by (dx,dy) + @param src The path to add as a new contour + @param dx The amount to translate the path in X as it is added + @param dx The amount to translate the path in Y as it is added + */ + void addPath(const SkPath& src, SkScalar dx, SkScalar dy); + /** Add a copy of src to the path + */ + void addPath(const SkPath& src) { SkMatrix m; m.reset(); this->addPath(src, m); } + /** Add a copy of src to the path, transformed by matrix + @param src The path to add as a new contour + */ + void addPath(const SkPath& src, const SkMatrix& matrix); + + /** Offset the path by (dx,dy), returning true on success + @param dx The amount in the X direction to offset the entire path + @param dy The amount in the Y direction to offset the entire path + @param dst The translated path is written here + @return true + */ + bool offset(SkScalar dx, SkScalar dy, SkPath* dst) const; + /** Offset the path by (dx,dy), returning true on success + @param dx The amount in the X direction to offset the entire path + @param dy The amount in the Y direction to offset the entire path + @return true + */ + bool offset(SkScalar dx, SkScalar dy) + { + return this->offset(dx, dy, this); + } + /** Transform the points in this path by matrix, and write the answer into dst. + @param matrix The matrix to apply to the path + @param dst The transformed path is written here + @return true + */ + bool transform(const SkMatrix& matrix, SkPath* dst) const; + /** Transform the points in this path by matrix, and write the answer into dst. + @param matrix The matrix to apply to the path + @return true + */ + bool transform(const SkMatrix& matrix) + { + return this->transform(matrix, this); + } + + /** Return the last point on the path. If no points have been added, (0,0) is returned. + @param lastPt The last point on the path is returned here + */ + void getLastPt(SkPoint* lastPt) const; + /** Set the last point on the path. If no points have been added, moveTo(x,y) is automatically called. + @param x The new x-coordinate for the last point + @param y The new y-coordinate for the last point + */ + void setLastPt(SkScalar x, SkScalar y); + /** Set the last point on the path. If no points have been added, moveTo(p) is automatically called. + @param p The new location for the last point + */ + void setLastPt(const SkPoint& p) { this->setLastPt(p.fX, p.fY); } + + enum Verb { + kMove_Verb, //!< iter.next returns 1 point + kLine_Verb, //!< iter.next returns 2 points + kQuad_Verb, //!< iter.next returns 3 points + kCubic_Verb, //!< iter.next returns 4 points + kClose_Verb, //!< iter.next returns 1 point (the last point) + kDone_Verb //!< iter.next returns 0 points + }; + /** Iterate through all of the segments (lines, quadratics, cubics) of + each contours in a path. + */ + class Iter { + public: + Iter(); + Iter(const SkPath&, bool forceClose); + void setPath(const SkPath&, bool forceClose); + + /** Return the next verb in this iteration of the path. When all segments have been + visited, return kDone_Verb. + @param pts The point(s) representing the current verb and/or segment + @return The verb for the current segment + */ + Verb next(SkPoint pts[4]); + + /** If next() returns kLine_Verb, then this query returns + true if the line was the result of a close() command + (i.e. the end point is the initial moveto for this contour). + If next() returned a different verb, this returns an + undefined value. + @return If the last call to next() returned kLine_Verb, return true if it was + the result of an explicit close command. + */ + bool isCloseLine() const { return SkToBool(fCloseLine); } + + /** Returns true if the current contour is closed (i.e. has a kClose_Verb) + @return true if the current contour is closed (i.e. has a kClose_Verb) + */ + bool isClosedContour() const; + + private: + const SkPoint* fPts; + const uint8_t* fVerbs; + const uint8_t* fVerbStop; + SkPoint fMoveTo; + SkPoint fLastPt; + SkBool8 fForceClose; + SkBool8 fNeedClose; + SkBool8 fNeedMoveTo; + SkBool8 fCloseLine; + + bool cons_moveTo(SkPoint pts[1]); + Verb autoClose(SkPoint pts[2]); + }; + +#ifdef SK_DEBUG + /** @cond UNIT_TEST */ + void dump(bool forceClose, const char title[] = nil) const; + static void UnitTest(); + /** @endcond */ +#endif + + /** Return the number of bytes (padded to a multiple of 4) needed to + flatten the path into a block of memory. If bufferOrNil is not nil, + the path is written into it. The format of the buffer is private, + and can be used to create a new path by calling unflatten(). + */ + uint32_t flatten(void* bufferOrNil) const; + void unflatten(const void* buffer); + + /** Subdivide the path so that no segment is longer that dist. + If bendLines is true, then turn all line segments into curves. + If dst == nil, then the original path itself is modified (not const!) + */ + void subdivide(SkScalar dist, bool bendLines, SkPath* dst = nil) const; + + /** Return an SVG-compatible string of the path. + */ + void toString(SkString*) const; + +private: + SkTDArray<SkPoint> fPts; + SkTDArray<uint8_t> fVerbs; + uint8_t fFillType; + + friend class Iter; + void cons_moveto(); + + friend class SkPathStroker; + /* Append the first contour of path, ignoring path's initial point. + If no moveTo() call has been made for this contour, the first point is automatically set to (0,0). + */ + void pathTo(const SkPath& path); + /* Append, in reverse order, the first contour of path, ignoring path's last point. + If no moveTo() call has been made for this contour, the first point is automatically set to (0,0). + */ + void reversePathTo(const SkPath&); + + friend const SkPoint* sk_get_path_points(const SkPath&, int index); +}; + +#endif + diff --git a/include/graphics/SkPathEffect.h b/include/graphics/SkPathEffect.h new file mode 100644 index 0000000000..c560f67000 --- /dev/null +++ b/include/graphics/SkPathEffect.h @@ -0,0 +1,143 @@ +#ifndef SkPathEffect_DEFINED +#define SkPathEffect_DEFINED + +#include "SkFlattenable.h" + +class SkPath; + +/** \class SkPathEffect + + SkPathEffect is the base class for objects in the SkPaint that affect + the geometry of a drawing primitive before it is transformed by the + canvas' matrix and drawn. + + Dashing is implemented as a subclass of SkPathEffect. +*/ +class SkPathEffect : public SkFlattenable { +public: + SkPathEffect() {} + + /** Given a src path and a width value, return true if the patheffect + has produced a new path (dst) and a new width value. If false is returned, + ignore dst and width. + On input, width >= 0 means the src should be stroked + On output, width >= 0 means the dst should be stroked + */ + virtual bool filterPath(SkPath* dst, const SkPath& src, SkScalar* width); + /** overrides for SkFlattenable. + Subclasses should override this to (re)create their subclass. + */ + // This method is not exported to java. + virtual Factory getFactory(); + +protected: + // visible to our subclasses + SkPathEffect(SkRBuffer&) {} + +private: + // illegal + SkPathEffect(const SkPathEffect&); + SkPathEffect& operator=(const SkPathEffect&); +}; + +/** \class SkPairPathEffect + + Common baseclass for Compose and Sum. This subclass manages two pathEffects, + including flattening them. It does nothing in filterPath, and is only useful + for managing the lifetimes of its two arguments. +*/ +// This class is not exported to java. +class SkPairPathEffect : public SkPathEffect { +public: + SkPairPathEffect(SkPathEffect* pe0, SkPathEffect* pe1); + virtual ~SkPairPathEffect(); + + // overrides + + // This method is not exported to java. + virtual void flatten(SkWBuffer&); + +protected: + // these are visible to our subclasses + SkPathEffect* fPE0, *fPE1; + SkPairPathEffect(SkRBuffer&); + +private: + typedef SkPathEffect INHERITED; +}; + +/** \class SkComposePathEffect + + This subclass of SkPathEffect composes its two arguments, to create + a compound pathEffect. +*/ +class SkComposePathEffect : public SkPairPathEffect { +public: + /** Construct a pathEffect whose effect is to apply first the inner pathEffect + and the the outer pathEffect (e.g. outer(inner(path))) + The reference counts for outer and inner are both incremented in the constructor, + and decremented in the destructor. + */ + SkComposePathEffect(SkPathEffect* outer, SkPathEffect* inner) + : SkPairPathEffect(outer, inner) {} + + // overrides + + virtual bool filterPath(SkPath* dst, const SkPath& src, SkScalar* width); + + // overrides for SkFlattenable + + // This method is not exported to java. + virtual Factory getFactory(); + +private: + SkPathEffect* fOuter, *fInner; + + static SkFlattenable* CreateProc(SkRBuffer&); + SkComposePathEffect(SkRBuffer& buffer) : SkPairPathEffect(buffer) {} + + // illegal + SkComposePathEffect(const SkComposePathEffect&); + SkComposePathEffect& operator=(const SkComposePathEffect&); + + typedef SkPairPathEffect INHERITED; +}; + +/** \class SkSumPathEffect + + This subclass of SkPathEffect applies two pathEffects, one after the other. + Its filterPath() returns true if either of the effects succeeded. +*/ +class SkSumPathEffect : public SkPairPathEffect { +public: + /** Construct a pathEffect whose effect is to apply two effects, in sequence. + (e.g. first(path) + second(path)) + The reference counts for first and second are both incremented in the constructor, + and decremented in the destructor. + */ + SkSumPathEffect(SkPathEffect* first, SkPathEffect* second) + : SkPairPathEffect(first, second) {} + + // overrides + virtual bool filterPath(SkPath* dst, const SkPath& src, SkScalar* width); + + // overrides for SkFlattenable + + // This method is not exported to java. + virtual Factory getFactory(); + +private: + SkPathEffect* fFirst, *fSecond; + + static SkFlattenable* CreateProc(SkRBuffer&); + SkSumPathEffect(SkRBuffer& buffer) : SkPairPathEffect(buffer) {} + + // illegal + SkSumPathEffect(const SkSumPathEffect&); + SkSumPathEffect& operator=(const SkSumPathEffect&); + + typedef SkPairPathEffect INHERITED; +}; + +#endif + diff --git a/include/graphics/SkPathMeasure.h b/include/graphics/SkPathMeasure.h new file mode 100644 index 0000000000..7d901a2c6b --- /dev/null +++ b/include/graphics/SkPathMeasure.h @@ -0,0 +1,95 @@ +#ifndef SkPathMeasure_DEFINED +#define SkPathMeasure_DEFINED + +#include "SkPath.h" +#include "SkTDArray.h" + +class SkPathMeasure { +public: + SkPathMeasure(); + SkPathMeasure(const SkPath& path, bool forceClosed); + ~SkPathMeasure(); + + /** Assign a new path, or nil to have none. + */ + void setPath(const SkPath*, bool forceClosed); + + /** Return the total length of the current contour, or 0 if no path + is associated (e.g. resetPath(nil)) + */ + SkScalar getLength(); + + /** Pins distance to 0 <= distance <= getLength(), and then computes + the corresponding position and tangent. + Returns false if there is no path, or a zero-length path was specified, in which case + position and tangent are unchanged. + */ + bool getPosTan(SkScalar distance, SkPoint* position, SkVector* tangent); + + enum MatrixFlags { + kGetPosition_MatrixFlag = 0x01, + kGetTangent_MatrixFlag = 0x02, + kGetPosAndTan_MatrixFlag = kGetPosition_MatrixFlag | kGetTangent_MatrixFlag + }; + /** Pins distance to 0 <= distance <= getLength(), and then computes + the corresponding matrix (by calling getPosTan). + Returns false if there is no path, or a zero-length path was specified, in which case + matrix is unchanged. + */ + bool getMatrix(SkScalar distance, SkMatrix* matrix, MatrixFlags flags = kGetPosAndTan_MatrixFlag); + /** Given a start and stop distance, return in dst the intervening segment(s). + If the segment is zero-length, return false, else return true. + startD and stopD are pinned to legal values (0..getLength()). If startD <= stopD + then return false (and leave dst untouched). + Begin the segment with a moveTo if startWithMoveTo is true + */ + bool getSegment(SkScalar startD, SkScalar stopD, SkPath* dst, bool startWithMoveTo); + + /** Return true if the current contour is closed() + */ + bool isClosed(); + + /** Move to the next contour in the path. Return true if one exists, or false if + we're done with the path. + */ + bool nextContour(); + +#ifdef SK_DEBUG + void dump(); + static void UnitTest(); +#endif + +private: + SkPath::Iter fIter; + const SkPath* fPath; + SkScalar fLength; // relative to the current contour + int fFirstPtIndex; // relative to the current contour + bool fIsClosed; // relative to the current contour + bool fForceClosed; + + struct Segment { + SkScalar fDistance; // total distance up to this point + unsigned fPtIndex : 15; + unsigned fTValue : 15; + unsigned fType : 2; + + SkScalar getScalarT() const; + }; + SkTDArray<Segment> fSegments; + + static const Segment* NextSegment(const Segment*); + + void buildSegments(); + SkScalar compute_quad_segs(const SkPoint pts[3], SkScalar distance, + int mint, int maxt, int ptIndex); + SkScalar compute_cubic_segs(const SkPoint pts[3], SkScalar distance, + int mint, int maxt, int ptIndex); + const Segment* distanceToSegment(SkScalar distance, SkScalar* t); + + // illegal (for now) + SkPathMeasure(const SkPathMeasure&); + SkPathMeasure& operator=(const SkPathMeasure&); +}; + +#endif + diff --git a/include/graphics/SkPorterDuff.h b/include/graphics/SkPorterDuff.h new file mode 100644 index 0000000000..6dbcf589cb --- /dev/null +++ b/include/graphics/SkPorterDuff.h @@ -0,0 +1,47 @@ +#ifndef SkPorterDuff_DEFINED +#define SkPorterDuff_DEFINED + +#include "SkColor.h" + +class SkXfermode; + +class SkPorterDuff { +public: + /** List of predefined xfermodes. In general, the algebra for the modes + uses the following symbols: + Sa, Sc - source alpha and color + Da, Dc - destination alpha and color (before compositing) + [a, c] - Resulting (alpha, color) values + For these equations, the colors are in premultiplied state. + If no xfermode is specified, kSrcOver is assumed. + */ + enum Mode { + kClear_Mode, //!< [0, 0] + kSrc_Mode, //!< [Sa, Sc] + kDst_Mode, //!< [Da, Dc] + kSrcOver_Mode, //!< [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] this is the default mode + kDstOver_Mode, //!< [Sa + (1 - Sa)*Da, Rc = Dc + (1 - Da)*Sc] + kSrcIn_Mode, //!< [Sa * Da, Sc * Da] + kDstIn_Mode, //!< [Sa * Da, Sa * Dc] + kSrcOut_Mode, //!< [Sa * (1 - Da), Sc * (1 - Da)] + kDstOut_Mode, //!< [Da * (1 - Sa), Dc * (1 - Sa)] + kSrcATop_Mode, //!< [Da, Sc * Da + (1 - Sa) * Dc] + kDstATop_Mode, //!< [Sa, Sa * Dc + Sc * (1 - Da)] + kXor_Mode, //!< [Sa + Da - 2 * Sa * Da, Sc * (1 - Da) + (1 - Sa) * Dc] + kDarken_Mode, //!< [Sa + Da - Sa\u00B7Da, Sc\u00B7(1 - Da) + Dc\u00B7(1 - Sa) + min(Sc, Dc)] + kLighten_Mode, //!< [Sa + Da - Sa\u00B7Da, Sc\u00B7(1 - Da) + Dc\u00B7(1 - Sa) + max(Sc, Dc)] + + kModeCount + }; + /** Return an SkXfermode object for the specified mode. + */ + static SkXfermode* CreateXfermode(Mode mode); + + /** Return a function pointer to a routine that applies the specified porter-duff + transfer mode. + */ + static SkXfermodeProc GetXfermodeProc(Mode mode); +}; + +#endif + diff --git a/include/graphics/SkPrefix_Debug_Fixed.h b/include/graphics/SkPrefix_Debug_Fixed.h new file mode 100644 index 0000000000..72d15e426d --- /dev/null +++ b/include/graphics/SkPrefix_Debug_Fixed.h @@ -0,0 +1,14 @@ +#ifndef SkPrefix_Debug_Fixed_DEFINED +#define SkPrefix_Debug_Fixed_DEFINED + +#define SK_DEBUG + +/* define this to test fixed-point */ +#define SK_SCALAR_IS_FIXED + +/* these are for expat */ +#define MACOS_CLASSIC + +#endif + + diff --git a/include/graphics/SkPrefix_Release_Fixed.h b/include/graphics/SkPrefix_Release_Fixed.h new file mode 100644 index 0000000000..81238aba20 --- /dev/null +++ b/include/graphics/SkPrefix_Release_Fixed.h @@ -0,0 +1,26 @@ +#ifndef SkPrefix_Release_Fixed_DEFINED + +#define SkPrefix_Release_Fixed_DEFINED + +/* this means we're a release build */ + +#define NDEBUG + + + +/* define this to test fixed-point */ + +#define SK_SCALAR_IS_FIXED + + + +/* these are for expat */ + +#define MACOS_CLASSIC + + + +#endif + + + diff --git a/include/graphics/SkProgressBarView.h b/include/graphics/SkProgressBarView.h new file mode 100644 index 0000000000..64351c8bc4 --- /dev/null +++ b/include/graphics/SkProgressBarView.h @@ -0,0 +1,41 @@ +#ifndef SkProgressBarView_DEFINED +#define SkProgressBarView_DEFINED + +#include "SkView.h" +#include "SkWidgetViews.h" +#include "SkAnimator.h" + +class SkProgressBarView : public SkWidgetView { + public: + SkProgressBarView(); + //SkProgressBarView(int max); + + //inflate: "sk-progress" + + void reset(); //reset progress to zero + void setProgress(int progress); + void changeProgress(int diff); + void setMax(int max); + + int getProgress() const { return fProgress; } + int getMax() const { return fMax; } + + protected: + //overrides + virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node); + virtual void onSizeChange(); + virtual void onDraw(SkCanvas* canvas); + virtual bool onEvent(const SkEvent& evt); + + private: + SkAnimator fAnim; + int fProgress; + int fMax; + + typedef SkWidgetView INHERITED; +}; + + + + +#endif diff --git a/include/graphics/SkRasterizer.h b/include/graphics/SkRasterizer.h new file mode 100644 index 0000000000..3a323b0b02 --- /dev/null +++ b/include/graphics/SkRasterizer.h @@ -0,0 +1,33 @@ +#ifndef SkRasterizer_DEFINED +#define SkRasterizer_DEFINED + +#include "SkFlattenable.h" +#include "SkMask.h" + +class SkMaskFilter; +class SkMatrix; +class SkPath; +struct SkRect16; + +class SkRasterizer : public SkFlattenable { +public: + SkRasterizer() {} + + /** Turn the path into a mask, respecting the specified local->device matrix. + */ + bool rasterize(const SkPath& path, const SkMatrix& matrix, + const SkRect16* clipBounds, SkMaskFilter* filter, + SkMask* mask, SkMask::CreateMode mode); + +protected: + SkRasterizer(SkRBuffer&); + + virtual bool onRasterize(const SkPath& path, const SkMatrix& matrix, + const SkRect16* clipBounds, + SkMask* mask, SkMask::CreateMode mode); + +private: + typedef SkFlattenable INHERITED; +}; + +#endif diff --git a/include/graphics/SkRefCnt.h b/include/graphics/SkRefCnt.h new file mode 100644 index 0000000000..679f955e6f --- /dev/null +++ b/include/graphics/SkRefCnt.h @@ -0,0 +1,87 @@ +#ifndef SkRefCnt_DEFINED +#define SkRefCnt_DEFINED + +#include "SkTypes.h" + +/** \class SkRefCnt + + SkRefCnt is the base class for objects that may be shared by multiple objects. + When a new owner wants a reference, it calls ref(). When an owner wants to release + its reference, it calls unref(). When the shared object's reference count goes to + zero as the result of an unref() call, its (virtual) destructor is called. It is + an error for the destructor to be called explicitly (or via the object going out + of scope on the stack or calling delete) if getRefCnt() > 1. +*/ +class SkRefCnt { +public: + /** Default construct, initializing the reference count to 1. + */ + SkRefCnt() : fRefCnt(1) {} + /** Destruct, asserting that the reference count is 1. + */ + virtual ~SkRefCnt() { SkASSERT(fRefCnt == 1); } + + /** Return the reference count. + */ + int getRefCnt() const { return fRefCnt; } + /** Increment the reference count. Must be balanced by a call to unref(). + */ + void ref() const { SkASSERT(fRefCnt > 0); ++fRefCnt; } + /** Decrement the reference count. If the reference count is 1 before the + decrement, then call delete on the object. Note that if this is the case, + then the object needs to have been allocated via new, and not on the stack. + */ + void unref() const + { + SkASSERT(fRefCnt > 0); + if (fRefCnt == 1) + delete this; + else + --fRefCnt; + } + + /** Helper version of ref(), that first checks to see if this is not nil. + If this is nil, then do nothing. + */ + void safeRef() const { if (this) this->ref(); } + /** Helper version of unref(), that first checks to see if this is not nil. + If this is nil, then do nothing. + */ + void safeUnref() const { if (this) this->unref(); } + +private: + mutable int fRefCnt; +}; + +/** \class SkAutoUnref + + SkAutoUnref is a stack-helper class that will automatically call unref() on + the object it points to when the SkAutoUnref object goes out of scope. +*/ +class SkAutoUnref { +public: + SkAutoUnref(SkRefCnt* obj) : fObj(obj) {} + ~SkAutoUnref(); + + SkRefCnt* get() const { return fObj; } + bool ref(); + bool unref(); + SkRefCnt* detach(); + +private: + SkRefCnt* fObj; +}; + +/** Helper macro to safely assign one SkRefCnt* to another, checking for + nil in on each side of the assignment, and ensuring that ref() is called + before unref(), in case the two pointers point to the same object. +*/ +#define SkRefCnt_SafeAssign(dst, src) \ + do { \ + if (src) src->ref(); \ + if (dst) dst->unref(); \ + dst = src; \ + } while (0) + +#endif + diff --git a/include/graphics/SkSVGAttribute.h b/include/graphics/SkSVGAttribute.h new file mode 100644 index 0000000000..b6ae0df888 --- /dev/null +++ b/include/graphics/SkSVGAttribute.h @@ -0,0 +1,33 @@ +#ifndef SkSVGAttribute_DEFINED +#define SkSVGAttribute_DEFINED + +#include "SkTypes.h" + +struct SkSVGAttribute { + const char* fName; +#ifdef SK_DEBUG + size_t fOffset; +#endif +}; + +#ifndef SK_OFFSETOF +#define SK_OFFSETOF(a, b) (((size_t) (&(((a*) 1)->b)))-1) +#endif + +#ifdef SK_DEBUG +#define SVG_ATTRIBUTE(attr) { #attr, SK_OFFSETOF(BASE_CLASS, f_##attr) } +#define SVG_LITERAL_ATTRIBUTE(svgAttr, cAttr) { #svgAttr, SK_OFFSETOF(BASE_CLASS, cAttr) } +#else +#define SVG_ATTRIBUTE(attr) { #attr } +#define SVG_LITERAL_ATTRIBUTE(svgAttr, cAttr) { #svgAttr } +#endif + +#define SVG_ADD_ATTRIBUTE(attr) \ + if (f_##attr.size() > 0) \ + parser._addAttributeLen(#attr, f_##attr.c_str(), f_##attr.size()) + +#define SVG_ADD_ATTRIBUTE_ALIAS(attr, alias) \ + if (f_##alias.size() > 0) \ + parser._addAttributeLen(#attr, f_##alias.c_str(), f_##alias.size()) + +#endif // SkSVGAttribute_DEFINED diff --git a/include/graphics/SkSVGBase.h b/include/graphics/SkSVGBase.h new file mode 100644 index 0000000000..83b72d96b9 --- /dev/null +++ b/include/graphics/SkSVGBase.h @@ -0,0 +1,16 @@ +#ifndef SkSVGBase_DEFINED +#define SkSVGBase_DEFINED + +#include "SkSVGAttribute.h" + +class SkSVGParser; + +class SkSVGBase { +public: + virtual ~SkSVGBase(); + virtual void addAttribute(SkSVGParser& parser, int attrIndex, + const char* attrValue, size_t attrLength); + virtual int getAttributes(const SkSVGAttribute** attrPtr) = 0; +}; + +#endif // SkSVGBase_DEFINED
\ No newline at end of file diff --git a/include/graphics/SkSVGPaintState.h b/include/graphics/SkSVGPaintState.h new file mode 100644 index 0000000000..4ec018de1f --- /dev/null +++ b/include/graphics/SkSVGPaintState.h @@ -0,0 +1,80 @@ +#ifndef SkSVGPaintState_DEFINED +#define SkSVGPaintState_DEFINED + +#include "SkSVGBase.h" +#include "SkString.h" + +class SkSVGPaint : public SkSVGBase { +public: + enum Field { + kInitial = -1, + kClipPath, + kClipRule, + kEnableBackground, + kFill, + kFillRule, + kFilter, + kFontFamily, + kFontSize, + kLetterSpacing, + kMask, + kOpacity, + kStopColor, + kStopOpacity, + kStroke, + kStroke_Dasharray, + kStroke_Linecap, + kStroke_Linejoin, + kStroke_Miterlimit, + kStroke_Width, + kStyle, + kTransform, + kTerminal + }; + + SkSVGPaint(); + virtual void addAttribute(SkSVGParser& parser, int attrIndex, + const char* attrValue, size_t attrLength); + bool flush(SkSVGParser& , bool isFlushable, bool isDef); + virtual int getAttributes(const SkSVGAttribute** attrPtr); + static void Push(SkSVGPaint** head, SkSVGPaint* add); + static void Pop(SkSVGPaint** head); + SkString* operator[](int index); + SkString fInitial; + SkString f_clipPath; + SkString f_clipRule; + SkString f_enableBackground; + SkString f_fill; + SkString f_fillRule; + SkString f_filter; + SkString f_fontFamily; + SkString f_fontSize; + SkString f_letterSpacing; + SkString f_mask; + SkString f_opacity; + SkString f_stopColor; + SkString f_stopOpacity; + SkString f_stroke; + SkString f_strokeDasharray; + SkString f_strokeLinecap; + SkString f_strokeLinejoin; + SkString f_strokeMiterlimit; + SkString f_strokeWidth; + SkString f_style; // unused, but allows array access to the rest + SkString f_transform; +#ifdef SK_DEBUG + SkString fTerminal; +#endif + SkString fTransformID; + static SkSVGAttribute gAttributes[]; + static const int kAttributesSize; +private: + void setSave(SkSVGParser& ); + bool writeChangedAttributes(SkSVGParser& , SkSVGPaint& , bool* changed); + bool writeChangedElements(SkSVGParser& , SkSVGPaint& , bool* changed); + SkSVGPaint* fNext; + friend class SkSVGParser; + typedef SkSVGPaint BASE_CLASS; +}; + +#endif // SkSVGPaintState_DEFINED diff --git a/include/graphics/SkSVGParser.h b/include/graphics/SkSVGParser.h new file mode 100644 index 0000000000..b001c22438 --- /dev/null +++ b/include/graphics/SkSVGParser.h @@ -0,0 +1,65 @@ +#ifndef SkSVGParser_DEFINED +#define SkSVGParser_DEFINED + +#include "SkMatrix.h" +#include "SkTDict.h" +#include "SkTDStack.h" +#include "SkSVGPaintState.h" +#include "SkSVGTypes.h" +#include "SkStream.h" +#include "SkString.h" +#include "SkXMLParser.h" +#include "SkXMLWriter.h" + +class SkSVGBase; +class SkSVGElement; + +class SkSVGParser : public SkXMLParser { +public: + SkSVGParser(); + virtual ~SkSVGParser(); + void _addAttribute(const char* attrName, const char* attrValue) { + fXMLWriter.addAttribute(attrName, attrValue); } + void _addAttribute(const char* attrName, SkString& attrValue) { + fXMLWriter.addAttribute(attrName, attrValue.c_str()); } + void _addAttributeLen(const char* attrName, const char* attrValue, size_t len) { + fXMLWriter.addAttributeLen(attrName, attrValue, len); } + void _endElement() { fXMLWriter.endElement(); } + int findAttribute(SkSVGBase* , const char* attrValue, size_t len, bool isPaint); + const char* getFinal(); + SkTDict<SkSVGElement*>& getIDs() { return fIDs; } + SkString& getPaintLast(SkSVGPaint::Field field); + void _startElement(const char name[]) { fXMLWriter.startElement(name); } + void translate(SkSVGElement*, bool isDef); + void translateMatrix(SkString& , SkString* id); + static void ConvertToArray(SkString& vals); +protected: + virtual bool onAddAttribute(const char name[], const char value[]); + bool onAddAttributeLen(const char name[], const char value[], size_t len); + virtual bool onEndElement(const char elem[]); + virtual bool onStartElement(const char elem[]); + bool onStartElementLen(const char elem[], size_t len); + virtual bool onText(const char text[], int len); +private: + bool isStrokeAndFill(SkSVGPaint** stroke, SkSVGPaint** fill); + static SkSVGElement* CreateElement(SkSVGTypes type, SkSVGElement* parent); + static void Delete(SkTDArray<SkSVGElement*>& fChildren); + static SkSVGTypes GetType(const char name[], size_t len); + SkSVGPaint* fHead; + SkSVGPaint fEmptyPaint; + SkSVGPaint fLastFlush; + SkString fLastColor; + SkMatrix fLastTransform; + SkTDArray<SkSVGElement*> fChildren; + SkTDict<SkSVGElement*> fIDs; + SkTDArray<SkSVGElement*> fParents; + SkDynamicMemoryWStream fStream; + SkXMLStreamWriter fXMLWriter; + SkSVGElement* fCurrElement; + SkBool8 fInSVG; + SkBool8 fSuppressPaint; + friend class SkSVGPaint; + friend class SkSVGGradient; +}; + +#endif // SkSVGParser_DEFINED diff --git a/include/graphics/SkSVGTypes.h b/include/graphics/SkSVGTypes.h new file mode 100644 index 0000000000..b87bfbda7a --- /dev/null +++ b/include/graphics/SkSVGTypes.h @@ -0,0 +1,30 @@ +#ifndef SkSVGTypes_DEFINED +#define SkSVGTypes_DEFINED + +enum SkSVGTypes { + SkSVGType_Circle, + SkSVGType_ClipPath, + SkSVGType_Defs, + SkSVGType_Ellipse, + SkSVGType_FeColorMatrix, + SkSVGType_Filter, + SkSVGType_G, + SkSVGType_Image, + SkSVGType_Line, + SkSVGType_LinearGradient, + SkSVGType_Mask, + SkSVGType_Metadata, + SkSVGType_Path, + SkSVGType_Polygon, + SkSVGType_Polyline, + SkSVGType_RadialGradient, + SkSVGType_Rect, + SkSVGType_SVG, + SkSVGType_Stop, + SkSVGType_Symbol, + SkSVGType_Text, + SkSVGType_Tspan, + SkSVGType_Use +}; + +#endif // SkSVGTypes_DEFINED diff --git a/include/graphics/SkScalerContext.h b/include/graphics/SkScalerContext.h new file mode 100644 index 0000000000..db12c73b7d --- /dev/null +++ b/include/graphics/SkScalerContext.h @@ -0,0 +1,87 @@ +#ifndef SkScalerContext_DEFINED +#define SkScalerContext_DEFINED + +#include "SkMatrix.h" +#include "SkPath.h" +#include "SkPoint.h" + +class SkDescriptor; +class SkMaskFilter; +class SkPaint; +class SkPathEffect; +class SkRasterizer; + +#define SK_UnknownAuxScalerContextID 0 +#define SK_MaxAuxScalerContextID 16 + +struct SkGlyph { + void* fImage; + SkPath* fPath; + SkFixed fAdvanceX, fAdvanceY; + + uint16_t fGlyphID; + uint16_t fWidth, fHeight, fRowBytes; + int16_t fTop, fLeft; + + uint16_t fCharCode; // might go away with line layout. really wants 20bits + uint8_t fMaskFormat; + SkBool8 fUseAuxContext; // just need 1-bit for this field + + size_t computeImageSize() const; +}; + +class SkScalerContext { +public: + struct Rec { + SkScalar fTextSize, fPreScaleX, fPreSkewX; + SkScalar fPost2x2[2][2]; + SkScalar fFrameWidth, fMiterLimit; + SkBool8 fUseHints; + SkBool8 fFrameAndFill; + SkBool8 fDoAA; + uint8_t fStrokeJoin; + + void getMatrixFrom2x2(SkMatrix*) const; + void getLocalMatrix(SkMatrix*) const; + void getSingleMatrix(SkMatrix*) const; + }; + + SkScalerContext(const SkDescriptor* desc); + virtual ~SkScalerContext(); + + void getMetrics(SkGlyph*); + void getImage(const SkGlyph&); + void getPath(const SkGlyph&, SkPath*); + void getLineHeight(SkPoint* above, SkPoint* below); + + static inline void MakeRec(const SkPaint&, const SkMatrix*, Rec* rec); + static SkScalerContext* Create(const SkDescriptor*); + +protected: + Rec fRec; + + virtual void generateMetrics(SkGlyph*) = 0; + virtual void generateImage(const SkGlyph&) = 0; + virtual void generatePath(const SkGlyph&, SkPath*) = 0; + virtual void generateLineHeight(SkPoint* above, SkPoint* below) = 0; + +private: + SkPathEffect* fPathEffect; + SkMaskFilter* fMaskFilter; + SkRasterizer* fRasterizer; + SkScalar fDevFrameWidth; + + void internalGetPath(const SkGlyph& glyph, SkPath* fillPath, SkPath* devPath, SkMatrix* fillToDevMatrix); + + // we index into this with scalerContextID-1 + SkScalerContext* fAuxContext[SK_MaxAuxScalerContextID]; +}; + +#define kRec_SkDescriptorTag SkSetFourByteTag('s', 'r', 'e', 'c') +#define kTypeface_SkDescriptorTag SkSetFourByteTag('t', 'p', 'f', 'c') +#define kPathEffect_SkDescriptorTag SkSetFourByteTag('p', 't', 'h', 'e') +#define kMaskFilter_SkDescriptorTag SkSetFourByteTag('m', 's', 'k', 'f') +#define kRasterizer_SkDescriptorTag SkSetFourByteTag('r', 'a', 's', 't') + +#endif + diff --git a/include/graphics/SkScrollBarView.h b/include/graphics/SkScrollBarView.h new file mode 100644 index 0000000000..ee43a3cec1 --- /dev/null +++ b/include/graphics/SkScrollBarView.h @@ -0,0 +1,35 @@ +#ifndef SkScrollBarView_DEFINED +#define SkScrollBarView_DEFINED + +#include "SkView.h" +#include "SkWidgetViews.h" +#include "SkAnimator.h" + +class SkScrollBarView : public SkWidgetView { +public: + SkScrollBarView(); + + unsigned getStart() const { return fStartPoint; } + unsigned getShown() const { return fShownLength; } + unsigned getTotal() const { return fTotalLength; } + + void setStart(unsigned start); + void setShown(unsigned shown); + void setTotal(unsigned total); + +protected: + //overrides + virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node); + virtual void onSizeChange(); + virtual void onDraw(SkCanvas* canvas); + virtual bool onEvent(const SkEvent& evt); + +private: + SkAnimator fAnim; + unsigned fTotalLength, fStartPoint, fShownLength; + + void adjust(); + + typedef SkWidgetView INHERITED; +}; +#endif
\ No newline at end of file diff --git a/include/graphics/SkShader.h b/include/graphics/SkShader.h new file mode 100644 index 0000000000..e823a7bb71 --- /dev/null +++ b/include/graphics/SkShader.h @@ -0,0 +1,146 @@ +#ifndef SkShader_DEFINED +#define SkShader_DEFINED + +#include "SkRefCnt.h" +#include "SkBitmap.h" +#include "SkMask.h" +#include "SkMatrix.h" +#include "SkPaint.h" + +class SkPath; + +/** \class SkShader + + SkShader is the based class for objects that return horizontal spans of colors during drawing. + A subclass of SkShader is installed in a SkPaint calling paint.setShader(shader). After that + any object (other than a bitmap) that is drawn with that paint will get its color(s) from the + shader. +*/ +class SkShader : public SkRefCnt { +public: + SkShader(); + virtual ~SkShader(); + + /** Return the shader's optional local matrix, or nil. + */ + const SkMatrix* getLocalMatrix() const { return fLocalMatrix; } + /** Set the shader's optional local matrix. If the specified matrix is identity, then + getLocalMatrix() will return nil. + */ + void setLocalMatrix(const SkMatrix&); + + enum TileMode { + kClamp_TileMode, //!< replicate the edge color if the shader draws outside of its original bounds + kRepeat_TileMode, //!< repeat the shader's image horizontally and vertically + kMirror_TileMode, //!< repeat the shader's image horizontally and vertically, alternating mirror images so that adjacent images always seam + + kTileModeCount + }; + + // override these in your subclass + + enum Flags { + kOpaqueAlpha_Flag = 0x01, //!< set if all of the colors will be opaque (if so, kConstAlpha_Flag will not be set) + kConstAlpha_Flag = 0x02, //!< set if all of the colors have the same (non-opaque) alpha + kHasSpan16_Flag = 0x04, //!< set if this shader's shadeSpanOpaque16() method can be called + + kFlagsMask = kOpaqueAlpha_Flag | kConstAlpha_Flag | kHasSpan16_Flag + }; + + /** Called sometimes before drawing with this shader. + Return the type of alpha your shader will return. + The default implementation returns 0. Your subclass should override if it can + (even sometimes) report a non-zero value, since that will enable various blitters + to perform faster. + */ + virtual U32 getFlags(); + + /** Called once before drawing, with the current paint and + device matrix. Return true if your shader supports these + parameters, or false if not. If false is returned, nothing + will be drawn. + */ + virtual bool setContext( const SkBitmap& device, + const SkPaint& paint, + const SkMatrix& matrix); + + /** Called for each span of the object being drawn. Your subclass + should set the appropriate colors (with premultiplied alpha) that + correspond to the specified device coordinates. + */ + virtual void shadeSpan(int x, int y, SkPMColor[], int count) = 0; + /** Called only for 16bit devices when getFlags() returns kOpaqueAlphaFlag | kHasSpan16_Flag + */ + virtual void shadeSpanOpaque16(int x, int y, U16[], int count); + /** Similar to shadeSpan, but only returns the alpha-channel for a span. + The default implementation calls shadeSpan() and then extracts the alpha + values from the returned colors. + */ + virtual void shadeSpanAlpha(int x, int y, U8 alpha[], int count); + + /** Helper function that returns true if this shader's shadeSpanOpaque16() method can + be called. + */ + bool canCallShadeSpanOpaque16() + { + return SkShader::CanCallShadeSpanOpaque16(this->getFlags()); + } + + /** Helper to check the flags to know if it is legal to call shadeSpanOpaque16() + */ + static bool CanCallShadeSpanOpaque16(U32 flags) + { + return (flags & (kOpaqueAlpha_Flag | kHasSpan16_Flag)) == (kOpaqueAlpha_Flag | kHasSpan16_Flag); + } + + ////////////////////////////////////////////////////////////////////////// + // Factory methods for stock shaders + + /** Call this to create a new shader that will draw with the specified bitmap. + @param src The bitmap to use inside the shader + @param transferOwnershipOfPixels If true, the shader will call setOwnsPixels(true) on its private bitmap + and setOwnsPixels(false) on the src bitmap, resulting in the bitmap's pixels + being disposed when the shader is deleted. + @param ft The filter type to be used when scaling or rotating the bitmap when it is drawn. + @param tmx The tiling mode to use when sampling the bitmap in the x-direction. + @param tmy The tiling mode to use when sampling the bitmap in the y-direction. + @return Returns a new shader object. Note: this function never returns nil. + */ + static SkShader* CreateBitmapShader(const SkBitmap& src, + bool transferOwnershipOfPixels, + SkPaint::FilterType ft, + TileMode tmx, TileMode tmy); + +protected: + enum MatrixClass { + kLinear_MatrixClass, // no perspective + kFixedStepInX_MatrixClass, // fast perspective, need to call fixedStepInX() each scanline + kPerspective_MatrixClass // slow perspective, need to mappoints each pixel + }; + static MatrixClass ComputeMatrixClass(const SkMatrix&); + + // These can be called by your subclass after setContext() has been called + U8 getPaintAlpha() const { return fPaintAlpha; } + SkBitmap::Config getDeviceConfig() const { return (SkBitmap::Config)fDeviceConfig; } + const SkMatrix& getTotalInverse() const { return fTotalInverse; } + MatrixClass getInverseClass() const { return (MatrixClass)fTotalInverseClass; } + SkMatrix::MapPtProc getInverseMapPtProc() const { return fInverseMapPtProc; } + +private: + SkMatrix* fLocalMatrix; + SkMatrix fTotalInverse; + SkMatrix::MapPtProc fInverseMapPtProc; + U8 fPaintAlpha; + U8 fDeviceConfig; + U8 fTotalInverseClass; + + static SkShader* CreateBitmapShader(const SkBitmap& src, + bool transferOwnershipOfPixels, + SkPaint::FilterType, + TileMode, TileMode, + void* storage, size_t storageSize); + friend class SkAutoBitmapShaderInstall; +}; + +#endif + diff --git a/include/graphics/SkShaderExtras.h b/include/graphics/SkShaderExtras.h new file mode 100644 index 0000000000..e914304e8a --- /dev/null +++ b/include/graphics/SkShaderExtras.h @@ -0,0 +1,36 @@ +#ifndef SkShaderExtras_DEFINED +#define SkShaderExtras_DEFINED + +#include "SkShader.h" + +class SkColorCombine : public SkRefCnt { +public: + /** Called with two scanlines of color. The implementation writes out its combination of + those into the result[] scaline. + */ + virtual void combineSpan(const SkPMColor srcA[], const SkPMColor srcB[], int count, SkPMColor result[]) = 0; +}; + +/////////////////////////////////////////////////////////////////////////////////////////// + +class SkComposeShader : public SkShader { +public: + SkComposeShader(SkShader* sA, SkShader* sB, SkColorCombine* combine); + virtual ~SkComposeShader(); + + // override + virtual bool setContext(const SkBitmap& device, const SkPaint& paint, const SkMatrix& matrix); + virtual void shadeSpan(int x, int y, SkPMColor result[], int count); + +private: + enum { + COUNT = 32 + }; + SkShader* fShaderA; + SkShader* fShaderB; + SkColorCombine* fCombine; + + typedef SkShader INHERITED; +}; + +#endif diff --git a/include/graphics/SkStackViewLayout.h b/include/graphics/SkStackViewLayout.h new file mode 100644 index 0000000000..778a32c8b7 --- /dev/null +++ b/include/graphics/SkStackViewLayout.h @@ -0,0 +1,80 @@ +#ifndef SkStackViewLayout_DEFINED +#define SkStackViewLayout_DEFINED + +#include "SkView.h" + +class SkStackViewLayout : public SkView::Layout { +public: + SkStackViewLayout(); + + enum Orient { + kHorizontal_Orient, + kVertical_Orient, + + kOrientCount + }; + Orient getOrient() const { return (Orient)fOrient; } + void setOrient(Orient); + + void getMargin(SkRect*) const; + void setMargin(const SkRect&); + + SkScalar getSpacer() const { return fSpacer; } + void setSpacer(SkScalar); + + /** Controls the posititioning in the same direction as the orientation + */ + enum Pack { + kStart_Pack, + kCenter_Pack, + kEnd_Pack, + + kPackCount + }; + Pack getPack() const { return (Pack)fPack; } + void setPack(Pack); + + /** Controls the posititioning at right angles to the orientation + */ + enum Align { + kStart_Align, + kCenter_Align, + kEnd_Align, + kStretch_Align, + + kAlignCount + }; + Align getAlign() const { return (Align)fAlign; } + void setAlign(Align); + + bool getRound() const { return SkToBool(fRound); } + void setRound(bool); + +protected: + virtual void onLayoutChildren(SkView* parent); + virtual void onInflate(const SkDOM&, const SkDOM::Node*); + +private: + SkRect fMargin; + SkScalar fSpacer; + U8 fOrient, fPack, fAlign, fRound; +}; + +class SkFillViewLayout : public SkView::Layout { +public: + SkFillViewLayout(); + void getMargin(SkRect*) const; + void setMargin(const SkRect&); + +protected: + // overrides; + virtual void onLayoutChildren(SkView* parent); + virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node); + +private: + SkRect fMargin; + typedef SkView::Layout INHERITED; +}; + +#endif + diff --git a/include/graphics/SkStdLib_Redirect.h b/include/graphics/SkStdLib_Redirect.h new file mode 100644 index 0000000000..d5525e122a --- /dev/null +++ b/include/graphics/SkStdLib_Redirect.h @@ -0,0 +1,17 @@ +#ifndef SkStdLib_Redirect_DEFINED +#define SkStdLib_Redirect_DEFINED + +#error + +#include "SkTypes.h" + +#define fread(buffer, count, size, file) sk_stdlib_fread(buffer, count, size, file) +#define qsort +#define tolower +#define setjmp +#define longjmp +#define memmove +#define malloc +#define realloc +#endif + diff --git a/include/graphics/SkStream.h b/include/graphics/SkStream.h new file mode 100644 index 0000000000..14c71d3dce --- /dev/null +++ b/include/graphics/SkStream.h @@ -0,0 +1,199 @@ +#ifndef SkStream_DEFINED +#define SkStream_DEFINED + +#include "SkScalar.h" + +class SkStream { +public: + virtual ~SkStream() {} + /** Called to rewind to the beginning of the stream. If this cannot be + done, return false. + */ + virtual bool rewind() = 0; + /** If this stream represents a file, this method returns the file's name. + If it does not, it returns nil (the default behavior). + */ + virtual const char* getFileName(); + /** Called to read or skip size number of bytes. If buffer is nil, skip + the bytes, else copy them into buffer. If this cannot be done, return false. + If buffer is nil and size is zero, return the file length + @param buffer If buffer is nil, ignore and just skip size bytes, otherwise copy size bytes into buffer + @param size The number of bytes to skip or copy + @return bytes read on success + */ + virtual size_t read(void* buffer, size_t size) = 0; + static SkStream* GetURIStream(const char prefix[], const char path[]); + static bool IsAbsoluteURI(const char path[]); +}; + +class SkWStream { +public: + virtual ~SkWStream(); + + /** Called to write bytes to a SkWStream. Returns true on success + @param buffer the address of at least size bytes to be written to the stream + @param size The number of bytes in buffer to write to the stream + @return true on success + */ + virtual bool write(const void* buffer, size_t size) = 0; + virtual void newline(); + virtual void flush(); + + // helpers + + bool writeText(const char text[]); + bool writeDecAsText(S32); + bool writeHexAsText(U32, int minDigits = 0); + bool writeScalarAsText(SkScalar); + + SkDEBUGCODE(static void UnitTest();) +}; + +//////////////////////////////////////////////////////////////////////////////////////// + +#include "SkString.h" + +struct SkFILE; + +class SkFILEStream : public SkStream { +public: + SkFILEStream(const char path[] = nil); + virtual ~SkFILEStream(); + + /** Returns true if the current path could be opened. + */ + bool isValid() const { return fFILE != nil; } + /** Close the current file, and open a new file with the specified + path. If path is nil, just close the current file. + */ + void setPath(const char path[]); + + SkFILE* getSkFILE() const { return fFILE; } + + virtual bool rewind(); + virtual size_t read(void* buffer, size_t size); + virtual const char* getFileName(); + +private: + SkFILE* fFILE; + SkString fName; +}; + +class SkMemoryStream : public SkStream { +public: + SkMemoryStream(const void* src, size_t length); + + virtual bool rewind(); + virtual size_t read(void* buffer, size_t size); + +private: + const void* fSrc; + size_t fSize, fOffset; +}; + +/** \class SkBufferStream + This is a wrapper class that adds buffering to another stream. + The caller can provide the buffer, or ask SkBufferStream to allocated/free + it automatically. +*/ +class SkBufferStream : public SkStream { +public: + /** Provide the stream to be buffered (proxy), and the size of the buffer that + should be used. This will be allocated and freed automatically. If bufferSize is 0, + a default buffer size will be used. + */ + SkBufferStream(SkStream& proxy, size_t bufferSize = 0); + /** Provide the stream to be buffered (proxy), and a buffer and size to be used. + This buffer is owned by the caller, and must be at least bufferSize bytes big. + Passing nil for buffer will cause the buffer to be allocated/freed automatically. + If buffer is not nil, it is an error for bufferSize to be 0. + */ + SkBufferStream(SkStream& proxy, void* buffer, size_t bufferSize); + virtual ~SkBufferStream(); + + virtual bool rewind(); + virtual const char* getFileName(); + virtual size_t read(void* buffer, size_t size); +private: + enum { + kDefaultBufferSize = 128 + }; + // illegal + SkBufferStream(const SkBufferStream&); + SkBufferStream& operator=(const SkBufferStream&); + + SkStream& fProxy; + char* fBuffer; + size_t fOrigBufferSize, fBufferSize, fBufferOffset; + bool fWeOwnTheBuffer; + + void init(void*, size_t); +}; + +///////////////////////////////////////////////////////////////////////////////////////////// + +class SkFILEWStream : public SkWStream { +public: + SkFILEWStream(const char path[]); + virtual ~SkFILEWStream(); + + /** Returns true if the current path could be opened. + */ + bool isValid() const { return fFILE != nil; } + + virtual bool write(const void* buffer, size_t size); + virtual void flush(); +private: + SkFILE* fFILE; +}; + +class SkMemoryWStream : public SkWStream { +public: + SkMemoryWStream(void* buffer, size_t size); + virtual bool write(const void* buffer, size_t size); + +private: + char* fBuffer; + size_t fMaxLength; + size_t fBytesWritten; +}; + +class SkDynamicMemoryWStream : public SkWStream { +public: + SkDynamicMemoryWStream(); + virtual ~SkDynamicMemoryWStream(); + virtual bool write(const void* buffer, size_t size); + // random access write + // modifies stream and returns true if offset + size is less than or equal to getOffset() + bool write(const void* buffer, size_t offset, size_t size); + size_t getOffset() { return fBytesWritten; } + + // copy what has been written to the stream into dst + void copyTo(void* dst) const; + /* return a cache of the flattened data returned by copyTo(). + This copy is only valid until the next call to write(). + The memory is managed by the stream class. + */ + const char* getStream() const; + +private: + struct Block; + Block* fHead; + Block* fTail; + size_t fBytesWritten; + mutable char* fCopyToCache; +}; + + +class SkDebugWStream : public SkWStream { +public: + // overrides + virtual bool write(const void* buffer, size_t size); + virtual void newline(); +}; + +// for now +typedef SkFILEStream SkURLStream; + +#endif + diff --git a/include/graphics/SkStream_Win.h b/include/graphics/SkStream_Win.h new file mode 100644 index 0000000000..8fdef0af2f --- /dev/null +++ b/include/graphics/SkStream_Win.h @@ -0,0 +1,37 @@ +#ifndef SkStream_Win_DEFINED +#define SkStream_Win_DEFINED + +#ifndef SK_BUILD_FOR_WIN +#error "only valid for windows and wince builds" +#endif + +#ifndef SkStream_DEFINED +#include "SkStream.h" +#endif +#include "SkString.h" +#include "Wininet.h" + +/** \cond ZERO */ +class SkURLStream : public SkStream { +public: + SkURLStream(const char url[] = nil); + virtual ~SkURLStream(); + + /** Close the current URL, and open a new URL. + If URL is nil, just close the current URL. + */ + void setURL(const char url[]); + + // overrides + virtual bool rewind(); + virtual size_t read(void* buffer, size_t size); + +private: + SkString fURL; + HINTERNET fConnection; + HINTERNET fURLStream; +}; + +/** \endcond */ +#endif // SkStream_Win_DEFINED + diff --git a/include/graphics/SkString.h b/include/graphics/SkString.h new file mode 100644 index 0000000000..84b8493bfb --- /dev/null +++ b/include/graphics/SkString.h @@ -0,0 +1,150 @@ +#ifndef SkString_DEFINED +#define SkString_DEFINED + +#include "SkScalar.h" + +/* Some helper functions for C strings +*/ + +bool SkStrStartsWith(const char string[], const char prefix[]); +bool SkStrEndsWith(const char string[], const char suffix[]); +int SkStrStartsWithOneOf(const char string[], const char prefixes[]); + +#define SkStrAppendS32_MaxSize 11 +char* SkStrAppendS32(char buffer[], int32_t); +#define SkStrAppendScalar_MaxSize 11 +char* SkStrAppendScalar(char buffer[], SkScalar); + +/** \class SkString + + Light weight class for managing strings. Uses reference + counting to make string assignments and copies very fast + with no extra RAM cost. Assumes UTF8 encoding. +*/ +class SkString { +public: + SkString(); + explicit SkString(size_t len); + explicit SkString(const char text[]); + SkString(const char text[], size_t len); + explicit SkString(const SkString&); + ~SkString(); + + bool isEmpty() const { return fRec->fLength == 0; } + size_t size() const { return (size_t) fRec->fLength; } + const char* c_str() const { return fRec->data(); } + + bool equals(const SkString&) const; + bool equals(const char text[]) const; + bool equals(const char text[], size_t len) const; + + bool startsWith(const char prefix[]) const + { + return SkStrStartsWith(fRec->data(), prefix); + } + bool endsWith(const char suffix[]) const + { + return SkStrEndsWith(fRec->data(), suffix); + } + + friend int operator==(const SkString& a, const SkString& b) + { + return a.equals(b); + } + friend int operator!=(const SkString& a, const SkString& b) + { + return !a.equals(b); + } + + // these methods edit the string + + SkString& operator=(const SkString&); + + char* writable_str(); + + void reset(); + void resize(size_t len) { this->set(nil, len); } + void set(const SkString& src) { *this = src; } + void set(const char text[]); + void set(const char text[], size_t len); + void setUTF16(const U16[]); + + void insert(size_t offset, const SkString& src) { this->insert(offset, src.c_str(), src.size()); } + void insert(size_t offset, const char text[]); + void insert(size_t offset, const char text[], size_t len); + void insertUnichar(size_t offset, SkUnichar); + void insertS32(size_t offset, S32 value); + void insertHex(size_t offset, U32 value, int minDigits = 0); + void insertScalar(size_t offset, SkScalar); + + void append(const SkString& str) { this->insert((size_t)-1, str); } + void append(const char text[]) { this->insert((size_t)-1, text); } + void append(const char text[], size_t len) { this->insert((size_t)-1, text, len); } + void appendUnichar(SkUnichar uni) { this->insertUnichar((size_t)-1, uni); } + void appendS32(S32 value) { this->insertS32((size_t)-1, value); } + void appendHex(U32 value, int minDigits = 0) { this->insertHex((size_t)-1, value, minDigits); } + void appendScalar(SkScalar value) { this->insertScalar((size_t)-1, value); } + + void prepend(const SkString& str) { this->insert(0, str); } + void prepend(const char text[]) { this->insert(0, text); } + void prepend(const char text[], size_t len) { this->insert(0, text, len); } + void prependUnichar(SkUnichar uni) { this->insertUnichar(0, uni); } + void prependS32(S32 value) { this->insertS32(0, value); } + void prependHex(U32 value, int minDigits = 0) { this->insertHex(0, value, minDigits); } + void prependScalar(SkScalar value) { this->insertScalar((size_t)-1, value); } + + void printf(const char format[], ...); + + void remove(size_t offset, size_t length); + + /** Swap contents between this and other. This function is guaranteed + to never fail or throw. + */ + void swap(SkString& other); + + /** @cond UNIT_TEST */ + SkDEBUGCODE(static void UnitTest();) + /** @endcond */ + +private: +#ifdef SK_DEBUG + const char* fStr; +#endif + struct Rec { + U16 fLength; + U16 fRefCnt; + // data[] + char* data() { return (char*)(this) + sizeof(Rec); } + const char* data() const { return (const char*)(this) + sizeof(Rec); } + }; + Rec* fRec; + +#ifdef SK_DEBUG + void validate() const; +#else + void validate() const {} +#endif + + static Rec* AllocRec(const char text[], U16CPU len); + static Rec* RefRec(Rec*); +}; + +class SkAutoUCS2 { +public: + SkAutoUCS2(const char utf8[]); + ~SkAutoUCS2(); + + /** This returns the number of ucs2 characters + */ + int count() const { return fCount; } + /** This returns a null terminated ucs2 string + */ + const U16* getUCS2() const { return fUCS2; } + +private: + int fCount; + U16* fUCS2; +}; + +#endif + diff --git a/include/graphics/SkStroke.h b/include/graphics/SkStroke.h new file mode 100644 index 0000000000..8b148a4efc --- /dev/null +++ b/include/graphics/SkStroke.h @@ -0,0 +1,58 @@ +#ifndef SkStroke_DEFINED +#define SkStroke_DEFINED + +#include "SkPoint.h" +#include "SkPaint.h" + +struct SkRect; +class SkPath; + +#define SK_DefaultStrokeWidth SK_Scalar1 +#define SK_DefaultMiterLimit SkIntToScalar(4) + + +/** \class SkStroke + SkStroke is the utility class that constructs paths by stroking + geometries (lines, rects, ovals, roundrects, paths). This is + invoked when a geometry or text is drawn in a canvas with the + kStroke_Mask bit set in the paint. +*/ +class SkStroke { +public: + SkStroke(); + SkStroke(const SkPaint&); + SkStroke(const SkPaint&, SkScalar width); // width overrides paint.getStrokeWidth() + + SkPaint::Cap getCap() const { return (SkPaint::Cap)fCap; } + void setCap(SkPaint::Cap); + + SkPaint::Join getJoin() const { return (SkPaint::Join)fJoin; } + void setJoin(SkPaint::Join); + +// SkScalar getMiterLimit() const { return fMiterLimit; } + void setMiterLimit(SkScalar); + +// SkScalar getWidth() const { return fWidth; } + void setWidth(SkScalar); + + bool getDoFill() const { return SkToBool(fDoFill); } + void setDoFill(bool doFill) { fDoFill = SkToU8(doFill); } + + void strokeLine(const SkPoint& start, const SkPoint& end, SkPath*) const; + void strokeRect(const SkRect& rect, SkPath*) const; + void strokeOval(const SkRect& oval, SkPath*) const; + void strokeRRect(const SkRect& rect, SkScalar rx, SkScalar ry, SkPath*) const; + void strokePath(const SkPath& path, SkPath*) const; + + //////////////////////////////////////////////////////////////// + +private: + SkScalar fWidth, fMiterLimit; + U8 fCap, fJoin; + SkBool8 fDoFill; + + friend class SkPaint; +}; + +#endif + diff --git a/include/graphics/SkSystemEventTypes.h b/include/graphics/SkSystemEventTypes.h new file mode 100644 index 0000000000..3cf826c4bc --- /dev/null +++ b/include/graphics/SkSystemEventTypes.h @@ -0,0 +1,16 @@ +#ifndef SkSystemEventTypes_DEFINED +#define SkSystemEventTypes_DEFINED + +/* + The goal of these strings is two-fold: + 1) make funny strings (containing at least one char < 32) to avoid colliding with "user" strings + 2) keep them <= 4 bytes, so we can avoid an allocation in SkEvent::setType() +*/ +#define SK_EventType_Delay "\xd" "lay" +#define SK_EventType_Inval "nv" "\xa" "l" +#define SK_EventType_Key "key" "\x1" +#define SK_EventType_OnEnd "on" "\xe" "n" +#define SK_EventType_Unichar "\xc" "har" +#define SK_EventType_KeyUp "key" "\xf" + +#endif diff --git a/include/graphics/SkTDArray.h b/include/graphics/SkTDArray.h new file mode 100644 index 0000000000..d33205b789 --- /dev/null +++ b/include/graphics/SkTDArray.h @@ -0,0 +1,288 @@ +#ifndef SkTDArray_DEFINED +#define SkTDArray_DEFINED + +#include "SkTypes.h" + +template <typename T> class SkTDArray { +public: + SkTDArray() + { + fReserve = fCount = 0; + fArray = NULL; +#ifdef SK_DEBUG + fData = NULL; +#endif + } + SkTDArray(const T src[], U16CPU count) + { + SkASSERT(src || count == 0); + + fReserve = fCount = 0; + fArray = NULL; +#ifdef SK_DEBUG + fData = NULL; +#endif + if (count) + { + fArray = (T*)sk_malloc_throw(count * sizeof(T)); +#ifdef SK_DEBUG + // fData = (T (*)[kDebugArraySize]) fArray; + (T*&)fData = fArray; +#endif + memcpy(fArray, src, sizeof(T) * count); + fReserve = fCount = SkToU16(count); + } + } + SkTDArray(const SkTDArray<T>& src) + { + fReserve = fCount = 0; + fArray = NULL; +#ifdef SK_DEBUG + fData = NULL; +#endif + SkTDArray<T> tmp(src.fArray, src.fCount); + this->swap(tmp); + } + ~SkTDArray() + { + sk_free(fArray); + } + + SkTDArray<T>& operator=(const SkTDArray<T>& src) + { + if (this != &src) + { + if (src.fCount > fReserve) + { + SkTDArray<T> tmp(src.fArray, src.fCount); + this->swap(tmp); + } + else + { + memcpy(fArray, src.fArray, sizeof(T) * src.fCount); + fCount = src.fCount; + } + } + return *this; + } + + friend int operator==(const SkTDArray<T>& a, const SkTDArray<T>& b) + { + return a.fCount == b.fCount && + (a.fCount == 0 || !memcmp(a.fArray, b.fArray, a.fCount * sizeof(T))); + } + + void swap(SkTDArray<T>& other) + { + SkTSwap(fArray, other.fArray); +#ifdef SK_DEBUG + SkTSwap(fData, other.fData); +#endif + SkTSwap(fReserve, other.fReserve); + SkTSwap(fCount, other.fCount); + } + + bool isEmpty() const { return fCount == 0; } + int count() const { return fCount; } + T* begin() const { return fArray; } + T* end() const { return fArray ? fArray + fCount : NULL; } + T& operator[](int index) const { SkASSERT((unsigned)index < fCount); return fArray[index]; } + + void reset() + { + if (fArray) + { + sk_free(fArray); + fArray = NULL; +#ifdef SK_DEBUG + fData = NULL; +#endif + fReserve = fCount = 0; + } + else + { + SkASSERT(fReserve == 0 && fCount == 0); + } + } + + void setCount(U16CPU count) + { + if (count > fReserve) + this->growBy(count - fCount); + else + fCount = SkToU16(count); + } + + void setReserve(U16CPU reserve) + { + if (reserve > fReserve) + { + SkASSERT(reserve > fCount); + U16 count = fCount; + this->growBy(reserve - fCount); + fCount = count; + } + } + + T* prepend() + { + this->growBy(1); + memmove(fArray + 1, fArray, (fCount - 1) * sizeof(T)); + return fArray; + } + + T* append() { return this->append(1, NULL); } + T* append(U16CPU count, const T* src = NULL) + { + unsigned oldCount = fCount; + if (count) + { + SkASSERT(src == NULL || fArray == NULL || + src + count <= fArray || fArray + oldCount <= src); + + this->growBy(count); + if (src) + memcpy(fArray + oldCount, src, sizeof(T) * count); + } + return fArray + oldCount; + } + + T* appendClear() + { + T* result = this->append(); + *result = 0; + return result; + } + + T* insert(U16CPU index) { return this->insert(index, 1, NULL); } + T* insert(U16CPU index, U16CPU count, const T* src = NULL) + { + SkASSERT(count); + int oldCount = fCount; + this->growBy(count); + T* dst = fArray + index; + memmove(dst + count, dst, sizeof(T) * (oldCount - index)); + if (src) + memcpy(dst, src, sizeof(T) * count); + return dst; + } + + void remove(U16CPU index, U16CPU count = 1) + { + SkASSERT(index + count <= fCount); + fCount = SkToU16(fCount - count); + memmove(fArray + index, fArray + index + count, sizeof(T) * (fCount - index)); + } + + void removeShuffle(U16CPU index) + { + SkASSERT(index < fCount); + unsigned newCount = fCount - 1; + fCount = SkToU16(newCount); + if (index != newCount) + memcpy(fArray + index, fArray + newCount, sizeof(T)); + } + + int find(const T& elem) const + { + const T* iter = fArray; + const T* stop = fArray + fCount; + + for (; iter < stop; iter++) + { + if (*iter == elem) + return (int) (iter - fArray); + } + return -1; + } + + int rfind(const T& elem) const + { + const T* iter = fArray + fCount; + const T* stop = fArray; + + while (iter > stop) + { + if (*--iter == elem) + return iter - stop; + } + return -1; + } + + // routines to treat the array like a stack + T* push() { return this->append(); } + void push(T& elem) { *this->append() = elem; } + const T& top() const { return (*this)[fCount - 1]; } + T& top() { return (*this)[fCount - 1]; } + void pop(T* elem) { if (elem) *elem = (*this)[fCount - 1]; --fCount; } + void pop() { --fCount; } + + void deleteAll() + { + T* iter = fArray; + T* stop = fArray + fCount; + while (iter < stop) + { + delete (*iter); + iter += 1; + } + this->reset(); + } + + void freeAll() + { + T* iter = fArray; + T* stop = fArray + fCount; + while (iter < stop) + { + sk_free(*iter); + iter += 1; + } + this->reset(); + } + + void unrefAll() + { + T* iter = fArray; + T* stop = fArray + fCount; + while (iter < stop) + { + (*iter)->unref(); + iter += 1; + } + this->reset(); + } + +private: +#ifdef SK_DEBUG + enum { + kDebugArraySize = 16 + }; + T(* fData)[kDebugArraySize]; +#endif + T* fArray; + U16 fReserve, fCount; + + void growBy(U16CPU extra) + { + SkASSERT(extra); + SkASSERT(fCount + extra <= 0xFFFF); + + if (fCount + extra > fReserve) + { + size_t size = fCount + extra + 4; + size += size >> 2; + + fArray = (T*)sk_realloc_throw(fArray, size * sizeof(T)); +#ifdef SK_DEBUG + // fData = (T (*)[kDebugArraySize]) fArray; + (T*&)fData = fArray; +#endif + fReserve = SkToU16((U16CPU)size); + } + fCount = SkToU16(fCount + extra); + } +}; + +#endif + diff --git a/include/graphics/SkTDStack.h b/include/graphics/SkTDStack.h new file mode 100644 index 0000000000..ccd7b8e79e --- /dev/null +++ b/include/graphics/SkTDStack.h @@ -0,0 +1,104 @@ +#ifndef SkTDStack_DEFINED +#define SkTDStack_DEFINED + +#include "SkTypes.h" + +template <typename T> class SkTDStack { +public: + SkTDStack() : fCount(0), fTotalCount(0) + { + fInitialRec.fNext = nil; + fRec = &fInitialRec; + + // fCount = kSlotCount; + } + ~SkTDStack() + { + Rec* rec = fRec; + while (rec != &fInitialRec) + { + Rec* next = rec->fNext; + sk_free(rec); + rec = next; + } + } + + int count() const { return fTotalCount; } + + T* push() + { + SkASSERT(fCount <= kSlotCount); + if (fCount == kSlotCount) + { + Rec* rec = (Rec*)sk_malloc_throw(sizeof(Rec)); + rec->fNext = fRec; + fRec = rec; + fCount = 0; + } + ++fTotalCount; + return &fRec->fSlots[fCount++]; + } + void push(const T& elem) { *this->push() = elem; } + const T& index(int idx) const + { + SkASSERT(fRec && fCount > idx); + return fRec->fSlots[fCount - idx - 1]; + } + T& index(int idx) + { + SkASSERT(fRec && fCount > idx); + return fRec->fSlots[fCount - idx - 1]; + } + const T& top() const + { + SkASSERT(fRec && fCount > 0); + return fRec->fSlots[fCount - 1]; + } + T& top() + { + SkASSERT(fRec && fCount > 0); + return fRec->fSlots[fCount - 1]; + } + void pop(T* elem) + { + if (elem) + *elem = fRec->fSlots[fCount - 1]; + this->pop(); + } + void pop() + { + SkASSERT(fCount > 0 && fRec); + --fTotalCount; + if (--fCount == 0) + { + if (fRec != &fInitialRec) + { + Rec* rec = fRec->fNext; + sk_free(fRec); + fCount = kSlotCount; + fRec = rec; + } + else + SkASSERT(fTotalCount == 0); + } + } + +private: + enum { + kSlotCount = 8 + }; + + struct Rec; + friend struct Rec; + + struct Rec { + Rec* fNext; + T fSlots[kSlotCount]; + }; + Rec fInitialRec; + Rec* fRec; + int fCount, fTotalCount; +}; + +#endif + diff --git a/include/graphics/SkTDict.h b/include/graphics/SkTDict.h new file mode 100644 index 0000000000..c717ad15df --- /dev/null +++ b/include/graphics/SkTDict.h @@ -0,0 +1,154 @@ +#ifndef SkTDict_DEFINED +#define SkTDict_DEFINED + +#include "SkChunkAlloc.h" +//#include "SkTemplates.h" +#include "SkTSearch.h" +#include "SkTDArray.h" + +template <typename T> class SkTDict { +public: + SkTDict(size_t minStringAlloc) : fStrings(minStringAlloc) {} + + void reset() + { + fArray.reset(); + fStrings.reset(); + } + + int count() const { return fArray.count(); } + + bool set(const char name[], const T& value) + { + return set(name, strlen(name), value); + } + + bool set(const char name[], size_t len, const T& value) + { + SkASSERT(name); + + int index = this->find_index(name, len); + + if (index >= 0) + { + fArray[index].fValue = value; + return false; + } + else + { + Pair* pair = fArray.insert(~index); + char* copy = (char*)fStrings.alloc(len + 1, SkChunkAlloc::kThrow_AllocFailType); + memcpy(copy, name, len); + copy[len] = '\0'; + pair->fName = copy; + pair->fValue = value; + return true; + } + } + + bool find(const char name[]) const + { + return this->find_index(name) >= 0; + } + + bool find(const char name[], size_t len) const + { + return this->find_index(name, len) >= 0; + } + + bool find(const char name[], T* value) const + { + return find(name, strlen(name), value); + } + + bool find(const char name[], size_t len, T* value) const + { + int index = this->find_index(name, len); + + if (index >= 0) + { + if (value) + *value = fArray[index].fValue; + return true; + } + return false; + } + + bool findKey(T& value, const char** name) const + { + Pair* end = fArray.end(); + for (Pair* pair = fArray.begin(); pair < end; pair++) { + if (pair->fValue != value) + continue; + *name = pair->fName; + return true; + } + return false; + } + +public: + struct Pair { + const char* fName; + T fValue; + + friend int operator<(const Pair& a, const Pair& b) + { + return strcmp(a.fName, b.fName); + } + friend int operator!=(const Pair& a, const Pair& b) + { + return strcmp(a.fName, b.fName); + } + }; + friend class Iter; + +public: + class Iter { + public: + Iter(const SkTDict<T>& dict) + { + fIter = dict.fArray.begin(); + fStop = dict.fArray.end(); + } + const char* next(T* value) + { + const char* name = nil; + if (fIter < fStop) + { + name = fIter->fName; + if (value) + *value = fIter->fValue; + fIter += 1; + } + return name; + } + private: + Pair* fIter; + Pair* fStop; + }; + +private: + SkTDArray<Pair> fArray; + SkChunkAlloc fStrings; + + int find_index(const char name[]) const + { + return find_index(name, strlen(name)); + } + + int find_index(const char name[], size_t len) const + { + SkASSERT(name); + + int count = fArray.count(); + int index = ~0; + + if (count) + index = SkStrSearch(&fArray.begin()->fName, count, name, len, sizeof(Pair)); + return index; + } + friend class Iter; +}; + +#endif + diff --git a/include/graphics/SkTSearch.h b/include/graphics/SkTSearch.h new file mode 100644 index 0000000000..e76d767d6a --- /dev/null +++ b/include/graphics/SkTSearch.h @@ -0,0 +1,56 @@ +#ifndef SkTSearch_DEFINED +#define SkTSearch_DEFINED + +#include "SkTypes.h" + +template <typename T> +int SkTSearch(const T* base, int count, T target, size_t elemSize) +{ + SkASSERT(base != nil); + SkASSERT(count >= 0); + + if (count <= 0) + return ~0; + + int lo = 0; + int hi = count - 1; + + while (lo < hi) + { + int mid = (hi + lo) >> 1; + const T* elem = (const T*)((const char*)base + mid * elemSize); + + if (*elem < target) + lo = mid + 1; + else + hi = mid; + } + + const T* elem = (const T*)((const char*)base + hi * elemSize); + if (*elem != target) + { + if (*elem < target) + hi += 1; + hi = ~hi; + } + return hi; +} + +int SkStrSearch(const char*const* base, int count, const char target[], size_t target_len, size_t elemSize); +int SkStrSearch(const char*const* base, int count, const char target[], size_t elemSize); + +/** Like SkStrSearch, but treats target as if it were all lower-case. Assumes that + base points to a table of lower-case strings. +*/ +int SkStrLCSearch(const char*const* base, int count, const char target[], size_t target_len, size_t elemSize); +int SkStrLCSearch(const char*const* base, int count, const char target[], size_t elemSize); + +extern "C" { + typedef int (*SkQSortCompareProc)(const void*, const void*); + void SkQSort(void* base, size_t count, size_t elemSize, SkQSortCompareProc); +} + +SkDEBUGCODE(void SkQSort_UnitTest();) + +#endif + diff --git a/include/graphics/SkTextBox.h b/include/graphics/SkTextBox.h new file mode 100644 index 0000000000..1a7a3b6421 --- /dev/null +++ b/include/graphics/SkTextBox.h @@ -0,0 +1,61 @@ +#ifndef SkTextBox_DEFINED +#define SkTextBox_DEFINED + +#include "SkCanvas.h" + +/** \class SkTextBox + + SkTextBox is a helper class for drawing 1 or more lines of text + within a rectangle. The textbox is positioned and clipped by its Frame. + The Margin rectangle controls where the text is drawn relative to + the Frame. Line-breaks occur inside the Margin rectangle. + + Spacing is a linear equation used to compute the distance between lines + of text. Spacing consists of two scalars: mul and add, and the spacing + between lines is computed as: spacing = paint.getTextSize() * mul + add +*/ +class SkTextBox { +public: + SkTextBox(); + + enum Mode { + kOneLine_Mode, + kLineBreak_Mode, + + kModeCount + }; + Mode getMode() const { return (Mode)fMode; } + void setMode(Mode); + + enum SpacingAlign { + kStart_SpacingAlign, + kCenter_SpacingAlign, + kEnd_SpacingAlign, + + kSpacingAlignCount + }; + SpacingAlign getSpacingAlign() const { return (SpacingAlign)fSpacingAlign; } + void setSpacingAlign(SpacingAlign); + + void getBox(SkRect*) const; + void setBox(const SkRect&); + void setBox(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom); + + void getSpacing(SkScalar* mul, SkScalar* add) const; + void setSpacing(SkScalar mul, SkScalar add); + + void draw(SkCanvas*, const char text[], size_t len, const SkPaint&); + +private: + SkRect fBox; + SkScalar fSpacingMul, fSpacingAdd; + U8 fMode, fSpacingAlign; +}; + +class SkTextLineBreaker { +public: + static int CountLines(const char text[], size_t len, const SkPaint&, SkScalar width); +}; + +#endif + diff --git a/include/graphics/SkTextLayout.h b/include/graphics/SkTextLayout.h new file mode 100644 index 0000000000..4aa938e059 --- /dev/null +++ b/include/graphics/SkTextLayout.h @@ -0,0 +1,44 @@ +#ifndef SkTextLayout_DEFINED +#define SkTextLayout_DEFINED + +#include "SkRefCnt.h" +#include "SkPaint.h" +#include "SkScalar.h" + +class SkTextLayout : public SkRefCnt { +public: + /** Create a textlayout that implements the CSS features of letter-spacing + and word-spacing. It takes values to add to the advance width for each + letter (charExtra) and to add to the advance width for each space + (spaceExtra). + @param charExtra amount to add to every character's advance width + @param spaceExtra amount to add to every space character's advance width + @return a new textlayout subclass that implements tracking + */ + static SkTextLayout* CreateTrackingLayout(SkScalar charExtra, SkScalar spaceExtra); + + class Rec; + + int layout( const SkPaint& paint, + const char* text, size_t byteLength, SkUnicodeWalkerProc proc, + Rec rec[]); + + class Rec { + public: + SkUnichar charCode() const { return fCharCode; } + SkScalar fDeltaAdvance; //!< set by the subclass in onLayout() + + private: + SkUnichar fCharCode; + // to be used in the future + uint16_t fGlyphID; + uint16_t fFlags; + + friend class SkTextLayout; + }; + +protected: + virtual void onLayout(const SkPaint& paint, Rec rec[], int count) {} +}; + +#endif diff --git a/include/graphics/SkTime.h b/include/graphics/SkTime.h new file mode 100644 index 0000000000..efcef8b292 --- /dev/null +++ b/include/graphics/SkTime.h @@ -0,0 +1,32 @@ +#ifndef SkTime_DEFINED +#define SkTime_DEFINED + +#include "SkTypes.h" + +/** \class SkTime + Platform-implemented utilities to return time of day, and millisecond counter. +*/ +class SkTime { +public: + struct DateTime { + U16 fYear; //!< e.g. 2005 + U8 fMonth; //!< 1..12 + U8 fDayOfWeek; //!< 0..6, 0==Sunday + U8 fDay; //!< 1..31 + U8 fHour; //!< 0..23 + U8 fMinute; //!< 0..59 + U8 fSecond; //!< 0..59 + }; + static void GetDateTime(DateTime*); + + static SkMSec GetMSecs(); +}; + +#if defined(SK_DEBUG) && defined(SK_BUILD_FOR_WIN32) + extern SkMSec gForceTickCount; +#endif + +#define SK_TIME_FACTOR 1 + +#endif + diff --git a/include/graphics/SkTransparentShader.h b/include/graphics/SkTransparentShader.h new file mode 100644 index 0000000000..3146caf99a --- /dev/null +++ b/include/graphics/SkTransparentShader.h @@ -0,0 +1,23 @@ +#ifndef SkTransparentShader_DEFINED +#define SkTransparentShader_DEFINED + +#include "SkShader.h" + +class SkTransparentShader : public SkShader { +public: + virtual U32 getFlags(); + virtual bool setContext( const SkBitmap& device, + const SkPaint& paint, + const SkMatrix& matrix); + virtual void shadeSpan(int x, int y, SkPMColor[], int count); + virtual void shadeSpanOpaque16(int x, int y, U16 span[], int count); + +private: + SkBitmap fDevice; + U8 fAlpha; + + typedef SkShader INHERITED; +}; + +#endif + diff --git a/include/graphics/SkTypeface.h b/include/graphics/SkTypeface.h new file mode 100644 index 0000000000..cd0c21fbaf --- /dev/null +++ b/include/graphics/SkTypeface.h @@ -0,0 +1,67 @@ +#ifndef SkTypeface_DEFINED +#define SkTypeface_DEFINED + +#include "SkRefCnt.h" + +/** \class SkTypeface + + The SkTypeface class specifies the typeface and intrinsic style of a font. + This is used in the paint, along with optionally algorithmic settings like + textSize, textSkewX, textScaleX, kFakeBoldText_Mask, to specify + how text appears when drawn (and measured). +*/ +class SkTypeface : public SkRefCnt { +public: + /** Style enum specifies the possible intrinsic style attributes of a given typeface + */ + enum Style { + kNormal = 0, + kBold = 0x01, + kItalic = 0x02, + + // helpers + kBoldItalic = 0x03 + }; + + /** Returns the typeface's intrinsic style attributes + */ + Style getStyle() const { return (Style)fStyle; } + /** Returns true if getStyle() has the kBold bit set. + */ + bool isBold() const { return (fStyle & kBold) != 0; } + /** Returns true if getStyle() has the kItalic bit set. + */ + bool isItalic() const { return (fStyle & kItalic) != 0; } + + /** Create a typeface object given a family name, and option style information. + If NULL is passed for the name, then the "default" font will be chosen. + The resulting typeface object can be queried (getStyle()) to discover what + its "real" style characteristics are. + + @param familyName May be NULL. The name of the font family. + @param s The style (normal, bold, italic) of the type face. + + */ + static SkTypeface* Create(const char familyName[], Style s = kNormal); + /** Create a typeface object that best matches the specified existing typeface + and the specified Style. Use this call if you want to pick a new style + from the same family of an existing typeface object. If family is NULL, + this selects from the default font's family. + + @param family May be NULL. The name of the existing type face. + @param s The style (normal, bold, italic) of the type face. + */ + static SkTypeface* CreateFromTypeface(const SkTypeface* family, Style s); + +protected: + void setStyle(Style s) { + fStyle = SkToU8(s); + } + + SkTypeface() {} + +private: + uint8_t fStyle; +}; + +#endif diff --git a/include/graphics/SkUnitMapper.h b/include/graphics/SkUnitMapper.h new file mode 100644 index 0000000000..0bd00bb03d --- /dev/null +++ b/include/graphics/SkUnitMapper.h @@ -0,0 +1,35 @@ +#ifndef SkUnitMapper_DEFINED +#define SkUnitMapper_DEFINED + +#include "SkRefCnt.h" +#include "SkScalar.h" + +class SkUnitMapper : public SkRefCnt { +public: + /** Given a value in [0..0xFFFF], return a value in the same range. + */ + virtual U16CPU mapUnit16(U16CPU x) = 0; +}; + +/** This returns N values between [0...1] +*/ +class SkDiscreteMapper : public SkUnitMapper { +public: + SkDiscreteMapper(unsigned segments); + // override + virtual U16CPU mapUnit16(U16CPU x); +private: + unsigned fSegments; + SkFract fScale; +}; + +/** This returns 1 - cos(x), to simulate lighting a sphere +*/ +class SkFlipCosineMapper : public SkUnitMapper { +public: + // override + virtual U16CPU mapUnit16(U16CPU x); +}; + +#endif + diff --git a/include/graphics/SkUtils.h b/include/graphics/SkUtils.h new file mode 100644 index 0000000000..3ccba3133e --- /dev/null +++ b/include/graphics/SkUtils.h @@ -0,0 +1,85 @@ +#ifndef SkUtils_DEFINED +#define SkUtils_DEFINED + +#include "SkTypes.h" + +#ifdef FMS_ARCH_ANDROID_ARM + #include "utils/memory.h" + + #define SK_MEMSET16_REDIRECT(dst, value, count) android_memset16(dst, value, (count) << 1) + #define SK_MEMSET32_REDIRECT(dst, value, count) android_memset32(dst, value, (count) << 2) +#endif + +/////////////////////////////////////////////////////////////////////////// + +#ifdef SK_MEMSET16_REDIRECT + #define sk_memset16(dst, value, count) SK_MEMSET16_REDIRECT(dst, value, count) +#else + /** Similar to memset(), but this function assigns a 16bit value into the buffer. + @param buffer The memory to have value copied into it + @param value The 16bit value to be copied into buffer + @param count The number of times value should be copied into the buffer. + */ + void sk_memset16(uint16_t dst[], U16CPU value, int count); +#endif + +#ifdef SK_MEMSET32_REDIRECT + #define sk_memset32(dst, value, count) SK_MEMSET32_REDIRECT(dst, value, count) +#else + /** Similar to memset(), but this function assigns a 32bit value into the buffer. + @param buffer The memory to have value copied into it + @param value The 32bit value to be copied into buffer + @param count The number of times value should be copied into the buffer. + */ + void sk_memset32(uint32_t dst[], uint32_t value, int count); +#endif + + +/////////////////////////////////////////////////////////////////////////// + +#define kMaxBytesInUTF8Sequence 4 + +#ifdef SK_DEBUG + int SkUTF8_LeadByteToCount(unsigned c); +#else + #define SkUTF8_LeadByteToCount(c) ((((0xE5 << 24) >> ((unsigned)c >> 4 << 1)) & 3) + 1) +#endif + +inline int SkUTF8_CountUTF8Bytes(const char utf8[]) +{ + SkASSERT(utf8); + return SkUTF8_LeadByteToCount(*(const uint8_t*)utf8); +} + +int SkUTF8_CountUnichars(const char utf8[]); +int SkUTF8_CountUnichars(const char utf8[], size_t byteLength); +SkUnichar SkUTF8_ToUnichar(const char utf8[]); +SkUnichar SkUTF8_NextUnichar(const char**); + +/** Return the number of bytes need to convert a unichar + into a utf8 sequence. Will be 1..kMaxBytesInUTF8Sequence, + or 0 if uni is illegal. +*/ +size_t SkUTF8_FromUnichar(SkUnichar uni, char utf8[] = nil); + +///////////////////////////////////////////////////////////////////////////////// + +#define SkUTF16_IsHighSurrogate(c) (((c) & 0xFC00) == 0xD800) +#define SkUTF16_IsLowSurrogate(c) (((c) & 0xFC00) == 0xDC00) + +int SkUTF16_CountUnichars(const uint16_t utf16[]); +int SkUTF16_CountUnichars(const uint16_t utf16[], int numberOf16BitValues); +SkUnichar SkUTF16_NextUnichar(const U16**); +size_t SkUTF16_FromUnichar(SkUnichar uni, uint16_t utf16[] = nil); + +size_t SkUTF16_ToUTF8(const uint16_t utf16[], int numberOf16BitValues, char utf8[] = nil); + +class SkUtils { +public: +#ifdef SK_DEBUG + static void UnitTest(); +#endif +}; + +#endif + diff --git a/include/graphics/SkView.h b/include/graphics/SkView.h new file mode 100644 index 0000000000..a7868d6ecc --- /dev/null +++ b/include/graphics/SkView.h @@ -0,0 +1,328 @@ +#ifndef SkView_DEFINED +#define SkView_DEFINED + +#include "SkEventSink.h" +#include "SkRect.h" +#include "SkDOM.h" +#include "SkTDict.h" + +class SkCanvas; +class SkLayerView; + +/** \class SkView + + SkView is the base class for screen management. All widgets and controls inherit + from SkView. +*/ +class SkView : public SkEventSink { +public: + enum Flag_Shift { + kVisible_Shift, + kEnabled_Shift, + kFocusable_Shift, + kFlexH_Shift, + kFlexV_Shift, + + kFlagShiftCount + }; + enum Flag_Mask { + kVisible_Mask = 1 << kVisible_Shift, //!< set if the view is visible + kEnabled_Mask = 1 << kEnabled_Shift, //!< set if the view is enabled + kFocusable_Mask = 1 << kFocusable_Shift, //!< set if the view can receive focus + kFlexH_Mask = 1 << kFlexH_Shift, //!< set if the view's width is stretchable + kFlexV_Mask = 1 << kFlexV_Shift, //!< set if the view's height is stretchable + + kAllFlagMasks = (U32)(0 - 1) >> (32 - kFlagShiftCount) + }; + + SkView(U32 flags = 0); + virtual ~SkView(); + + /** Return the flags associated with the view + */ + U32 getFlags() const { return fFlags; } + /** Set the flags associated with the view + */ + void setFlags(U32 flags); + + /** Helper that returns non-zero if the kVisible_Mask bit is set in the view's flags + */ + int isVisible() const { return fFlags & kVisible_Mask; } + int isEnabled() const { return fFlags & kEnabled_Mask; } + int isFocusable() const { return fFlags & kFocusable_Mask; } + /** Helper to set/clear the view's kVisible_Mask flag */ + void setVisibleP(bool); + void setEnabledP(bool); + void setFocusableP(bool); + + /** Return the view's width */ + SkScalar width() const { return fWidth; } + /** Return the view's height */ + SkScalar height() const { return fHeight; } + /** Set the view's width and height. These must both be >= 0. This does not affect the view's loc */ + void setSize(SkScalar width, SkScalar height); + void setSize(const SkPoint& size) { this->setSize(size.fX, size.fY); } + void setWidth(SkScalar width) { this->setSize(width, fHeight); } + void setHeight(SkScalar height) { this->setSize(fWidth, height); } + /** Return a rectangle set to [0, 0, width, height] */ + void getLocalBounds(SkRect* bounds) const; + + /** Return the view's left edge */ + SkScalar locX() const { return fLoc.fX; } + /** Return the view's top edge */ + SkScalar locY() const { return fLoc.fY; } + /** Set the view's left and top edge. This does not affect the view's size */ + void setLoc(SkScalar x, SkScalar y); + void setLoc(const SkPoint& loc) { this->setLoc(loc.fX, loc.fY); } + void setLocX(SkScalar x) { this->setLoc(x, fLoc.fY); } + void setLocY(SkScalar y) { this->setLoc(fLoc.fX, y); } + /** Offset (move) the view by the specified dx and dy. This does not affect the view's size */ + void offset(SkScalar dx, SkScalar dy); + + /** Call this to have the view draw into the specified canvas. */ + void draw(SkCanvas* canvas); + /** Call this to invalidate part of all of a view, requesting that the view's + draw method be called. The rectangle parameter specifies the part of the view + that should be redrawn. If it is nil, it specifies the entire view bounds. + */ + void inval(SkRect* rectOrNil); + + // Focus management + + SkView* getFocusView() const; + bool hasFocus() const; + + enum FocusDirection { + kNext_FocusDirection, + kPrev_FocusDirection, + + kFocusDirectionCount + }; + bool acceptFocus(); + SkView* moveFocus(FocusDirection); + + // Click handling + + class Click { + public: + Click(SkView* target); + virtual ~Click(); + + const char* getType() const { return fType; } + bool isType(const char type[]) const; + void setType(const char type[]); // does NOT make a copy of the string + void copyType(const char type[]); // makes a copy of the string + + enum State { + kDown_State, + kMoved_State, + kUp_State + }; + SkPoint fOrig, fPrev, fCurr; + SkPoint16 fIOrig, fIPrev, fICurr; + State fState; + private: + SkEventSinkID fTargetID; + char* fType; + bool fWeOwnTheType; + + void resetType(); + + friend class SkView; + }; + Click* findClickHandler(SkScalar x, SkScalar y); + + static void DoClickDown(Click*, int x, int y); + static void DoClickMoved(Click*, int x, int y); + static void DoClickUp(Click*, int x, int y); + + /** Send the event to the view's parent, and its parent etc. until one of them + returns true from its onEvent call. This view is returned. If no parent handles + the event, nil is returned. + */ + SkView* sendEventToParents(const SkEvent&); + /** Depricated helper function. Just call event->post(sinkID, delay); + */ + bool postEvent(SkEvent* evt, SkEventSinkID sinkID, SkMSec delay) { return evt->post(sinkID, delay); } + + // View hierarchy management + + /** Return the view's parent, or nil if it has none. This does not affect the parent's reference count. */ + SkView* getParent() const { return fParent; } + SkView* attachChildToFront(SkView* child); + /** Attach the child view to this view, and increment the child's reference count. The child view is added + such that it will be drawn before all other child views. + The child view parameter is returned. + */ + SkView* attachChildToBack(SkView* child); + /** If the view has a parent, detach the view from its parent and decrement the view's reference count. + If the parent was the only owner of the view, this will cause the view to be deleted. + */ + void detachFromParent(); + /** Attach the child view to this view, and increment the child's reference count. The child view is added + such that it will be drawn after all other child views. + The child view parameter is returned. + */ + /** Detach all child views from this view. */ + void detachAllChildren(); + + /** Convert the specified point from global coordinates into view-local coordinates + */ + void globalToLocal(SkPoint* pt) const { if (pt) this->globalToLocal(pt->fX, pt->fY, pt); } + /** Convert the specified x,y from global coordinates into view-local coordinates, returning + the answer in the local parameter. + */ + void globalToLocal(SkScalar globalX, SkScalar globalY, SkPoint* local) const; + + /** \class F2BIter + + Iterator that will return each of this view's children, in + front-to-back order (the order used for clicking). The first + call to next() returns the front-most child view. When + next() returns nil, there are no more child views. + */ + class F2BIter { + public: + F2BIter(const SkView* parent); + SkView* next(); + private: + SkView* fFirstChild, *fChild; + }; + + /** \class B2FIter + + Iterator that will return each of this view's children, in + back-to-front order (the order they are drawn). The first + call to next() returns the back-most child view. When + next() returns nil, there are no more child views. + */ + class B2FIter { + public: + B2FIter(const SkView* parent); + SkView* next(); + private: + SkView* fFirstChild, *fChild; + }; + + /** \class Artist + + Install a subclass of this in a view (calling setArtist()), and then the + default implementation of that view's onDraw() will invoke this object + automatically. + */ + class Artist : public SkRefCnt { + public: + void draw(SkView*, SkCanvas*); + void inflate(const SkDOM&, const SkDOM::Node*); + protected: + virtual void onDraw(SkView*, SkCanvas*) = 0; + virtual void onInflate(const SkDOM&, const SkDOM::Node*); + }; + /** Return the artist attached to this view (or nil). The artist's reference + count is not affected. + */ + Artist* getArtist() const; + /** Attach the specified artist (or nil) to the view, replacing any existing + artist. If the new artist is not nil, its reference count is incremented. + The artist parameter is returned. + */ + Artist* setArtist(Artist* artist); + + /** \class Layout + + Install a subclass of this in a view (calling setLayout()), and then the + default implementation of that view's onLayoutChildren() will invoke + this object automatically. + */ + class Layout : public SkRefCnt { + public: + void layoutChildren(SkView* parent); + void inflate(const SkDOM&, const SkDOM::Node*); + protected: + virtual void onLayoutChildren(SkView* parent) = 0; + virtual void onInflate(const SkDOM&, const SkDOM::Node*); + }; + + /** Return the layout attached to this view (or nil). The layout's reference + count is not affected. + */ + Layout* getLayout() const; + /** Attach the specified layout (or nil) to the view, replacing any existing + layout. If the new layout is not nil, its reference count is incremented. + The layout parameter is returned. + */ + Layout* setLayout(Layout*, bool invokeLayoutNow = true); + /** If a layout is attached to this view, call its layoutChildren() method + */ + void invokeLayout(); + + /** Call this to initialize this view based on the specified XML node + */ + void inflate(const SkDOM& dom, const SkDOM::Node* node); + /** After a view hierarchy is inflated, this may be called with a dictionary + containing pairs of <name, view*>, where the name string was the view's + "id" attribute when it was inflated. + + This will call the virtual onPostInflate for this view, and the recursively + call postInflate on all of the view's children. + */ + void postInflate(const SkTDict<SkView*>& ids); + + SkDEBUGCODE(void dump(bool recurse) const;) + +protected: + /** Override this to draw inside the view. Be sure to call the inherited version too */ + virtual void onDraw(SkCanvas*); + /** Override this to be notified when the view's size changes. Be sure to call the inherited version too */ + virtual void onSizeChange(); + /** Override this if you want to handle an inval request from this view or one of its children. + Tyically this is only overridden by the by the "window". If your subclass does handle the + request, return true so the request will not continue to propogate to the parent. + */ + virtual bool handleInval(const SkRect&); + /** Override this if you might handle the click + */ + virtual Click* onFindClickHandler(SkScalar x, SkScalar y); + /** Override this to track clicks, returning true as long as you want to track + the pen/mouse. + */ + virtual bool onClick(Click*); + /** Override this to initialize your subclass from the XML node. Be sure to call the inherited version too */ + virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node); + /** Override this if you want to perform post initialization work based on the ID dictionary built + during XML parsing. Be sure to call the inherited version too. + */ + virtual void onPostInflate(const SkTDict<SkView*>&); + +public: + // default action is to inval the view + virtual void onFocusChange(bool gainFocusP); +protected: + + // override these if you're acting as a layer/host + virtual bool onGetFocusView(SkView**) const { return false; } + virtual bool onSetFocusView(SkView*) { return false; } + +private: + SkScalar fWidth, fHeight; + SkPoint fLoc; + SkView* fParent; + SkView* fFirstChild; + SkView* fNextSibling; + SkView* fPrevSibling; + + U8 fFlags; + U8 fContainsFocus; + + friend class B2FIter; + friend class F2BIter; + + friend class SkLayerView; + + bool setFocusView(SkView* fvOrNil); + SkView* acceptFocus(FocusDirection); + void detachFromParent_NoLayout(); +}; + +#endif + diff --git a/include/graphics/SkViewInflate.h b/include/graphics/SkViewInflate.h new file mode 100644 index 0000000000..5bf9f43d93 --- /dev/null +++ b/include/graphics/SkViewInflate.h @@ -0,0 +1,63 @@ +#ifndef SkViewInflate_DEFINED +#define SkViewInflate_DEFINED + +#include "SkDOM.h" +#include "SkTDict.h" +#include "SkEvent.h" + +class SkView; + +class SkViewInflate { +public: + SkViewInflate(); + virtual ~SkViewInflate(); + + /** Return the tree of inflated views. If root is nil, create the root element + as a view, otherwise assume root is that view, and just "inflate" it. + + Returns nil if the tree cannot be built. + */ + SkView* inflate(const SkDOM& dom, const SkDOM::Node* node, SkView* root = nil); + SkView* inflate(const char xml[], size_t len, SkView* root = nil); + + /** Given an id attribute value, return the corresponding view, or nil + if no match is found. + */ + SkView* findViewByID(const char id[]) const; + + SkDEBUGCODE(void dump() const;) + +protected: + /* Override this in your subclass to handle instantiating views + Call the inherited version for nodes you don't recognize. + + Do not call "inflate" on the view, just return it. This will + get called automatically after createView returns. + */ + virtual SkView* createView(const SkDOM& dom, const SkDOM::Node* node); + /** Base implementation calls view->inflate(dom, node). Subclasses may override this + to perform additional initializations to view, either before or after calling + the inherited version. + */ + virtual void inflateView(SkView* view, const SkDOM& dom, const SkDOM::Node* node); + +private: + enum { + kMinIDStrAlloc = 64 + }; + SkTDict<SkView*> fIDs; + + struct IDStr { + SkView* fView; + char* fStr; + }; + SkTDArray<IDStr> fListenTo, fBroadcastTo; + SkChunkAlloc fStrings; + + void addIDStr(SkTDArray<IDStr>* list, SkView*, const char* str); + + void rInflate(const SkDOM& dom, const SkDOM::Node* node, SkView* parent); +}; + +#endif + diff --git a/include/graphics/SkWidget.h b/include/graphics/SkWidget.h new file mode 100644 index 0000000000..586eb7ad30 --- /dev/null +++ b/include/graphics/SkWidget.h @@ -0,0 +1,460 @@ +#ifndef SkWidget_DEFINED +#define SkWidget_DEFINED + +#include "SkView.h" +#include "SkBitmap.h" +#include "SkDOM.h" +#include "SkPaint.h" +#include "SkString.h" +#include "SkTDArray.h" + +////////////////////////////////////////////////////////////////////////////// + +class SkWidget : public SkView { +public: + SkWidget(U32 flags = 0) : SkView(flags | kFocusable_Mask | kEnabled_Mask) {} + + /** Call this to post the widget's event to its listeners */ + void postWidgetEvent(); + + static void Init(); + static void Term(); +protected: + // override to add slots to an event before posting + virtual void prepareWidgetEvent(SkEvent*); + virtual void onEnabledChange(); + + // <event ...> to initialize the event from XML + virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node); + +private: + SkEvent fEvent; + typedef SkView INHERITED; +}; + +class SkHasLabelWidget : public SkWidget { +public: + SkHasLabelWidget(U32 flags = 0) : SkWidget(flags) {} + + size_t getLabel(SkString* label = nil) const; + size_t getLabel(char lable[] = nil) const; + void setLabel(const SkString&); + void setLabel(const char label[]); + void setLabel(const char label[], size_t len); + +protected: + // called when the label changes + virtual void onLabelChange(); + + // overrides + virtual void onInflate(const SkDOM& dom, const SkDOM::Node*); + +private: + SkString fLabel; + typedef SkWidget INHERITED; +}; + +class SkButtonWidget : public SkHasLabelWidget { +public: + SkButtonWidget(U32 flags = 0) : SkHasLabelWidget(flags), fState(kOff_State) {} + + enum State { + kOff_State, //!< XML: buttonState="off" + kOn_State, //!< XML: buttonState="on" + kUnknown_State //!< XML: buttonState="unknown" + }; + State getButtonState() const { return fState; } + void setButtonState(State); + +protected: + /** called when the label changes. default behavior is to inval the widget */ + virtual void onButtonStateChange(); + + // overrides + virtual void onInflate(const SkDOM& dom, const SkDOM::Node*); + +private: + State fState; + typedef SkHasLabelWidget INHERITED; +}; + +class SkPushButtonWidget : public SkButtonWidget { +public: + SkPushButtonWidget(U32 flags = 0) : SkButtonWidget(flags) {} + +protected: + virtual bool onEvent(const SkEvent&); + virtual void onDraw(SkCanvas*); + virtual Click* onFindClickHandler(SkScalar x, SkScalar y); + virtual bool onClick(Click* click); + +private: + typedef SkButtonWidget INHERITED; +}; + +class SkCheckBoxWidget : public SkButtonWidget { +public: + SkCheckBoxWidget(U32 flags = 0); + +protected: + virtual bool onEvent(const SkEvent&); + virtual void onDraw(SkCanvas*); + virtual void onInflate(const SkDOM& dom, const SkDOM::Node*); + +private: + typedef SkButtonWidget INHERITED; +}; + +#include "SkTextBox.h" + +class SkStaticTextView : public SkView { +public: + SkStaticTextView(U32 flags = 0); + virtual ~SkStaticTextView(); + + enum Mode { + kFixedSize_Mode, + kAutoWidth_Mode, + kAutoHeight_Mode, + + kModeCount + }; + Mode getMode() const { return (Mode)fMode; } + void setMode(Mode); + + SkTextBox::SpacingAlign getSpacingAlign() const { return (SkTextBox::SpacingAlign)fSpacingAlign; } + void setSpacingAlign(SkTextBox::SpacingAlign); + + void getMargin(SkPoint* margin) const; + void setMargin(SkScalar dx, SkScalar dy); + + size_t getText(SkString* text = nil) const; + size_t getText(char text[] = nil) const; + void setText(const SkString&); + void setText(const char text[]); + void setText(const char text[], size_t len); + + void getPaint(SkPaint*) const; + void setPaint(const SkPaint&); + +protected: + // overrides + virtual void onDraw(SkCanvas*); + virtual void onInflate(const SkDOM& dom, const SkDOM::Node*); + +private: + SkPoint fMargin; + SkString fText; + SkPaint fPaint; + U8 fMode; + U8 fSpacingAlign; + + void computeSize(); + + typedef SkView INHERITED; +}; + +class SkBitmapView : public SkView { +public: + SkBitmapView(U32 flags = 0); + virtual ~SkBitmapView(); + + bool getBitmap(SkBitmap*) const; + void setBitmap(const SkBitmap*, bool viewOwnsPixels); + bool loadBitmapFromFile(const char path[]); + +protected: + virtual void onDraw(SkCanvas*); + virtual void onInflate(const SkDOM&, const SkDOM::Node*); + +private: + SkBitmap fBitmap; + typedef SkView INHERITED; +}; + +///////////////////////////////////////////////////////////////////////////// + +class SkShader; +class SkInterpolator; + +class SkWidgetView : public SkView { +public: + SkWidgetView(U32 flags = 0); + virtual ~SkWidgetView(); + + static const char* GetEventType(); +}; + +class SkSliderView : public SkWidgetView { +public: + SkSliderView(U32 flags = 0); + + U16 getValue() const { return fValue; } + U16 getMax() const { return fMax; } + + void setMax(U16CPU max); + void setValue(U16CPU value); + +protected: + virtual void onDraw(SkCanvas*); + virtual Click* onFindClickHandler(SkScalar x, SkScalar y); + virtual bool onClick(Click*); + +private: + U16 fValue, fMax; + + typedef SkWidgetView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +class SkHasLabelView : public SkView { +public: + void getLabel(SkString*) const; + void setLabel(const SkString&); + void setLabel(const char label[]); + +protected: + SkString fLabel; + + // called when the label changes + virtual void onLabelChange(); + + // overrides + virtual void onInflate(const SkDOM& dom, const SkDOM::Node*); +}; + +class SkPushButtonView : public SkHasLabelView { +public: + SkPushButtonView(U32 flags = 0); + +protected: + virtual void onDraw(SkCanvas*); + virtual void onInflate(const SkDOM& dom, const SkDOM::Node*); +}; + +class SkCheckBoxView : public SkHasLabelView { +public: + SkCheckBoxView(U32 flags = 0); + + enum State { + kOff_State, + kOn_State, + kMaybe_State + }; + State getState() const { return fState; } + void setState(State); + +protected: + virtual void onDraw(SkCanvas*); + virtual void onInflate(const SkDOM& dom, const SkDOM::Node*); + +private: + State fState; +}; + +class SkProgressView : public SkView { +public: + SkProgressView(U32 flags = 0); + virtual ~SkProgressView(); + + U16 getValue() const { return fValue; } + U16 getMax() const { return fMax; } + + void setMax(U16CPU max); + void setValue(U16CPU value); + +protected: + virtual void onDraw(SkCanvas*); + virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node); + +private: + U16 fValue, fMax; + SkShader* fOnShader, *fOffShader; + SkInterpolator* fInterp; + bool fDoInterp; + + typedef SkView INHERITED; +}; + +class SkTextView : public SkView { +public: + SkTextView(U32 flags = 0); + virtual ~SkTextView(); + + enum AnimaDir { + kNeutral_AnimDir, + kForward_AnimDir, + kBackward_AnimDir, + kAnimDirCount + }; + + void getText(SkString*) const; + void setText(const SkString&, AnimaDir dir = kNeutral_AnimDir); + void setText(const char text[], AnimaDir dir = kNeutral_AnimDir); + void setText(const char text[], size_t len, AnimaDir dir = kNeutral_AnimDir); + + void getMargin(SkPoint* margin) const; + void setMargin(const SkPoint&); + + SkPaint& paint() { return fPaint; } + +protected: + virtual void onDraw(SkCanvas*); + virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node); + +private: + SkString fText; + SkPaint fPaint; + SkPoint fMargin; + + class Interp; + Interp* fInterp; + bool fDoInterp; + // called by the other setText methods. This guy does not check for != + // before doing the assign, so the caller must check for us + void privSetText(const SkString&, AnimaDir dir); + + typedef SkView INHERITED; +}; + +////////////////////////////////////////////////////////// + +class SkEvent; + +class SkListSource : public SkEventSink { +public: + virtual int countRows() = 0; + virtual void getRow(int index, SkString* left, SkString* right) = 0; + virtual SkEvent* getEvent(int index); + + static SkListSource* CreateFromDir(const char path[], const char suffix[], + const char targetPrefix[]); + static SkListSource* CreateFromDOM(const SkDOM& dom, const SkDOM::Node* node); +}; + +class SkListView : public SkWidgetView { +public: + SkListView(U32 flags = 0); + virtual ~SkListView(); + + SkScalar getRowHeight() const { return fRowHeight; } + void setRowHeight(SkScalar); + + /** Return the index of the selected row, or -1 if none + */ + int getSelection() const { return fCurrIndex; } + /** Set the index of the selected row, or -1 for none + */ + void setSelection(int); + + void moveSelectionUp(); + void moveSelectionDown(); + + enum Attr { + kBG_Attr, + kNormalText_Attr, + kHiliteText_Attr, + kHiliteCell_Attr, + kAttrCount + }; + SkPaint& paint(Attr); + + SkListSource* getListSource() const { return fSource; } + SkListSource* setListSource(SkListSource*); + +#if 0 + enum Action { + kSelectionChange_Action, + kSelectionPicked_Action, + kActionCount + }; + /** If event is not nil, it is retained by the view, and a copy + of the event will be posted to its listeners when the specified + action occurs. If event is nil, then no event will be posted for + the specified action. + */ + void setActionEvent(Action, SkEvent* event); +#endif + +protected: + virtual void onDraw(SkCanvas*); + virtual void onSizeChange(); + virtual bool onEvent(const SkEvent&); + virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node); + +private: + SkPaint fPaint[kAttrCount]; + SkListSource* fSource; + SkScalar fRowHeight; + int fCurrIndex; // logical index + int fScrollIndex; // logical index of top-most visible row + int fVisibleRowCount; + SkString* fStrCache; + + void dirtyStrCache(); + void ensureStrCache(int visibleCount); + + int logicalToVisualIndex(int index) const { return index - fScrollIndex; } + void invalSelection(); + bool getRowRect(int index, SkRect*) const; + void ensureSelectionIsVisible(); + + typedef SkWidgetView INHERITED; +}; + +////////////////////////////////////////////////////////// + +class SkGridView : public SkWidgetView { +public: + SkGridView(U32 flags = 0); + virtual ~SkGridView(); + + void getCellSize(SkPoint*) const; + void setCellSize(SkScalar x, SkScalar y); + + /** Return the index of the selected item, or -1 if none + */ + int getSelection() const { return fCurrIndex; } + /** Set the index of the selected row, or -1 for none + */ + void setSelection(int); + + void moveSelectionUp(); + void moveSelectionDown(); + + enum Attr { + kBG_Attr, + kHiliteCell_Attr, + kAttrCount + }; + SkPaint& paint(Attr); + + SkListSource* getListSource() const { return fSource; } + SkListSource* setListSource(SkListSource*); + +protected: + virtual void onDraw(SkCanvas*); + virtual void onSizeChange(); + virtual bool onEvent(const SkEvent&); + virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node); + +private: + SkView* fScrollBar; + SkPaint fPaint[kAttrCount]; + SkListSource* fSource; + int fCurrIndex; // logical index + + SkPoint fCellSize; + SkPoint16 fVisibleCount; + + int logicalToVisualIndex(int index) const { return index; } + void invalSelection(); + bool getCellRect(int index, SkRect*) const; + void ensureSelectionIsVisible(); + + typedef SkWidgetView INHERITED; +}; + +#endif + diff --git a/include/graphics/SkWidgetViews.h b/include/graphics/SkWidgetViews.h new file mode 100644 index 0000000000..52a3e0d243 --- /dev/null +++ b/include/graphics/SkWidgetViews.h @@ -0,0 +1,294 @@ +#ifndef SkWidgetViews_DEFINED +#define SkWidgetViews_DEFINED + +#include "SkView.h" + + +enum SkWidgetEnum { + kBorder_WidgetEnum, //!< <sk-border> + kButton_WidgetEnum, //!< <sk-button> + kImage_WidgetEnum, //!< <sk-image> + kList_WidgetEnum, //!< <sk-list> + kProgress_WidgetEnum, //!< <sk-progress> + kScroll_WidgetEnum, //!< <sk-scroll> + kText_WidgetEnum, //!< <sk-text> + + kWidgetEnumCount +}; + +//determines which skin to use +enum SkinEnum { + kBorder_SkinEnum, + kButton_SkinEnum, + kProgress_SkinEnum, + kScroll_SkinEnum, + kStaticText_SkinEnum, + + kSkinEnumCount +}; + +#include "SkAnimator.h" +//used for inflates +const char* get_skin_enum_path(SkinEnum se); +void init_skin_anim(const char path[], SkAnimator* anim); +void init_skin_anim(SkinEnum se, SkAnimator* anim); +void init_skin_paint(SkinEnum se, SkPaint* paint); +void inflate_paint(const SkDOM& dom, const SkDOM::Node* node, SkPaint* paint); + +/** Given an enum value, return an instance of the specified widget. + If the enum is out of range, returns nil +*/ +SkView* SkWidgetFactory(SkWidgetEnum); +/** Given the inflate/element name of a widget, return an instance of + the specified widget, or nil if name does not match any known + widget type. +*/ +SkView* SkWidgetFactory(const char name[]); + +//////////////////////////////////////////////////////////////////////////////////////////////// + +class SkWidgetView : public SkView { +public: + SkWidgetView(); + + const char* getLabel() const; + void getLabel(SkString* label) const; + + void setLabel(const char[]); + void setLabel(const char[], size_t len); + void setLabel(const SkString&); + + SkEvent& event() { return fEvent; } + const SkEvent& event() const { return fEvent; } + + /** Returns true if the widget can post its event to its listeners. + */ + bool postWidgetEvent(); + + /** Returns the sinkID of the widgetview that posted the event, or 0 + */ + static SkEventSinkID GetWidgetEventSinkID(const SkEvent&); + +protected: + /** called when the label changes. override in subclasses. default action invals the view's bounds. + called with the old and new labels, before the label has actually changed. + */ + virtual void onLabelChange(const char oldLabel[], const char newLabel[]); + /** called before posting the event to our listeners. Override to add slots to the event + before posting. Return true to proceed with posting, or false to not post the event to any + listener. Note: the event passed in may not be the same as calling this->event(). + Be sure to call your INHERITED method as well, so that all classes in the hierarchy get a shot + at modifying the event (and possibly returning false to abort). + */ + virtual bool onPrepareWidgetEvent(SkEvent* evt); + + // overrides + virtual void onInflate(const SkDOM& dom, const SkDOM::Node*); + +private: + SkString fLabel; + SkEvent fEvent; + + typedef SkView INHERITED; +}; + +//////////////////////////////////////////////////////////////////////////////////////////////// + +class SkButtonView : public SkWidgetView { +public: + // inflate: "sk-button" + +protected: + // overrides + virtual bool onEvent(const SkEvent&); +}; + +//////////////////////////////////////////////////////////////////////////////////////////////// + +class SkCheckButtonView : public SkWidgetView { +public: + SkCheckButtonView(); + + // inflate: "sk-checkbutton" + + enum CheckState { + kOff_CheckState, //!< inflate: check-state="off" + kOn_CheckState, //!< inflate: check-state="on" + kUnknown_CheckState //!< inflate: check-state="unknown" + }; + CheckState getCheckState() const { return (CheckState)fCheckState; } + void setCheckState(CheckState); + + /** use this to extract the CheckState from an event (i.e. one that as posted + by a SkCheckButtonView). Returns true if the proper slot was present in the event, + and sets state to that value. If no proper slot is found, returns false and does not + modify state. + */ + static bool GetWidgetEventCheckState(const SkEvent&, CheckState* state); + +protected: + // called when the check-state is about to change, but before it actually has + virtual void onCheckStateChange(CheckState oldState, CheckState newState); + + // overrides + virtual void onInflate(const SkDOM& dom, const SkDOM::Node*); + virtual bool onPrepareWidgetEvent(SkEvent* evt); + +private: + U8 fCheckState; + + typedef SkWidgetView INHERITED; +}; + +//////////////////////////////////////////////////////////////////////////////////////////////// +#include "SkTextBox.h" + +class SkStaticTextView : public SkView { +public: + SkStaticTextView(); + virtual ~SkStaticTextView(); + + enum Mode { + kFixedSize_Mode, + kAutoWidth_Mode, + kAutoHeight_Mode, + + kModeCount + }; + Mode getMode() const { return (Mode)fMode; } + void setMode(Mode); + + SkTextBox::SpacingAlign getSpacingAlign() const { return (SkTextBox::SpacingAlign)fSpacingAlign; } + void setSpacingAlign(SkTextBox::SpacingAlign); + + void getMargin(SkPoint* margin) const; + void setMargin(SkScalar dx, SkScalar dy); + + size_t getText(SkString* text = nil) const; + size_t getText(char text[] = nil) const; + void setText(const SkString&); + void setText(const char text[]); + void setText(const char text[], size_t len); + + void getPaint(SkPaint*) const; + void setPaint(const SkPaint&); + +protected: + // overrides + virtual void onDraw(SkCanvas*); + virtual void onInflate(const SkDOM& dom, const SkDOM::Node*); + +private: + SkPoint fMargin; + SkString fText; + SkPaint fPaint; + U8 fMode; + U8 fSpacingAlign; + + void computeSize(); + + typedef SkView INHERITED; +}; + +//////////////////////////////////////////////////////////////////////////////////////////////// + +class SkAnimator; +class SkListSource; +class SkScrollBarView; + +class SkListView : public SkWidgetView { +public: + SkListView(); + virtual ~SkListView(); + + bool hasScrollBar() const { return fScrollBar != nil; } + void setHasScrollBar(bool); + + /** Return the number of visible rows + */ + int getVisibleRowCount() const { return fVisibleRowCount; } + /** Return the index of the selected row, or -1 if none + */ + int getSelection() const { return fCurrIndex; } + /** Set the index of the selected row, or -1 for none + */ + void setSelection(int); + /** If possible, move the selection up and return true, + else do nothing and return false + If nothing is selected, select the last item (unless there are no items). + */ + bool moveSelectionUp(); + /** If possible, move the selection down and return true, + else do nothing and return false. + If nothing is selected, select the first item (unless there are no items). + */ + bool moveSelectionDown(); + + SkListSource* getListSource() const { return fSource; } + SkListSource* setListSource(SkListSource*); + + /** Call this in your event handler. If the specified event is from a SkListView, + then it returns the index of the selected item in this list, otherwise it + returns -1 + */ + static int GetWidgetEventListIndex(const SkEvent&); + +protected: + // overrides + virtual void onDraw(SkCanvas*); + virtual void onSizeChange(); + virtual bool onEvent(const SkEvent&); + virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node); + virtual bool onPrepareWidgetEvent(SkEvent*); + +private: + enum DirtyFlags { + kAnimCount_DirtyFlag = 0x01, + kAnimContent_DirtyFlag = 0x02 + }; + void dirtyCache(unsigned dirtyFlags); + bool ensureCache(); + + int logicalToVisualIndex(int index) const { return index - fScrollIndex; } + void invalSelection(); + SkScalar getContentWidth() const; + bool getRowRect(int index, SkRect*) const; + void ensureSelectionIsVisible(); + void ensureVisibleRowCount(); + + struct BindingRec; + + enum Heights { + kNormal_Height, + kSelected_Height + }; + SkListSource* fSource; + SkScrollBarView* fScrollBar; + SkAnimator* fAnims; + BindingRec* fBindings; + SkString fSkinName; + SkScalar fHeights[2]; + S16 fScrollIndex, fCurrIndex; + U16 fVisibleRowCount, fBindingCount; + SkBool8 fAnimContentDirty; + SkBool8 fAnimFocusDirty; + + typedef SkWidgetView INHERITED; +}; + +class SkListSource : public SkRefCnt { +public: + virtual int countFields(); + virtual void getFieldName(int index, SkString* field); + /** Return the index of the named field, or -1 if not found */ + virtual int findFieldIndex(const char field[]); + + virtual int countRecords(); + virtual void getRecord(int rowIndex, int fieldIndex, SkString* data); + + virtual bool prepareWidgetEvent(SkEvent*, int rowIndex); + + static SkListSource* Factory(const char name[]); +}; + +#endif diff --git a/include/graphics/SkWindow.h b/include/graphics/SkWindow.h new file mode 100644 index 0000000000..8132b115f5 --- /dev/null +++ b/include/graphics/SkWindow.h @@ -0,0 +1,84 @@ +#ifndef SkWindow_DEFINED +#define SkWindow_DEFINED + +#include "SkView.h" +#include "SkBitmap.h" +#include "SkRegion.h" +#include "SkEvent.h" +#include "SkKey.h" +#include "SkTDArray.h" + +#ifdef SK_BUILD_FOR_WINCEx + #define SHOW_FPS +#endif +//#define USE_GX_SCREEN + +class SkOSMenu; + +class SkWindow : public SkView { +public: + SkWindow(); + virtual ~SkWindow(); + + const SkBitmap& getBitmap() const { return fBitmap; } + + void setConfig(SkBitmap::Config); + void resize(int width, int height, SkBitmap::Config config = SkBitmap::kNo_Config); + void eraseARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b); + void eraseRGB(U8CPU r, U8CPU g, U8CPU b); + + bool isDirty() const { return !fDirtyRgn.isEmpty(); } + bool update(SkRect16* updateArea); + bool handleClick(int x, int y, Click::State); + bool handleChar(SkUnichar); + bool handleKey(SkKey); + bool handleKeyUp(SkKey); + bool handleMenu(U32 os_cmd); + + void addMenu(SkOSMenu*); + +protected: + virtual bool onEvent(const SkEvent&); + + // called if part of our bitmap is invalidated + virtual void onHandleInval(const SkRect16&); + virtual bool onHandleChar(SkUnichar); + virtual bool onHandleKey(SkKey); + virtual bool onHandleKeyUp(SkKey); + virtual void onAddMenu(const SkOSMenu*) {} + + // overrides from SkView + virtual bool handleInval(const SkRect&); + virtual bool onGetFocusView(SkView** focus) const; + virtual bool onSetFocusView(SkView* focus); + +private: + SkBitmap::Config fConfig; + SkBitmap fBitmap; + SkRegion fDirtyRgn; + Click* fClick; // to track clicks + + SkTDArray<SkOSMenu*> fMenus; + + SkView* fFocusView; + bool fWaitingOnInval; + + typedef SkView INHERITED; +}; + +/////////////////////////////////////////////////////////// + +#ifndef SK_USE_WXWIDGETS +#ifdef SK_BUILD_FOR_MAC + #include "SkOSWindow_Mac.h" +#elif defined(SK_BUILD_FOR_WIN) + #include "SkOSWindow_Win.h" +#elif defined(SK_BUILD_FOR_UNIXx) + #include "SkOSWindow_Unix.h" +#endif +#else + #include "SkOSWindow_wxwidgets.h" +#endif + +#endif + diff --git a/include/graphics/SkXMLParser.h b/include/graphics/SkXMLParser.h new file mode 100644 index 0000000000..1255822161 --- /dev/null +++ b/include/graphics/SkXMLParser.h @@ -0,0 +1,79 @@ +#ifndef SkXMLParser_DEFINED +#define SkXMLParser_DEFINED + +#include "SkMath.h" +#include "SkString.h" + +class SkStream; + +class SkDOM; +struct SkDOMNode; + +class SkXMLParserError { +public: + enum ErrorCode { + kNoError, + kEmptyFile, + kUnknownElement, + kUnknownAttributeName, + kErrorInAttributeValue, + kDuplicateIDs, + kUnknownError + }; + + SkXMLParserError(); + virtual ~SkXMLParserError(); + ErrorCode getErrorCode() const { return fCode; } + virtual void getErrorString(SkString* str) const; + int getLineNumber() const { return fLineNumber; } + int getNativeCode() const { return fNativeCode; } + bool hasError() const { return fCode != kNoError || fNativeCode != -1; } + bool hasNoun() const { return fNoun.size() > 0; } + void reset(); + void setCode(ErrorCode code) { fCode = code; } + void setNoun(const SkString& str) { fNoun.set(str); } + void setNoun(const char* ch) { fNoun.set(ch); } + void setNoun(const char* ch, size_t len) { fNoun.set(ch, len); } +protected: + ErrorCode fCode; +private: + int fLineNumber; + int fNativeCode; + SkString fNoun; + friend class SkXMLParser; +}; + +class SkXMLParser { +public: + SkXMLParser(SkXMLParserError* parserError = nil); + virtual ~SkXMLParser(); + + /** Returns true for success + */ + bool parse(const char doc[], size_t len); + bool parse(SkStream& docStream); + bool parse(const SkDOM&, const SkDOMNode*); + + static void GetNativeErrorString(int nativeErrorCode, SkString* str); + +protected: + // override in subclasses; return true to stop parsing + virtual bool onStartElement(const char elem[]); + virtual bool onAddAttribute(const char name[], const char value[]); + virtual bool onEndElement(const char elem[]); + virtual bool onText(const char text[], int len); + +public: + // public for ported implementation, not meant for clients to call + virtual bool startElement(const char elem[]); + virtual bool addAttribute(const char name[], const char value[]); + virtual bool endElement(const char elem[]); + virtual bool text(const char text[], int len); + void* fParser; +protected: + SkXMLParserError* fError; +private: + void reportError(void* parser); +}; + +#endif diff --git a/include/graphics/SkXMLWriter.h b/include/graphics/SkXMLWriter.h new file mode 100644 index 0000000000..cc2c55a023 --- /dev/null +++ b/include/graphics/SkXMLWriter.h @@ -0,0 +1,77 @@ +#ifndef SkXMLWriter_DEFINED +#define SkXMLWriter_DEFINED + +#include "SkTDArray.h" +#include "SkString.h" +#include "SkDOM.h" + +class SkWStream; +class SkXMLParser; + +class SkXMLWriter { +public: + SkXMLWriter(bool doEscapeMarkup = true); + virtual ~SkXMLWriter(); + + void addS32Attribute(const char name[], S32 value); + void addAttribute(const char name[], const char value[]); + void addAttributeLen(const char name[], const char value[], size_t length); + void addHexAttribute(const char name[], U32 value, int minDigits = 0); + void addScalarAttribute(const char name[], SkScalar value); + void endElement() { this->onEndElement(); } + void startElement(const char elem[]); + void startElementLen(const char elem[], size_t length); + void writeDOM(const SkDOM&, const SkDOM::Node*, bool skipRoot); + void flush(); + virtual void writeHeader(); + +protected: + virtual void onStartElementLen(const char elem[], size_t length) = 0; + virtual void onAddAttributeLen(const char name[], const char value[], size_t length) = 0; + virtual void onEndElement() = 0; + + struct Elem { + SkString fName; + bool fHasChildren; + }; + void doEnd(Elem* elem); + bool doStart(const char name[], size_t length); + Elem* getEnd(); + const char* getHeader(); + SkTDArray<Elem*> fElems; + +private: + bool fDoEscapeMarkup; + // illegal + SkXMLWriter& operator=(const SkXMLWriter&); +}; + +class SkXMLStreamWriter : public SkXMLWriter { +public: + SkXMLStreamWriter(SkWStream*); + virtual ~SkXMLStreamWriter(); + virtual void writeHeader(); + SkDEBUGCODE(static void UnitTest();) +protected: + virtual void onStartElementLen(const char elem[], size_t length); + virtual void onEndElement(); + virtual void onAddAttributeLen(const char name[], const char value[], size_t length); +private: + SkWStream& fStream; +}; + +class SkXMLParserWriter : public SkXMLWriter { +public: + SkXMLParserWriter(SkXMLParser*); + virtual ~SkXMLParserWriter(); +protected: + virtual void onStartElementLen(const char elem[], size_t length); + virtual void onEndElement(); + virtual void onAddAttributeLen(const char name[], const char value[], size_t length); +private: + SkXMLParser& fParser; +}; + + +#endif + diff --git a/include/graphics/SkXfermode.h b/include/graphics/SkXfermode.h new file mode 100644 index 0000000000..9a6faaded0 --- /dev/null +++ b/include/graphics/SkXfermode.h @@ -0,0 +1,62 @@ +#ifndef SkXfermode_DEFINED +#define SkXfermode_DEFINED + +#include "SkFlattenable.h" +#include "SkColor.h" + +/** \class SkXfermode + + SkXfermode is the base class for objects that are called to implement custom + "transfer-modes" in the drawing pipeline. The static function Create(Modes) + can be called to return an instance of any of the predefined subclasses as + specified in the Modes enum. When an SkXfermode is assigned to an SkPaint, then + objects drawn with that paint have the xfermode applied. +*/ +class SkXfermode : public SkFlattenable { +public: + SkXfermode() {} + + virtual void xfer32(SkPMColor dst[], const SkPMColor src[], int count, const SkAlpha aa[]); + virtual void xfer16(uint16_t dst[], const SkPMColor src[], int count, const SkAlpha aa[]); + virtual void xferA8(SkAlpha dst[], const SkPMColor src[], int count, const SkAlpha aa[]); + +protected: + SkXfermode(SkRBuffer&) {} + +private: + typedef SkFlattenable INHERITED; +}; + +//////////////////////////////////////////////////////////////////////////////// + +/** \class SkProcXfermode + + SkProcXfermode is a xfermode that applies the specified proc to its colors. + This class is not exported to java. +*/ +class SkProcXfermode : public SkXfermode { +public: + SkProcXfermode(SkXfermodeProc proc) : fProc(proc) {} + + // overrides from SkXfermode + virtual void xfer32(SkPMColor dst[], const SkPMColor src[], int count, const SkAlpha aa[]); + virtual void xfer16(uint16_t dst[], const SkPMColor src[], int count, const SkAlpha aa[]); + virtual void xferA8(SkAlpha dst[], const SkPMColor src[], int count, const SkAlpha aa[]); + + // overrides from SkFlattenable + virtual Factory getFactory(); + virtual void flatten(SkWBuffer&); + +protected: + SkProcXfermode(SkRBuffer&); + +private: + SkXfermodeProc fProc; + + static SkFlattenable* CreateProc(SkRBuffer&); + + typedef SkXfermode INHERITED; +}; + +#endif + diff --git a/libs/corecg/Makefile b/libs/corecg/Makefile new file mode 100644 index 0000000000..2e78667e69 --- /dev/null +++ b/libs/corecg/Makefile @@ -0,0 +1,30 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + Sk64.cpp \ + SkBuffer.cpp \ + SkChunkAlloc.cpp \ + SkCordic.cpp \ + SkDebug.cpp \ + SkDebug_stdio.cpp \ + SkFloat.cpp \ + SkMath.cpp \ + SkMatrix.cpp \ + SkMemory_stdlib.cpp \ + SkPoint.cpp \ + SkRect.cpp \ + SkRegion.cpp + +LOCAL_SHARED_LIBRARIES := \ + libutils + +LOCAL_C_INCLUDES += \ + include/corecg + +#LOCAL_CFLAGS+= +#LOCAL_LDFLAGS:= + +LOCAL_TARGET:= libcorecg + +include $(BUILD_SHARED_LIBRARY) diff --git a/libs/corecg/Sk64.cpp b/libs/corecg/Sk64.cpp new file mode 100644 index 0000000000..3a557fb609 --- /dev/null +++ b/libs/corecg/Sk64.cpp @@ -0,0 +1,553 @@ +#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 SK_CAN_USE_LONGLONG + 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 { + S8 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 SK_CAN_USE_LONGLONG + 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 SK_CAN_USE_LONGLONG + 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_FLOAT + 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; + SkASSERT(SkAbs32(diff) <= 1); +#endif + } +#endif +#endif +} + +#endif + diff --git a/libs/corecg/SkBuffer.cpp b/libs/corecg/SkBuffer.cpp new file mode 100644 index 0000000000..8f4792f3af --- /dev/null +++ b/libs/corecg/SkBuffer.cpp @@ -0,0 +1,106 @@ +#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; +} + +size_t SkRBuffer::skipToAlign4() +{ + size_t pos = this->pos(); + size_t n = SkAlign4(pos) - pos; + fPos += n; + return n; +} + +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/libs/corecg/SkChunkAlloc.cpp b/libs/corecg/SkChunkAlloc.cpp new file mode 100644 index 0000000000..cfeccc00c9 --- /dev/null +++ b/libs/corecg/SkChunkAlloc.cpp @@ -0,0 +1,45 @@ +#include "SkChunkAlloc.h" + +SkChunkAlloc::~SkChunkAlloc() +{ + this->reset(); +} + +void SkChunkAlloc::reset() +{ + Block* block = fBlock; + + while (block) + { + Block* next = block->fNext; + sk_free(block); + block = next; + } + fBlock = nil; +} + +void* SkChunkAlloc::alloc(size_t bytes, AllocFailType ftype) +{ + bytes = SkAlign4(bytes); + + if (fBlock == nil || bytes > fBlock->fFreeSize) + { + size_t size = SkMax32((S32)bytes, (S32)fMinSize); + Block* block = (Block*)sk_malloc_flags(sizeof(Block) + size, ftype == kThrow_AllocFailType ? SK_MALLOC_THROW : 0); + if (block == nil) + return nil; + + block->fNext = fBlock; + block->fFreeSize = size; + block->fFreePtr = (char*)block + sizeof(Block); + fBlock = block; + } + + SkASSERT(fBlock && bytes <= fBlock->fFreeSize); + void* ptr = fBlock->fFreePtr; + + fBlock->fFreeSize -= bytes; + fBlock->fFreePtr += bytes; + return ptr; +} + diff --git a/libs/corecg/SkCordic.cpp b/libs/corecg/SkCordic.cpp new file mode 100644 index 0000000000..32266e9cc1 --- /dev/null +++ b/libs/corecg/SkCordic.cpp @@ -0,0 +1,284 @@ +#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/libs/corecg/SkCordic.h b/libs/corecg/SkCordic.h new file mode 100644 index 0000000000..5d8dfe1d67 --- /dev/null +++ b/libs/corecg/SkCordic.h @@ -0,0 +1,20 @@ +#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/libs/corecg/SkDebug.cpp b/libs/corecg/SkDebug.cpp new file mode 100644 index 0000000000..65c89105ef --- /dev/null +++ b/libs/corecg/SkDebug.cpp @@ -0,0 +1,42 @@ +#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/libs/corecg/SkDebug_stdio.cpp b/libs/corecg/SkDebug_stdio.cpp new file mode 100644 index 0000000000..6a35857052 --- /dev/null +++ b/libs/corecg/SkDebug_stdio.cpp @@ -0,0 +1,21 @@ +#include "SkTypes.h" + +#if (defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_UNIX)) && defined(SK_DEBUG) + +#include <stdarg.h> +#include <stdio.h> + +void SkDebugf(const char format[], ...) +{ + static const size_t kBufferSize = 256; + + 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/libs/corecg/SkFP.h b/libs/corecg/SkFP.h new file mode 100644 index 0000000000..8f1f23db61 --- /dev/null +++ b/libs/corecg/SkFP.h @@ -0,0 +1,68 @@ +#ifndef SkFP_DEFINED +#define SkFP_DEFINED + +#include "SkMath.h" + +#ifdef SK_SCALAR_IS_FLOAT + + typedef float SkFP; + + #define SkIntToFloat(n) SkIntToScalar(n) + #define SkFloatRound(x) SkScalarRound(n) + #define SkFloatCeil(x) SkScalarCeil(n) + #define SkFloatFloor(x) SkScalarFloor(n) + + #define SkFloatNeg(x) (-(x)) + #define SkFloatAbs(x) SkScalarAbs(x) + #define SkFloatAdd(a, b) ((a) + (b)) + #define SkFloatSub(a, b) ((a) - (b)) + #define SkFloatMul(a, b) ((a) * (b)) + #define SkFloatMulInt(a, n) ((a) * (n)) + #define SkFloatDiv(a, b) ((a) / (b)) + #define SkFloatDivInt(a, n) ((a) / (n)) + #define SkFloatInvert(x) SkScalarInvert(x) + #define SkFloatSqrt(x) SkScalarSqrt(x) + #define SkFloatCubeRoot(x) pow(x, 1.0f/3) + + #define SkFloatLT(a, b) ((a) < (b)) + #define SkFloatLE(a, b) ((a) <= (b)) + #define SkFloatGT(a, b) ((a) > (b)) + #define SkFloatGE(a, b) ((a) >= (b)) + +#else // scalar is fixed + + #include "SkFloat.h" + + typedef S32 SkFP; + + #define SkIntToFloat(n) SkFloat::SetShift(n, 0) + #define SkFloatRound(x) SkFloat::Round(x); + #define SkFloatCeil(x) SkFloat::Ceil(); + #define SkFloatFloor(x) SkFloat::Floor(); + + #define SkScalarToFloat(n) SkFloat::SetShift(n, -16) + #define SkFloatToScalar(n) SkFloat::GetShift(n, -16) + #define SkFloatNeg(x) SkFloat::Neg(x) + #define SkFloatAbs(x) SkFloat::Abs(x) + #define SkFloatAdd(a, b) SkFloat::Add(a, b) + #define SkFloatSub(a, b) SkFloat::Add(a, SkFloat::Neg(b)) + #define SkFloatMul(a, b) SkFloat::Mul(a, b) + #define SkFloatMulInt(a, n) SkFloat::MulInt(a, n) + #define SkFloatDiv(a, b) SkFloat::Div(a, b) + #define SkFloatDivInt(a, n) SkFloat::DivInt(a, n) + #define SkFloatInvert(x) SkFloat::Invert(x) + #define SkFloatSqrt(x) SkFloat::Sqrt(x) + #define SkFloatCubeRoot(x) SkFloat::CubeRoot(x) + + #define SkFloatLT(a, b) (SkFloat::Cmp(a, b) < 0) + #define SkFloatLE(a, b) (SkFloat::Cmp(a, b) <= 0) + #define SkFloatGT(a, b) (SkFloat::Cmp(a, b) > 0) + #define SkFloatGE(a, b) (SkFloat::Cmp(a, b) >= 0) + +#endif + +#ifdef SK_DEBUG + void SkFP_UnitTest(); +#endif + +#endif diff --git a/libs/corecg/SkFloat.cpp b/libs/corecg/SkFloat.cpp new file mode 100644 index 0000000000..044e9824d4 --- /dev/null +++ b/libs/corecg/SkFloat.cpp @@ -0,0 +1,387 @@ +#include "SkFloat.h" + +#define EXP_BIAS (127+23) + +static int get_unsigned_exp(U32 packed) +{ + return (packed << 1 >> 24); +} + +static unsigned get_unsigned_value(U32 packed) +{ + return (packed << 9 >> 9) | (1 << 23); +} + +static int get_signed_value(S32 packed) +{ + return SkApplySign(get_unsigned_value(packed), SkExtractSign(packed)); +} + +///////////////////////////////////////////////////////////////////////// + +int SkFloat::GetShift(S32 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)); +} + +///////////////////////////////////////////////////////////////////////////////////// + +S32 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; + } + S32 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; + } +} + +S32 SkFloat::Neg(S32 packed) +{ + if (packed) + packed = packed ^ (1 << 31); + return packed; +} + +S32 SkFloat::Add(S32 packed_a, S32 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 S32 mul24(S32 a, S32 b) +{ + Sk64 tmp; + + tmp.setMul(a, b); + tmp.roundRight(24); + return tmp.get32(); +} + +S32 SkFloat::Mul(S32 packed_a, S32 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); +} + +S32 SkFloat::MulInt(S32 packed, int n) +{ + return Mul(packed, SetShift(n, 0)); +} + +S32 SkFloat::Div(S32 packed_n, S32 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); +} + +S32 SkFloat::DivInt(S32 packed, int n) +{ + return Div(packed, SetShift(n, 0)); +} + +S32 SkFloat::Invert(S32 packed) +{ + return Div(packed, SetShift(1, 0)); +} + +S32 SkFloat::Sqrt(S32 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 + +S32 SkFloat::CubeRoot(S32 packed) +{ + sk_throw(); + return 0; +} + +#if defined _WIN32 && _MSC_VER >= 1300 +#pragma warning ( pop ) +#endif + +static inline S32 clear_high_bit(S32 n) +{ + return ((U32)(n << 1)) >> 1; +} + +static inline int int_sign(S32 a, S32 b) +{ + return a > b ? 1 : (a < b ? -1 : 0); +} + +int SkFloat::Cmp(S32 packed_a, S32 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/libs/corecg/SkFloat.h b/libs/corecg/SkFloat.h new file mode 100644 index 0000000000..6ff252dc31 --- /dev/null +++ b/libs/corecg/SkFloat.h @@ -0,0 +1,91 @@ +#ifndef SkFloat_DEFINED +#define SkFloat_DEFINED + +#include "SkMath.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) + { + S32 s = *(S32*)&f; + int d = s - fPacked; + SkASSERT(SkAbs32(d) <= tolerance); + } + float getFloat() const + { + return *(float*)&fPacked; + } +#endif + +private: + S32 fPacked; + + SkFloat(S32 packed) : fPacked(fPacked) {} + +public: + static int GetShift(S32 packed, int shift); + static S32 SetShift(int value, int shift); + static S32 Neg(S32); + static S32 Abs(S32 packed) { return (U32)(packed << 1) >> 1; } + static S32 Shift(S32, int bits); + static S32 Add(S32, S32); + static S32 Mul(S32, S32); + static S32 MulInt(S32, int); + static S32 Div(S32, S32); + static S32 DivInt(S32, int); + static S32 Invert(S32); + static S32 Sqrt(S32); + static S32 CubeRoot(S32); + static int Cmp(S32, S32); +}; + +#endif diff --git a/libs/corecg/SkMath.cpp b/libs/corecg/SkMath.cpp new file mode 100644 index 0000000000..822131054e --- /dev/null +++ b/libs/corecg/SkMath.cpp @@ -0,0 +1,703 @@ +#include "SkMath.h" +#include "SkCordic.h" +#include "SkFloatingPoint.h" +#include "Sk64.h" + +#ifdef SK_SCALAR_IS_FLOAT + const uint32_t gIEEENotANumber = 0x7FFFFFFF; + const uint32_t gIEEEInfinity = 0x7F800000; +#endif + +int SkCLZ(uint32_t x) +{ +#if defined(__arm__) && !defined(__thumb) && defined(__ARM_ARCH_5T__) + asm( "clz %0,%0" : "=r"(x) : "0"(x) : ); + return x; +#else + if (x == 0) + return 32; + + SkDEBUGCODE(uint32_t n = x;) + + 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) - 1) >> 31; + zeros += (~x) >> 31; + +#ifdef SK_DEBUG + int slow_zeros; + + if (n == 0) + slow_zeros = 32; + else + { + slow_zeros = 0; + while ((int32_t)n > 0) + { + slow_zeros += 1; + n <<= 1; + } + } + SkASSERT(zeros == slow_zeros); +#endif + return zeros; +#endif +} + +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 + return (hi << (32 - shift)) | (lo >> shift); + } +} + +#if !defined(SK_BUILD_FOR_BREW) || defined(AEE_SIMULATOR) +SkFixed SkFixedMul(SkFixed a, SkFixed b) +{ +#if 0 + Sk64 tmp; + + tmp.setMul(a, b); + tmp.shiftRight(16); + return tmp.fLo; +#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(SkFract a, SkFract b) +{ +#if 0 + Sk64 tmp; + tmp.setMul(a, b); + return tmp.getFract(); +#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 +} +#endif + +int SkFixedMulCommon(SkFixed a, int b, int bias) +{ + // this function only works if b is 16bits + SkASSERT(b == (S16)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); +} + +int32_t SkDivBits(int32_t numer, int32_t denom, int shift_bias) +{ + SkASSERT(denom != 0); + if (numer == 0) + return 0; + + SkFixed result; + int32_t sign; + + // make numer and denom positive, and sign hold the resulting sign + sign = SkExtractSign(numer ^ denom); + numer = SkAbs32(numer); + denom = SkAbs32(denom); + +#if 0 // faster assuming we have HW divide + if ((numer >> (32 - shift_bias)) == 0) + { + result = (uint32_t)(numer << shift_bias) / denom; + } + else +#endif + { + 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; + result = 0; + do { + result <<= 1; + #ifdef SK_CPU_HAS_CONDITIONAL_INSTR + if ((uint32_t)denom <= (uint32_t)numer) + { + numer -= denom; + result |= 1; + } + #else + int32_t diff = (denom - numer - 1) >> 31; + result -= diff; + numer -= denom & diff; + #endif + numer <<= 1; + } while (--bits >= 0); + } + + 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 partial256) +{ + SkASSERT((unsigned)index < kTableSize); + + SkFixed lower = table[index]; + SkFixed upper = (index == kTableSize - 1) ? SK_Fixed1 : table[index + 1]; + + SkASSERT(lower < upper); + SkASSERT(lower >= 0); + SkASSERT(upper <= SK_Fixed1); + SkASSERT((unsigned)partial256 <= 255); + + return lower + ((upper - lower) * partial256 >> 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; + + 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) <= 8); + } +#endif + + // restore the sign for negative angles + 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 SK_CAN_USE_LONGLONG +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 + +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 + + { + SkScalar x = SK_ScalarNaN; + SkASSERT(SkScalarIsNaN(x)); + } + + 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); + } + +#ifdef SK_CAN_USE_LONGLONG + 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 == 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) + 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/libs/corecg/SkMatrix.cpp b/libs/corecg/SkMatrix.cpp new file mode 100644 index 0000000000..f510e9fbd2 --- /dev/null +++ b/libs/corecg/SkMatrix.cpp @@ -0,0 +1,1220 @@ +#include "SkMatrix.h" +#include "Sk64.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 ] +*/ + +enum { + kMScaleX, + kMSkewX, + kMTransX, + kMSkewY, + kMScaleY, + kMTransY, + kMPersp0, + kMPersp1, + kMPersp2 +}; + +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; +} + +static inline int has_perspective(const SkScalar mat[9]) +{ +#ifdef SK_SCALAR_IS_FLOAT + return mat[kMPersp0] || mat[kMPersp1] || mat[kMPersp2] != kMatrix22Elem; +#else + return mat[kMPersp0] | mat[kMPersp1] | (mat[kMPersp2] - kMatrix22Elem); +#endif +} + +SkMatrix::TypeMask SkMatrix::getType() const +{ + unsigned type = 0; + + type |= (fMat[kMPersp0] || fMat[kMPersp1] || fMat[kMPersp2] != kMatrix22Elem) << kPerspective_Shift; + type |= (fMat[kMSkewX] || fMat[kMSkewY]) << kAffine_Shift; + type |= (fMat[kMScaleX] != SK_Scalar1 || fMat[kMScaleY] != SK_Scalar1) << kScale_Shift; + type |= (fMat[kMTransX] || fMat[kMTransY]) << kTranslate_Shift; + + return (TypeMask)type; +} + +static inline bool is_identity(const SkScalar fMat[9]) +{ +#ifdef SK_SCALAR_IS_FLOAT + return fMat[kMPersp0] == 0 && fMat[kMPersp1] == 0 && fMat[kMPersp2] == kMatrix22Elem && + fMat[kMSkewX] == 0 && fMat[kMSkewY] == 0 && fMat[kMTransX] == 0 && fMat[kMTransY] == 0 && + fMat[kMScaleX] == SK_Scalar1 && fMat[kMScaleY] == SK_Scalar1; +#else + return !(fMat[kMPersp0] | fMat[kMPersp1] | (fMat[kMPersp2] - kMatrix22Elem) | + fMat[kMSkewX] | fMat[kMSkewY] | fMat[kMTransX] | fMat[kMTransY] | + (fMat[kMScaleX] - SK_Scalar1) | (fMat[kMScaleY] - SK_Scalar1)); +#endif +} + +bool SkMatrix::isIdentity() const +{ + return is_identity(fMat); +} + +//////////////////////////////////////////////////////////////////////////////////// + +void SkMatrix::setTranslate(SkScalar dx, SkScalar 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; +} + +bool SkMatrix::preTranslate(SkScalar dx, SkScalar dy) +{ + if (has_perspective(fMat)) + { + SkMatrix m; + m.setTranslate(dx, dy); + return this->preConcat(m); + } + else + { + fMat[kMTransX] += SkScalarMul(fMat[kMScaleX], dx) + SkScalarMul(fMat[kMSkewX], dy); + fMat[kMTransY] += SkScalarMul(fMat[kMSkewY], dx) + SkScalarMul(fMat[kMScaleY], dy); + return true; + } +} + +bool SkMatrix::postTranslate(SkScalar dx, SkScalar dy) +{ + if (has_perspective(fMat)) + { + SkMatrix m; + m.setTranslate(dx, dy); + return this->postConcat(m); + } + else + { + fMat[kMTransX] += dx; + fMat[kMTransY] += dy; + 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[kMSkewX] = fMat[kMSkewY] = + fMat[kMPersp0] = fMat[kMPersp1] = 0; + fMat[kMPersp2] = kMatrix22Elem; +} + +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::postScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) +{ + SkMatrix m; + m.setScale(sx, sy, px, py); + return this->postConcat(m); +} + +//////////////////////////////////////////////////////////////////////////////////// + +void SkMatrix::setSinCos(SkScalar sinV, SkScalar cosV, SkScalar px, SkScalar py) +{ + fMat[kMScaleX] = cosV; + fMat[kMSkewX] = -sinV; + fMat[kMTransX] = SkScalarMul(sinV, py) + SkScalarMul(SK_Scalar1 - cosV, px); + + fMat[kMSkewY] = sinV; + fMat[kMScaleY] = cosV; + fMat[kMTransY] = SkScalarMul(-sinV, px) + SkScalarMul(SK_Scalar1 - cosV, py); + + fMat[kMPersp0] = fMat[kMPersp1] = 0; + fMat[kMPersp2] = kMatrix22Elem; +} + +void SkMatrix::setRotate(SkScalar degrees, SkScalar px, SkScalar py) +{ + SkScalar sinV, cosV; + sinV = SkScalarSinCos(SkDegreesToRadians(degrees), &cosV); + this->setSinCos(sinV, cosV, px, py); +} + +bool SkMatrix::preRotate(SkScalar degrees, SkScalar px, SkScalar py) +{ + SkMatrix m; + m.setRotate(degrees, px, py); + return this->preConcat(m); +} + +bool SkMatrix::postRotate(SkScalar degrees, SkScalar px, SkScalar py) +{ + SkMatrix m; + m.setRotate(degrees, px, py); + 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; +} + +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::postSkew(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) +{ + SkMatrix m; + m.setSkew(sx, sy, px, py); + 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()) + memset(fMat, 0, 8 * sizeof(SkScalar)); + 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; + } + // 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 float rowcol3(const float row[], const float col[]) + { + return row[0] * col[0] + row[1] * col[3] + row[2] * col[6]; + } + 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); + + S32 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 SkFixed rowcol3(const SkFixed row[], const SkFixed col[]) + { + 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); + + return tmp1.getFixed(); + } + static inline int negifaddoverflows(SkFixed& result, SkFixed a, SkFixed b) + { + SkFixed c = a + b; + result = c; + SkASSERT(((c ^ a) & (c ^ b)) >= 0); + return (c ^ a) & (c ^ b); + } +#endif + +static void normalize_perspective(SkScalar mat[9]) +{ + if (SkScalarAbs(mat[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 (aType == kIdentity_Mask) + *this = b; + else if (bType == kIdentity_Mask) + *this = a; + else + { + SkMatrix tmp; + SkMatrix* c = this; + + if (this == &a || this == &b) + c = &tmp; + + if ((aType | bType) & kPerspective_Mask) + { + c->fMat[kMScaleX] = rowcol3(&a.fMat[0], &b.fMat[0]); + c->fMat[kMSkewX] = rowcol3(&a.fMat[0], &b.fMat[1]); + c->fMat[kMTransX] = rowcol3(&a.fMat[0], &b.fMat[2]); + + c->fMat[kMSkewY] = rowcol3(&a.fMat[3], &b.fMat[0]); + c->fMat[kMScaleY] = rowcol3(&a.fMat[3], &b.fMat[1]); + c->fMat[kMTransY] = rowcol3(&a.fMat[3], &b.fMat[2]); + + c->fMat[kMPersp0] = rowcol3(&a.fMat[6], &b.fMat[0]); + c->fMat[kMPersp1] = rowcol3(&a.fMat[6], &b.fMat[1]); + c->fMat[kMPersp2] = rowcol3(&a.fMat[6], &b.fMat[2]); + + normalize_perspective(c->fMat); + } + else // not perspective + { + if (!fixmuladdmul(a.fMat[kMScaleX], b.fMat[kMScaleX], a.fMat[kMSkewX], b.fMat[kMSkewY], &c->fMat[kMScaleX])) + return false; + if (!fixmuladdmul(a.fMat[kMScaleX], b.fMat[kMSkewX], a.fMat[kMSkewX], b.fMat[kMScaleY], &c->fMat[kMSkewX])) + return false; + if (!fixmuladdmul(a.fMat[kMScaleX], b.fMat[kMTransX], a.fMat[kMSkewX], b.fMat[kMTransY], &c->fMat[kMTransX])) + return false; + if (negifaddoverflows(c->fMat[kMTransX], c->fMat[kMTransX], a.fMat[kMTransX]) < 0) + return false; + + if (!fixmuladdmul(a.fMat[kMSkewY], b.fMat[kMScaleX], a.fMat[kMScaleY], b.fMat[kMSkewY], &c->fMat[kMSkewY])) + return false; + if (!fixmuladdmul(a.fMat[kMSkewY], b.fMat[kMSkewX], a.fMat[kMScaleY], b.fMat[kMScaleY], &c->fMat[kMScaleY])) + return false; + if (!fixmuladdmul(a.fMat[kMSkewY], b.fMat[kMTransX], a.fMat[kMScaleY], b.fMat[kMTransY], &c->fMat[kMTransY])) + return false; + if (negifaddoverflows(c->fMat[kMTransY], c->fMat[kMTransY], a.fMat[kMTransY]) < 0) + return false; + + c->fMat[kMPersp0] = c->fMat[kMPersp1] = 0; + c->fMat[kMPersp2] = kMatrix22Elem; + } + + if (c == &tmp) + *this = tmp; + } + return true; +} + +bool SkMatrix::preConcat(const SkMatrix& mat) +{ + return this->setConcat(*this, mat); +} + +bool SkMatrix::postConcat(const SkMatrix& mat) +{ + return this->setConcat(mat, *this); +} + + +//////////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_SCALAR_IS_FLOAT + #define SkPerspMul(a, b) SkScalarMul(a, b) + #define SkScalarMulShift(a, b, s) SkScalarMul(a, b) + static float sk_inv_determinant(const float mat[9], int isPerspective, int* /* (only used in Fixed case) */) + { + double det; + + if (isPerspective) + det = mat[kMScaleX] * ((double)mat[kMScaleY] * mat[kMPersp2] - (double)mat[kMTransY] * mat[kMPersp1]) + + mat[kMSkewX] * ((double)mat[kMTransY] * mat[kMPersp0] - (double)mat[kMSkewY] * mat[kMPersp2]) + + mat[kMTransX] * ((double)mat[kMSkewY] * mat[kMPersp1] - (double)mat[kMScaleY] * mat[kMPersp0]); + else + det = (double)mat[kMScaleX] * mat[kMScaleY] - (double)mat[kMSkewX] * mat[kMSkewY]; + + if (SkScalarNearlyZero((float)det)) + return 0; + return (float)(1.0 / det); + } +#else + #define SkPerspMul(a, b) SkFractMul(a, b) + #define SkScalarMulShift(a, b, s) SkMulShift(a, b, s) + static void set_muladdmul(Sk64* dst, S32 a, S32 b, S32 c, S32 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[kMScaleX], fracmuladdmul(mat[kMScaleY], mat[kMPersp2], -mat[kMTransY], mat[kMPersp1])); + tmp2.setMul(mat[kMSkewX], fracmuladdmul(mat[kMTransY], mat[kMPersp0], -mat[kMSkewY], mat[kMPersp2])); + tmp1.add(tmp2); + tmp2.setMul(mat[kMTransX], fracmuladdmul(mat[kMSkewY], mat[kMPersp1], -mat[kMScaleY], mat[kMPersp0])); + tmp1.add(tmp2); + } + else + { + tmp1.setMul(mat[kMScaleX], mat[kMScaleY]); + tmp2.setMul(mat[kMSkewX], mat[kMSkewY]); + tmp1.sub(tmp2); + } + + int s = tmp1.getClzAbs(); + *shift = s; + + SkFixed denom; + if (s <= 32) + denom = tmp1.getShiftRight(33 - s); + else + denom = (S32)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(fMat); + 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 + { + S32 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; + } + return true; +} + +//////////////////////////////////////////////////////////////////////////////////// + +bool SkMatrix::mapPoints(SkPoint dst[], const SkPoint src[], int count, TypeMask typeMask) const +{ + SkASSERT(dst && src && count > 0 || count == 0); + SkASSERT(src == dst || SkAbs32((S32)(src - dst)) >= count); // no partial overlap + + if (count <= 0) + return true; + + bool ok = true; + + if (typeMask & kPerspective_Mask) + { +#ifdef SK_SCALAR_IS_FIXED + SkFixed persp2 = SkFractToFixed(fMat[kMPersp2]); +#endif + for (int i = count - 1; i >= 0; --i) + { + SkScalar sx = src[i].fX; + SkScalar sy = src[i].fY; + SkScalar x = SkScalarMul(sx, fMat[kMScaleX]) + SkScalarMul(sy, fMat[kMSkewX]) + fMat[kMTransX]; + SkScalar y = SkScalarMul(sx, fMat[kMSkewY]) + SkScalarMul(sy, fMat[kMScaleY]) + fMat[kMTransY]; +#ifdef SK_SCALAR_IS_FIXED + SkFixed z = SkFractMul(sx, fMat[kMPersp0]) + SkFractMul(sy, fMat[kMPersp1]) + persp2; +#else + float z = SkScalarMul(sx, fMat[kMPersp0]) + SkScalarMul(sy, fMat[kMPersp1]) + fMat[kMPersp2]; +#endif + if (z) + z = SkScalarInvert(z); + dst[i].fX = SkScalarMul(x, z); + dst[i].fY = SkScalarMul(y, z); + } + } + else if (typeMask & kAffine_Mask) + { + for (int i = count - 1; i >= 0; --i) + { + SkScalar sx = src[i].fX; + SkScalar sy = src[i].fY; + dst[i].fX = SkScalarMul(sx, fMat[kMScaleX]) + SkScalarMul(sy, fMat[kMSkewX]) + fMat[kMTransX]; + dst[i].fY = SkScalarMul(sx, fMat[kMSkewY]) + SkScalarMul(sy, fMat[kMScaleY]) + fMat[kMTransY]; + } + } + else if (typeMask & kScale_Mask) + { + for (int i = count - 1; i >= 0; --i) + { + dst[i].fX = SkScalarMul(src[i].fX, fMat[kMScaleX]) + fMat[kMTransX]; + dst[i].fY = SkScalarMul(src[i].fY, fMat[kMScaleY]) + fMat[kMTransY]; + } + } + else if (typeMask & kTranslate_Mask) + { + for (int i = count - 1; i >= 0; --i) + { + dst[i].fX = src[i].fX + fMat[kMTransX]; + dst[i].fY = src[i].fY + fMat[kMTransY]; + } + } + else + { + SkASSERT(typeMask == 0); + if (dst != src) + memcpy(dst, src, count * sizeof(SkPoint)); + } + return ok; +} + +bool SkMatrix::mapVectors(SkPoint dst[], const SkPoint src[], int count, TypeMask maskType) const +{ + bool ok; + + if (maskType & kPerspective_Mask) + { + SkPoint origin; + + origin.set(0, 0); + ok = this->mapPoints(&origin, &origin, 1, maskType); + + for (int i = count - 1; i >= 0; --i) + { + SkPoint tmp; + + ok &= this->mapPoints(&tmp, &src[i], 1, maskType); + dst[i].set(tmp.fX - origin.fX, tmp.fY - origin.fY); + } + } + else + { + SkMatrix tmp = *this; + + tmp.fMat[kMTransX] = tmp.fMat[kMTransY] = 0; + ok = tmp.mapPoints(dst, src, count, maskType); + } + return ok; +} + +bool SkMatrix::mapRect(SkRect* dst, const SkRect& src, TypeMask maskType) const +{ + SkASSERT(dst && &src); + + bool ok; + + if (RectStaysRect(maskType)) + { + ok = this->mapPoints((SkPoint*)dst, (const SkPoint*)&src, 2, maskType); + dst->sort(); + } + else + { + SkPoint quad[4]; + + src.toQuad(quad); + ok = this->mapPoints(quad, quad, 4, maskType); + dst->set(quad, 4); + } + return ok; +} + +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::Perspective_ptProc(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 = SkScalarInvert(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::Affine_ptProc(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]) + SkScalarMul(sy, m.fMat[kMSkewX]) + m.fMat[kMTransX]; + pt->fY = SkScalarMul(sx, m.fMat[kMSkewY]) + SkScalarMul(sy, m.fMat[kMScaleY]) + m.fMat[kMTransY]; +#endif +} + +void SkMatrix::Scale_ptProc(const SkMatrix& m, SkScalar sx, SkScalar sy, SkPoint* pt) +{ + SkASSERT((m.getType() & (kScale_Mask | kAffine_Mask | kPerspective_Mask)) == kScale_Mask); + + pt->fX = SkScalarMul(sx, m.fMat[kMScaleX]) + m.fMat[kMTransX]; + pt->fY = SkScalarMul(sy, m.fMat[kMScaleY]) + m.fMat[kMTransY]; +} + +void SkMatrix::Translate_ptProc(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_ptProc(const SkMatrix& m, SkScalar sx, SkScalar sy, SkPoint* pt) +{ + SkASSERT(m.getType() == kIdentity_Mask); + + pt->fX = sx; + pt->fY = sy; +} + +SkMatrix::MapPtProc SkMatrix::getMapPtProc() const +{ + TypeMask typeMask = this->getType(); + + if (typeMask & kPerspective_Mask) + return Perspective_ptProc; + if (typeMask & kAffine_Mask) + return Affine_ptProc; + if (typeMask & kScale_Mask) + return Scale_ptProc; + if (typeMask & kTranslate_Mask) + return Translate_ptProc; + return Identity_ptProc; +} + +////////////////////////////////////////////////////////////////////////////////////////// + +// if its nearly zero (just made up 24 for that, 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; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_SCALAR_IS_FIXED + +static inline void 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); + 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); + x = w1.get32(); + break; + } + } + pt->set(x, y); +} + +static inline void Map1Pt(const SkPoint source[], SkMatrix* dst) +{ + dst->setTranslate(source[0].fX, source[0].fY); +} + +void SkMatrix::Map2Pt(const SkPoint srcPt[], SkMatrix* dst, SkFixed scale) +{ + 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; +} + +void SkMatrix::Map3Pt(const SkPoint srcPt[], SkMatrix* dst, SkFixed scaleX, SkFixed scaleY) +{ + dst->fMat[kMScaleX] = SkFixedDiv(srcPt[2].fX - srcPt[0].fX, scaleX); + dst->fMat[kMSkewY] = SkFixedDiv(srcPt[2].fY - srcPt[0].fY, scaleX); + dst->fMat[kMPersp0] = 0; + dst->fMat[kMSkewX] = SkFixedDiv(srcPt[1].fX - srcPt[0].fX, scaleY); + dst->fMat[kMScaleY] = SkFixedDiv(srcPt[1].fY - srcPt[0].fY, scaleY); + dst->fMat[kMPersp1] = 0; + dst->fMat[kMTransX] = srcPt[0].fX; + dst->fMat[kMTransY] = srcPt[0].fY; + dst->fMat[kMPersp2] = SK_Fract1; +} + +void SkMatrix::Map4Pt(const SkPoint srcPt[], SkMatrix* dst, SkFixed scaleX, SkFixed scaleY) +{ + 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) + a1 = SkFractDiv(SkMulDiv(x0 - x1, y2, x2) - y0 + y1, SkMulDiv(x1, y2, x2) - y1); + else + a1 = SkFractDiv(x0 - x1 - SkMulDiv(y0 - y1, x2, y2), x1 - SkMulDiv(y1, x2, y2)); + + /* check if abs(x1) > abs(y1) */ + if ( x1 > 0 ? y1 > 0 ? x1 > y1 : x1 > -y1 : y1 > 0 ? -x1 > y1 : x1 < y1) + a2 = SkFractDiv(y0 - y2 - SkMulDiv(x0 - x2, y1, x1), y2 - SkMulDiv(x2, y1, x1)); + else + a2 = SkFractDiv(SkMulDiv(y0 - y2, x1, y1) - x0 + x2, SkMulDiv(y2, x1, y1) - x2); + + dst->fMat[kMScaleX] = SkFixedDiv(SkFractMul(a2, srcPt[3].fX) + srcPt[3].fX - srcPt[0].fX, scaleX); + dst->fMat[kMSkewY] = SkFixedDiv(SkFractMul(a2, srcPt[3].fY) + srcPt[3].fY - srcPt[0].fY, scaleX); + dst->fMat[kMPersp0] = SkFixedDiv(a2, scaleX); + dst->fMat[kMSkewX] = SkFixedDiv(SkFractMul(a1, srcPt[1].fX) + srcPt[1].fX - srcPt[0].fX, scaleY); + dst->fMat[kMScaleY] = SkFixedDiv(SkFractMul(a1, srcPt[1].fY) + srcPt[1].fY - srcPt[0].fY, scaleY); + dst->fMat[kMPersp1] = SkFixedDiv(a1, scaleY); + dst->fMat[kMTransX] = srcPt[0].fX; + dst->fMat[kMTransY] = srcPt[0].fY; + dst->fMat[kMPersp2] = SK_Fract1; +} + +#else /* Scalar is float */ + +static inline void 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); + 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); +} + +static inline void Map1Pt(const SkPoint source[], SkMatrix* dst) +{ + dst->setTranslate(source[0].fX, source[0].fY); +} + +void SkMatrix::Map2Pt(const SkPoint srcPt[], SkMatrix* dst, float scale) +{ + float invScale = 1 / scale; + + 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; +} + +void SkMatrix::Map3Pt(const SkPoint srcPt[], SkMatrix* dst, float scaleX, float scaleY) +{ + float invScale = 1 / scaleX; + + 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 / scaleY; + 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; +} + +void SkMatrix::Map4Pt(const SkPoint srcPt[], SkMatrix* dst, float scaleX, float scaleY) +{ + 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) + a1 = SkScalarDiv(SkScalarMulDiv(x0 - x1, y2, x2) - y0 + y1, SkScalarMulDiv(x1, y2, x2) - y1); + else + a1 = SkScalarDiv(x0 - x1 - SkScalarMulDiv(y0 - y1, x2, y2), x1 - SkScalarMulDiv(y1, x2, y2)); + + /* check if abs(x1) > abs(y1) */ + if ( x1 > 0 ? y1 > 0 ? x1 > y1 : x1 > -y1 : y1 > 0 ? -x1 > y1 : x1 < y1) + a2 = SkScalarDiv(y0 - y2 - SkScalarMulDiv(x0 - x2, y1, x1), y2 - SkScalarMulDiv(x2, y1, x1)); + else + a2 = SkScalarDiv(SkScalarMulDiv(y0 - y2, x1, y1) - x0 + x2, SkScalarMulDiv(y2, x1, y1) - x2); + + scaleX = 1 / scaleX; + dst->fMat[kMScaleX] = SkScalarMul(SkScalarMul(a2, srcPt[3].fX) + srcPt[3].fX - srcPt[0].fX, scaleX); + dst->fMat[kMSkewY] = SkScalarMul(SkScalarMul(a2, srcPt[3].fY) + srcPt[3].fY - srcPt[0].fY, scaleX); + dst->fMat[kMPersp0] = SkScalarMul(a2, scaleX); + scaleY = 1 / scaleY; + dst->fMat[kMSkewX] = SkScalarMul(SkScalarMul(a1, srcPt[1].fX) + srcPt[1].fX - srcPt[0].fX, scaleY); + dst->fMat[kMScaleY] = SkScalarMul(SkScalarMul(a1, srcPt[1].fY) + srcPt[1].fY - srcPt[0].fY, scaleY); + dst->fMat[kMPersp1] = SkScalarMul(a1, scaleY); + dst->fMat[kMTransX] = srcPt[0].fX; + dst->fMat[kMTransY] = srcPt[0].fY; + dst->fMat[kMPersp2] = 1; +} + +#endif + +/* Taken from Rob Johnson's original sample code in QuickDraw GX +*/ +bool SkMatrix::setPolyToPoly(const SkPoint dst[], const SkPoint src[], int count) +{ + SkASSERT((unsigned)count <= 4); + + SkPoint tempPt; + SkMatrix tempMap; + + poly_to_point(&tempPt, src, count); + switch (count) { + case 0: + this->reset(); + break; + case 1: + this->setTranslate(dst[0].fX - src[0].fX, dst[0].fY - src[0].fY); + break; + case 2: + Map2Pt(src, &tempMap, tempPt.fY); + if (tempMap.invert(this) == false) + return false; + Map2Pt(dst, &tempMap, tempPt.fY); + goto mapMap; + case 3: + Map3Pt(src, &tempMap, tempPt.fX, tempPt.fY); + if (tempMap.invert(this) == false) + return false; + Map3Pt(dst, &tempMap, tempPt.fX, tempPt.fY); + goto mapMap; + default: + Map4Pt(src, &tempMap, tempPt.fX, tempPt.fY); + if (tempMap.invert(this) == false) + return false; + Map4Pt(dst, &tempMap, tempPt.fX, tempPt.fY); + mapMap: + this->setConcat(tempMap, *this); + break; + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_DEBUG + +void SkMatrix::dump() const +{ + static const char* gTypeNames[] = { + "translate", + "scale", + "rotate", + "perspective" + }; + + SkDebugf("SkMatrix mask = "); + unsigned mask = this->getType(); + + if (mask == 0) + SkDebugf("identity\n"); + else + { + for (int i = 0; i < kShiftCount; i++) + { + if (mask & (1 << i)) + SkDebugf(" %s", gTypeNames[i]); + } + SkDebugf("\n"); + } + +#ifdef SK_CAN_USE_FLOAT + SkDebugf("[%8.4f %8.4f %8.4f]\n[%8.4f %8.4f %8.4f]\n[%8.4f %8.4f %8.4f]\n", +#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 + SkDebugf("[%x %x %x]\n[%x %x %x]\n[%x %x %x]\n", + fMat[0], fMat[1], fMat[2], fMat[3], fMat[4], fMat[5], + fMat[6], fMat[7], fMat[8]); +#endif +} + +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), 0, 0); + mat.invert(&inverse); + inverse.dump(); + iden1.setConcat(mat, inverse); + iden1.dump(); + + mat.setScale(SK_Scalar1/2, SK_Scalar1/2, 0, 0); + mat.invert(&inverse); + inverse.dump(); + iden1.setConcat(mat, inverse); + iden1.dump(); + + mat.setScale(SkIntToScalar(3), SkIntToScalar(5), SkIntToScalar(20), 0); + mat.postRotate(SkIntToScalar(25), 0, 0); + + SkASSERT(mat.invert(nil)); + mat.invert(&inverse); + + iden1.setConcat(mat, inverse); + iden2.setConcat(inverse, mat); + + iden1.dump(); + iden2.dump(); +#endif +} + +#endif diff --git a/libs/corecg/SkMemory_stdlib.cpp b/libs/corecg/SkMemory_stdlib.cpp new file mode 100644 index 0000000000..5a03dacf3a --- /dev/null +++ b/libs/corecg/SkMemory_stdlib.cpp @@ -0,0 +1,79 @@ +#include "SkTypes.h" +#include <stdio.h> +#include <stdlib.h> + +void sk_throw() +{ +#ifdef ANDROID + fprintf(stderr, "throwing...\n"); +#endif + abort(); +} + +void sk_out_of_memory(void) +{ +#ifdef ANDROID + fprintf(stderr,"- out of memory in SGL -\n"); +#endif + 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) +{ + SkDEBUGCODE(if (size) size += 4;) + SkDEBUGCODE(if (addr) addr = (char*)addr - 4;) + + void* p = realloc(addr, size); + if (size == 0) + return p; + + if (p == NULL) + sk_throw(); +#ifdef SK_DEBUG + else + { + memcpy(p, "skia", 4); + p = (char*)p + 4; + } +#endif + return p; +} + +void sk_free(void* p) +{ + if (p) + { +#ifdef SK_DEBUG + SkDEBUGCODE(p = (char*)p - 4;) + SkASSERT(memcmp(p, "skia", 4) == 0); +#endif + free(p); + } +} + +void* sk_malloc_flags(size_t size, unsigned flags) +{ + SkDEBUGCODE(size += 4;) + + void* p = malloc(size); + if (p == NULL) + { + if (flags & SK_MALLOC_THROW) + sk_throw(); + } +#ifdef SK_DEBUG + else + { + memcpy(p, "skia", 4); + p = (char*)p + 4; + memset(p, 0xCD, size - 4); + } +#endif + return p; +} + diff --git a/libs/corecg/SkPoint.cpp b/libs/corecg/SkPoint.cpp new file mode 100644 index 0000000000..b76c4b3bf4 --- /dev/null +++ b/libs/corecg/SkPoint.cpp @@ -0,0 +1,238 @@ +#include "SkPoint.h" + +void SkPoint16::rotateCW(SkPoint16* dst) const +{ + SkASSERT(dst); + + // use a tmp in case this == dst + S16 tmp = fX; + dst->fX = -fY; + dst->fY = tmp; +} + +void SkPoint16::rotateCCW(SkPoint16* dst) const +{ + SkASSERT(dst); + + // use a tmp in case this == dst + S16 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::setUnit(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 U32 squarefixed(unsigned x) +{ + x >>= 16; + return x*x; +} + +/* + 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 + diff --git a/libs/corecg/SkRect.cpp b/libs/corecg/SkRect.cpp new file mode 100644 index 0000000000..0a093930e8 --- /dev/null +++ b/libs/corecg/SkRect.cpp @@ -0,0 +1,103 @@ +#include "SkRect.h" + +bool SkRect16::intersect(S16CPU left, S16CPU top, S16CPU right, S16CPU bottom) +{ + if (fLeft < right && left < fRight && fTop < bottom && top < fBottom) + { + if (fLeft < left) fLeft = SkToS16(left); + if (fTop < top) fTop = SkToS16(top); + if (fRight > right) fRight = SkToS16(right); + if (fBottom > bottom) fBottom = SkToS16(bottom); + return true; + } + return false; +} + +bool SkRect16::intersect(const SkRect16& r) +{ + SkASSERT(&r); + return this->intersect(r.fLeft, r.fTop, r.fRight, r.fBottom); +} + +bool SkRect16::intersect(const SkRect16& a, const SkRect16& b) +{ + SkASSERT(&a && &b); + + *this = a; + return this->intersect(b.fLeft, b.fTop, b.fRight, b.fBottom); +} + +void SkRect16::sort() +{ + if (fLeft > fRight) + SkTSwap<S16>(fLeft, fRight); + if (fTop > fBottom) + SkTSwap<S16>(fTop, fBottom); +} + +///////////////////////////////////////////////////////////////////////////// + +void SkRect::sort() +{ + if (fLeft > fRight) + SkTSwap<SkScalar>(fLeft, fRight); + if (fTop > fBottom) + SkTSwap<SkScalar>(fTop, fBottom); +} + +void SkRect::toQuad(SkPoint quad[4]) const +{ + SkASSERT(quad); + + quad[0].set(fLeft, fTop); + quad[1].set(fRight, fTop); + quad[2].set(fRight, fBottom); + quad[3].set(fLeft, fBottom); +} + +void SkRect::set(const SkPoint pts[], int count) +{ + SkASSERT(pts && count > 0 || count == 0); + + if (count <= 0) + memset(this, 0, sizeof(SkRect)); + else + { + SkScalar l, t, r, b; + + l = r = pts[0].fX; + t = b = pts[0].fY; + + for (int i = 1; i < count; i++) + { + SkScalar x = pts[i].fX; + SkScalar y = pts[i].fY; + + if (x < l) l = x; else if (x > r) r = x; + if (y < t) t = y; else if (y > b) b = y; + } + this->set(l, t, r, b); + } +} + +bool SkRect::intersect(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) +{ + if (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); +} + + + diff --git a/libs/corecg/SkRegion.cpp b/libs/corecg/SkRegion.cpp new file mode 100644 index 0000000000..20c7c739fc --- /dev/null +++ b/libs/corecg/SkRegion.cpp @@ -0,0 +1,1168 @@ +#include "SkRegionPriv.h" +#include "SkTemplates.h" +#include "SkThread.h" + +SkDEBUGCODE(int32_t gRgnAllocCounter;) + +///////////////////////////////////////////////////////////////////////////////////////////////// + +static SkRegion::RunType* skip_scanline(const SkRegion::RunType runs[]) +{ + while (runs[0] != 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 == kRunTypeSentinel || *runs == kRunTypeSentinel) + break; + return (SkRegion::RunType*)runs; + } + top = bot; + runs = skip_scanline(runs); + } + } + return nil; +} + +// returns true if runs are a rect +bool SkRegion::compute_run_bounds(const SkRegion::RunType runs[], int count, SkRect16* 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 < 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] < kRunTypeSentinel); + bounds->fLeft = SkToS16(left); + bounds->fRight = SkToS16(rite); + bounds->fBottom = SkToS16(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 SkRect16& 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<SkRect16>(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(S16CPU left, S16CPU top, S16CPU right, S16CPU 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 SkRect16& 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 SkRect16& rect, Op op) +{ + SkRegion tmp(rect); + + return this->op(*this, tmp, op); +} + +bool SkRegion::op(const SkRect16& rect, const SkRegion& rgn, Op op) +{ + SkRegion tmp(rect); + + return this->op(tmp, rgn, op); +} + +////////////////////////////////////////////////////////////////////////////////////// + +int SkRegion::count_runtype_values(int* itop, int* ibot) const +{ + if (this == nil) + { + *itop = SK_MinS16; + *ibot = SK_MaxS16; + return 0; + } + + int maxT; + + if (this->isRect()) + maxT = 2; + else + { + SkASSERT(this->isComplex()); + // skip the top + const RunType* runs = fRunHead->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] < kRunTypeSentinel); + } + *itop = fBounds.fTop; + *ibot = fBounds.fBottom; + return maxT; +} + +bool SkRegion::setRuns(RunType runs[], int count) +{ + 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] == 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] == kRunTypeSentinel) // eek, stop[-3] was a bottom with no x-runs + { + stop[-3] = 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 (compute_run_bounds(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 < kRunTypeSentinel) + { + SkDebugf(" bottom: %d", *r++); + while (*r < kRunTypeSentinel) + { + SkDebugf(" [%d %d]", r[0], r[1]); + r += 2; + } + SkDebugf("\n"); + } + } +#endif + this->freeRuns(); + this->allocateRuns(count); + } + memcpy(fRunHead->runs(), runs, count * sizeof(RunType)); + + SkDEBUGCODE(this->validate();) + + return true; +} + +void SkRegion::build_rect_runs(const SkRect16& 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[], S16CPU 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] == kRunTypeSentinel) + break; + if (y < runs[0]) + return (SkRegion::RunType*)&runs[1]; + runs = skip_scanline(runs); + } + return nil; +} + +bool SkRegion::contains(S16CPU x, S16CPU y) const +{ + if (!fBounds.contains(x, y)) + return false; + + if (this->isRect()) + return true; + + SkASSERT(this->isComplex()); + const RunType* runs = find_scanline(fRunHead->runs(), y); + + if (runs) + { for (;;) + { if (x < runs[0]) + break; + if (x < runs[1]) + return true; + runs += 2; + } + } + return false; +} + +const SkRegion::RunType* SkRegion::getRuns(RunType tmpStorage[], int* count) const +{ + SkASSERT(tmpStorage && count); + + if (this->isEmpty()) + { + tmpStorage[0] = kRunTypeSentinel; + *count = 1; + } + else if (this->isRect()) + { + build_rect_runs(fBounds, tmpStorage); + *count = kRectRegionRuns; + } + else + { + *count = fRunHead->fRunCount; + tmpStorage = fRunHead->runs(); + } + return tmpStorage; +} + +///////////////////////////////////////////////////////////////////////////////////// + +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->runs(), bh->runs(), ah->fRunCount * sizeof(SkRegion::RunType)); +} + +void SkRegion::translate(int dx, int dy, SkRegion* dst) const +{ + SkDEBUGCODE(this->validate();) + + if (dst == nil) + 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->runs(); + RunType* druns = dst->fRunHead->runs(); + + *druns++ = SkToS16(*sruns++ + dy); // top + for (;;) + { + int bottom = *sruns++; + if (bottom == kRunTypeSentinel) + break; + *druns++ = SkToS16(bottom + dy); // bottom; + for (;;) + { + int x = *sruns++; + if (x == kRunTypeSentinel) + break; + *druns++ = SkToS16(x + dx); + *druns++ = SkToS16(*sruns++ + dx); + } + *druns++ = kRunTypeSentinel; // x sentinel + } + *druns++ = kRunTypeSentinel; // y sentinel + + SkASSERT(sruns - fRunHead->runs() == fRunHead->fRunCount); + SkASSERT(druns - dst->fRunHead->runs() == dst->fRunHead->fRunCount); + } +} + +///////////////////////////////////////////////////////////////////////////////////// + +#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(S16CPU left, S16CPU rite) +{ + SkASSERT(left == 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 <= kRunTypeSentinel); + SkASSERT(fB_left <= kRunTypeSentinel); + return fA_left == kRunTypeSentinel && fB_left == 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++ = SkToS16(left); + *dst++ = SkToS16(rite); + firstInterval = false; + } + else // update the right edge + dst[-1] = SkToS16(rite); + } + } + + *dst++ = kRunTypeSentinel; + return dst; +} + +#if defined _WIN32 && _MSC_VER >= 1300 +#pragma warning ( pop ) +#endif + +class RgnOper { +public: + RgnOper(S16CPU top, SkRegion::RunType dst[], SkRegion::Op op) + { + fStartDst = dst; + fPrevDst = dst + 1; + fPrevLen = 0; // will never match a length from operate_on_span + fTop = SkToS16(top); // just a first guess, we might update this + + static const struct { + U8 fMin; + U8 fMax; + } gOpMinMax[] = { + { 1, 1 }, // Difference + { 3, 3 }, // Intersection + { 1, 3 }, // Union + { 1, 2 } // XOR + }; + + SkASSERT((unsigned)op < SkRegion::kOpCount); + fMin = gOpMinMax[op].fMin; + fMax = gOpMinMax[op].fMax; + } + + void addSpan(S16CPU 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] = SkToS16(bottom); + else // accept the new span + { + if (len == 1 && fPrevLen == 0) + fTop = SkToS16(bottom); // just update our bottom + else + { + start[-1] = SkToS16(bottom); + fPrevDst = start; + fPrevLen = len; + } + } + } + + int flush() + { + fStartDst[0] = fTop; + fPrevDst[fPrevLen] = kRunTypeSentinel; + return (int)(fPrevDst - fStartDst + fPrevLen + 1); + } + + U8 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 = 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 = kRunTypeSentinel; // so we fail the first test + + while (a_bot < kRunTypeSentinel || b_bot < 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 == 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 == kRunTypeSentinel) + b_top = b_bot; + } + + prevBot = bot; + } + return oper.flush(); +} + +bool SkRegion::op(const SkRegion& rgna, const SkRegion& rgnb, Op op) +{ + SkDEBUGCODE(this->validate();) + + SkASSERT((unsigned)op < kOpCount); + + SkRect16 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 || !SkRect16::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: + break; + } + + RunType tmpA[kRectRegionRuns]; + RunType tmpB[kRectRegionRuns]; + + int a_count, b_count; + const RunType* a_runs = rgna.getRuns(tmpA, &a_count); + const RunType* b_runs = rgnb.getRuns(tmpB, &b_count); + + int dstCount = 3 * SkMax32(a_count, b_count); + SkAutoSTMalloc<32, RunType> array(dstCount); + + int count = operate(a_runs, b_runs, array.get(), op); + SkASSERT(count <= dstCount); + return this->setRuns(array.get(), count); +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "SkBuffer.h" + +size_t SkRegion::computeBufferSize() const +{ + size_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; +} + +size_t SkRegion::writeToBuffer(void* storage) const +{ + 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->runs(), fRunHead->fRunCount * sizeof(RunType)); + } + } + return buffer.pos(); +} + +size_t SkRegion::readFromBuffer(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->runs(), count * sizeof(RunType)); + } + } + this->swap(tmp); + return buffer.pos(); +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_DEBUG + +static const SkRegion::RunType* validate_line(const SkRegion::RunType run[], const SkRect16& bounds) +{ + SkASSERT(*run >= bounds.fTop); + SkASSERT(*run <= bounds.fBottom); + run += 1; + while (*run < kRunTypeSentinel) + { + SkASSERT(run[0] < run[1]); + SkASSERT(run[0] >= bounds.fLeft); + SkASSERT(run[1] <= bounds.fRight); + run += 2; + } + 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->runs(); + const RunType* stop = run + fRunHead->fRunCount; + + SkASSERT(*run++ == fBounds.fTop); + 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->runs(); + for (int i = 0; i < fRunHead->fRunCount; i++) + SkDebugf(" %d", runs[i]); + } + SkDebugf("\n"); + } +} + +#endif + +///////////////////////////////////////////////////////////////////////////////////// + +SkRegion::Iterator::Iterator(const SkRegion& rgn) +{ + if (rgn.isEmpty()) + fDone = true; + else + { + fDone = false; + if (rgn.isRect()) + { + fRect = rgn.fBounds; + fRuns = nil; + } + else + { + fRuns = rgn.fRunHead->runs(); + fRect.set(fRuns[2], fRuns[0], fRuns[3], fRuns[1]); + fRuns += 4; + } + } +} + +void SkRegion::Iterator::next() +{ + if (fDone) return; + + if (fRuns == nil) // 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 SkRect16& clip) + : fIter(rgn), fClip(clip), fDone(true) +{ + const SkRect16& 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 SkRect16& 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) +{ + const SkRect16& 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 = nil; // means we're a rect, not a rgn + fDone = false; + } + else + { + const SkRegion::RunType* runs = find_y(rgn.fRunHead->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 == nil) // we're a rect + { + fDone = true; // ok, now we're done + if (left) *left = fLeft; + if (right) *right = fRight; + return true; // this interval is legal + } + + const SkRegion::RunType* runs = fRuns; + + if (runs[0] >= fRight) + { + fDone = true; + return false; + } + + SkASSERT(runs[1] > fLeft); + + if (left) + *left = SkMax32(fLeft, runs[0]); + if (right) + *right = SkMin32(fRight, runs[1]); + fRuns = runs + 2; + return true; +} + diff --git a/libs/corecg/SkRegionPriv.h b/libs/corecg/SkRegionPriv.h new file mode 100644 index 0000000000..7626a35973 --- /dev/null +++ b/libs/corecg/SkRegionPriv.h @@ -0,0 +1,70 @@ +#ifndef SkRegionPriv_DEFINED +#define SkRegionPriv_DEFINED + +#include "SkRegion.h" +#include "SkThread.h" + +#define kRunTypeSentinel 0x7FFF +#define assert_sentinel(value, isSentinel) SkASSERT(((value) == 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* runs() + { + SkASSERT(this->isComplex()); + return (SkRegion::RunType*)(this + 1); + } + const SkRegion::RunType* 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->runs(), this->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/libs/corecg/SkSinTable.h b/libs/corecg/SkSinTable.h new file mode 100644 index 0000000000..2c4c11d488 --- /dev/null +++ b/libs/corecg/SkSinTable.h @@ -0,0 +1,268 @@ +#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 U16 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/libs/corecg/SkTSort.h b/libs/corecg/SkTSort.h new file mode 100644 index 0000000000..bdfbf6daf4 --- /dev/null +++ b/libs/corecg/SkTSort.h @@ -0,0 +1,48 @@ +#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/libs/graphics/Makefile b/libs/graphics/Makefile new file mode 100644 index 0000000000..7bccd3a347 --- /dev/null +++ b/libs/graphics/Makefile @@ -0,0 +1,137 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + effects/Sk1DPathEffect.cpp \ + effects/Sk2DPathEffect.cpp \ + effects/SkBlurMask.cpp \ + effects/SkBlurMaskFilter.cpp \ + effects/SkCamera.cpp \ + effects/SkColorFilters.cpp \ + effects/SkCornerPathEffect.cpp \ + effects/SkDashPathEffect.cpp \ + effects/SkDiscretePathEffect.cpp \ + effects/SkEmbossMask.cpp \ + effects/SkEmbossMaskFilter.cpp \ + effects/SkGradientShader.cpp \ + effects/SkLayerRasterizer.cpp \ + effects/SkNinePatch.cpp \ + effects/SkShaderExtras.cpp \ + effects/SkTransparentShader.cpp \ + images/SkBitmapRef.cpp \ + images/SkImageDecoder.cpp \ + images/SkImageDecoder_libgif.cpp \ + images/SkImageDecoder_libjpeg.cpp \ + images/SkImageDecoder_libpng.cpp \ + images/SkStream.cpp \ + sgl/SkAlphaRuns.cpp \ + sgl/SkBitmap.cpp \ + sgl/SkBitmapSampler.cpp \ + sgl/SkBitmapShader.cpp \ + sgl/SkBlitter.cpp \ + sgl/SkBlitter_A1.cpp \ + sgl/SkBlitter_A8.cpp \ + sgl/SkBlitter_ARGB32.cpp \ + sgl/SkBlitter_RGB16.cpp \ + sgl/SkBlitter_Sprite.cpp \ + sgl/SkCanvas.cpp \ + sgl/SkColor.cpp \ + sgl/SkColorFilter.cpp \ + sgl/SkColorTable.cpp \ + sgl/SkDeque.cpp \ + sgl/SkDraw.cpp \ + sgl/SkEdge.cpp \ + sgl/SkFilterProc.cpp \ + sgl/SkGeometry.cpp \ + sgl/SkGlobals.cpp \ + sgl/SkGlyphCache.cpp \ + sgl/SkGraphics.cpp \ + sgl/SkMaskFilter.cpp \ + sgl/SkPaint.cpp \ + sgl/SkPath.cpp \ + sgl/SkPathEffect.cpp \ + sgl/SkPathMeasure.cpp \ + sgl/SkProcSpriteBlitter.cpp \ + sgl/SkRasterizer.cpp \ + sgl/SkRefCnt.cpp \ + sgl/SkRegion_path.cpp \ + sgl/SkScalerContext.cpp \ + sgl/SkScan.cpp \ + sgl/SkScan_AntiPath.cpp \ + sgl/SkScan_Antihair.cpp \ + sgl/SkScan_Hairline.cpp \ + sgl/SkScan_Path.cpp \ + sgl/SkShader.cpp \ + sgl/SkSpriteBlitter_ARGB32.cpp \ + sgl/SkSpriteBlitter_RGB16.cpp \ + sgl/SkString.cpp \ + sgl/SkStroke.cpp \ + sgl/SkStrokerPriv.cpp \ + sgl/SkTSearch.cpp \ + sgl/SkTextLayout.cpp \ + sgl/SkUtils.cpp \ + sgl/SkXfermode.cpp \ + views/SkEvent.cpp \ + views/SkEventSink.cpp \ + views/SkMetaData.cpp \ + views/SkTagList.cpp \ + views/SkTextBox.cpp \ + ports/SkImageDecoder_Factory.cpp \ + ports/SkFontHost.cpp \ + ports/SkFontHost_FreeType.cpp \ + ports/SkGlobals_global.cpp \ + ports/SkOSFile_stdio.cpp \ + ports/SkOSEvent_android.cpp \ + ports/SkTime_Unix.cpp \ + ports/SkXMLParser_expat.cpp \ + xml/SkDOM.cpp \ + xml/SkXMLParser.cpp \ + xml/SkXMLWriter.cpp \ + xml/SkParseColor.cpp \ + xml/SkParse.cpp + +LOCAL_SHARED_LIBRARIES := \ + libutils \ + libcorecg \ + libpng \ + libgif \ + libjpeg \ + libft2 \ + libexpat \ + libz + +LOCAL_C_INCLUDES += \ + $(LOCAL_PATH)/animator \ + $(LOCAL_PATH)/sgl \ + $(LOCAL_PATH)/images \ + $(LOCAL_PATH)/ports \ + include/graphics \ + include/corecg \ + libs/corecg \ + extlibs/freetype-2.1.10/include \ + extlibs/zlib-1.2.3 \ + extlibs/libpng-1.2.8 \ + extlibs/libgif-4.0 \ + extlibs/jpeg-6b \ + extlibs/expat-2.0.0/lib + +ifeq ($(DEVICE_OS),darwin) + LOCAL_CFLAGS += -fPIC +else + LOCAL_CFLAGS += -fpic +endif + +ifeq ($(DEVICE_ARCH),arm) + LOCAL_CFLAGS += -DFMS_ARCH_ANDROID_ARM +endif + +# TODO RELEASE_BUILD isn't right +ifeq ($(RELEASE_BUILD),true) + LOCAL_CFLAGS += -O2 +endif + +LOCAL_LDLIBS += -lpthread + +LOCAL_TARGET:= libsgl + +include $(BUILD_SHARED_LIBRARY) diff --git a/libs/graphics/animator/SkAnimate.h b/libs/graphics/animator/SkAnimate.h new file mode 100644 index 0000000000..aa593bfe6c --- /dev/null +++ b/libs/graphics/animator/SkAnimate.h @@ -0,0 +1,26 @@ +#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/libs/graphics/animator/SkAnimate3DSchema.xsd b/libs/graphics/animator/SkAnimate3DSchema.xsd new file mode 100644 index 0000000000..5063b75722 --- /dev/null +++ b/libs/graphics/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/libs/graphics/animator/SkAnimate3DSchema.xsx b/libs/graphics/animator/SkAnimate3DSchema.xsx new file mode 100644 index 0000000000..ceb7d890c9 --- /dev/null +++ b/libs/graphics/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/libs/graphics/animator/SkAnimateActive.cpp b/libs/graphics/animator/SkAnimateActive.cpp new file mode 100644 index 0000000000..85f676f3c1 --- /dev/null +++ b/libs/graphics/animator/SkAnimateActive.cpp @@ -0,0 +1,492 @@ +#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(nil, 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, nil, + 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, nil, + 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 == nil) // 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/libs/graphics/animator/SkAnimateActive.h b/libs/graphics/animator/SkAnimateActive.h new file mode 100644 index 0000000000..3bdf9330df --- /dev/null +++ b/libs/graphics/animator/SkAnimateActive.h @@ -0,0 +1,70 @@ +#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; + S32 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/libs/graphics/animator/SkAnimateBase.cpp b/libs/graphics/animator/SkAnimateBase.cpp new file mode 100644 index 0000000000..fd3bd67a4a --- /dev/null +++ b/libs/graphics/animator/SkAnimateBase.cpp @@ -0,0 +1,230 @@ +#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(nil), fFieldInfo(nil), fFieldOffset(0), fStart((SkMSec) -1), fTarget(nil), + 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 == nil) + 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 == nil) + 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/libs/graphics/animator/SkAnimateBase.h b/libs/graphics/animator/SkAnimateBase.h new file mode 100644 index 0000000000..510b59362c --- /dev/null +++ b/libs/graphics/animator/SkAnimateBase.h @@ -0,0 +1,74 @@ +#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/libs/graphics/animator/SkAnimateField.cpp b/libs/graphics/animator/SkAnimateField.cpp new file mode 100644 index 0000000000..b2b63555c2 --- /dev/null +++ b/libs/graphics/animator/SkAnimateField.cpp @@ -0,0 +1,113 @@ +#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 == nil) // if nil, 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 == nil) { + maker.setErrorNoun(field); + maker.setErrorCode(SkDisplayXMLParserError::kFieldNotInTarget); + } + if (resolved == false || fFieldInfo == nil) + return; + SkDisplayTypes outType = fFieldInfo->getType(); + if (fHasValues) { + SkASSERT(to.size() > 0); + fFieldInfo->setValue(maker, &fValues, 0, 0, nil, 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/libs/graphics/animator/SkAnimateMaker.cpp b/libs/graphics/animator/SkAnimateMaker.cpp new file mode 100644 index 0000000000..e66f7df470 --- /dev/null +++ b/libs/graphics/animator/SkAnimateMaker.cpp @@ -0,0 +1,356 @@ +#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); + 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 = *(S32*) stepPtr; + value->fType = SkType_Int; + return true; + } + return false; +} diff --git a/libs/graphics/animator/SkAnimateMaker.h b/libs/graphics/animator/SkAnimateMaker.h new file mode 100644 index 0000000000..acc2c54b9e --- /dev/null +++ b/libs/graphics/animator/SkAnimateMaker.h @@ -0,0 +1,153 @@ +#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; + SkBitmap fBitmap; + 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/libs/graphics/animator/SkAnimateProperties.h b/libs/graphics/animator/SkAnimateProperties.h new file mode 100644 index 0000000000..17bc7771bf --- /dev/null +++ b/libs/graphics/animator/SkAnimateProperties.h @@ -0,0 +1,12 @@ +#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/libs/graphics/animator/SkAnimateSchema.xsd b/libs/graphics/animator/SkAnimateSchema.xsd new file mode 100644 index 0000000000..2c75eb4a89 --- /dev/null +++ b/libs/graphics/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/libs/graphics/animator/SkAnimateSchema.xsx b/libs/graphics/animator/SkAnimateSchema.xsx new file mode 100644 index 0000000000..ceb7d890c9 --- /dev/null +++ b/libs/graphics/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/libs/graphics/animator/SkAnimateSet.cpp b/libs/graphics/animator/SkAnimateSet.cpp new file mode 100644 index 0000000000..4de2de5600 --- /dev/null +++ b/libs/graphics/animator/SkAnimateSet.cpp @@ -0,0 +1,81 @@ +#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, nil, + fFieldInfo->getType(), to); +} + +void SkSet::onEndElement(SkAnimateMaker& maker) { + if (resolveCommon(maker) == false) + return; + if (fFieldInfo == nil) { + 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/libs/graphics/animator/SkAnimateSet.h b/libs/graphics/animator/SkAnimateSet.h new file mode 100644 index 0000000000..476459f5b7 --- /dev/null +++ b/libs/graphics/animator/SkAnimateSet.h @@ -0,0 +1,19 @@ +#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/libs/graphics/animator/SkAnimator.cpp b/libs/graphics/animator/SkAnimator.cpp new file mode 100644 index 0000000000..17f6855355 --- /dev/null +++ b/libs/graphics/animator/SkAnimator.cpp @@ -0,0 +1,705 @@ +#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(nil) { + 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); + 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 == nil) + return draw(canvas, time); + fMaker->fScreenplay.time = time; + fMaker->fCanvas = canvas; + fMaker->fPaint = paint; + fMaker->fCanvas->getPixels(&fMaker->fBitmap); + 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 nil; + 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 nil; + return (const SkDisplayable*) element; +} + +SkElementType SkAnimator::getElementType(const SkDisplayable* ae) { + SkDisplayable* element = (SkDisplayable*) ae; + const SkMemberInfo* info = SkDisplayType::GetMembers(fMaker, element->getType(), nil); + 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 == nil) + return SK_NaN32; + const SkMemberInfo* field = getField(element, fieldID); + if (field == nil) + 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 == nil) + return SK_ScalarNaN; + const SkMemberInfo* field = getField(element, fieldID); + if (field == nil) + 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() : nil; +} + +const char* SkAnimator::getArrayString(const char* id, const char* fieldID, int index) { + const SkDisplayable* element = getElement(id); + if (element == nil) + return nil; + const SkMemberInfo* field = getField(element, fieldID); + if (field == nil) + return nil; + 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 == nil) + return SK_NaN32; + const SkMemberInfo* field = getField(element, fieldID); + if (field == nil) + 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 == nil) + return SK_ScalarNaN; + const SkMemberInfo* field = getField(element, fieldID); + if (field == nil) + 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 == nil) + return nil; + const SkMemberInfo* field = getField(element, fieldID); + if (field == nil) + return nil; + return getString(element, field); +} + +const char* SkAnimator::getURIBase() { + return fMaker->fPrefix.c_str(); +} + +void SkAnimator::initialize() { + SkDELETE(fMaker); + fMaker = SkNEW_ARGS(SkAnimateMaker, (this, nil, nil)); + 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 == nil) + 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 == nil) + 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 'nil's to 'NULL'? + if (element == nil) + return false; + const SkMemberInfo* field = getField(element, fieldID); + if (field == nil) + 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 == nil) + return false; + const SkMemberInfo* field = getField(element, fieldID); + if (field == nil) + 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 == nil) + return false; + const SkMemberInfo* field = getField(element, fieldID); + if (field == nil) + 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, nil, 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 == nil) + return false; + const SkMemberInfo* field = getField(element, fieldID); + if (field == nil) + 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 (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/libs/graphics/animator/SkAnimatorScript.cpp b/libs/graphics/animator/SkAnimatorScript.cpp new file mode 100644 index 0000000000..cce5723416 --- /dev/null +++ b/libs/graphics/animator/SkAnimatorScript.cpp @@ -0,0 +1,590 @@ +#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/libs/graphics/animator/SkAnimatorScript.h b/libs/graphics/animator/SkAnimatorScript.h new file mode 100644 index 0000000000..2a02a2f5e6 --- /dev/null +++ b/libs/graphics/animator/SkAnimatorScript.h @@ -0,0 +1,67 @@ +#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/libs/graphics/animator/SkAnimatorScript2.cpp b/libs/graphics/animator/SkAnimatorScript2.cpp new file mode 100755 index 0000000000..a3aeaf6fac --- /dev/null +++ b/libs/graphics/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 == nil) + 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 == nil || 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) != nil; + } +}; + + +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(nil /* fMaker */, type)) + return SkOperand2::kObject; + if (SkDisplayType::IsEnum(nil /* 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 == nil) { + 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, nil, 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/libs/graphics/animator/SkAnimatorScript2.h b/libs/graphics/animator/SkAnimatorScript2.h new file mode 100755 index 0000000000..61261e91eb --- /dev/null +++ b/libs/graphics/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/libs/graphics/animator/SkBase64.cpp b/libs/graphics/animator/SkBase64.cpp new file mode 100644 index 0000000000..664841ab1f --- /dev/null +++ b/libs/graphics/animator/SkBase64.cpp @@ -0,0 +1,171 @@ +#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(nil) { +} + +#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 = (U8) (bytes[0] << 2); + two = bytes[1]; + one |= two >> 4; + two = (U8) (two << 4); + three = bytes[2]; + two |= three >> 2; + three = (U8) (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 = (U8) *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, nil); + 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/libs/graphics/animator/SkBase64.h b/libs/graphics/animator/SkBase64.h new file mode 100644 index 0000000000..4132dd3d20 --- /dev/null +++ b/libs/graphics/animator/SkBase64.h @@ -0,0 +1,30 @@ +#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/libs/graphics/animator/SkBoundable.cpp b/libs/graphics/animator/SkBoundable.cpp new file mode 100644 index 0000000000..92c8fd00b1 --- /dev/null +++ b/libs/graphics/animator/SkBoundable.cpp @@ -0,0 +1,47 @@ +#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 == (S16)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(nil); + fBoundable->setBounds(fMaker.fDisplayList.fBounds); +} + diff --git a/libs/graphics/animator/SkBoundable.h b/libs/graphics/animator/SkBoundable.h new file mode 100644 index 0000000000..d3e3586527 --- /dev/null +++ b/libs/graphics/animator/SkBoundable.h @@ -0,0 +1,33 @@ +#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 != (S16)0x8000U; } + void setBounds(SkRect16& bounds) { fBounds = bounds; } +protected: + void clearBounds() { fBounds.fLeft = (S16) SkToU16(0x8000); }; // mark bounds as unset + SkRect16 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/libs/graphics/animator/SkBuildCondensedInfo.cpp b/libs/graphics/animator/SkBuildCondensedInfo.cpp new file mode 100644 index 0000000000..6c29499302 --- /dev/null +++ b/libs/graphics/animator/SkBuildCondensedInfo.cpp @@ -0,0 +1,275 @@ +#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] == nil); + 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 == nil) + 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, nil); + 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 == nil) + 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 == nil) + 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 == nil) + 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 == nil) + 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/libs/graphics/animator/SkCondensedDebug.cpp b/libs/graphics/animator/SkCondensedDebug.cpp new file mode 100644 index 0000000000..f60487ba87 --- /dev/null +++ b/libs/graphics/animator/SkCondensedDebug.cpp @@ -0,0 +1,1380 @@ +#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/libs/graphics/animator/SkCondensedRelease.cpp b/libs/graphics/animator/SkCondensedRelease.cpp new file mode 100644 index 0000000000..70bb1b4e12 --- /dev/null +++ b/libs/graphics/animator/SkCondensedRelease.cpp @@ -0,0 +1,1357 @@ +#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/libs/graphics/animator/SkDisplayAdd.cpp b/libs/graphics/animator/SkDisplayAdd.cpp new file mode 100644 index 0000000000..0264a5f9d6 --- /dev/null +++ b/libs/graphics/animator/SkDisplayAdd.cpp @@ -0,0 +1,237 @@ +#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(nil), where(nil) { +} + +SkDisplayable* SkAdd::deepCopy(SkAnimateMaker* maker) { + SkDrawable* saveUse = use; + SkDrawable* saveWhere = where; + use = nil; + where = nil; + 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 == nil) // not set in apply yet + return true; + } + bool skipAddToParent = true; + SkASSERT(type != SkType_Replace || where); + SkTDDrawableArray* grandList SK_INIT_TO_AVOID_WARNING; + SkGroup* parentGroup = nil; + SkGroup* thisGroup = nil; + int index = where ? displayList.findGroup(where, &parentList, &parentGroup, + &thisGroup, &grandList) : 0; + if (index < 0) + return true; + int max = parentList->count(); + if (where == nil && 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 == nil) + index += max + 1; + switch (type) { + case SkType_Add: + if (offset == SK_MaxS32 && where == nil) { + if (use->isDrawable()) { + skipAddToParent = mode == kMode_immediate; + if (skipAddToParent) { + if (where == nil) { + 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 == nil && use != nil && 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/libs/graphics/animator/SkDisplayAdd.h b/libs/graphics/animator/SkDisplayAdd.h new file mode 100644 index 0000000000..ed727307f7 --- /dev/null +++ b/libs/graphics/animator/SkDisplayAdd.h @@ -0,0 +1,64 @@ +#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; + S32 offset; + SkDrawable* use; + SkDrawable* where; // if nil, 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/libs/graphics/animator/SkDisplayApply.cpp b/libs/graphics/animator/SkDisplayApply.cpp new file mode 100644 index 0000000000..af886312f9 --- /dev/null +++ b/libs/graphics/animator/SkDisplayApply.cpp @@ -0,0 +1,797 @@ +#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(nil), steps(-1), transition((Transition) -1), fActive(nil), /*fCurrentScope(nil),*/ + 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 != nil) { + 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 == nil) + 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 != nil); + 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(), nil); + } 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 = nil; + 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 ==nil) + 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 != nil; + 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 == nil) + 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 != nil) { + 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 == nil || 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 != nil && 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 == nil) + apply->activate(maker); + apply->append(this); + parentGroup = nil; + } 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 = nil; + 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(), nil)) + 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 = nil, * found = nil; + 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 != nil) { + 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 = nil; +} + +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 == nil) + return false; + maker.removeActive(fActive); + delete fActive; + fActive = nil; + return true; +} + +#ifdef SK_DEBUG +SkMSec lastTime = (SkMSec) -1; +#endif + +bool SkApply::interpolate(SkAnimateMaker& maker, SkMSec rawTime) { + if (fActive == nil) + 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 != nil && + 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, nil, + 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 == nil) + 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 != nil && 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 == nil || 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 != nil && 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] == nil) { + 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/libs/graphics/animator/SkDisplayApply.h b/libs/graphics/animator/SkDisplayApply.h new file mode 100644 index 0000000000..883323af83 --- /dev/null +++ b/libs/graphics/animator/SkDisplayApply.h @@ -0,0 +1,99 @@ +#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; + S32 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/libs/graphics/animator/SkDisplayBounds.cpp b/libs/graphics/animator/SkDisplayBounds.cpp new file mode 100644 index 0000000000..e6abcc31cd --- /dev/null +++ b/libs/graphics/animator/SkDisplayBounds.cpp @@ -0,0 +1,37 @@ +#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) { + SkRect16& rect = maker.fDisplayList.fInvalBounds; + maker.fDisplayList.fHasUnion = true; + if (rect.isEmpty()) + rect = fBounds; + else + rect.join(fBounds); + } + return result; +} + + + diff --git a/libs/graphics/animator/SkDisplayBounds.h b/libs/graphics/animator/SkDisplayBounds.h new file mode 100644 index 0000000000..077d4cc8f7 --- /dev/null +++ b/libs/graphics/animator/SkDisplayBounds.h @@ -0,0 +1,16 @@ +#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/libs/graphics/animator/SkDisplayEvent.cpp b/libs/graphics/animator/SkDisplayEvent.cpp new file mode 100644 index 0000000000..46af81cffe --- /dev/null +++ b/libs/graphics/animator/SkDisplayEvent.cpp @@ -0,0 +1,248 @@ +#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(nil) { +} + +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 nil; +} + +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 != nil) { + 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)) != nil) { + 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; +} + diff --git a/libs/graphics/animator/SkDisplayEvent.h b/libs/graphics/animator/SkDisplayEvent.h new file mode 100644 index 0000000000..4d5be0236f --- /dev/null +++ b/libs/graphics/animator/SkDisplayEvent.h @@ -0,0 +1,58 @@ +#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/libs/graphics/animator/SkDisplayEvents.cpp b/libs/graphics/animator/SkDisplayEvents.cpp new file mode 100644 index 0000000000..405e1341bc --- /dev/null +++ b/libs/graphics/animator/SkDisplayEvents.cpp @@ -0,0 +1,104 @@ +#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 : nil; + 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 != nil && evt->fTarget != displayable) + continue; + if (state == nil || 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 : nil; + 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 != nil && evt->fTarget != displayable) + continue; + int index = fEvents.find(evt); + fEvents.remove(index); + } +} diff --git a/libs/graphics/animator/SkDisplayEvents.h b/libs/graphics/animator/SkDisplayEvents.h new file mode 100644 index 0000000000..7000b672f3 --- /dev/null +++ b/libs/graphics/animator/SkDisplayEvents.h @@ -0,0 +1,34 @@ +#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/libs/graphics/animator/SkDisplayInclude.cpp b/libs/graphics/animator/SkDisplayInclude.cpp new file mode 100644 index 0000000000..eee5f96677 --- /dev/null +++ b/libs/graphics/animator/SkDisplayInclude.cpp @@ -0,0 +1,50 @@ +#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/libs/graphics/animator/SkDisplayInclude.h b/libs/graphics/animator/SkDisplayInclude.h new file mode 100644 index 0000000000..b842c69d21 --- /dev/null +++ b/libs/graphics/animator/SkDisplayInclude.h @@ -0,0 +1,17 @@ +#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/libs/graphics/animator/SkDisplayInput.cpp b/libs/graphics/animator/SkDisplayInput.cpp new file mode 100644 index 0000000000..7eec8d3bf0 --- /dev/null +++ b/libs/graphics/animator/SkDisplayInput.cpp @@ -0,0 +1,46 @@ +#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 : nil; +} + +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/libs/graphics/animator/SkDisplayInput.h b/libs/graphics/animator/SkDisplayInput.h new file mode 100644 index 0000000000..b118b220f6 --- /dev/null +++ b/libs/graphics/animator/SkDisplayInput.h @@ -0,0 +1,25 @@ +#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/libs/graphics/animator/SkDisplayList.cpp b/libs/graphics/animator/SkDisplayList.cpp new file mode 100644 index 0000000000..2e4d92841e --- /dev/null +++ b/libs/graphics/animator/SkDisplayList.cpp @@ -0,0 +1,151 @@ +#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 = nil; + *list = &fDrawList; + *grandList = &fDrawList; + return SearchForMatch(match, list, parent, found, grandList); +} + +void SkDisplayList::hardReset() { + fDrawList.reset(); + fActiveList.reset(); +} + +bool SkDisplayList::onIRect(const SkRect16& r) { + fBounds = r; + return fDrawBounds; +} + +int SkDisplayList::SearchForMatch(SkDrawable* match, SkTDDrawableArray** list, + SkGroup** parent, SkGroup** found, SkTDDrawableArray**grandList) { + *found = nil; + 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/libs/graphics/animator/SkDisplayList.h b/libs/graphics/animator/SkDisplayList.h new file mode 100644 index 0000000000..30c21a646f --- /dev/null +++ b/libs/graphics/animator/SkDisplayList.h @@ -0,0 +1,62 @@ +#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 SkRect16& 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: + SkRect16 fBounds; + SkRect16 fInvalBounds; + bool fDrawBounds; + bool fHasUnion; + bool fUnionBounds; +private: + SkTDDrawableArray fDrawList; + SkTDActiveArray fActiveList; + SkMSec fInTime; + friend class SkEvents; +}; + +#endif // SkDisplayList_DEFINED + diff --git a/libs/graphics/animator/SkDisplayMath.cpp b/libs/graphics/animator/SkDisplayMath.cpp new file mode 100644 index 0000000000..da757a35c2 --- /dev/null +++ b/libs/graphics/animator/SkDisplayMath.cpp @@ -0,0 +1,231 @@ +#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 == nil) + 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/libs/graphics/animator/SkDisplayMath.h b/libs/graphics/animator/SkDisplayMath.h new file mode 100644 index 0000000000..ced6e42d7b --- /dev/null +++ b/libs/graphics/animator/SkDisplayMath.h @@ -0,0 +1,23 @@ +#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/libs/graphics/animator/SkDisplayMovie.cpp b/libs/graphics/animator/SkDisplayMovie.cpp new file mode 100644 index 0000000000..3727efec46 --- /dev/null +++ b/libs/graphics/animator/SkDisplayMovie.cpp @@ -0,0 +1,121 @@ +#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 == nil) + 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, nil); + movieMaker->fEvents.removeEvent(SkDisplayEvent::kOnload, nil); + 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/libs/graphics/animator/SkDisplayMovie.h b/libs/graphics/animator/SkDisplayMovie.h new file mode 100644 index 0000000000..f81a740076 --- /dev/null +++ b/libs/graphics/animator/SkDisplayMovie.h @@ -0,0 +1,43 @@ +#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/libs/graphics/animator/SkDisplayNumber.cpp b/libs/graphics/animator/SkDisplayNumber.cpp new file mode 100644 index 0000000000..03de65cfb3 --- /dev/null +++ b/libs/graphics/animator/SkDisplayNumber.cpp @@ -0,0 +1,50 @@ +#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/libs/graphics/animator/SkDisplayNumber.h b/libs/graphics/animator/SkDisplayNumber.h new file mode 100644 index 0000000000..7b2ea58557 --- /dev/null +++ b/libs/graphics/animator/SkDisplayNumber.h @@ -0,0 +1,13 @@ +#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/libs/graphics/animator/SkDisplayPost.cpp b/libs/graphics/animator/SkDisplayPost.cpp new file mode 100644 index 0000000000..fad3aee528 --- /dev/null +++ b/libs/graphics/animator/SkDisplayPost.cpp @@ -0,0 +1,298 @@ +#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(nil), + fSinkID(0), fTargetMaker(nil), 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)) != nil) { + //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") != nil; + 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 = nil; + 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 == nil || displayable->getType() != SkType_Movie) { + fTargetMaker->setErrorCode(SkDisplayXMLParserError::kExpectedMovie); + return; + } + SkDisplayMovie* movie = (SkDisplayMovie*) displayable; + fTargetMaker = movie->fMovie.fMaker; + } + if (end == nil) + 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/libs/graphics/animator/SkDisplayPost.h b/libs/graphics/animator/SkDisplayPost.h new file mode 100644 index 0000000000..cfc2fabb66 --- /dev/null +++ b/libs/graphics/animator/SkDisplayPost.h @@ -0,0 +1,50 @@ +#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/libs/graphics/animator/SkDisplayRandom.cpp b/libs/graphics/animator/SkDisplayRandom.cpp new file mode 100644 index 0000000000..53775d30d8 --- /dev/null +++ b/libs/graphics/animator/SkDisplayRandom.cpp @@ -0,0 +1,63 @@ +#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 = SkInterpolatorBase::Blend(random, 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/libs/graphics/animator/SkDisplayRandom.h b/libs/graphics/animator/SkDisplayRandom.h new file mode 100644 index 0000000000..bcfc945cc5 --- /dev/null +++ b/libs/graphics/animator/SkDisplayRandom.h @@ -0,0 +1,32 @@ +#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/libs/graphics/animator/SkDisplayScreenplay.cpp b/libs/graphics/animator/SkDisplayScreenplay.cpp new file mode 100644 index 0000000000..44c7a8954b --- /dev/null +++ b/libs/graphics/animator/SkDisplayScreenplay.cpp @@ -0,0 +1,13 @@ +#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/libs/graphics/animator/SkDisplayScreenplay.h b/libs/graphics/animator/SkDisplayScreenplay.h new file mode 100644 index 0000000000..6d11f7aced --- /dev/null +++ b/libs/graphics/animator/SkDisplayScreenplay.h @@ -0,0 +1,12 @@ +#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/libs/graphics/animator/SkDisplayType.cpp b/libs/graphics/animator/SkDisplayType.cpp new file mode 100644 index 0000000000..dfb536ff4a --- /dev/null +++ b/libs/graphics/animator/SkDisplayType.cpp @@ -0,0 +1,757 @@ +#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/libs/graphics/animator/SkDisplayType.h b/libs/graphics/animator/SkDisplayType.h new file mode 100644 index 0000000000..d6c4a931e2 --- /dev/null +++ b/libs/graphics/animator/SkDisplayType.h @@ -0,0 +1,198 @@ +#ifndef SkDisplayType_DEFINED +#define SkDisplayType_DEFINED + +#include "SkMath.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/libs/graphics/animator/SkDisplayTypes.cpp b/libs/graphics/animator/SkDisplayTypes.cpp new file mode 100644 index 0000000000..b38a04cc29 --- /dev/null +++ b/libs/graphics/animator/SkDisplayTypes.cpp @@ -0,0 +1,212 @@ +#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 + +// S32 +#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 == nil) + 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 = (S32) 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/libs/graphics/animator/SkDisplayTypes.h b/libs/graphics/animator/SkDisplayTypes.h new file mode 100644 index 0000000000..ce654ab081 --- /dev/null +++ b/libs/graphics/animator/SkDisplayTypes.h @@ -0,0 +1,98 @@ +#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: + S32 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/libs/graphics/animator/SkDisplayXMLParser.cpp b/libs/graphics/animator/SkDisplayXMLParser.cpp new file mode 100644 index 0000000000..fd4840a085 --- /dev/null +++ b/libs/graphics/animator/SkDisplayXMLParser.cpp @@ -0,0 +1,301 @@ +#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(nil) +{ +} + +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 == nil) // 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, nil)) { + 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 == nil) { + fError->setNoun(name); + fError->setCode(SkXMLParserError::kUnknownAttributeName); + return true; + } + if (info->setValue(fMaker, nil, 0, info->getCount(), displayable, info->getType(), attrValue, + attrValueLen)) + return false; + if (fMaker.fError.hasError()) { + fError->setNoun(attrValue, attrValueLen); + return true; + } + SkDisplayable* ref = nil; + if (fMaker.find(attrValue, attrValueLen, &ref) == false) { + ref = fMaker.createInstance(attrValue, attrValueLen); + if (ref == nil) { + 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)) != nil) { + 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 = nil; + 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, nil); + if (fMaker.fError.hasError()) + return true; + fMaker.fEvents.removeEvent(SkDisplayEvent::kOnload, nil); + + } + 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 = nil; // 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 == nil) { + 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 = nil; + const SkMemberInfo* lastResort = nil; + 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 != nil) + 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 nil; +} + + diff --git a/libs/graphics/animator/SkDisplayXMLParser.h b/libs/graphics/animator/SkDisplayXMLParser.h new file mode 100644 index 0000000000..3aa42cfc51 --- /dev/null +++ b/libs/graphics/animator/SkDisplayXMLParser.h @@ -0,0 +1,84 @@ +#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/libs/graphics/animator/SkDisplayable.cpp b/libs/graphics/animator/SkDisplayable.cpp new file mode 100644 index 0000000000..41c4b47933 --- /dev/null +++ b/libs/graphics/animator/SkDisplayable.cpp @@ -0,0 +1,549 @@ +#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 nil; +} + +SkDisplayable* SkDisplayable::deepCopy(SkAnimateMaker* maker) { + SkDisplayTypes type = getType(); + if (type == SkType_Unknown) { + SkASSERT(0); + return nil; + } + SkDisplayable* copy = SkDisplayType::CreateInstance(maker, type); + int index = -1; + int propIndex = 0; + const SkMemberInfo* info; + do { + info = copy->getMember(++index); + if (info == nil) + 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 == nil || (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 == nil || *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 (nil == 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 == nil || (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 (nil == info) { + break; + } + if (SkDisplayType::IsDisplayable(maker, info->fType)) { + SkDisplayable** displayable = (SkDisplayable**) info->memberData(this); + if (*displayable == nil || *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 nil; +} + +const SkMemberInfo* SkDisplayable::getMember(int index) { + return nil; +} + +const SkMemberInfo* SkDisplayable::getMember(const char name[]) { + return nil; +} + +const SkFunctionParamType* SkDisplayable::getParameters(const SkMemberInfo* info, + int* paramCount) { + const SkFunctionParamType* params = getFunctionsParameters(); + SkASSERT(params != nil); + 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 nil; +} + +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 nil; +} + +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/libs/graphics/animator/SkDisplayable.h b/libs/graphics/animator/SkDisplayable.h new file mode 100644 index 0000000000..aea64b516e --- /dev/null +++ b/libs/graphics/animator/SkDisplayable.h @@ -0,0 +1,103 @@ +#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/libs/graphics/animator/SkDraw3D.cpp b/libs/graphics/animator/SkDraw3D.cpp new file mode 100644 index 0000000000..8a69445212 --- /dev/null +++ b/libs/graphics/animator/SkDraw3D.cpp @@ -0,0 +1,100 @@ +#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(nil) { +} + +Sk3D_Camera::~Sk3D_Camera() { +} + +bool Sk3D_Camera::draw(SkAnimateMaker& maker) { + fCamera.update(); + SkMatrix matrix; + fCamera.computeMatrix(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/libs/graphics/animator/SkDraw3D.h b/libs/graphics/animator/SkDraw3D.h new file mode 100644 index 0000000000..55f76645e8 --- /dev/null +++ b/libs/graphics/animator/SkDraw3D.h @@ -0,0 +1,42 @@ +#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/libs/graphics/animator/SkDrawBitmap.cpp b/libs/graphics/animator/SkDrawBitmap.cpp new file mode 100644 index 0000000000..3ad4e007d8 --- /dev/null +++ b/libs/graphics/animator/SkDrawBitmap.cpp @@ -0,0 +1,186 @@ +#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(nil) { + base64.fData = nil; + 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()); + SkAutoTDelete<SkStream> autoDel(stream); + SkImageDecoder::DecodeStream(stream, &fBitmap); + } +} diff --git a/libs/graphics/animator/SkDrawBitmap.h b/libs/graphics/animator/SkDrawBitmap.h new file mode 100644 index 0000000000..f5ef3303cd --- /dev/null +++ b/libs/graphics/animator/SkDrawBitmap.h @@ -0,0 +1,65 @@ +#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; + S32 height; + S32 rowBytes; + S32 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/libs/graphics/animator/SkDrawBlur.cpp b/libs/graphics/animator/SkDrawBlur.cpp new file mode 100644 index 0000000000..170a770ba7 --- /dev/null +++ b/libs/graphics/animator/SkDrawBlur.cpp @@ -0,0 +1,23 @@ +#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 nil; + return SkBlurMaskFilter::Create(radius, (SkBlurMaskFilter::BlurStyle) blurStyle); +} + diff --git a/libs/graphics/animator/SkDrawBlur.h b/libs/graphics/animator/SkDrawBlur.h new file mode 100644 index 0000000000..4bfb2689d0 --- /dev/null +++ b/libs/graphics/animator/SkDrawBlur.h @@ -0,0 +1,17 @@ +#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/libs/graphics/animator/SkDrawClip.cpp b/libs/graphics/animator/SkDrawClip.cpp new file mode 100644 index 0000000000..471750101e --- /dev/null +++ b/libs/graphics/animator/SkDrawClip.cpp @@ -0,0 +1,31 @@ +#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(nil), path(nil) { +} + +bool SkDrawClip::draw(SkAnimateMaker& maker ) { + if (rect != nil) + maker.fCanvas->clipRect(rect->fRect); + else { + SkASSERT(path != nil); + maker.fCanvas->clipPath(path->fPath); + } + return false; +} + diff --git a/libs/graphics/animator/SkDrawClip.h b/libs/graphics/animator/SkDrawClip.h new file mode 100644 index 0000000000..6c896660a4 --- /dev/null +++ b/libs/graphics/animator/SkDrawClip.h @@ -0,0 +1,20 @@ +#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/libs/graphics/animator/SkDrawColor.cpp b/libs/graphics/animator/SkDrawColor.cpp new file mode 100644 index 0000000000..a21bde4e75 --- /dev/null +++ b/libs/graphics/animator/SkDrawColor.cpp @@ -0,0 +1,261 @@ +#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 != nil) + 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 != nil); + 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): + U8 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/libs/graphics/animator/SkDrawColor.h b/libs/graphics/animator/SkDrawColor.h new file mode 100644 index 0000000000..ad7c3f8c24 --- /dev/null +++ b/libs/graphics/animator/SkDrawColor.h @@ -0,0 +1,33 @@ +#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/libs/graphics/animator/SkDrawDash.cpp b/libs/graphics/animator/SkDrawDash.cpp new file mode 100644 index 0000000000..78e2d66db8 --- /dev/null +++ b/libs/graphics/animator/SkDrawDash.cpp @@ -0,0 +1,27 @@ +#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 nil; + return new SkDashPathEffect(intervals.begin(), count, phase); +} + diff --git a/libs/graphics/animator/SkDrawDash.h b/libs/graphics/animator/SkDrawDash.h new file mode 100644 index 0000000000..ff89f50c24 --- /dev/null +++ b/libs/graphics/animator/SkDrawDash.h @@ -0,0 +1,18 @@ +#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/libs/graphics/animator/SkDrawDiscrete.cpp b/libs/graphics/animator/SkDrawDiscrete.cpp new file mode 100644 index 0000000000..9e32772e34 --- /dev/null +++ b/libs/graphics/animator/SkDrawDiscrete.cpp @@ -0,0 +1,26 @@ +#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 nil; + else + return new SkDiscretePathEffect(segLength, deviation); +} + diff --git a/libs/graphics/animator/SkDrawDiscrete.h b/libs/graphics/animator/SkDrawDiscrete.h new file mode 100644 index 0000000000..61aeac039f --- /dev/null +++ b/libs/graphics/animator/SkDrawDiscrete.h @@ -0,0 +1,15 @@ +#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/libs/graphics/animator/SkDrawEmboss.cpp b/libs/graphics/animator/SkDrawEmboss.cpp new file mode 100644 index 0000000000..f0aee87623 --- /dev/null +++ b/libs/graphics/animator/SkDrawEmboss.cpp @@ -0,0 +1,25 @@ +#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 nil; + return SkBlurMaskFilter::CreateEmboss(direction.begin(), ambient, specular, radius); +} + diff --git a/libs/graphics/animator/SkDrawEmboss.h b/libs/graphics/animator/SkDrawEmboss.h new file mode 100644 index 0000000000..8014f45c8a --- /dev/null +++ b/libs/graphics/animator/SkDrawEmboss.h @@ -0,0 +1,16 @@ +#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/libs/graphics/animator/SkDrawExtraPathEffect.cpp b/libs/graphics/animator/SkDrawExtraPathEffect.cpp new file mode 100644 index 0000000000..b008643ffb --- /dev/null +++ b/libs/graphics/animator/SkDrawExtraPathEffect.cpp @@ -0,0 +1,499 @@ +#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, nil, 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(SkScalar distance, const SkMatrix& mat, SkPath* dst) + { + fMaker->setExtraPropertyCallBack(fDraw->fType, GetDistance, &distance); + SkDrawPath* drawPath = nil; + 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(mat); + 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, nil, fDraw->spacing.c_str(), &result); + if (drawPath) + dst->addPath(drawPath->getPath(), m); + fMaker->clearExtraPropertyCallBack(fDraw->fType); + return result; + } + +private: + + 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(nil), addMatrix(nil), path(nil), fPathEffect(nil) { +} + +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 == nil || (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 SkRect16& 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 == nil) + 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; + S32 fU; + S32 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 == nil || (addPath->isPath() == false && addPath->isApply() == false) || + matrix == nil) + 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(nil), effect2(nil) { +} + +SkDrawComposePathEffect::~SkDrawComposePathEffect() { + delete effect1; + delete effect2; +} + +bool SkDrawComposePathEffect::add(SkAnimateMaker& , SkDisplayable* child) { + if (effect1 == nil) + 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 = nil; + 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 = nil; + 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 = nil; + 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(nil), fExtraStorage(nil) { +} diff --git a/libs/graphics/animator/SkDrawFull.cpp b/libs/graphics/animator/SkDrawFull.cpp new file mode 100644 index 0000000000..3bf27d9124 --- /dev/null +++ b/libs/graphics/animator/SkDrawFull.cpp @@ -0,0 +1,10 @@ +#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/libs/graphics/animator/SkDrawFull.h b/libs/graphics/animator/SkDrawFull.h new file mode 100644 index 0000000000..8822b0266b --- /dev/null +++ b/libs/graphics/animator/SkDrawFull.h @@ -0,0 +1,13 @@ +#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/libs/graphics/animator/SkDrawGradient.cpp b/libs/graphics/animator/SkDrawGradient.cpp new file mode 100644 index 0000000000..ae40728d25 --- /dev/null +++ b/libs/graphics/animator/SkDrawGradient.cpp @@ -0,0 +1,214 @@ +#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) { + } +protected: + virtual U16CPU mapUnit16(U16CPU x) { + fUnit = SkUnitToScalar(x); + SkScriptValue value; + SkAnimatorScript engine(*fMaker, nil, 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(nil) { +} + +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 nil; + 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 nil; + 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/libs/graphics/animator/SkDrawGradient.h b/libs/graphics/animator/SkDrawGradient.h new file mode 100644 index 0000000000..2940b76142 --- /dev/null +++ b/libs/graphics/animator/SkDrawGradient.h @@ -0,0 +1,59 @@ +#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/libs/graphics/animator/SkDrawGroup.cpp b/libs/graphics/animator/SkDrawGroup.cpp new file mode 100644 index 0000000000..89232dd223 --- /dev/null +++ b/libs/graphics/animator/SkDrawGroup.cpp @@ -0,0 +1,314 @@ +#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(nil), fOriginal(nil) { +} + +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 == nil); + 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/libs/graphics/animator/SkDrawGroup.h b/libs/graphics/animator/SkDrawGroup.h new file mode 100644 index 0000000000..45e3a43b90 --- /dev/null +++ b/libs/graphics/animator/SkDrawGroup.h @@ -0,0 +1,63 @@ +#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 != nil; } + 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/libs/graphics/animator/SkDrawLine.cpp b/libs/graphics/animator/SkDrawLine.cpp new file mode 100644 index 0000000000..3c38fbe822 --- /dev/null +++ b/libs/graphics/animator/SkDrawLine.cpp @@ -0,0 +1,28 @@ +#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) { + SkPoint start = {x1, y1}; + SkPoint stop = {x2, y2}; + SkBoundableAuto boundable(this, maker); + maker.fCanvas->drawLine(start, stop, *maker.fPaint); + return false; +} diff --git a/libs/graphics/animator/SkDrawLine.h b/libs/graphics/animator/SkDrawLine.h new file mode 100644 index 0000000000..682c7ee269 --- /dev/null +++ b/libs/graphics/animator/SkDrawLine.h @@ -0,0 +1,20 @@ +#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/libs/graphics/animator/SkDrawMatrix.cpp b/libs/graphics/animator/SkDrawMatrix.cpp new file mode 100644 index 0000000000..18b30e6fec --- /dev/null +++ b/libs/graphics/animator/SkDrawMatrix.cpp @@ -0,0 +1,273 @@ +#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.getScaleX(); + 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/libs/graphics/animator/SkDrawMatrix.h b/libs/graphics/animator/SkDrawMatrix.h new file mode 100644 index 0000000000..df55292311 --- /dev/null +++ b/libs/graphics/animator/SkDrawMatrix.h @@ -0,0 +1,65 @@ +#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/libs/graphics/animator/SkDrawOval.cpp b/libs/graphics/animator/SkDrawOval.cpp new file mode 100644 index 0000000000..11614ebd1c --- /dev/null +++ b/libs/graphics/animator/SkDrawOval.cpp @@ -0,0 +1,20 @@ +#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/libs/graphics/animator/SkDrawOval.h b/libs/graphics/animator/SkDrawOval.h new file mode 100644 index 0000000000..e91576adaf --- /dev/null +++ b/libs/graphics/animator/SkDrawOval.h @@ -0,0 +1,14 @@ +#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/libs/graphics/animator/SkDrawPaint.cpp b/libs/graphics/animator/SkDrawPaint.cpp new file mode 100644 index 0000000000..1f13091f49 --- /dev/null +++ b/libs/graphics/animator/SkDrawPaint.cpp @@ -0,0 +1,260 @@ +#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(filterType, FilterType), + 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), filterType((SkPaint::FilterType) -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 { + SkScalar above, below; + SkPaint paint; + setupPaint(&paint); + paint.measureText("", 0, &above, &below); + switch (index) { + case SK_PROPERTY(ascent): + value->fOperand.fScalar = above; + break; + case SK_PROPERTY(descent): + value->fOperand.fScalar = below; + break; + 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->setAntiAliasOn(SkToBool(antiAlias)); + if (color != NULL) + paint->setColor(color->getColor()); + if (fakeBold != -1) + paint->setFakeBoldTextOn(SkToBool(fakeBold)); + if (filterType != (SkPaint::FilterType) -1) + paint->setFilterType((SkPaint::FilterType) filterType); + // 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->setLinearTextOn(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->setStrikeThruTextOn(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->setUnderlineTextOn(SkToBool(underline)); + if (xfermode != (SkPorterDuff::Mode) -1) + paint->setPorterDuffXfermode((SkPorterDuff::Mode) xfermode); +} + diff --git a/libs/graphics/animator/SkDrawPaint.h b/libs/graphics/animator/SkDrawPaint.h new file mode 100644 index 0000000000..3ae0ffa492 --- /dev/null +++ b/libs/graphics/animator/SkDrawPaint.h @@ -0,0 +1,71 @@ +#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; + int /*SkPaint::FilterType*/ filterType; + 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/libs/graphics/animator/SkDrawPath.cpp b/libs/graphics/animator/SkDrawPath.cpp new file mode 100644 index 0000000000..302460089d --- /dev/null +++ b/libs/graphics/animator/SkDrawPath.cpp @@ -0,0 +1,212 @@ +#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 = nil; + 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/libs/graphics/animator/SkDrawPath.h b/libs/graphics/animator/SkDrawPath.h new file mode 100644 index 0000000000..3c005a5dfd --- /dev/null +++ b/libs/graphics/animator/SkDrawPath.h @@ -0,0 +1,60 @@ +#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/libs/graphics/animator/SkDrawPoint.cpp b/libs/graphics/animator/SkDrawPoint.cpp new file mode 100644 index 0000000000..4bbd00611f --- /dev/null +++ b/libs/graphics/animator/SkDrawPoint.cpp @@ -0,0 +1,37 @@ +#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/libs/graphics/animator/SkDrawPoint.h b/libs/graphics/animator/SkDrawPoint.h new file mode 100644 index 0000000000..c59c78a972 --- /dev/null +++ b/libs/graphics/animator/SkDrawPoint.h @@ -0,0 +1,24 @@ +#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/libs/graphics/animator/SkDrawRectangle.cpp b/libs/graphics/animator/SkDrawRectangle.cpp new file mode 100644 index 0000000000..3ccb347ed5 --- /dev/null +++ b/libs/graphics/animator/SkDrawRectangle.cpp @@ -0,0 +1,136 @@ +#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(nil) { + 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/libs/graphics/animator/SkDrawRectangle.h b/libs/graphics/animator/SkDrawRectangle.h new file mode 100644 index 0000000000..6abb6e486b --- /dev/null +++ b/libs/graphics/animator/SkDrawRectangle.h @@ -0,0 +1,47 @@ +#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/libs/graphics/animator/SkDrawSaveLayer.cpp b/libs/graphics/animator/SkDrawSaveLayer.cpp new file mode 100644 index 0000000000..c6addb888d --- /dev/null +++ b/libs/graphics/animator/SkDrawSaveLayer.cpp @@ -0,0 +1,67 @@ +#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); + } + else + maker.fCanvas->saveLayer(bounds->fRect, *save); + 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); +}
\ No newline at end of file diff --git a/libs/graphics/animator/SkDrawSaveLayer.h b/libs/graphics/animator/SkDrawSaveLayer.h new file mode 100644 index 0000000000..964445711f --- /dev/null +++ b/libs/graphics/animator/SkDrawSaveLayer.h @@ -0,0 +1,27 @@ +#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/libs/graphics/animator/SkDrawShader.cpp b/libs/graphics/animator/SkDrawShader.cpp new file mode 100644 index 0000000000..4b071a15b1 --- /dev/null +++ b/libs/graphics/animator/SkDrawShader.cpp @@ -0,0 +1,74 @@ +#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(nil), + 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(filterType, FilterType), + SK_MEMBER(image, BaseBitmap) +}; + +#endif + +DEFINE_GET_MEMBER(SkDrawBitmapShader); + +SkDrawBitmapShader::SkDrawBitmapShader() : filterType(SkPaint::kNo_FilterType), image(nil) {} + +bool SkDrawBitmapShader::add() { + if (fPaint->shader != (SkDrawShader*) -1) + return true; + fPaint->shader = this; + fPaint->fOwnsShader = true; + return false; +} + +SkShader* SkDrawBitmapShader::getShader() { + if (image == NULL) + return NULL; + + // note: bitmap shader now supports independent tile modes for X and Y + // we pass the same to both, but later we should extend this flexibility + // to the xml (e.g. tileModeX="repeat" tileModeY="clmap") + // <reed> + SkShader* shader = SkShader::CreateBitmapShader(image->fBitmap, false, + (SkPaint::FilterType) filterType, + (SkShader::TileMode) tileMode, + (SkShader::TileMode) tileMode); + SkAutoTDelete<SkShader> autoDel(shader); + addPostlude(shader); + (void)autoDel.detach(); + return shader; +} + diff --git a/libs/graphics/animator/SkDrawShader.h b/libs/graphics/animator/SkDrawShader.h new file mode 100644 index 0000000000..860140593c --- /dev/null +++ b/libs/graphics/animator/SkDrawShader.h @@ -0,0 +1,21 @@ +#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: + int /*SkPaint::FilterType*/ filterType; + SkBaseBitmap* image; +private: + typedef SkDrawShader INHERITED; +}; + +#endif // SkDrawShader_DEFINED diff --git a/libs/graphics/animator/SkDrawText.cpp b/libs/graphics/animator/SkDrawText.cpp new file mode 100644 index 0000000000..093e7e496c --- /dev/null +++ b/libs/graphics/animator/SkDrawText.cpp @@ -0,0 +1,47 @@ +#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 = (S32) text.size(); + return true; +} + diff --git a/libs/graphics/animator/SkDrawText.h b/libs/graphics/animator/SkDrawText.h new file mode 100644 index 0000000000..2cc8c86239 --- /dev/null +++ b/libs/graphics/animator/SkDrawText.h @@ -0,0 +1,27 @@ +#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/libs/graphics/animator/SkDrawTextBox.cpp b/libs/graphics/animator/SkDrawTextBox.cpp new file mode 100644 index 0000000000..204d398694 --- /dev/null +++ b/libs/graphics/animator/SkDrawTextBox.cpp @@ -0,0 +1,73 @@ +#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/libs/graphics/animator/SkDrawTextBox.h b/libs/graphics/animator/SkDrawTextBox.h new file mode 100644 index 0000000000..20bb14c196 --- /dev/null +++ b/libs/graphics/animator/SkDrawTextBox.h @@ -0,0 +1,30 @@ +#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/libs/graphics/animator/SkDrawTo.cpp b/libs/graphics/animator/SkDrawTo.cpp new file mode 100644 index 0000000000..2019c4b1c6 --- /dev/null +++ b/libs/graphics/animator/SkDrawTo.cpp @@ -0,0 +1,47 @@ +#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(nil), 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/libs/graphics/animator/SkDrawTo.h b/libs/graphics/animator/SkDrawTo.h new file mode 100644 index 0000000000..403ced6f3a --- /dev/null +++ b/libs/graphics/animator/SkDrawTo.h @@ -0,0 +1,25 @@ +#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/libs/graphics/animator/SkDrawTransparentShader.cpp b/libs/graphics/animator/SkDrawTransparentShader.cpp new file mode 100644 index 0000000000..c71fb37295 --- /dev/null +++ b/libs/graphics/animator/SkDrawTransparentShader.cpp @@ -0,0 +1,7 @@ +#include "SkDrawTransparentShader.h" +#include "SkTransparentShader.h" + +SkShader* SkDrawTransparentShader::getShader() { + return new SkTransparentShader(); +} + diff --git a/libs/graphics/animator/SkDrawTransparentShader.h b/libs/graphics/animator/SkDrawTransparentShader.h new file mode 100644 index 0000000000..06599378d7 --- /dev/null +++ b/libs/graphics/animator/SkDrawTransparentShader.h @@ -0,0 +1,12 @@ +#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/libs/graphics/animator/SkDrawable.cpp b/libs/graphics/animator/SkDrawable.cpp new file mode 100644 index 0000000000..e3e3662467 --- /dev/null +++ b/libs/graphics/animator/SkDrawable.cpp @@ -0,0 +1,16 @@ +#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/libs/graphics/animator/SkDrawable.h b/libs/graphics/animator/SkDrawable.h new file mode 100644 index 0000000000..6c62b15203 --- /dev/null +++ b/libs/graphics/animator/SkDrawable.h @@ -0,0 +1,19 @@ +#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/libs/graphics/animator/SkDump.cpp b/libs/graphics/animator/SkDump.cpp new file mode 100644 index 0000000000..cd7dcf09be --- /dev/null +++ b/libs/graphics/animator/SkDump.cpp @@ -0,0 +1,141 @@ +#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, nil, 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 == nil) { + result->reset(); + result->appendS32(badEnum); + return; + } + str += 1; + } + const char* end = strchr(str, '|'); + if (end == nil) + 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/libs/graphics/animator/SkDump.h b/libs/graphics/animator/SkDump.h new file mode 100644 index 0000000000..2d9c99f4e4 --- /dev/null +++ b/libs/graphics/animator/SkDump.h @@ -0,0 +1,34 @@ +#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/libs/graphics/animator/SkExtraPathEffects.xsd b/libs/graphics/animator/SkExtraPathEffects.xsd new file mode 100644 index 0000000000..9592443a08 --- /dev/null +++ b/libs/graphics/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/libs/graphics/animator/SkExtras.h b/libs/graphics/animator/SkExtras.h new file mode 100644 index 0000000000..f8af4c5047 --- /dev/null +++ b/libs/graphics/animator/SkExtras.h @@ -0,0 +1,25 @@ +#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/libs/graphics/animator/SkGetCondensedInfo.cpp b/libs/graphics/animator/SkGetCondensedInfo.cpp new file mode 100644 index 0000000000..28290fde1e --- /dev/null +++ b/libs/graphics/animator/SkGetCondensedInfo.cpp @@ -0,0 +1,113 @@ +#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 nil; + 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 != nil) + return result; + if (--count == 0) + return nil; + 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 nil; + 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 != nil) + return result; + if (--count == 0) + return nil; + info++; + } + SkASSERT(info->fType != SkType_BaseClassInfo); + if (*index >= count) { + *index -= count; + return nil; + } + 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/libs/graphics/animator/SkHitClear.cpp b/libs/graphics/animator/SkHitClear.cpp new file mode 100644 index 0000000000..6d90a5a449 --- /dev/null +++ b/libs/graphics/animator/SkHitClear.cpp @@ -0,0 +1,24 @@ +#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/libs/graphics/animator/SkHitClear.h b/libs/graphics/animator/SkHitClear.h new file mode 100644 index 0000000000..57bc6b55ae --- /dev/null +++ b/libs/graphics/animator/SkHitClear.h @@ -0,0 +1,16 @@ +#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/libs/graphics/animator/SkHitTest.cpp b/libs/graphics/animator/SkHitTest.cpp new file mode 100644 index 0000000000..7e48adbe2c --- /dev/null +++ b/libs/graphics/animator/SkHitTest.cpp @@ -0,0 +1,66 @@ +#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 == (S16)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/libs/graphics/animator/SkHitTest.h b/libs/graphics/animator/SkHitTest.h new file mode 100644 index 0000000000..a8c9fa457e --- /dev/null +++ b/libs/graphics/animator/SkHitTest.h @@ -0,0 +1,21 @@ +#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/libs/graphics/animator/SkIntArray.h b/libs/graphics/animator/SkIntArray.h new file mode 100644 index 0000000000..f1824a129a --- /dev/null +++ b/libs/graphics/animator/SkIntArray.h @@ -0,0 +1,49 @@ +#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/libs/graphics/animator/SkInterpolator.cpp b/libs/graphics/animator/SkInterpolator.cpp new file mode 100644 index 0000000000..f151bd4a23 --- /dev/null +++ b/libs/graphics/animator/SkInterpolator.cpp @@ -0,0 +1,252 @@ +#include "SkInterpolator.h" +#include "SkTSearch.h" + +SkInterpolatorBase::SkInterpolatorBase() +{ + fStorage = nil; + fTimes = nil; + SkDEBUGCODE(fTimesArray = nil;) +} + +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 = nil; + fTimes = nil; + SkDEBUGCODE(fTimesArray = nil); + } +} + +/* 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, SkScalar blend) +{ + SkASSERT(time > prevTime && time < nextTime); + SkASSERT(blend >= 0); + + SkScalar t = SkScalarDiv((SkScalar)(time - prevTime), (SkScalar)(nextTime - prevTime)); + return Blend(t, blend); +} + +SkScalar SkInterpolatorBase::Blend(SkScalar t, SkScalar blend) +{ + // f(t) = -2(1-blend)t^3 + 3(1 - blend)t^2 + blend*t + return SkScalarMul(SkScalarMul(SkScalarMul(-2*(SK_Scalar1 - blend), t) + 3*(SK_Scalar1 - blend), t) + blend, 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 = nil; + SkDEBUGCODE(fScalarsArray = nil;) +} + +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 +} + +bool SkInterpolator::setKeyFrame(int index, SkMSec time, const SkScalar values[], SkScalar blend) +{ + SkASSERT(values != nil); + 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 = blend; + 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; +} + + +/////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////// + +#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/libs/graphics/animator/SkMatrixParts.cpp b/libs/graphics/animator/SkMatrixParts.cpp new file mode 100644 index 0000000000..b9a06333d6 --- /dev/null +++ b/libs/graphics/animator/SkMatrixParts.cpp @@ -0,0 +1,284 @@ +#include "SkMatrixParts.h" +#include "SkAnimateMaker.h" +#include "SkDrawMatrix.h" +#include "SkDrawRectangle.h" +#include "SkDrawPath.h" + +SkMatrixPart::SkMatrixPart() : fMatrix(nil) { +} + +void SkMatrixPart::dirty() { + fMatrix->dirty(); +} + +SkDisplayable* SkMatrixPart::getParent() const { + return fMatrix; +} + +bool SkMatrixPart::setParent(SkDisplayable* parent) { + SkASSERT(parent != nil); + 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(nil) { +} + +SkFromPath::~SkFromPath() { +} + +bool SkFromPath::add() { + if (path == nil) + return true; + static const U8 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(nil), destination(nil) { +} + +SkRectToRect::~SkRectToRect() { +} + +bool SkRectToRect::add() { + if (source == nil || destination == nil) + return true; + SkMatrix temp; + temp.setRectToRect(source->fRect, destination->fRect); + 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 == nil) + return getMember("source"); // !!! cwap! need to refer to member through enum like kScope instead + else { + SkASSERT(destination == nil); + 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(nil), destination(nil) { +} + +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(dst, src, 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 == nil) + return getMember("source"); // !!! cwap! need to refer to member through enum like kScope instead + else { + SkASSERT(destination == nil); + return getMember("destination"); + } +} + + diff --git a/libs/graphics/animator/SkMatrixParts.h b/libs/graphics/animator/SkMatrixParts.h new file mode 100644 index 0000000000..95353551c0 --- /dev/null +++ b/libs/graphics/animator/SkMatrixParts.h @@ -0,0 +1,110 @@ +#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(); + S32 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/libs/graphics/animator/SkMemberInfo.cpp b/libs/graphics/animator/SkMemberInfo.cpp new file mode 100644 index 0000000000..9ad26d6894 --- /dev/null +++ b/libs/graphics/animator/SkMemberInfo.cpp @@ -0,0 +1,551 @@ +#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(S32); + 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 == nil) + 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 = nil; + bool success = true; + void* untypedStorage = nil; + 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(), '#')) != nil) { + 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() : nil; + 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, nil); + 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 != nil && + 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(nil /* !!! 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 != nil) + return result; + if (--count == 0) + return nil; + info++; + } + SkASSERT(info->fName); + SkASSERT(info->fType != SkType_BaseClassInfo); + if (*index >= count) { + *index -= count; + return nil; + } + 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 != nil) + return result; + if (--count == 0) + return nil; + info++; + } + SkASSERT(info->fName); + SkASSERT(info->fType != SkType_BaseClassInfo); + int index = SkStrSearch(&info->fName, count, match, sizeof(*info)); + if (index < 0 || index >= count) + return nil; + 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/libs/graphics/animator/SkMemberInfo.h b/libs/graphics/animator/SkMemberInfo.h new file mode 100644 index 0000000000..ec66846b07 --- /dev/null +++ b/libs/graphics/animator/SkMemberInfo.h @@ -0,0 +1,266 @@ +#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 nil 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(nil, SkType_##_type, &index); } \ + virtual const SkMemberInfo* getMember(const char name[]) { \ + return SkDisplayType::GetMember(nil, 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(nil, SkType_##_type, &index); } \ + virtual const SkMemberInfo* getMember(const char name[]) { \ + return SkDisplayType::GetMember(nil, 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(nil, SkType_##_type, &index); } \ + virtual const SkMemberInfo* getMember(const char name[]) { \ + return SkDisplayType::GetMember(nil, 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(nil, SkType_##_type, &index); } \ + virtual const SkMemberInfo* getMember(const char name[]) { \ + return SkDisplayType::GetMember(nil, 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/libs/graphics/animator/SkOpArray.cpp b/libs/graphics/animator/SkOpArray.cpp new file mode 100755 index 0000000000..c6c9ae9c7a --- /dev/null +++ b/libs/graphics/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/libs/graphics/animator/SkOpArray.h b/libs/graphics/animator/SkOpArray.h new file mode 100755 index 0000000000..19ae968c7e --- /dev/null +++ b/libs/graphics/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/libs/graphics/animator/SkOperand.h b/libs/graphics/animator/SkOperand.h new file mode 100644 index 0000000000..547cdbda6a --- /dev/null +++ b/libs/graphics/animator/SkOperand.h @@ -0,0 +1,37 @@ +#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/libs/graphics/animator/SkOperand2.h b/libs/graphics/animator/SkOperand2.h new file mode 100755 index 0000000000..f482e669b9 --- /dev/null +++ b/libs/graphics/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/libs/graphics/animator/SkOperandInterpolator.h b/libs/graphics/animator/SkOperandInterpolator.h new file mode 100644 index 0000000000..c6e38c25eb --- /dev/null +++ b/libs/graphics/animator/SkOperandInterpolator.h @@ -0,0 +1,39 @@ +#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/libs/graphics/animator/SkOperandIterpolator.cpp b/libs/graphics/animator/SkOperandIterpolator.cpp new file mode 100644 index 0000000000..8d9f44929d --- /dev/null +++ b/libs/graphics/animator/SkOperandIterpolator.cpp @@ -0,0 +1,139 @@ +#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 != nil); + 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 = 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) { + S32 a = prevSrc[i].fS32; + S32 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/libs/graphics/animator/SkPaintParts.cpp b/libs/graphics/animator/SkPaintParts.cpp new file mode 100644 index 0000000000..b45a125489 --- /dev/null +++ b/libs/graphics/animator/SkPaintParts.cpp @@ -0,0 +1,94 @@ +#include "SkPaintParts.h" +#include "SkDrawPaint.h" +#ifdef SK_DUMP_ENABLED +#include "SkDisplayList.h" +#include "SkDump.h" +#endif + +SkPaintPart::SkPaintPart() : fPaint(nil) { +} + +SkDisplayable* SkPaintPart::getParent() const { + return fPaint; +} + +bool SkPaintPart::setParent(SkDisplayable* parent) { + SkASSERT(parent != nil); + 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 nil; +} + + +// 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*) nil, this); + return false; +} + +SkPathEffect* SkDrawPathEffect::getPathEffect() { + return nil; +} + + +// SkDrawShader +SkShader* SkDrawShader::getShader() { + return nil; +} + + +// 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/libs/graphics/animator/SkPaintParts.h b/libs/graphics/animator/SkPaintParts.h new file mode 100644 index 0000000000..c0f84da78e --- /dev/null +++ b/libs/graphics/animator/SkPaintParts.h @@ -0,0 +1,66 @@ +#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/libs/graphics/animator/SkPathParts.cpp b/libs/graphics/animator/SkPathParts.cpp new file mode 100644 index 0000000000..537a32db77 --- /dev/null +++ b/libs/graphics/animator/SkPathParts.cpp @@ -0,0 +1,311 @@ +#include "SkPathParts.h" +#include "SkAnimateMaker.h" +#include "SkDrawMatrix.h" +#include "SkDrawRectangle.h" +#include "SkDrawPath.h" + +SkPathPart::SkPathPart() : fPath(nil) { +} + +void SkPathPart::dirty() { + fPath->dirty(); +} + +SkDisplayable* SkPathPart::getParent() const { + return fPath; +} + +bool SkPathPart::setParent(SkDisplayable* parent) { + SkASSERT(parent != nil); + 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(nil), path(nil) { +} + +bool SkAddPath::add() { + SkASSERT (path != nil); + if (matrix) + fPath->fPath.addPath(path->fPath, matrix->getMatrix()); + else + fPath->fPath.addPath(path->fPath); + return false; +} + + diff --git a/libs/graphics/animator/SkPathParts.h b/libs/graphics/animator/SkPathParts.h new file mode 100644 index 0000000000..ed78c92cac --- /dev/null +++ b/libs/graphics/animator/SkPathParts.h @@ -0,0 +1,156 @@ +#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/libs/graphics/animator/SkPostParts.cpp b/libs/graphics/animator/SkPostParts.cpp new file mode 100644 index 0000000000..3782e47fae --- /dev/null +++ b/libs/graphics/animator/SkPostParts.cpp @@ -0,0 +1,48 @@ +#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(nil) {} + +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/libs/graphics/animator/SkPostParts.h b/libs/graphics/animator/SkPostParts.h new file mode 100644 index 0000000000..4aca6c0d5d --- /dev/null +++ b/libs/graphics/animator/SkPostParts.h @@ -0,0 +1,22 @@ +#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/libs/graphics/animator/SkSVGPath.cpp b/libs/graphics/animator/SkSVGPath.cpp new file mode 100644 index 0000000000..e3c4083df2 --- /dev/null +++ b/libs/graphics/animator/SkSVGPath.cpp @@ -0,0 +1,226 @@ +#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, nil); + 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/libs/graphics/animator/SkScript.cpp b/libs/graphics/animator/SkScript.cpp new file mode 100644 index 0000000000..136e1fd7eb --- /dev/null +++ b/libs/graphics/animator/SkScript.cpp @@ -0,0 +1,1901 @@ +#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 = nil; + 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 ? nil : &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, nil, 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 == nil) { + 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 = nil); + 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 : nil); + 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 == nil) { + 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; + } + S32 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 == nil) { + 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 == nil) { + 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 { + S32 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(nil /* 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) != nil; + } + 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) != nil; + } + 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(S32 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(nil /* fMaker */, type)) + return (SkOpType) kObject; + if (SkDisplayType::IsEnum(nil /* 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/libs/graphics/animator/SkScript.h b/libs/graphics/animator/SkScript.h new file mode 100644 index 0000000000..e172c9ed63 --- /dev/null +++ b/libs/graphics/animator/SkScript.h @@ -0,0 +1,257 @@ +#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(S32 ); + 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; + S32 fIntAnswer; + SkScalar fScalarAnswer; + const char* fStringAnswer; +}; + +#endif + +#endif // SkScript_DEFINED diff --git a/libs/graphics/animator/SkScript2.h b/libs/graphics/animator/SkScript2.h new file mode 100755 index 0000000000..2443d822d1 --- /dev/null +++ b/libs/graphics/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(S32 ); + 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; + S32 fIntAnswer; + SkScalar fScalarAnswer; + const char* fStringAnswer; +}; + +#endif + + +#endif // SkScript2_DEFINED + diff --git a/libs/graphics/animator/SkScriptCallBack.h b/libs/graphics/animator/SkScriptCallBack.h new file mode 100755 index 0000000000..4b2510590e --- /dev/null +++ b/libs/graphics/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/libs/graphics/animator/SkScriptDecompile.cpp b/libs/graphics/animator/SkScriptDecompile.cpp new file mode 100644 index 0000000000..a9f25b7ea9 --- /dev/null +++ b/libs/graphics/animator/SkScriptDecompile.cpp @@ -0,0 +1,204 @@ +#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: { + S32 integer; + memcpy(&integer, opCode, sizeof(integer)); + opCode += sizeof(S32); + 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/libs/graphics/animator/SkScriptRuntime.cpp b/libs/graphics/animator/SkScriptRuntime.cpp new file mode 100755 index 0000000000..1851b00fd4 --- /dev/null +++ b/libs/graphics/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(S32)); + opCode += sizeof(S32); + 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] = nil; +} + +void SkScriptRuntime::untrack(SkString* string) { + int index = fTrackString.find(string); + SkASSERT(index >= 0); + fTrackString.begin()[index] = nil; +} + diff --git a/libs/graphics/animator/SkScriptRuntime.h b/libs/graphics/animator/SkScriptRuntime.h new file mode 100755 index 0000000000..75bf7db97d --- /dev/null +++ b/libs/graphics/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/libs/graphics/animator/SkScriptTokenizer.cpp b/libs/graphics/animator/SkScriptTokenizer.cpp new file mode 100755 index 0000000000..e8548073e4 --- /dev/null +++ b/libs/graphics/animator/SkScriptTokenizer.cpp @@ -0,0 +1,1513 @@ +#include "SkScript2.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 = nil); + 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, nil); + 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 == nil) { + 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) != nil; + } + 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) != nil; + } + 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(S32 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/libs/graphics/animator/SkSnapshot.cpp b/libs/graphics/animator/SkSnapshot.cpp new file mode 100644 index 0000000000..9bb74e604e --- /dev/null +++ b/libs/graphics/animator/SkSnapshot.cpp @@ -0,0 +1,54 @@ +#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; +} + +bool SkSnapshot::draw(SkAnimateMaker& maker) { + SkASSERT(type >= 0); + SkASSERT(filename.size() > 0); + SkImageEncoder* encoder = SkImageEncoder::Create((SkImageEncoder::Type) type); + SkBitmap bitmap; + maker.fCanvas->getPixels(&bitmap); + 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(), bitmap, SkScalarFloor(quality)); + return false; +} + +#endif diff --git a/libs/graphics/animator/SkSnapshot.h b/libs/graphics/animator/SkSnapshot.h new file mode 100644 index 0000000000..941df79215 --- /dev/null +++ b/libs/graphics/animator/SkSnapshot.h @@ -0,0 +1,25 @@ +#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/libs/graphics/animator/SkTDArray_Experimental.h b/libs/graphics/animator/SkTDArray_Experimental.h new file mode 100644 index 0000000000..9511d4ed52 --- /dev/null +++ b/libs/graphics/animator/SkTDArray_Experimental.h @@ -0,0 +1,133 @@ +#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, nil); } + int32_t* append(U16CPU count, const int32_t* src = nil); + + 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 = nil; +#ifdef SK_DEBUG + fData = nil; +#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=nil); 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 = nil) { return (T*) SkDS32Array::append(count, (const int32_t*) src); } + T* begin() const { SYNC(); return (T*) fArray; } + T* end() const { return (T*) (fArray ? fArray + fCount : nil); } + int find(const T& elem) const { return SkDS32Array::find((const int32_t&) elem); } + T* insert(U16CPU index) { return this->insert(index, 1, nil); } + T* insert(U16CPU index, U16CPU count, const T* src = nil) { + 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/libs/graphics/animator/SkTextOnPath.cpp b/libs/graphics/animator/SkTextOnPath.cpp new file mode 100644 index 0000000000..c0e991775b --- /dev/null +++ b/libs/graphics/animator/SkTextOnPath.cpp @@ -0,0 +1,30 @@ +#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(nil), text(nil) { +} + +bool SkTextOnPath::draw(SkAnimateMaker& maker) { + SkASSERT(text); + SkASSERT(path); + SkBoundableAuto boundable(this, maker); + maker.fCanvas->drawTextOnPath(text->getText(), text->getSize(), + path->getPath(), offset, *maker.fPaint); + return false; +} diff --git a/libs/graphics/animator/SkTextOnPath.h b/libs/graphics/animator/SkTextOnPath.h new file mode 100644 index 0000000000..c818aed727 --- /dev/null +++ b/libs/graphics/animator/SkTextOnPath.h @@ -0,0 +1,21 @@ +#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/libs/graphics/animator/SkTextToPath.cpp b/libs/graphics/animator/SkTextToPath.cpp new file mode 100644 index 0000000000..d259c698f6 --- /dev/null +++ b/libs/graphics/animator/SkTextToPath.cpp @@ -0,0 +1,39 @@ +#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(nil), path(nil), text(nil) { +} + +bool SkTextToPath::draw(SkAnimateMaker& maker) { + path->draw(maker); + return false; +} + +void SkTextToPath::onEndElement(SkAnimateMaker& maker) { + if (paint == nil || path == nil || text == nil) { + // !!! 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/libs/graphics/animator/SkTextToPath.h b/libs/graphics/animator/SkTextToPath.h new file mode 100644 index 0000000000..e36083f585 --- /dev/null +++ b/libs/graphics/animator/SkTextToPath.h @@ -0,0 +1,23 @@ +#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/libs/graphics/animator/SkTime.cpp b/libs/graphics/animator/SkTime.cpp new file mode 100644 index 0000000000..928480f550 --- /dev/null +++ b/libs/graphics/animator/SkTime.cpp @@ -0,0 +1,72 @@ +#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/libs/graphics/animator/SkTypedArray.cpp b/libs/graphics/animator/SkTypedArray.cpp new file mode 100644 index 0000000000..f73d8aa324 --- /dev/null +++ b/libs/graphics/animator/SkTypedArray.cpp @@ -0,0 +1,170 @@ +#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 = nil; +#ifdef SK_DEBUG + fData = nil; +#endif +} + +SkDS32Array::SkDS32Array(const SkDS32Array& src) +{ + fReserve = fCount = 0; + fArray = nil; +#ifdef SK_DEBUG + fData = nil; +#endif + SkDS32Array tmp(src.fArray, src.fCount); + this->swap(tmp); +} + +SkDS32Array::SkDS32Array(const S32 src[], U16CPU count) +{ + SkASSERT(src || count == 0); + + fReserve = fCount = 0; + fArray = nil; +#ifdef SK_DEBUG + fData = nil; +#endif + if (count) + { + fArray = (S32*)sk_malloc_throw(count * sizeof(S32)); +#ifdef SK_DEBUG + fData = (S32 (*)[kDebugArraySize]) fArray; +#endif + memcpy(fArray, src, sizeof(S32) * 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(S32) * 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(S32))); +} + +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); +} + +S32* SkDS32Array::append(U16CPU count, const S32* src) +{ + unsigned oldCount = fCount; + if (count) + { + SkASSERT(src == nil || fArray == nil || + src + count <= fArray || fArray + count <= src); + + this->growBy(count); + if (src) + memcpy(fArray + oldCount, src, sizeof(S32) * count); + } + return fArray + oldCount; +} + +int SkDS32Array::find(const S32& elem) const +{ + const S32* iter = fArray; + const S32* 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; + S32* array = (S32*)sk_malloc_throw(size * sizeof(S32)); + memcpy(array, fArray, fCount * sizeof(S32)); + + sk_free(fArray); + fArray = array; +#ifdef SK_DEBUG + fData = (S32 (*)[kDebugArraySize]) fArray; +#endif + fReserve = SkToU16((U16CPU)size); + } + fCount = SkToU16(fCount + extra); +} + +S32* SkDS32Array::insert(U16CPU index, U16CPU count, const S32* src) +{ + SkASSERT(count); + int oldCount = fCount; + this->growBy(count); + S32* dst = fArray + index; + memmove(dst + count, dst, sizeof(S32) * (oldCount - index)); + if (src) + memcpy(dst, src, sizeof(S32) * count); + return dst; +} + + + int SkDS32Array::rfind(const S32& elem) const + { + const S32* iter = fArray + fCount; + const S32* stop = fArray; + + while (iter > stop) + { + if (*--iter == elem) + return (int) (iter - stop); + } + return -1; + } + +#endif diff --git a/libs/graphics/animator/SkTypedArray.h b/libs/graphics/animator/SkTypedArray.h new file mode 100644 index 0000000000..96b42acfa1 --- /dev/null +++ b/libs/graphics/animator/SkTypedArray.h @@ -0,0 +1,22 @@ +#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/libs/graphics/animator/SkXMLAnimatorWriter.cpp b/libs/graphics/animator/SkXMLAnimatorWriter.cpp new file mode 100644 index 0000000000..8045fdea40 --- /dev/null +++ b/libs/graphics/animator/SkXMLAnimatorWriter.cpp @@ -0,0 +1,74 @@ +#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->drawRGB(255, 255, 255); + s.draw(canvas, &paint, 0); +} + +#endif + diff --git a/libs/graphics/animator/SkXMLAnimatorWriter.h b/libs/graphics/animator/SkXMLAnimatorWriter.h new file mode 100644 index 0000000000..7180dbda0d --- /dev/null +++ b/libs/graphics/animator/SkXMLAnimatorWriter.h @@ -0,0 +1,25 @@ +#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/libs/graphics/animator/thingstodo.txt b/libs/graphics/animator/thingstodo.txt new file mode 100644 index 0000000000..8d0d47a02d --- /dev/null +++ b/libs/graphics/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/libs/graphics/effects/Sk1DPathEffect.cpp b/libs/graphics/effects/Sk1DPathEffect.cpp new file mode 100644 index 0000000000..938858e166 --- /dev/null +++ b/libs/graphics/effects/Sk1DPathEffect.cpp @@ -0,0 +1,162 @@ +#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; +} + +SkScalar Sk1DPathEffect::begin(SkScalar contourLength) +{ + return 0; +} + +SkScalar Sk1DPathEffect::next(SkPath* dst, SkScalar distance, SkPathMeasure&) +{ + return 0; +} + +/////////////////////////////////////////////////////////////////////////////////////////// + +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 + { + if (phase >= advance) + phase = SkScalarMod(phase, advance); + fAdvance = advance; + fPhase = 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; + } + } +} + +SkScalar SkPath1DPathEffect::begin(SkScalar contourLength) +{ + // should we offer an option to scale to a multiple of the contourLength? + return fPhase; +} + +SkScalar SkPath1DPathEffect::next(SkPath* dst, SkScalar distance, SkPathMeasure& meas) +{ + switch (fStyle) { + case kTranslate_Style: + { + SkPoint pos; + meas.getPosTan(distance, &pos, nil); + 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/libs/graphics/effects/Sk2DPathEffect.cpp b/libs/graphics/effects/Sk2DPathEffect.cpp new file mode 100644 index 0000000000..601666d39f --- /dev/null +++ b/libs/graphics/effects/Sk2DPathEffect.cpp @@ -0,0 +1,89 @@ +#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; + SkRect16 r; + + src.transform(fInverse, &tmp); + tmp.computeBounds(&bounds, SkPath::kExact_BoundsType); + bounds.round(&r); + if (!r.isEmpty()) + { + this->begin(r, dst); + SkScan::FillPath(tmp, nil, &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 SkRect16& uvBounds, SkPath* dst) {} +void Sk2DPathEffect::next(const SkPoint& loc, int u, int v, SkPath* dst) {} +void Sk2DPathEffect::end(SkPath* dst) {} + +//////////////////////////////////////////////////////////////////////////////// + +void Sk2DPathEffect::flatten(SkWBuffer& buffer) +{ + this->INHERITED::flatten(buffer); + + buffer.write(&fMatrix, sizeof(fMatrix)); +} + +Sk2DPathEffect::Sk2DPathEffect(SkRBuffer& buffer) : SkPathEffect(buffer) +{ + buffer.read(&fMatrix, sizeof(fMatrix)); + fMatrix.invert(&fInverse); +} + +SkFlattenable::Factory Sk2DPathEffect::getFactory() +{ + return CreateProc; +} + +SkFlattenable* Sk2DPathEffect::CreateProc(SkRBuffer& buffer) +{ + return SkNEW_ARGS(Sk2DPathEffect, (buffer)); +} + + + diff --git a/libs/graphics/effects/SkAvoidXfermode.cpp b/libs/graphics/effects/SkAvoidXfermode.cpp new file mode 100644 index 0000000000..80f03100c1 --- /dev/null +++ b/libs/graphics/effects/SkAvoidXfermode.cpp @@ -0,0 +1,131 @@ +#include "SkAvoidXfermode.h" +#include "SkColorPriv.h" + +SkAvoidXfermode::SkAvoidXfermode(SkColor opColor, U8CPU tolerance, bool reverse) +{ + if (tolerance > 255) + tolerance = 255; + + fOpColor = opColor; + fDistMul = (256 << 14) / (tolerance + 1); + fReverse = reverse; +} + +static unsigned color_dist16(uint16_t c, unsigned r, unsigned g, unsigned b) +{ + unsigned dr = SkAbs32(SkPacked16ToR32(c) - r); + unsigned dg = SkAbs32(SkPacked16ToG32(c) - g); + unsigned db = SkAbs32(SkPacked16ToB32(c) - b); + + return SkMax32(dr, SkMax32(dg, db)); +} + +static unsigned color_dist32(SkPMColor c, U8CPU r, U8CPU g, U8CPU b) +{ + 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 float dot14tofloat(int x) +{ + return (float)x / (1 << 14); +} + +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, 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); +} + +void SkAvoidXfermode::xfer32(SkPMColor dst[], const SkPMColor src[], int count, const SkAlpha aa[]) +{ + // override in subclass +} + +static inline U16CPU SkBlend3216(SkPMColor src, U16CPU dst, unsigned scale) +{ + SkASSERT(scale <= 256); + + 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[]) +{ + SkColor opColor = fOpColor; + unsigned opR = SkPacked16ToR32(opColor); + unsigned opG = SkPacked16ToG32(opColor); + unsigned opB = SkPacked16ToB32(opColor); + uint32_t mul = fDistMul; + uint32_t sub = (fDistMul - (1 << 14)) << 8; + + int MAX, mask; + + if (fReverse) + { + mask = -1; + MAX = 256; + } + else + { + mask = 0; + MAX = 0; + } + + if (NULL == aa) + for (int i = 0; i < count; i++) { + int d = color_dist16(dst[i], opR, opG, opB); + d = SkAlpha255To256(d); + // now reverse d if we need to + d = MAX + (d ^ mask) - mask; + SkASSERT((unsigned)d <= 256); + + d = scale_dist_14(d, mul, sub); + SkASSERT(d <= 256); + + if (d > 0) + dst[i] = SkBlend3216(src[i], dst[i], d); + } + else + for (int i = 0; i < count; i++) { + unsigned antialias = aa[i]; + if (0 == antialias) + continue; + + int d = color_dist16(dst[i], opR, opG, opB); + d = SkAlpha255To256(d); + // now reverse d if we need to + d = MAX + (d ^ mask) - mask; + SkASSERT((unsigned)d <= 256); + + d = scale_dist_14(d, mul, sub); + SkASSERT(d <= 256); + + if (d > 0) + dst[i] = SkBlend3216(src[i], dst[i], SkAlphaMul(antialias, d)); + } +} + +void SkAvoidXfermode::xferA8(SkAlpha dst[], const SkPMColor src[], int count, const SkAlpha aa[]) +{ + // override in subclass +} + diff --git a/libs/graphics/effects/SkBlurMask.cpp b/libs/graphics/effects/SkBlurMask.cpp new file mode 100644 index 0000000000..85023424a7 --- /dev/null +++ b/libs/graphics/effects/SkBlurMask.cpp @@ -0,0 +1,308 @@ +#include "SkBlurMask.h" +#include "SkTemplates.h" + +static void build_sum_buffer(uint32_t dst[], int w, int h, const uint8_t src[]) +{ + int x, y; + + // special case first row + uint32_t X = 0; + for (x = w - 1; x >= 0; --x) + { + X = *src++ + X; + *dst++ = X; + } + // 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; + } + } +} + +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 = nil; + + 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()); + + SkAutoTCallProc<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); + 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/libs/graphics/effects/SkBlurMask.h b/libs/graphics/effects/SkBlurMask.h new file mode 100644 index 0000000000..40cea94a82 --- /dev/null +++ b/libs/graphics/effects/SkBlurMask.h @@ -0,0 +1,23 @@ +#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/libs/graphics/effects/SkBlurMaskFilter.cpp b/libs/graphics/effects/SkBlurMaskFilter.cpp new file mode 100644 index 0000000000..8073bc52ec --- /dev/null +++ b/libs/graphics/effects/SkBlurMaskFilter.cpp @@ -0,0 +1,96 @@ +#include "SkBlurMaskFilter.h" +#include "SkBlurMask.h" +#include "SkBuffer.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, SkPoint16* margin); + + // overrides from SkFlattenable + // This method is not exported to java. + virtual Factory getFactory(); + // This method is not exported to java. + virtual void flatten(SkWBuffer&); + +private: + SkScalar fRadius; + SkBlurMaskFilter::BlurStyle fBlurStyle; + + static SkFlattenable* CreateProc(SkRBuffer&); + SkBlurMaskFilterImpl(SkRBuffer&); + + 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, SkPoint16* margin) +{ + SkScalar radius = matrix.mapRadius(fRadius); + + if (margin) + margin->set(SkScalarCeil(radius), SkScalarCeil(radius)); + + return SkBlurMask::Blur(dst, src, radius, (SkBlurMask::Style)fBlurStyle); +} + +SkFlattenable* SkBlurMaskFilterImpl::CreateProc(SkRBuffer& buffer) +{ + return SkNEW_ARGS(SkBlurMaskFilterImpl, (buffer)); +} + +SkFlattenable::Factory SkBlurMaskFilterImpl::getFactory() +{ + return CreateProc; +} + +SkBlurMaskFilterImpl::SkBlurMaskFilterImpl(SkRBuffer& buffer) : SkMaskFilter(buffer) +{ + fRadius = buffer.readScalar(); + fBlurStyle = (SkBlurMaskFilter::BlurStyle)buffer.readS32(); + SkASSERT(fRadius >= 0); + SkASSERT((unsigned)fBlurStyle < SkBlurMaskFilter::kBlurStyleCount); +} + +void SkBlurMaskFilterImpl::flatten(SkWBuffer& buffer) +{ + this->INHERITED::flatten(buffer); + buffer.writeScalar(fRadius); + buffer.write32(fBlurStyle); +} + diff --git a/libs/graphics/effects/SkCamera.cpp b/libs/graphics/effects/SkCamera.cpp new file mode 100644 index 0000000000..fcebf44e09 --- /dev/null +++ b/libs/graphics/effects/SkCamera.cpp @@ -0,0 +1,251 @@ +#include "SkCamera.h" + +static SkScalar VectorMultiplyDivide(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; + } + if (denom) // denom == 0 is a formal signal to not divide + prod /= denom; + 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; + } + if (denom) + { + prod.div(denom, Sk64::kRound_DivOption); + return prod.get32(); + } + else // denom == 0 is a formal signal to treat the product as a fixed (i.e. denom = SK_Fixed1) + 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); +} + +struct SkMatrix3D { + SkScalar fMat[3][3]; + + void set(int row, SkScalar a, SkScalar b, SkScalar c) + { + fMat[row][0] = a; + fMat[row][1] = b; + fMat[row][2] = c; + } + void 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] = VectorMultiplyDivide(3, &a.fMat[i][0], 1, &b.fMat[0][j], 3, 0); + + if (c == &tmp) + *this = tmp; + } + + void map(SkPoint3D* v) const + { + SkScalar x = VectorMultiplyDivide(3, &fMat[0][0], 1, &v->fX, 1, 0); + SkScalar y = VectorMultiplyDivide(3, &fMat[1][0], 1, &v->fX, 1, 0); + SkScalar z = VectorMultiplyDivide(3, &fMat[2][0], 1, &v->fX, 1, 0); + v->set(x, y, z); + } +}; + +void SkPatch3D::rotate(SkScalar radX, SkScalar radY, SkScalar radZ) +{ + SkScalar s, c; + SkMatrix3D mx, my, mz, m; + + s = SkScalarSinCos(radX, &c); + mx.set(0, SK_Scalar1, 0, 0); + mx.set(1, 0, c, -s); + mx.set(2, 0, s, c); + + s = SkScalarSinCos(radY, &c); + my.set(0, c, 0, -s); + my.set(1, 0, SK_Scalar1, 0); + my.set(2, s, 0, c); + + s = SkScalarSinCos(radZ, &c); + mz.set(0, c, -s, 0); + mz.set(1, s, c, 0); + mz.set(2, 0, 0, SK_Scalar1); + + m.setConcat(mx, my); + m.setConcat(m, mz); + + m.map(&fU); + m.map(&fV); +} + +SkScalar SkPatch3D::dotWith(SkScalar dx, SkScalar dy, SkScalar dz) const +{ + SkUnit3D normal; + + SkUnit3D::Cross(*(SkUnit3D*)&fU, *(SkUnit3D*)&fV, &normal); + return SkUnitScalarMul(normal.fX, dx) + + SkUnitScalarMul(normal.fY, dy) + + SkUnitScalarMul(normal.fZ, dz); +} + +/////////////////////////////////////////////////////////////////////////// + +SkCamera3D::SkCamera3D() +{ + fLocation.set(0, 0, -SkIntToScalar(1440)); // 20 inches backward + fAxis.set(0, 0, SK_Scalar1); // forward + fZenith.set(0, -SK_Scalar1, 0); // up + fObserver.set(0, 0, -SkIntToScalar(1440)); // 20 inches backward + + this->update(); +} + +void SkCamera3D::update() +{ + SkUnit3D axis, zenith, cross; + + fAxis.normalize(&axis); + + { + SkScalar dot = SkUnit3D::Dot(*(SkUnit3D*)&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*)&zenith)->normalize(&zenith); + } + + SkUnit3D::Cross(axis, zenith, &cross); + + { + SkScalar* destPtr = (SkScalar*)&fOrientation; + SkScalar x = fObserver.fX; + SkScalar y = fObserver.fY; + SkScalar z = fObserver.fZ; + + destPtr[0] = SkUnitScalarMul(x, axis.fX) - SkUnitScalarMul(z, cross.fX); + destPtr[1] = SkUnitScalarMul(x, axis.fY) - SkUnitScalarMul(z, cross.fY); + destPtr[2] = SkUnitScalarMul(x, axis.fZ) - SkUnitScalarMul(z, cross.fZ); + destPtr[3] = SkUnitScalarMul(y, axis.fX) - SkUnitScalarMul(z, zenith.fX); + destPtr[4] = SkUnitScalarMul(y, axis.fY) - SkUnitScalarMul(z, zenith.fY); + destPtr[5] = SkUnitScalarMul(y, axis.fZ) - SkUnitScalarMul(z, zenith.fZ); + memcpy(&destPtr[6], &axis, sizeof(axis)); + } +} + +void SkCamera3D::computeMatrix(const SkPatch3D& quilt, SkMatrix* matrix) const +{ + SkScalar* destPtr = (SkScalar*)matrix; + const SkScalar* mapPtr = (const SkScalar*)&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*)&diff, *(const SkUnit3D*)((const SkScalar*)&fOrientation + 6)); + + patchPtr = (const SkScalar*)&quilt; + destPtr[0] = VectorMultiplyDivide(3, patchPtr, 1, mapPtr, 1, dot); + destPtr[3] = VectorMultiplyDivide(3, patchPtr, 1, mapPtr+3, 1, dot); + destPtr[6] = VectorMultiplyDivide(3, patchPtr, 1, mapPtr+6, 1, dot); + patchPtr += 3; + destPtr[1] = VectorMultiplyDivide(3, patchPtr, 1, mapPtr, 1, dot); + destPtr[4] = VectorMultiplyDivide(3, patchPtr, 1, mapPtr+3, 1, dot); + destPtr[7] = VectorMultiplyDivide(3, patchPtr, 1, mapPtr+6, 1, dot); + patchPtr = (const SkScalar*)&diff; + destPtr[2] = VectorMultiplyDivide(3, patchPtr, 1, mapPtr, 1, dot); + destPtr[5] = VectorMultiplyDivide(3, patchPtr, 1, mapPtr+3, 1, dot); + destPtr[8] = SK_UnitScalar1; +} diff --git a/libs/graphics/effects/SkColorFilters.cpp b/libs/graphics/effects/SkColorFilters.cpp new file mode 100644 index 0000000000..0809f0f816 --- /dev/null +++ b/libs/graphics/effects/SkColorFilters.cpp @@ -0,0 +1,264 @@ +#include "SkColorFilter.h" +#include "SkColorPriv.h" +#include "SkPorterDuff.h" +#include "SkUtils.h" + +class SkSrc_XfermodeColorFilter : public SkColorFilter { +public: + SkSrc_XfermodeColorFilter(SkColor color) : fColor(SkPreMultiplyColor(color)) {} + + virtual void filterSpan(const SkPMColor shader[], int count, SkPMColor result[]) + { + sk_memset32(result, fColor, count); + } + +private: + SkPMColor fColor; +}; + +class SkSrcOver_XfermodeColorFilter : public SkColorFilter { +public: + SkSrcOver_XfermodeColorFilter(SkColor color) : fColor(SkPreMultiplyColor(color)) {} + + 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); + } + +private: + SkPMColor fColor; +}; + +class SkXfermodeColorFilter : public SkColorFilter { +public: + SkXfermodeColorFilter(SkColor color, SkXfermodeProc proc) + { + fColor = SkPreMultiplyColor(color); + fProc = proc; + } + + 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]); + } + +private: + SkPMColor fColor; + SkXfermodeProc fProc; +}; + +SkColorFilter* SkColorFilter::CreatXfermodeFilter(SkColor color, SkXfermodeProc proc) +{ + return proc ? SkNEW_ARGS(SkXfermodeColorFilter, (color, proc)) : NULL; +} + +SkColorFilter* SkColorFilter::CreatePorterDuffFilter(SkColor color, SkPorterDuff::Mode mode) +{ + // first collaps some modes if possible + + if (SkPorterDuff::kClear_Mode == mode) + { + color = 0; + mode = SkPorterDuff::kSrc_Mode; + } + else if (SkPorterDuff::kSrcOver_Mode == mode) + { + unsigned alpha = SkColorGetA(color); + + if (0 == alpha) + mode = SkPorterDuff::kDst_Mode; + else if (255 == alpha) + mode = SkPorterDuff::kSrc_Mode; + // else just stay srcover + } + + switch (mode) { + case SkPorterDuff::kSrc_Mode: + return SkNEW_ARGS(SkSrc_XfermodeColorFilter, (color)); + case SkPorterDuff::kDst_Mode: + return SkNEW(SkColorFilter); + case SkPorterDuff::kSrcOver_Mode: + return SkNEW_ARGS(SkSrcOver_XfermodeColorFilter, (color)); + default: + return SkColorFilter::CreatXfermodeFilter(color, SkPorterDuff::GetXfermodeProc(mode)); + } +} + +///////////////////////////////////////////////////////////////////////////////////////////////// + +static inline unsigned pin(unsigned value, unsigned max) +{ + if (value > max) + value = max; + return value; +} + +static inline unsigned SkUClampMax(unsigned value, unsigned max) +{ + SkASSERT((S32)value >= 0); + SkASSERT((S32)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: + SkColor fMul, fAdd; +}; + +class SkLightingColorFilter_JustAdd : public SkColorFilter { +public: + SkLightingColorFilter_JustAdd(SkColor add) : fAdd(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; + } + } + +private: + SkColor fAdd; +}; + +class SkLightingColorFilter_JustMul : public SkColorFilter { +public: + SkLightingColorFilter_JustMul(SkColor mul) : fMul(mul) {} + + 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; + } + } + +private: + SkColor fMul; +}; + +class SkLightingColorFilter_NoPin : public SkLightingColorFilter { +public: + SkLightingColorFilter_NoPin(SkColor mul, SkColor add) + : SkLightingColorFilter(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; + } + } +}; + +SkColorFilter* SkColorFilter::CreateLightingFilter(SkColor mul, SkColor add) +{ + mul &= 0x00FFFFFF; + add &= 0x00FFFFFF; + + if (0xFFFFFF == mul) + { + if (0 == add) + return SkNEW(SkColorFilter); // no change to the colors + else + return SkNEW_ARGS(SkLightingColorFilter_JustAdd, (add)); + } + + if (0 == add) + return SkNEW_ARGS(SkLightingColorFilter_JustMul, (mul)); + + 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/libs/graphics/effects/SkCornerPathEffect.cpp b/libs/graphics/effects/SkCornerPathEffect.cpp new file mode 100644 index 0000000000..fdc36d481a --- /dev/null +++ b/libs/graphics/effects/SkCornerPathEffect.cpp @@ -0,0 +1,117 @@ +#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; + + 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 + break; + case SkPath::kCubic_Verb: + // TBD + break; + case SkPath::kClose_Verb: + 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(SkWBuffer& buffer) +{ + this->INHERITED::flatten(buffer); + buffer.writeScalar(fRadius); +} + +SkFlattenable* SkCornerPathEffect::CreateProc(SkRBuffer& buffer) +{ + return SkNEW_ARGS(SkCornerPathEffect, (buffer)); +} + +SkCornerPathEffect::SkCornerPathEffect(SkRBuffer& buffer) : SkPathEffect(buffer) +{ + fRadius = buffer.readScalar(); +} + diff --git a/libs/graphics/effects/SkCullPoints.cpp b/libs/graphics/effects/SkCullPoints.cpp new file mode 100644 index 0000000000..6d004fa136 --- /dev/null +++ b/libs/graphics/effects/SkCullPoints.cpp @@ -0,0 +1,141 @@ +#include "SkCullPoints.h" +#include "Sk64.h" + +static bool cross_product_is_neg(const SkPoint32& 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 SkRect16& 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 + SkPoint32 vec; + const SkPoint16* 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 SkRect16& r, SkPoint16 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() +{ + SkRect16 r; + r.setEmpty(); + this->reset(r); +} + +SkCullPoints::SkCullPoints(const SkRect16& r) +{ + this->reset(r); +} + +void SkCullPoints::reset(const SkRect16& r) +{ + fR = r; + toQuad(fR, fAsQuad); + fPrevPt.set(0, 0); +} + +void SkCullPoints::moveTo(int x, int y) +{ + fPrevPt.set(x, y); +} + +SkCullPoints::LineToResult SkCullPoints::lineTo(int x, int y, SkPoint16 result[]) +{ + SkASSERT(result != NULL); + + int x0 = fPrevPt.fX; + int y0 = fPrevPt.fY; + fPrevPt.set(x, y); + + // 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)) + { + result[0].set(x0, y0); + result[1].set(x, y); + return kMoveToLineTo_Result; + } + return kNo_Result; +} + +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "SkPath.h" + +SkCullPointsPath::SkCullPointsPath() + : fCP(), fPath(NULL) +{ +} + +SkCullPointsPath::SkCullPointsPath(const SkRect16& r, SkPath* dst) + : fCP(r), fPath(dst) +{ +} + +void SkCullPointsPath::reset(const SkRect16& 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) +{ + SkPoint16 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/libs/graphics/effects/SkDashPathEffect.cpp b/libs/graphics/effects/SkDashPathEffect.cpp new file mode 100644 index 0000000000..782cf2d5dc --- /dev/null +++ b/libs/graphics/effects/SkDashPathEffect.cpp @@ -0,0 +1,163 @@ +#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 ? nil : CreateProc; +} + +void SkDashPathEffect::flatten(SkWBuffer& buffer) +{ + this->INHERITED::flatten(buffer); + + SkASSERT(fInitialDashLength >= 0); + + buffer.write32(fCount); + buffer.write32(fInitialDashIndex); + buffer.writeScalar(fInitialDashLength); + buffer.writeScalar(fIntervalLength); + buffer.write32(fScaleToFit); + buffer.write(fIntervals, fCount * sizeof(fIntervals[0])); +} + +SkFlattenable* SkDashPathEffect::CreateProc(SkRBuffer& buffer) +{ + return SkNEW_ARGS(SkDashPathEffect, (buffer)); +} + +SkDashPathEffect::SkDashPathEffect(SkRBuffer& buffer) : SkPathEffect(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/libs/graphics/effects/SkDiscretePathEffect.cpp b/libs/graphics/effects/SkDiscretePathEffect.cpp new file mode 100644 index 0000000000..78d17d0fc5 --- /dev/null +++ b/libs/graphics/effects/SkDiscretePathEffect.cpp @@ -0,0 +1,89 @@ +#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); + U32 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(SkRBuffer& buffer) +{ + return SkNEW_ARGS(SkDiscretePathEffect, (buffer)); +} + +void SkDiscretePathEffect::flatten(SkWBuffer& buffer) +{ + this->INHERITED::flatten(buffer); + buffer.writeScalar(fSegLength); + buffer.writeScalar(fPerterb); +} + +SkDiscretePathEffect::SkDiscretePathEffect(SkRBuffer& buffer) : SkPathEffect(buffer) +{ + fSegLength = buffer.readScalar(); + fPerterb = buffer.readScalar(); +} + + diff --git a/libs/graphics/effects/SkEmbossMask.cpp b/libs/graphics/effects/SkEmbossMask.cpp new file mode 100644 index 0000000000..b47305f533 --- /dev/null +++ b/libs/graphics/effects/SkEmbossMask.cpp @@ -0,0 +1,165 @@ +#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(); + U8* alpha = mask->fImage; + U8* multiply = (U8*)alpha + planeSize; + U8* 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; + // ((U8*)alpha)[x] = alpha[x] * multiply[x] >> 8; + } + } + alpha += rowBytes; + multiply += rowBytes; + additive += rowBytes; + prev_row = rowBytes; + } +} + + diff --git a/libs/graphics/effects/SkEmbossMask.h b/libs/graphics/effects/SkEmbossMask.h new file mode 100644 index 0000000000..0e2ecbecf9 --- /dev/null +++ b/libs/graphics/effects/SkEmbossMask.h @@ -0,0 +1,12 @@ +#ifndef SkEmbossMask_DEFINED +#define SkEmbossMask_DEFINED + +#include "SkEmbossMaskFilter.h" + +class SkEmbossMask { +public: + static void Emboss(SkMask* mask, const SkEmbossMaskFilter::Light&); +}; + +#endif + diff --git a/libs/graphics/effects/SkEmbossMaskFilter.cpp b/libs/graphics/effects/SkEmbossMaskFilter.cpp new file mode 100644 index 0000000000..248eab90e7 --- /dev/null +++ b/libs/graphics/effects/SkEmbossMaskFilter.cpp @@ -0,0 +1,116 @@ +#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, SkPoint16* margin) +{ + SkScalar radius = matrix.mapRadius(fBlurRadius); + + SkBlurMask::Blur(dst, src, radius, SkBlurMask::kInner_Style); + + dst->fFormat = SkMask::k3D_Format; + if (src.fImage == NULL) + return true; + + // create a larger buffer for the other two channels (should force fBlur to do this for us) + + { + U8* 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*)light.fDirection, (SkVector*)fLight.fDirection, 1); + // now restore the length of the XY component + ((SkVector*)light.fDirection)->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(SkRBuffer& buffer) +{ + return SkNEW_ARGS(SkEmbossMaskFilter, (buffer)); +} + +SkFlattenable::Factory SkEmbossMaskFilter::getFactory() +{ + return CreateProc; +} + +SkEmbossMaskFilter::SkEmbossMaskFilter(SkRBuffer& 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(SkWBuffer& buffer) +{ + this->INHERITED::flatten(buffer); + + fLight.fPad = 0; // for the font-cache lookup to be clean + buffer.write(&fLight, sizeof(fLight)); + buffer.writeScalar(fBlurRadius); +} + diff --git a/libs/graphics/effects/SkEmbossMask_Table.h b/libs/graphics/effects/SkEmbossMask_Table.h new file mode 100644 index 0000000000..40b3e73b62 --- /dev/null +++ b/libs/graphics/effects/SkEmbossMask_Table.h @@ -0,0 +1,1029 @@ +#include "SkTypes.h" + +static const U16 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/libs/graphics/effects/SkGradientShader.cpp b/libs/graphics/effects/SkGradientShader.cpp new file mode 100644 index 0000000000..ed4daccfa3 --- /dev/null +++ b/libs/graphics/effects/SkGradientShader.cpp @@ -0,0 +1,1025 @@ +#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 U32 getFlags() { return fFlags; } + +protected: + SkUnitMapper* fMapper; + SkMatrix fPtsToUnit; // set by subclass + SkMatrix fDstToIndex; + SkMatrix::MapPtProc fDstToIndexProc; + SkPMColor* fARGB32; + 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 + }; + 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 nil, we need to recompute the cache values + SkPMColor* fCache32; // working ptr. If this is nil, 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 = nil; + fCache32 = fCache32Storage = nil; + + 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)); + fTileProc = gTileProcs[mode]; + + fRecs = (Rec*)(fARGB32 + colorCount); + if (colorCount > 2) + { + Rec* recs = fRecs; + + recs[0].fPos = 0; + // recs[0].fScale = 0; // unused; + if (pos) + { + for (int i = 1; i < colorCount; i++) + { + SkASSERT(pos[i] > pos[i-1] && pos[i] <= SK_Scalar1); + recs[i].fPos = SkScalarToFixed(pos[i]); + recs[i].fScale = (1 << 24) / SkScalarToFixed(pos[i] - pos[i-1]); + } + } + 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() +{ + if (fCache16Storage) + sk_free(fCache16Storage); + if (fCache32Storage) + sk_free(fCache32Storage); + if (fOrigColors != fStorage) + sk_free(fOrigColors); + fMapper->safeUnref(); +} + +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.getMapPtProc(); + fDstToIndexClass = (U8)SkShader::ComputeMatrixClass(fDstToIndex); + + // now convert our colors in to PMColors + int pa = paint.getAlpha(); + int scale = SkAlpha255To256(pa); + int prevAlpha = -1; + + for (unsigned i = 0; i < fColorCount; i++) + { + U32 src = fOrigColors[i]; + int sa = SkAlphaMul(SkColorGetA(src), scale); + int localScale = SkAlpha255To256(sa); + pa &= sa; + + if (i == 0) + prevAlpha = sa; + else + { + if (prevAlpha != sa) + prevAlpha = -1; + } + + fARGB32[i] = SkPackARGB32( sa, + SkAlphaMul(SkColorGetR(src), localScale), + SkAlphaMul(SkColorGetG(src), localScale), + SkAlphaMul(SkColorGetB(src), localScale)); + } + + if (pa == 0xFF) + { + SkASSERT(prevAlpha >= 0); + fFlags = kOpaqueAlpha_Flag; + } + else if (prevAlpha >= 0) + fFlags = kConstAlpha_Flag; + else + fFlags = 0; + + // 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 nil + if (fCacheAlpha != paint.getAlpha()) + { + fCache16 = nil; // inval the cache + fCache32 = nil; // inval the cache + fCacheAlpha = paint.getAlpha(); // 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 U32 dot8_blend_packed32(U32 s0, U32 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) + +static void build_16bit_cache(U16 cache[], SkPMColor c0, SkPMColor c1, int count) +{ + SkASSERT(count > 1); + + SkFixed r = SkGetPackedR32(c0); + SkFixed g = SkGetPackedG32(c0); + SkFixed b = SkGetPackedB32(c0); + + SkFixed dr = SkIntToFixed(SkGetPackedR32(c1) - r) / (count - 1); + SkFixed dg = SkIntToFixed(SkGetPackedG32(c1) - g) / (count - 1); + SkFixed db = SkIntToFixed(SkGetPackedB32(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 U16* Gradient_Shader::getCache16() +{ + if (fCache16 == nil) + { + if (fCache16Storage == nil) // set the storage and our working ptr +#ifdef TEST_GRADIENT_DITHER + fCache16Storage = (U16*)sk_malloc_throw(sizeof(U16) * kCache16Count * 2); +#else + fCache16Storage = (U16*)sk_malloc_throw(sizeof(U16) * kCache16Count); +#endif + fCache16 = fCache16Storage; + if (fColorCount == 2) + build_16bit_cache(fCache16, fARGB32[0], fARGB32[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, fARGB32[i-1], fARGB32[i], nextIndex - prevIndex + 1); + prevIndex = nextIndex; + } + SkASSERT(prevIndex == kCache16Count - 1); + } + + if (fMapper) + { +#ifdef TEST_GRADIENT_DITHER + fCache16Storage = (U16*)sk_malloc_throw(sizeof(U16) * kCache16Count * 2); +#else + fCache16Storage = (U16*)sk_malloc_throw(sizeof(U16) * kCache16Count); +#endif + U16* linear = fCache16; // just computed linear data + U16* 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 == nil) + { + if (fCache32Storage == nil) // 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, 0, 0); +} + +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 U32 getFlags() { return this->INHERITED::getFlags() | kHasSpan16_Flag; } + virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count); + virtual void shadeSpanOpaque16(int x, int y, U16 dstC[], int count); + +private: + typedef Gradient_Shader INHERITED; +}; + +void Linear_Gradient::shadeSpan(int x, int y, SkPMColor dstC[], int count) +{ + SkASSERT(count > 0); + + SkPoint srcPt; + SkMatrix::MapPtProc 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, nil); + 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) + { + 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); + } +} + +#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::shadeSpanOpaque16(int x, int y, U16 dstC[], int count) +{ + SkASSERT(count > 0); + SkASSERT(this->getPaintAlpha() == 0xFF); + + SkPoint srcPt; + SkMatrix::MapPtProc dstProc = fDstToIndexProc; + TileProc proc = fTileProc; + const U16* 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, nil); + 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); + *dstC++ = cache[fi >> (16 - kCache16Bits)]; + 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 U8 gSqrt8Table[] = {\n"); + + for (int i = 0; i < kSQRT_TABLE_SIZE; i++) + { + if ((i & 15) == 0) + ::fprintf(file, "\t"); + + U8 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, 0, 0); +} + +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 U32 getFlags() { return this->INHERITED::getFlags() | kHasSpan16_Flag; } + virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count) + { + SkASSERT(count > 0); + + SkPoint srcPt; + SkMatrix::MapPtProc 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 U8* 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 shadeSpanOpaque16(int x, int y, U16 dstC[], int count) + { + SkASSERT(count > 0); + + SkPoint srcPt; + SkMatrix::MapPtProc dstProc = fDstToIndexProc; + TileProc proc = fTileProc; + const U16* 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 U8* 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); + *dstC++ = cache[fi >> (16 - kCache16Bits)]; + dstX += SK_Scalar1; + } while (--count != 0); + } + } +private: + typedef Gradient_Shader INHERITED; +}; + +/////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////// + +SkDiscreteMapper::SkDiscreteMapper(unsigned segments) +{ + if (segments < 2) + { + fSegments = 0; + fScale = 0; + } + else + { + fSegments = segments; + fScale = SK_Fract1 / (segments - 1); + } +} + +U16CPU SkDiscreteMapper::mapUnit16(U16CPU x) +{ + x = x * fSegments >> 16; + return x * fScale >> 14; +} + +U16CPU SkFlipCosineMapper::mapUnit16(U16CPU x) +{ + x = SkFixedCos(x * (SK_FixedPI >> 2) >> 15); + x += x << 15 >> 31; // map 0x10000 to 0xFFFF + x = 0xFFFF - x; + return x; +} + +SkShader* SkGradientShader::CreateLinear( const SkPoint pts[2], + const SkColor colors[], const SkScalar pos[], int colorCount, + TileMode mode, SkUnitMapper* mapper) +{ + SkASSERT(pts && colors && colorCount >= 2); + + return SkNEW_ARGS(Linear_Gradient, (pts, colors, pos, colorCount, mode, mapper)); +} + +SkShader* SkGradientShader::CreateRadial( const SkPoint& center, SkScalar radius, + const SkColor colors[], const SkScalar pos[], int colorCount, + TileMode mode, SkUnitMapper* mapper) +{ + SkASSERT(radius > 0 && colors && colorCount >= 2); + + return SkNEW_ARGS(Radial_Gradient, (center, radius, colors, pos, colorCount, mode, mapper)); +} + diff --git a/libs/graphics/effects/SkLayerRasterizer.cpp b/libs/graphics/effects/SkLayerRasterizer.cpp new file mode 100644 index 0000000000..896c4f44ff --- /dev/null +++ b/libs/graphics/effects/SkLayerRasterizer.cpp @@ -0,0 +1,251 @@ +#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" + +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 SkRect16* clipBounds, SkRect16* bounds) +{ + SkDeque::Iter iter(layers); + SkLayerRasterizer_Rec* rec; + + bounds->set(SK_MaxS16, SK_MaxS16, SK_MinS16, SK_MinS16); + + 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 SkRect16* 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.fDevice = &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.fMapPtProc = drawMatrix.getMapPtProc(); + + draw.drawPath(path, rec->fPaint); + } + } + return true; +} + +/////////// Routines for flattening ///////////////// + +static SkFlattenable* load_flattenable(SkRBuffer& buffer) +{ + SkFlattenable::Factory fact = (SkFlattenable::Factory)buffer.readPtr(); + return fact ? fact(buffer) : NULL; +} + +static void write_flattenable(SkFlattenable* obj, SkWBuffer& buffer) +{ + if (NULL == obj) + buffer.writePtr(NULL); + else + { + SkFlattenable::Factory fact = obj->getFactory(); + SkASSERT(fact); + + buffer.writePtr((void*)fact); + obj->flatten(buffer); + } +} + +static void paint_read(SkPaint* paint, SkRBuffer& buffer) +{ + paint->setAntiAliasOn(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()); + } + + buffer.skipToAlign4(); + + paint->setMaskFilter((SkMaskFilter*)load_flattenable(buffer)); + paint->setPathEffect((SkPathEffect*)load_flattenable(buffer)); + paint->setRasterizer((SkRasterizer*)load_flattenable(buffer)); + paint->setXfermode((SkXfermode*)load_flattenable(buffer)); +} + +static void paint_write(const SkPaint& paint, SkWBuffer& buffer) +{ + buffer.writeBool(paint.isAntiAliasOn()); + 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.padToAlign4(); + + write_flattenable(paint.getMaskFilter(), buffer); + write_flattenable(paint.getPathEffect(), buffer); + write_flattenable(paint.getRasterizer(), buffer); + write_flattenable(paint.getXfermode(), buffer); +} + +SkLayerRasterizer::SkLayerRasterizer(SkRBuffer& 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(SkWBuffer& 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(SkRBuffer& buffer) +{ + return SkNEW_ARGS(SkLayerRasterizer, (buffer)); +} + +SkFlattenable::Factory SkLayerRasterizer::getFactory() +{ + return CreateProc; +} + diff --git a/libs/graphics/effects/SkNinePatch.cpp b/libs/graphics/effects/SkNinePatch.cpp new file mode 100644 index 0000000000..dfca76c206 --- /dev/null +++ b/libs/graphics/effects/SkNinePatch.cpp @@ -0,0 +1,177 @@ +#include "SkNinePatch.h" +#include "SkBitmap.h" +#include "SkCanvas.h" +#include "SkPaint.h" + +#define USE_TRACEx + +#ifdef USE_TRACE + static bool gTrace; +#endif + +static void* getSubAddr(const SkBitmap& bm, int x, int y) +{ + SkASSERT((unsigned)x < bm.width()); + SkASSERT((unsigned)y < bm.height()); + + switch (bm.getConfig()) { + case SkBitmap::kNo_Config: + case SkBitmap::kA1_Config: + SkASSERT(!"unsupported config for ninepatch"); + break; + case SkBitmap::kA8_Config: + case SkBitmap:: kIndex8_Config: + break; + case SkBitmap::kRGB_565_Config: + x <<= 1; + break; + case SkBitmap::kARGB_8888_Config: + x <<= 2; + break; + default: + break; + } + return (char*)bm.getPixels() + x + y * bm.rowBytes(); +} + +static void drawPatch(SkCanvas* canvas, const SkRect16& src, const SkRect& dst, + const SkBitmap& bitmap, const SkPaint& paint) +{ +#ifdef USE_TRACE + if (gTrace) SkDebugf("======== ninepatch src [%d %d %d,%d] dst[%g %g %g,%g]\n", + src.fLeft, src.fTop, src.width(), src.height(), + SkScalarToFloat(dst.fLeft), SkScalarToFloat(dst.fTop), + SkScalarToFloat(dst.width()), SkScalarToFloat(dst.height())); +#endif + + SkBitmap tmp; + + tmp.setConfig(bitmap.getConfig(), src.width(), src.height(), bitmap.rowBytes()); + tmp.setPixels(getSubAddr(bitmap, src.fLeft, src.fTop)); + + SkMatrix matrix; + SkRect tmpSrc; + tmpSrc.set(0, 0, SkIntToScalar(tmp.width()), SkIntToScalar(tmp.height())); + matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit); + + canvas->save(); +// canvas->clipRect(dst); try not to use, as it will suck visually if we're rotated (and make us way-slow) + canvas->concat(matrix); + canvas->drawBitmap(tmp, 0, 0, paint); + canvas->restore(); +} + +static bool pixel_is_transparent(const SkBitmap& bm, int x, int y) +{ + switch (bm.getConfig()) { + case SkBitmap::kARGB_8888_Config: + return 0 == *bm.getAddr32(x, y); + default: + return false; + } +} + +static void drawColumn(SkCanvas* canvas, SkRect16* src, SkRect* dst, + const SkRect& bounds, const SkRect16& mar, + const SkBitmap& bitmap, const SkPaint& paint) +{ +// upper row + src->fTop = 0; + src->fBottom = mar.fTop; + dst->fTop = bounds.fTop; + dst->fBottom = bounds.fTop + SkIntToScalar(mar.fTop); + drawPatch(canvas, *src, *dst, bitmap, paint); +// mid row + src->fTop = src->fBottom; + src->fBottom = bitmap.height() - mar.fBottom; + dst->fTop = dst->fBottom; + dst->fBottom = bounds.fBottom - SkIntToScalar(mar.fBottom); + if (src->width() != 1 || src->height() != 1 || + !pixel_is_transparent(bitmap, src->fLeft, src->fTop)) + { + drawPatch(canvas, *src, *dst, bitmap, paint); + } + else + { + // SkDEBUGF(("========= Skip transparent center of ninepatch\n")); + } +// lower row + src->fTop = src->fBottom; + src->fBottom = bitmap.height(); + dst->fTop = dst->fBottom; + dst->fBottom = bounds.fBottom; + drawPatch(canvas, *src, *dst, bitmap, paint); +} + +void SkNinePatch::Draw(SkCanvas* canvas, const SkRect& bounds, + const SkBitmap& bitmap, const SkRect16& margin, + const SkPaint* paint) +{ +#ifdef USE_TRACE + if (10 == margin.fLeft) gTrace = true; +#endif + + SkASSERT(canvas); + +#ifdef USE_TRACE + if (gTrace) SkDebugf("======== ninepatch bounds [%g %g]\n", SkScalarToFloat(bounds.width()), SkScalarToFloat(bounds.height())); + if (gTrace) SkDebugf("======== ninepatch paint bm [%d,%d]\n", bitmap.width(), bitmap.height()); + if (gTrace) SkDebugf("======== ninepatch margin [%d,%d,%d,%d]\n", margin.fLeft, margin.fTop, margin.fRight, margin.fBottom); +#endif + + + if (bounds.isEmpty() || + bitmap.width() == 0 || bitmap.height() == 0 || bitmap.getPixels() == NULL || + paint && paint->getXfermode() == NULL && paint->getAlpha() == 0) + { +#ifdef USE_TRACE + if (gTrace) SkDebugf("======== abort ninepatch draw\n"); +#endif + return; + } + + SkPaint defaultPaint; + if (NULL == paint) + paint = &defaultPaint; + + SkRect dst; + SkRect16 src, mar; + + mar.set(SkMax32(margin.fLeft, 0), SkMax32(margin.fTop, 0), + SkMax32(margin.fRight, 0), SkMax32(margin.fBottom, 0)); + +// left column + src.fLeft = 0; + src.fRight = mar.fLeft; + dst.fLeft = bounds.fLeft; + dst.fRight = bounds.fLeft + SkIntToScalar(mar.fLeft); + drawColumn(canvas, &src, &dst, bounds, mar, bitmap, *paint); +// mid column + src.fLeft = src.fRight; + src.fRight = bitmap.width() - mar.fRight; + dst.fLeft = dst.fRight; + dst.fRight = bounds.fRight - SkIntToScalar(mar.fRight); + drawColumn(canvas, &src, &dst, bounds, mar, bitmap, *paint); +// right column + src.fLeft = src.fRight; + src.fRight = bitmap.width(); + dst.fLeft = dst.fRight; + dst.fRight = bounds.fRight; + drawColumn(canvas, &src, &dst, bounds, mar, bitmap, *paint); + +#ifdef USE_TRACE + gTrace = false; +#endif +} + +void SkNinePatch::Draw(SkCanvas* canvas, const SkRect& bounds, + const SkBitmap& bitmap, int cx, int cy, + const SkPaint* paint) +{ + SkRect16 marginRect; + + marginRect.set(cx, cy, bitmap.width() - cx - 1, bitmap.height() - cy - 1); + + SkNinePatch::Draw(canvas, bounds, bitmap, marginRect, paint); +} + diff --git a/libs/graphics/effects/SkRadialGradient_Table.h b/libs/graphics/effects/SkRadialGradient_Table.h new file mode 100644 index 0000000000..24fc715591 --- /dev/null +++ b/libs/graphics/effects/SkRadialGradient_Table.h @@ -0,0 +1,130 @@ +static const U8 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/libs/graphics/effects/SkShaderExtras.cpp b/libs/graphics/effects/SkShaderExtras.cpp new file mode 100644 index 0000000000..cf8f047de1 --- /dev/null +++ b/libs/graphics/effects/SkShaderExtras.cpp @@ -0,0 +1,51 @@ +#include "SkShader.h" +#include "SkColorFilter.h" +#include "SkShaderExtras.h" + +////////////////////////////////////////////////////////////////////////////////////// + +SkComposeShader::SkComposeShader(SkShader* sA, SkShader* sB, SkColorCombine* combine) +{ + fShaderA = sA; sA->ref(); + fShaderB = sB; sB->ref(); + fCombine = combine; combine->ref(); +} + +SkComposeShader::~SkComposeShader() +{ + fCombine->unref(); + fShaderB->unref(); + fShaderA->unref(); +} + +bool SkComposeShader::setContext(const SkBitmap& device, + const SkPaint& paint, + const SkMatrix& matrix) +{ + return this->INHERITED::setContext(device, paint, matrix) && + fShaderA->setContext(device, paint, matrix) && + fShaderA->setContext(device, paint, matrix); +} + +void SkComposeShader::shadeSpan(int x, int y, SkPMColor result[], int count) +{ + SkShader* shaderA = fShaderA; + SkShader* shaderB = fShaderB; + SkColorCombine* combine = fCombine; + + SkPMColor tmp[COUNT]; + do { + int n = count; + if (n > COUNT) + n = COUNT; + + shaderA->shadeSpan(x, y, tmp, n); + shaderB->shadeSpan(x, y, result, n); + combine->combineSpan(tmp, result, n, result); + + result += n; + x += n; + count -= n; + } while (count > 0); +} + diff --git a/libs/graphics/effects/SkTransparentShader.cpp b/libs/graphics/effects/SkTransparentShader.cpp new file mode 100644 index 0000000000..a20c9a5b45 --- /dev/null +++ b/libs/graphics/effects/SkTransparentShader.cpp @@ -0,0 +1,104 @@ +#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); +} + +U32 SkTransparentShader::getFlags() +{ + U32 flags = 0; + + switch (fDevice.getConfig()) { + case SkBitmap::kRGB_565_Config: + flags |= kHasSpan16_Flag; + // fall through + case SkBitmap::kARGB_8888_Config: + flags |= (fAlpha == 255 ? kOpaqueAlpha_Flag : kConstAlpha_Flag); + 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 U16* 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) + { + U16 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::kIndex8_Config: + SkASSERT(!"index8 not supported as a destination device"); + break; + case SkBitmap::kA8_Config: + { + const U8* 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::shadeSpanOpaque16(int x, int y, U16 span[], int count) +{ + SkASSERT(fAlpha == 255); + SkASSERT(fDevice.getConfig() == SkBitmap::kRGB_565_Config); + + memcpy(span, fDevice.getAddr16(x, y), count << 1); +} + diff --git a/libs/graphics/images/SkBitmapRef.cpp b/libs/graphics/images/SkBitmapRef.cpp new file mode 100644 index 0000000000..750a841eac --- /dev/null +++ b/libs/graphics/images/SkBitmapRef.cpp @@ -0,0 +1,121 @@ +#include "SkBitmapRefPriv.h" +#include "SkTemplates.h" +#include "SkThread.h" +#include "SkString.h" +#include "SkGlobals.h" +#include "SkThread.h" + +SkGlobals::Rec* SkBitmapRef_Globals::Create() +{ + SkBitmapRef_Globals* rec = SkNEW(SkBitmapRef_Globals); + rec->fCache = nil; + return rec; +} + +/////////////////////////////////////////////////////////////////////////////////////////// + +SkBitmapRef::SkBitmapRef(Rec* rec) : fRec(rec) +{ + SkASSERT(rec); + rec->fRefCnt += 1; +} + +SkBitmapRef::SkBitmapRef(const SkBitmap& src, bool transferOwnsPixels) +{ + fRec = SkNEW_ARGS(SkBitmapRef::Rec, (src)); + fRec->fIsCache = false; + if (transferOwnsPixels) + { + fRec->fBM.setOwnsPixels(src.getOwnsPixels()); + ((SkBitmap*)&src)->setOwnsPixels(false); + } +} + +SkBitmapRef::~SkBitmapRef() +{ + if (fRec->fIsCache) + { + SkBitmapRef_Globals& globals = *(SkBitmapRef_Globals*)SkGlobals::Find(kBitmapRef_GlobalsTag, + SkBitmapRef_Globals::Create); + SkAutoMutexAcquire ac(globals.fMutex); + + SkASSERT(fRec->fRefCnt > 0); + fRec->fRefCnt -= 1; + } + else + { + SkDEBUGF(("~SkBitmapRef[%d %d]\n", fRec->fBM.width(), fRec->fBM.height())); + SkDELETE(fRec); + } +} + +const SkBitmap& SkBitmapRef::bitmap() +{ + return fRec->fBM; +} + +/////////////////////////////////////////////////////////////////////////////////////////// + +SkBitmapRef* SkBitmapRef::create(const SkBitmap& src, bool transferOwnsPixels) +{ + return SkNEW_ARGS(SkBitmapRef, (src, transferOwnsPixels)); +} + +void SkBitmapRef::PurgeCacheAll() +{ + SkBitmapRef_Globals* globals = (SkBitmapRef_Globals*)SkGlobals::Find(kBitmapRef_GlobalsTag, nil); + if (globals == nil) + return; + + SkAutoMutexAcquire ac(globals->fMutex); + SkBitmapRef::Rec* rec = globals->fCache; + SkDEBUGCODE(int count = 0;) + + while (rec) + { + SkDEBUGCODE(count += 1;) + SkASSERT(rec->fRefCnt == 0); + Rec* next = rec->fNext; + SkDELETE(rec); + rec = next; + } + globals->fCache = nil; + + SkDEBUGF(("PurgeCacheAll freed %d bitmaps\n", count)); +} + +bool SkBitmapRef::PurgeCacheOne() +{ + SkBitmapRef_Globals* globals = (SkBitmapRef_Globals*)SkGlobals::Find(kBitmapRef_GlobalsTag, nil); + if (globals == nil) + return false; + + SkAutoMutexAcquire ac(globals->fMutex); + SkBitmapRef::Rec* rec = globals->fCache; + SkBitmapRef::Rec* prev = nil; + SkDEBUGCODE(int count = 0;) + + while (rec) + { + SkDEBUGCODE(count += 1;) + + SkBitmapRef::Rec* next = rec->fNext; + if (rec->fRefCnt == 0) + { + if (prev) + prev = next; + else + globals->fCache = next; + + SkDEBUGF(("PurgeCacheOne for bitmap[%d %d]\n", rec->fBM.width(), rec->fBM.height())); + SkDELETE(rec); + return true; + } + prev = rec; + rec = next; + } + + SkDEBUGF(("PurgeCacheOne: nothing to purge from %d bitmaps\n", count)); + return false; +} + diff --git a/libs/graphics/images/SkBitmapRefPriv.h b/libs/graphics/images/SkBitmapRefPriv.h new file mode 100644 index 0000000000..24c0a2cc09 --- /dev/null +++ b/libs/graphics/images/SkBitmapRefPriv.h @@ -0,0 +1,31 @@ +#ifndef SkBitmapRefPriv_DEFINED +#define SkBitmapRefPriv_DEFINED + +#include "SkBitmapRef.h" +#include "SkGlobals.h" +#include "SkThread.h" +#include "SkBitmap.h" +#include "SkString.h" + +#define kBitmapRef_GlobalsTag SkSetFourByteTag('s', 'k', 'b', 'r') + +class SkBitmapRef_Globals : public SkGlobals::Rec { +public: + SkMutex fMutex; + SkBitmapRef::Rec* fCache; + + static Rec* Create(); +}; + +struct SkBitmapRef::Rec { + Rec(bool isCache): fIsCache(isCache) {} + Rec(const SkBitmap& src): fBM(src) {} + + Rec* fNext; + int32_t fRefCnt; + SkString fPath; + SkBitmap fBM; + bool fIsCache; +}; + +#endif diff --git a/libs/graphics/images/SkImageDecoder.cpp b/libs/graphics/images/SkImageDecoder.cpp new file mode 100644 index 0000000000..7c76bc8493 --- /dev/null +++ b/libs/graphics/images/SkImageDecoder.cpp @@ -0,0 +1,175 @@ +#include "SkImageDecoder.h" +#include "SkStream.h" +#include "SkTemplates.h" + +static SkBitmap::Config gDeviceConfig = SkBitmap::kNo_Config; + +SkBitmap::Config SkImageDecoder::GetDeviceConfig() +{ + return gDeviceConfig; +} + +void SkImageDecoder::SetDeviceConfig(SkBitmap::Config config) +{ + gDeviceConfig = config; +} + +SkImageDecoder::SkImageDecoder() +{ +} + +SkImageDecoder::~SkImageDecoder() +{ +} + +bool SkImageDecoder::DecodeFile(const char file[], SkBitmap* bm, SkBitmap::Config pref) +{ + SkASSERT(file); + SkASSERT(bm); + + SkFILEStream stream(file); + return stream.isValid() && SkImageDecoder::DecodeStream(&stream, bm, pref); +} + +bool SkImageDecoder::DecodeMemory(const void* buffer, size_t size, SkBitmap* bm, SkBitmap::Config pref) +{ + SkASSERT(buffer); + + SkMemoryStream stream(buffer, size); + return size && SkImageDecoder::DecodeStream(&stream, bm, pref); +} + +bool SkImageDecoder::DecodeURL(const char url[], SkBitmap* bm, SkBitmap::Config pref) +{ + SkASSERT(url); + SkASSERT(bm); + + SkURLStream stream(url); + return SkImageDecoder::DecodeStream(&stream, bm, pref); +} + +bool SkImageDecoder::DecodeStream(SkStream* stream, SkBitmap* bm, SkBitmap::Config pref) +{ + SkASSERT(stream); + SkASSERT(bm); + + SkImageDecoder* codec = SkImageDecoder::Factory(stream); + if (codec) + { + SkAutoTDelete<SkImageDecoder> ad(codec); + SkBitmap tmp; + + if (codec->onDecode(stream, &tmp, pref)) + { + /* We operate on a tmp bitmap until we know we succeed. This way + we're sure we don't change the caller's bitmap and then later + return false. Returning false must mean that their parameter + is unchanged. + */ + bm->swap(tmp); + return true; + } + } + return false; +} + +///////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_SUPPORT_IMAGE_ENCODE + +SkImageEncoder::~SkImageEncoder() +{ +} + +bool SkImageEncoder::encodeStream(SkWStream* stream, const SkBitmap& bm, int quality) +{ + quality = SkMin32(100, SkMax32(0, quality)); + return this->onEncode(stream, bm, quality); +} + +bool SkImageEncoder::encodeFile(const char file[], const SkBitmap& bm, int quality) +{ + quality = SkMin32(100, SkMax32(0, quality)); + SkFILEWStream stream(file); + return this->onEncode(&stream, bm, quality); +} + +extern SkImageEncoder* sk_create_jpeg_encoder(); +extern SkImageEncoder* sk_create_png_encoder(); + +SkImageEncoder* SkImageEncoder::Create(Type t) +{ + if (kJPEG_Type == t) + return sk_create_jpeg_encoder(); + if (kPNG_Type == t) + return sk_create_png_encoder(); + return nil; +} + +#endif + +/////////////////////////////////////////////////////////////////////////////////// + +#include "SkBitmapRefPriv.h" + +SkBitmapRef* SkBitmapRef::DecodeFile(const char path[], bool forceDecode) +{ + SkBitmapRef_Globals& globals = *(SkBitmapRef_Globals*)SkGlobals::Find(kBitmapRef_GlobalsTag, + SkBitmapRef_Globals::Create); + + SkAutoMutexAcquire ac(globals.fMutex); + SkBitmapRef::Rec* rec = globals.fCache; + + while (rec) + { + if (rec->fPath.equals(path)) + return SkNEW_ARGS(SkBitmapRef, (rec)); + rec = rec->fNext; + } + + if (forceDecode) + { + SkAutoTDelete<Rec> autoRec(rec = SkNEW_ARGS(Rec, (true))); + + if (SkImageDecoder::DecodeFile(path, &rec->fBM)) + { + rec->fPath.set(path); + rec->fRefCnt = 0; + rec->fNext = globals.fCache; + globals.fCache = rec; + + (void)autoRec.detach(); // detach from autoRec, so we don't delete it + return SkNEW_ARGS(SkBitmapRef, (rec)); + } + } + return nil; +} + +SkBitmapRef* SkBitmapRef::DecodeMemory(const void* bytes, size_t len) +{ + SkBitmapRef::Rec* rec; + SkAutoTDelete<Rec> autoRec(rec = SkNEW_ARGS(Rec, (false))); + + if (SkImageDecoder::DecodeMemory(bytes, len, &rec->fBM)) + { + rec->fRefCnt = 0; + (void)autoRec.detach(); + return SkNEW_ARGS(SkBitmapRef, (rec)); + } + return nil; +} + +SkBitmapRef* SkBitmapRef::DecodeStream(SkStream* stream) +{ + SkBitmapRef::Rec* rec; + SkAutoTDelete<Rec> autoRec(rec = SkNEW_ARGS(Rec, (false))); + + if (SkImageDecoder::DecodeStream(stream, &rec->fBM)) + { + rec->fRefCnt = 0; + (void)autoRec.detach(); + return SkNEW_ARGS(SkBitmapRef, (rec)); + } + return nil; +} + diff --git a/libs/graphics/images/SkImageDecoder_libgif.cpp b/libs/graphics/images/SkImageDecoder_libgif.cpp new file mode 100644 index 0000000000..621164d94b --- /dev/null +++ b/libs/graphics/images/SkImageDecoder_libgif.cpp @@ -0,0 +1,123 @@ +#include "SkImageDecoder.h" +#include "SkColor.h" +#include "SkColorPriv.h" +#include "SkStream.h" +#include "SkTemplates.h" + +class SkGIFImageDecoder : public SkImageDecoder { +protected: + virtual bool onDecode(SkStream* stream, SkBitmap* bm, SkBitmap::Config pref); +}; + + +extern "C" { +#include "gif_lib.h" +} + +#define GIF_STAMP "GIF" /* First chars in file - GIF stamp. */ +#define GIF_STAMP_LEN (sizeof(GIF_STAMP) - 1) + +SkImageDecoder* SkImageDecoder_GIF_Factory(SkStream* stream) +{ + char buf[GIF_STAMP_LEN]; + if (stream->read(buf, GIF_STAMP_LEN) == GIF_STAMP_LEN && + memcmp(GIF_STAMP, buf, GIF_STAMP_LEN) == 0) + { + stream->rewind(); + return SkNEW(SkGIFImageDecoder); + } + return NULL; +} + +static int Decode(GifFileType* fileType, GifByteType* out, int size) +{ + SkStream* stream = (SkStream*) fileType->UserData; + return (int) stream->read(out, size); +} + +bool SkGIFImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* bm, SkBitmap::Config prefConfig) +{ + GifFileType* gif = DGifOpen( sk_stream, Decode ); + if (gif == nil) + return false; + if (DGifSlurp(gif) != GIF_OK) + return false; + ColorMapObject* cmap = gif->SColorMap; + if (cmap == 0 || + gif->ImageCount < 1 || + cmap->ColorCount != (1 << cmap->BitsPerPixel)) + return false; + SavedImage* gif_image = gif->SavedImages + 0; + const int width = gif->SWidth; + const int height = gif->SHeight; + SkBitmap::Config config = SkBitmap::kIndex8_Config; + bm->setConfig(config, width, height, 0); + bm->allocPixels(); + + SkColorTable* colorTable = SkNEW(SkColorTable); + colorTable->setColors(cmap->ColorCount); + bm->setColorTable(colorTable)->unref(); + + int transparent = -1; + for (int i = 0; i < gif_image->ExtensionBlockCount; ++i) { + ExtensionBlock* eb = gif_image->ExtensionBlocks + i; + if (eb->Function == 0xF9 && + eb->ByteCount == 4) { + bool has_transparency = ((eb->Bytes[0] & 1) == 1); + if (has_transparency) { + transparent = 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] = SkColorSetRGB(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); + } + + DGifCloseFile(gif); + return true; +} diff --git a/libs/graphics/images/SkImageDecoder_libjpeg.cpp b/libs/graphics/images/SkImageDecoder_libjpeg.cpp new file mode 100644 index 0000000000..91f019eb9d --- /dev/null +++ b/libs/graphics/images/SkImageDecoder_libjpeg.cpp @@ -0,0 +1,427 @@ +#include "SkImageDecoder.h" +#include "SkColorPriv.h" +#include "SkStream.h" +#include "SkTemplates.h" + +#include <stdio.h> +extern "C" { + #include "jpeglib.h" +} + +class SkJPEGImageDecoder : public SkImageDecoder { +protected: + virtual bool onDecode(SkStream* stream, SkBitmap* bm, SkBitmap::Config pref); +}; + +SkImageDecoder* SkImageDecoder_JPEG_Factory(SkStream* stream) { + // !!! unimplemented; rely on PNG test first for now + return SkNEW(SkJPEGImageDecoder); +} + +////////////////////////////////////////////////////////////////////////// + +/* our source struct for directing jpeg to our stream object +*/ +struct sk_source_mgr : jpeg_source_mgr { + sk_source_mgr(SkStream* stream); + + SkStream* fStream; + + enum { + kBufferSize = 1024 + }; + char fBuffer[kBufferSize]; +}; + +/* Automatically clean up after throwing an exception */ +struct JPEGAutoClean +{ + JPEGAutoClean(jpeg_decompress_struct* info): cinfo_ptr(info) {} + ~JPEGAutoClean() + { + jpeg_destroy_decompress(cinfo_ptr); + } +private: + jpeg_decompress_struct* cinfo_ptr; +}; + +static void sk_init_source(j_decompress_ptr cinfo) +{ + sk_source_mgr* src = (sk_source_mgr*)cinfo->src; + + src->next_input_byte = (const JOCTET*)src->fBuffer; + src->bytes_in_buffer = 0; +} + +static boolean sk_fill_input_buffer(j_decompress_ptr cinfo) +{ + sk_source_mgr* src = (sk_source_mgr*)cinfo->src; + size_t bytes = src->fStream->read(src->fBuffer, sk_source_mgr::kBufferSize); + // note that JPEG is happy with less than the full read, as long as the result is non-zero + if (bytes == 0) + cinfo->err->error_exit((j_common_ptr)cinfo); + + src->next_input_byte = (const JOCTET*)src->fBuffer; + src->bytes_in_buffer = bytes; + return TRUE; +} + +static void sk_skip_input_data(j_decompress_ptr cinfo, long num_bytes) +{ + SkASSERT(num_bytes > 0); + + sk_source_mgr* src = (sk_source_mgr*)cinfo->src; + + long skip = num_bytes - src->bytes_in_buffer; + + if (skip > 0) + { + size_t bytes = src->fStream->read(nil, skip); + if (bytes != (size_t)skip) + cinfo->err->error_exit((j_common_ptr)cinfo); + + src->next_input_byte = (const JOCTET*)src->fBuffer; + src->bytes_in_buffer = 0; + } + else + { + src->next_input_byte += num_bytes; + src->bytes_in_buffer -= num_bytes; + } +} + +static boolean sk_resync_to_restart(j_decompress_ptr cinfo, int desired) +{ + sk_source_mgr* src = (sk_source_mgr*)cinfo->src; + + // what is the desired param for??? + + if (!src->fStream->rewind()) + cinfo->err->error_exit((j_common_ptr)cinfo); + + return TRUE; +} + +static void sk_term_source(j_decompress_ptr /*cinfo*/) +{ + // nothing to do +} + +sk_source_mgr::sk_source_mgr(SkStream* stream) + : fStream(stream) +{ + init_source = sk_init_source; + fill_input_buffer = sk_fill_input_buffer; + skip_input_data = sk_skip_input_data; + resync_to_restart = sk_resync_to_restart; + term_source = sk_term_source; +} + +#include <setjmp.h> + +struct sk_error_mgr : jpeg_error_mgr { + jmp_buf fJmpBuf; +}; + +static void sk_error_exit(j_common_ptr cinfo) +{ + sk_error_mgr* error = (sk_error_mgr*)cinfo->err; + + /* Always display the message */ + (*error->output_message) (cinfo); + + /* Let the memory manager delete any temp files before we die */ + jpeg_destroy(cinfo); + + longjmp(error->fJmpBuf, -1); +} + +bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, SkBitmap::Config prefConfig) +{ + jpeg_decompress_struct cinfo; + sk_error_mgr sk_err; + sk_source_mgr sk_stream(stream); + + cinfo.err = jpeg_std_error(&sk_err); + sk_err.error_exit = sk_error_exit; + + if (setjmp(sk_err.fJmpBuf)) + return false; + + jpeg_create_decompress(&cinfo); + JPEGAutoClean autoClean(&cinfo); + + //jpeg_stdio_src(&cinfo, file); + cinfo.src = &sk_stream; + + jpeg_read_header(&cinfo, true); + + // now we know the dimensions. can call jpeg_destroy() if we wish + + // check for supported formats + bool isRGB; // as opposed to gray8 + if (3 == cinfo.num_components && JCS_RGB == cinfo.out_color_space) + isRGB = true; + else if (1 == cinfo.num_components && JCS_GRAYSCALE == cinfo.out_color_space) + isRGB = false; // could use Index8 config if we want... + else { + SkDEBUGF(("SkJPEGImageDecoder: unsupported jpeg colorspace %d with %d components\n", + cinfo.jpeg_color_space, cinfo.num_components)); + return false; + } + + SkBitmap::Config config = prefConfig; + // if no user preference, see what the device recommends + if (config == SkBitmap::kNo_Config) + config = SkImageDecoder::GetDeviceConfig(); + + // only one of these two makes sense for jpegs + if (config != SkBitmap::kARGB_8888_Config && config != SkBitmap::kRGB_565_Config) + config = SkBitmap::kARGB_8888_Config; + + bm->setConfig(config, cinfo.image_width, cinfo.image_height, 0); + bm->allocPixels(); + bm->setIsOpaque(true); + + jpeg_start_decompress(&cinfo); + + SkASSERT(cinfo.output_width == bm->width()); + SkASSERT(cinfo.output_height == bm->height()); + + int width = bm->width(); + + if (config == SkBitmap::kARGB_8888_Config) + { + for (unsigned y = 0; y < cinfo.output_height; y++) + { + uint32_t* bmRow = bm->getAddr32(0, y); + JSAMPLE* rowptr = (JSAMPLE*)bmRow; + int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1); + SkASSERT(row_count == 1); + + // since we want to reuse bmRow for the RGB triples and the final ARGB quads + // we need to expand backwards, so we don't overwrite our src + + uint32_t* dst = bmRow + width; + + if (isRGB) { + const uint8_t* src = rowptr + width * 3; + while (dst > bmRow) + { + src -= 3; + *--dst = SkPackARGB32(0xFF, src[0], src[1], src[2]); + } + } + else { // grayscale + const uint8_t* src = rowptr + width; + while (dst > bmRow) + { + src -= 1; + *--dst = SkPackARGB32(0xFF, src[0], src[0], src[0]); + } + } + } + } + else // convert to 16bit + { + SkAutoMalloc srcStorage(width * 3); + U8* srcRow = (U8*)srcStorage.get(); + + for (unsigned y = 0; y < cinfo.output_height; y++) + { + U16* dstRow = bm->getAddr16(0, y); + int row_count = jpeg_read_scanlines(&cinfo, &srcRow, 1); + SkASSERT(row_count == 1); + + const U8* src = srcRow; + U16* dstStop = dstRow + width; + + if (isRGB) { + while (dstRow < dstStop) + { + *dstRow++ = SkPackRGB16(src[0] >> (8 - SK_R16_BITS), + src[1] >> (8 - SK_G16_BITS), + src[2] >> (8 - SK_B16_BITS)); + src += 3; + } + } + else { // grayscale + while (dstRow < dstStop) + { + *dstRow++ = SkPackRGB16(src[0] >> (8 - SK_R16_BITS), + src[0] >> (8 - SK_G16_BITS), + src[0] >> (8 - SK_B16_BITS)); + src += 1; + } + } + } + } + + jpeg_finish_decompress(&cinfo); + + return true; +} + +//////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_SUPPORT_IMAGE_ENCODE + +#include "SkColorPriv.h" + +static void convert_to_rgb24(U8 dst[], const SkBitmap& bm, int y) +{ + int width = bm.width(); + + switch (bm.getConfig()) { + case SkBitmap::kARGB_8888_Config: + { + const uint32_t* src = bm.getAddr32(0, y); + while (--width >= 0) + { + uint32_t c = *src++; + dst[0] = SkGetPackedR32(c); + dst[1] = SkGetPackedG32(c); + dst[2] = SkGetPackedB32(c); + dst += 3; + } + } + break; + case SkBitmap::kRGB_565_Config: // hmmm, I should encode 16bit jpegs, not expand to 24 bit (doh!) + { + const U16* src = bm.getAddr16(0, y); + while (--width >= 0) + { + U16 c = *src++; + dst[0] = SkPacked16ToR32(c); + dst[1] = SkPacked16ToG32(c); + dst[1] = SkPacked16ToB32(c); + dst += 3; + } + } + break; + default: + SkASSERT(!"unknown bitmap format for jpeg encode"); + } +} + +struct sk_destination_mgr : jpeg_destination_mgr +{ + sk_destination_mgr(SkWStream* stream); + + SkWStream* fStream; + + enum { + kBufferSize = 1024 + }; + U8 fBuffer[kBufferSize]; +}; + +static void sk_init_destination(j_compress_ptr cinfo) +{ + sk_destination_mgr* dest = (sk_destination_mgr*)cinfo->dest; + + dest->next_output_byte = dest->fBuffer; + dest->free_in_buffer = sk_destination_mgr::kBufferSize; +} + +static boolean sk_empty_output_buffer(j_compress_ptr cinfo) +{ + sk_destination_mgr* dest = (sk_destination_mgr*)cinfo->dest; + +// if (!dest->fStream->write(dest->fBuffer, sk_destination_mgr::kBufferSize - dest->free_in_buffer)) + if (!dest->fStream->write(dest->fBuffer, sk_destination_mgr::kBufferSize)) + sk_throw(); + // ERREXIT(cinfo, JERR_FILE_WRITE); + + dest->next_output_byte = dest->fBuffer; + dest->free_in_buffer = sk_destination_mgr::kBufferSize; + return TRUE; +} + +static void sk_term_destination (j_compress_ptr cinfo) +{ + sk_destination_mgr* dest = (sk_destination_mgr*)cinfo->dest; + + size_t size = sk_destination_mgr::kBufferSize - dest->free_in_buffer; + if (size > 0) + { + if (!dest->fStream->write(dest->fBuffer, size)) + sk_throw(); + } + dest->fStream->flush(); +} + +sk_destination_mgr::sk_destination_mgr(SkWStream* stream) + : fStream(stream) +{ + this->init_destination = sk_init_destination; + this->empty_output_buffer = sk_empty_output_buffer; + this->term_destination = sk_term_destination; +} + + +class SkJPEGImageEncoder : public SkImageEncoder { +protected: + virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) + { + jpeg_compress_struct cinfo; + sk_error_mgr sk_err; + sk_destination_mgr sk_wstream(stream); + + cinfo.err = jpeg_std_error(&sk_err); + sk_err.error_exit = sk_error_exit; + if (setjmp(sk_err.fJmpBuf)) + return false; + + jpeg_create_compress(&cinfo); + + cinfo.dest = &sk_wstream; + cinfo.image_width = bm.width(); + cinfo.image_height = bm.height(); + cinfo.input_components = 3; + cinfo.in_color_space = JCS_RGB; + + jpeg_set_defaults(&cinfo); + jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */); + jpeg_start_compress(&cinfo, TRUE); + + SkAutoMalloc oneRow(bm.width() * 3); + U8* oneRowP = (U8*)oneRow.get(); + + while (cinfo.next_scanline < cinfo.image_height) + { + JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */ + + convert_to_rgb24(oneRowP, bm, cinfo.next_scanline); + row_pointer[0] = oneRowP; + (void) jpeg_write_scanlines(&cinfo, row_pointer, 1); + } + + jpeg_finish_compress(&cinfo); + jpeg_destroy_compress(&cinfo); + return true; + } +}; + +SkImageEncoder* sk_create_jpeg_encoder(); +SkImageEncoder* sk_create_jpeg_encoder() +{ + 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/libs/graphics/images/SkImageDecoder_libpng.cpp b/libs/graphics/images/SkImageDecoder_libpng.cpp new file mode 100644 index 0000000000..2dae48c5c7 --- /dev/null +++ b/libs/graphics/images/SkImageDecoder_libpng.cpp @@ -0,0 +1,605 @@ +#include "SkImageDecoder.h" +#include "SkColor.h" +#include "SkColorPriv.h" +#include "SkMath.h" +#include "SkStream.h" +#include "SkTemplates.h" + +class SkPNGImageDecoder : public SkImageDecoder { +protected: + virtual bool onDecode(SkStream* stream, SkBitmap* bm, SkBitmap::Config pref); +}; + +#define TEST_DITHER + +extern "C" { +#include "png.h" +} + + +static void convert_from_32_to_16(SkBitmap* bm) +{ + SkBitmap tmp; + unsigned width = bm->width(); + unsigned height = bm->height(); + + tmp.setConfig(SkBitmap::kRGB_565_Config, width, height, 0); + tmp.allocPixels(); + + for (unsigned y = 0; y < height; y++) { + const uint32_t* src = bm->getAddr32(0, y); + uint16_t* dst = tmp.getAddr16(0, y); + for (unsigned x = 0; x < width; x++) { + *dst++ = SkPixel32ToPixel16(*src++); + } + } + bm->swap(tmp); +} + +#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)) + { + stream->rewind(); + 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!"); +} + +bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* bm, SkBitmap::Config prefConfig) +{ + /* 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, NULL, NULL); + // png_voidp user_error_ptr, user_error_fn, user_warning_fn); + if (png_ptr == NULL) + return false; // error + /* 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; // error + } + 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))) + { + /* If we get here, we had a problem reading the file */ + return false; // error + } + /* 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 */ ); + /* 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 width, height; + int bit_depth, color_type, interlace_type; + png_get_IHDR(png_ptr, info_ptr, &width, &height, &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); + + // record if the png claims to have alpha in one or more pixels (or palette entries) + bool hasAlpha = false; + // 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; + + if (color_type == PNG_COLOR_TYPE_PALETTE) + { + int num_palette; + png_colorp palette; + png_bytep trans; + int num_trans; + + SkColorTable* colorTable = SkNEW(SkColorTable); + SkAutoTDelete<SkColorTable> autoDel(colorTable); + + 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. + */ + colorTable->setColors(num_palette + (num_palette < 256)); + + 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); + } + if (num_trans > num_palette) // check for bad images that might make us crash + 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[num_palette] = colorPtr[num_palette - 1]; + + colorTable->unlockColors(true); + bm->setColorTable(colorTable)->unref(); + (void)autoDel.detach(); + } + + SkBitmap::Config config; + + if (color_type == PNG_COLOR_TYPE_PALETTE) + config = SkBitmap::kIndex8_Config; + else + { + png_color_16p transColor; + + if (png_get_tRNS(png_ptr, info_ptr, NULL, NULL, &transColor)) + { + SkDEBUGF(("********************* png_get_tRNS [%d %d %d]\n", transColor->red, transColor->green, transColor->blue)); + } + + if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) || color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + hasAlpha = true; + config = SkBitmap::kARGB_8888_Config; + } + else // we get to choose the config + { + config = prefConfig; + if (config == SkBitmap::kNo_Config) + config = SkImageDecoder::GetDeviceConfig(); + // only 565 or 8888 are "natural" if we have the choice + if (config != SkBitmap::kRGB_565_Config) + config = SkBitmap::kARGB_8888_Config; + } + } + + bm->setConfig(config, width, height, 0); + bm->allocPixels(); + + /* 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: + */ + 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); + + uint32_t* tmpStorage = nil; + if (config == SkBitmap::kRGB_565_Config) + { + SkASSERT(!hasAlpha); + tmpStorage = (uint32_t*)sk_malloc_throw(bm->rowBytes() * 2); + } + + /* The other way to read images - deal with interlacing: */ + for (int pass = 0; pass < number_passes; pass++) + { + /* Read the image a single row at a time */ + for (png_uint_32 y = 0; y < height; y++) + { + switch (config) { + case SkBitmap::kRGB_565_Config: // good place to consider cary's even/odd dither + { + uint32_t* tmp = tmpStorage; + uint16_t* dstRow = bm->getAddr16(0, y); + png_read_rows(png_ptr, (uint8_t**)&tmp, png_bytepp_NULL, 1); + + /* PNG's argb now needs to be set to the correct byte order for us, and match our alpha-byte + convention of either 0xFF for opaque, or premul the rgb components + */ + for (png_uint_32 x = 0; x < width; x++) { + uint32_t color = tmpStorage[x]; +#ifdef TEST_DITHER + if (SkShouldDitherXY(x, y)) { + #ifdef SK_CPU_BENDIAN + unsigned r = (color >> 24) & 0xFF; + unsigned g = (color >> 16) & 0xFF; + unsigned b = (color >> 8) & 0xFF; + #else + unsigned r = (color >> 0) & 0xFF; + unsigned g = (color >> 8) & 0xFF; + unsigned b = (color >> 16) & 0xFF; + #endif + dstRow[x] = SkDitherPack888ToRGB16(r, g, b); + } + else { // fall through to non TEST_DITHER code +#endif + #ifdef SK_CPU_BENDIAN + unsigned r = (color >> 27) & 0x1F; + unsigned g = (color >> 18) & 0x3F; + unsigned b = (color >> 11) & 0x1F; + #else + unsigned r = (color >> 3) & 0x1F; + unsigned g = (color >> 10) & 0x3F; + unsigned b = (color >> 19) & 0x1F; + #endif + dstRow[x] = SkPackRGB16(r, g, b); +#ifdef TEST_DITHER + } // else clause for shouldditherxy +#endif + } // for-loop + } + break; + case SkBitmap::kARGB_8888_Config: + { + uint32_t* bmRow = bm->getAddr32(0, y); + uint32_t* origRow = bmRow; + png_read_rows(png_ptr, (uint8_t**)&bmRow, png_bytepp_NULL, 1); + + /* PNG's argb now needs to be set to the correct byte order for us, and match our alpha-byte + convention of either 0xFF for opaque, or premul the rgb components + */ + + if (hasAlpha) { // premul the RGB components + for (png_uint_32 x = 0; x < width; x++) { + uint32_t color = origRow[x]; + unsigned a = (color >> 24) & 0xFF; + unsigned b = (color >> 16) & 0xFF; + unsigned c = (color >> 8) & 0xFF; + unsigned d = (color >> 0) & 0xFF; + +#ifdef SK_CPU_BENDIAN + origRow[x] = SkPreMultiplyARGB(d, a, b, c); + reallyHasAlpha |= (d != 0xFF); +#else + origRow[x] = SkPreMultiplyARGB(a, d, c, b); + reallyHasAlpha |= (a != 0xFF); +#endif + } + } + else { // jam in 0xFF for the alpha values + for (png_uint_32 x = 0; x < width; x++) { + uint32_t color = origRow[x]; +#ifdef SK_CPU_BENDIAN + unsigned a = (color >> 24) & 0xFF; + unsigned b = (color >> 16) & 0xFF; + unsigned c = (color >> 8) & 0xFF; + origRow[x] = SkPackARGB32(0xFF, a, b, c); +#else + unsigned b = (color >> 16) & 0xFF; + unsigned c = (color >> 8) & 0xFF; + unsigned d = (color >> 0) & 0xFF; + origRow[x] = SkPackARGB32(0xFF, d, c, b); +#endif + } + } + } + break; + case SkBitmap::kIndex8_Config: + { + uint8_t* bmRow = bm->getAddr8(0, y); + png_read_rows(png_ptr, (uint8_t**) &bmRow, png_bytepp_NULL, 1); + } + break; + default: // to avoid warnings + break; + } + } + } + + if (hasAlpha && !reallyHasAlpha) { + SkDEBUGF(("Image doesn't really have alpha [%d %d]\n", width, height)); + if (config == SkBitmap::kARGB_8888_Config && prefConfig != SkBitmap::kARGB_8888_Config) + convert_from_32_to_16(bm); + } + bm->setIsOpaque(!reallyHasAlpha); + + sk_free(tmpStorage); + + /* read rest of file, and get additional chunks in info_ptr - REQUIRED */ + png_read_end(png_ptr, info_ptr); + + return true; +} + +//////////////////////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_SUPPORT_IMAGE_ENCODE + +#include "SkColorPriv.h" + +static void sk_error_fn(png_structp png_ptr, png_const_charp msg) +{ + SkDEBUGF(("SkPNGImageEncoder::onEncode error: %s\n", msg)); +} + +static void sk_write_fn(png_structp png_ptr, png_bytep data, png_size_t length) +{ + SkWStream* sk_stream = (SkWStream*)png_ptr->io_ptr; + if (!sk_stream->write(data, length)) + png_error(png_ptr, "sk_write_fn Error!"); +} + +typedef void (*transform_scanline_proc)(const char src[], int width, char dst[]); + +static void transform_scanline_565(const char src[], int width, char dst[]) +{ + const uint16_t* 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 src[], int width, char dst[]) +{ + const SkPMColor* srcP = (const SkPMColor*)src; + for (int i = 0; i < width; i++) + { + SkPMColor c = *srcP++; + *dst++ = SkGetPackedR32(c); + *dst++ = SkGetPackedG32(c); + *dst++ = SkGetPackedB32(c); + } +} + +static char unpremul(unsigned v, unsigned alpha) +{ + if (alpha == 0) + return 0; + + if (alpha == 255) + return v; + + return v * 255 / alpha; +} + +static void transform_scanline_8888(const char src[], int width, char dst[]) +{ + const SkPMColor* srcP = (const SkPMColor*)src; + for (int i = 0; i < width; i++) + { + SkPMColor c = *srcP++; + unsigned alpha = SkGetPackedA32(c); + + *dst++ = unpremul(SkGetPackedR32(c), alpha); + *dst++ = unpremul(SkGetPackedG32(c), alpha); + *dst++ = unpremul(SkGetPackedB32(c), alpha); + *dst++ = (char)alpha; + } +} + +static transform_scanline_proc choose_proc(SkBitmap::Config config, bool hasAlpha) +{ + static const struct { + SkBitmap::Config fConfig; + bool fHasAlpha; + transform_scanline_proc fProc; + } gMap[] = { + { SkBitmap::kRGB_565_Config, false, transform_scanline_565 }, + { SkBitmap::kARGB_8888_Config, false, transform_scanline_888 }, + { SkBitmap::kARGB_8888_Config, true, transform_scanline_8888 } + }; + + 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 nil; +} + +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(); + + if (config != SkBitmap::kARGB_8888_Config && + config != SkBitmap::kRGB_565_Config) + { + SkDEBUGF(("SkPNGImageEncoder::onEncode can't encode %d config\n", config)); + return false; + } + + png_structp png_ptr; + png_infop info_ptr; + + png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, sk_error_fn, NULL); + if (png_ptr == NULL) + return false; + + info_ptr = png_create_info_struct(png_ptr); + if (info_ptr == NULL) + { + 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 + */ + + bool hasAlpha = (config == SkBitmap::kARGB_8888_Config) && !bitmap.isOpaque(); + + png_set_IHDR(png_ptr, info_ptr, bitmap.width(), bitmap.height(), 8, + hasAlpha ? PNG_COLOR_TYPE_RGB_ALPHA : PNG_COLOR_TYPE_RGB, + PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + +#if 0 + /* 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_color_8 sig_bit; + if (config == SkBitmap::kRGB_565_Config) + { + sig_bit.red = 5; + sig_bit.green = 6; + sig_bit.blue = 5; + } + else + { + sig_bit.red = 8; + sig_bit.green = 8; + sig_bit.blue = 8; + if (hasAlpha) + sig_bit.alpha = 8; + } + png_set_sBIT(png_ptr, info_ptr, &sig_bit); + } + + png_write_info(png_ptr, info_ptr); + + const char* srcImage = (const char*)bitmap.getPixels(); + char* storage = (char*)sk_malloc_throw(bitmap.width() << 2); + transform_scanline_proc proc = choose_proc(config, hasAlpha); + + for (unsigned 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(); + } + sk_free(storage); + + png_write_end(png_ptr, info_ptr); + +#if 0 + /* If you png_malloced a palette, free it here (don't free info_ptr->palette, + as recommended in versions 1.0.5m and earlier of this example; if + libpng mallocs info_ptr->palette, libpng will free it). If you + allocated it with malloc() instead of png_malloc(), use free() instead + of png_free(). */ + png_free(png_ptr, palette); +#endif + + /* clean up after the write, and free any memory allocated */ + png_destroy_write_struct(&png_ptr, &info_ptr); + return true; +} + +SkImageEncoder* sk_create_png_encoder(); +SkImageEncoder* sk_create_png_encoder() +{ + return SkNEW(SkPNGImageEncoder); +} + +#endif /* SK_SUPPORT_IMAGE_ENCODE */ + + diff --git a/libs/graphics/images/SkStream.cpp b/libs/graphics/images/SkStream.cpp new file mode 100644 index 0000000000..49984d1e73 --- /dev/null +++ b/libs/graphics/images/SkStream.cpp @@ -0,0 +1,585 @@ +#include "SkStream.h" +#include "SkFixed.h" +#include "SkString.h" +#include "SkOSFile.h" + +const char* SkStream::getFileName() +{ + // override in subclass if you represent a file + return NULL; +} + +static const char gPrefix[] = "file:\0" "http:\0" "https:\0" "ftp:\0" ; + +bool SkStream::IsAbsoluteURI(const char path[]) +{ + int prefix = SkStrStartsWithOneOf(path, gPrefix); + if (prefix >= 0) + return true; + return path[0] != '.'; +} + +SkStream* SkStream::GetURIStream(const char prefix[], const char path[]) +{ + int index = SkStrStartsWithOneOf(path, gPrefix); + if (prefix && prefix[0] && index < 0) + { + SkString fullPath(prefix); + fullPath.append(path); + return GetURIStream(NULL, fullPath.c_str()); + } + if (index > 0) + return SkNEW_ARGS(SkURLStream, (path)); + if (index == 0) + path += 5; + return SkNEW_ARGS(SkFILEStream, (path)); +} + +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(S32 dec) +{ + SkString tmp; + tmp.appendS32(dec); + return this->write(tmp.c_str(), tmp.size()); +} + +bool SkWStream::writeHexAsText(U32 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()); +} + +//////////////////////////////////////////////////////////////////////////// + +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 1 + if (buffer == NULL && size == 0) // funny, they want the size I think + return sk_fgetsize(fFILE); + return sk_fread(buffer, size, fFILE); +#else + if (buffer) + { + size_t bytes = sk_fread(buffer, size, fFILE); + if (ferror(fFILE) == 0) + return bytes; + } + else + { + if (size == 0 && ::fseek(fFILE, 0, SEEK_END) == 0) + { + size = ::ftell(fFILE); + ::fseek(fFILE, 0, SEEK_SET); + return size; + } + else if (::fseek(fFILE, (long)size, SEEK_CUR) == 0) + return size; + } +#endif + } + return 0; +} + +//////////////////////////////////////////////////////////////////////// + +SkMemoryStream::SkMemoryStream(const void* src, size_t size) + : fSrc(src), fSize(size) +{ + fOffset = 0; +} + +bool SkMemoryStream::rewind() +{ + fOffset = 0; + return true; +} + +size_t SkMemoryStream::read(void* buffer, size_t size) +{ + // 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; +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////// + +SkBufferStream::SkBufferStream(SkStream& proxy, size_t bufferSize) + : fProxy(proxy) +{ + SkASSERT(&proxy != NULL); + this->init(NULL, bufferSize); +} + +SkBufferStream::SkBufferStream(SkStream& proxy, void* buffer, size_t bufferSize) + : fProxy(proxy) +{ + SkASSERT(&proxy != NULL); + SkASSERT(buffer == NULL || bufferSize != 0); // init(addr, 0) makes no sense, we must know how big their buffer is + + this->init(buffer, bufferSize); +} + +void SkBufferStream::init(void* buffer, size_t bufferSize) +{ + if (bufferSize == 0) + bufferSize = kDefaultBufferSize; + + fOrigBufferSize = bufferSize; + fBufferSize = bufferSize; + fBufferOffset = bufferSize; // to trigger a reload on the first read() + + if (buffer == NULL) + { + fBuffer = (char*)sk_malloc_throw(fBufferSize); + fWeOwnTheBuffer = true; + } + else + { + fBuffer = (char*)buffer; + fWeOwnTheBuffer = false; + } +} + +SkBufferStream::~SkBufferStream() +{ + if (fWeOwnTheBuffer) + sk_free(fBuffer); +} + +bool SkBufferStream::rewind() +{ + fBufferOffset = fBufferSize = fOrigBufferSize; + return fProxy.rewind(); +} + +const char* SkBufferStream::getFileName() +{ + return fProxy.getFileName(); +} + +#ifdef SK_DEBUG +// #define SK_TRACE_BUFFERSTREAM +#endif + +size_t SkBufferStream::read(void* buffer, size_t size) +{ +#ifdef SK_TRACE_BUFFERSTREAM + SkDebugf("Request %d", size); +#endif + if (buffer == NULL || size == 0) + { + fBufferOffset += size; + return fProxy.read(buffer, size); + } + + size_t s = size; + size_t actuallyRead = 0; + + // flush what we can from our fBuffer + if (fBufferOffset < fBufferSize) + { + if (s > fBufferSize - fBufferOffset) + s = fBufferSize - fBufferOffset; + memcpy(buffer, fBuffer + fBufferOffset, s); +#ifdef SK_TRACE_BUFFERSTREAM + SkDebugf(" flush %d", s); +#endif + size -= s; + fBufferOffset += s; + buffer = (char*)buffer + s; + actuallyRead = s; + } + + // check if there is more to read + if (size) + { + SkASSERT(fBufferOffset >= fBufferSize); // need to refill our fBuffer + + if (size < fBufferSize) // lets try to read more than the request + { + s = fProxy.read(fBuffer, fBufferSize); +#ifdef SK_TRACE_BUFFERSTREAM + SkDebugf(" read %d into fBuffer", s); +#endif + if (size > s) // they asked for too much + size = s; + if (size) + { + memcpy(buffer, fBuffer, size); + actuallyRead += size; +#ifdef SK_TRACE_BUFFERSTREAM + SkDebugf(" memcpy %d into dst", size); +#endif + } + + fBufferOffset = size; + fBufferSize = s; // record the (possibly smaller) size for the buffer + } + else // just do a direct read + { + actuallyRead += fProxy.read(buffer, size); +#ifdef SK_TRACE_BUFFERSTREAM + SkDebugf(" direct read %d", size); +#endif + } + } +#ifdef SK_TRACE_BUFFERSTREAM + SkDebugf("\n"); +#endif + return actuallyRead; +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////////// + +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() +{ + sk_free(fCopyToCache); + + Block* block = fHead; + + while (block != NULL) { + Block* next = block->fNext; + sk_free(block); + block = next; + } +} + +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; +} + +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 SkDebugWStream::newline() +{ +#ifdef SK_DEBUG + SkDebugf("\n"); +#endif +} + +bool SkDebugWStream::write(const void* buffer, size_t size) +{ +#ifdef SK_DEBUG + char* s = new char[size+1]; + memcpy(s, buffer, size); + s[size] = 0; + SkDebugf("%s", s); + delete[] s; +#endif + return true; +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_DEBUG + +#include "SkRandom.h" + +void SkWStream::UnitTest() +{ +#ifdef SK_SUPPORT_UNITTEST + { + static const char s[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + char copy[sizeof(s)]; + SkRandom rand; + + for (int i = 0; i < 65; i++) + { + char* copyPtr = copy; + SkMemoryStream mem(s, sizeof(s)); + SkBufferStream buff(mem, i); + + do { + copyPtr += buff.read(copyPtr, rand.nextU() & 15); + } while (copyPtr < copy + sizeof(s)); + SkASSERT(copyPtr == copy + sizeof(s)); + SkASSERT(memcmp(s, copy, sizeof(s)) == 0); + } + } + { + SkDebugWStream s; + + s.writeText("testing wstream helpers\n"); + s.writeText("compare: 0 "); s.writeDecAsText(0); s.newline(); + s.writeText("compare: 591 "); s.writeDecAsText(591); s.newline(); + s.writeText("compare: -9125 "); s.writeDecAsText(-9125); s.newline(); + s.writeText("compare: 0 "); s.writeHexAsText(0, 0); s.newline(); + s.writeText("compare: 03FA "); s.writeHexAsText(0x3FA, 4); s.newline(); + s.writeText("compare: DEADBEEF "); s.writeHexAsText(0xDEADBEEF, 4); s.newline(); + s.writeText("compare: 0 "); s.writeScalarAsText(SkIntToScalar(0)); s.newline(); + s.writeText("compare: 27 "); s.writeScalarAsText(SkIntToScalar(27)); s.newline(); + s.writeText("compare: -119 "); s.writeScalarAsText(SkIntToScalar(-119)); s.newline(); + s.writeText("compare: 851.3333 "); s.writeScalarAsText(SkIntToScalar(851) + SK_Scalar1/3); s.newline(); + s.writeText("compare: -0.08 "); s.writeScalarAsText(-SK_Scalar1*8/100); s.newline(); + } + + { + SkDynamicMemoryWStream ds; + const char s[] = "abcdefghijklmnopqrstuvwxyz"; + int i; + for (i = 0; i < 100; i++) { + bool result = ds.write(s, 26); + SkASSERT(result); + } + SkASSERT(ds.getOffset() == 100 * 26); + char* dst = new char[100 * 26 + 1]; + dst[100*26] = '*'; + ds.copyTo(dst); + SkASSERT(dst[100*26] == '*'); + // char* p = dst; + for (i = 0; i < 100; i++) + SkASSERT(memcmp(&dst[i * 26], s, 26) == 0); + SkASSERT(memcmp(dst, ds.getStream(), 100*26) == 0); + delete[] dst; + } +#endif +} + +#endif diff --git a/libs/graphics/ports/SkFontHost.cpp b/libs/graphics/ports/SkFontHost.cpp new file mode 100644 index 0000000000..3a7998a8ab --- /dev/null +++ b/libs/graphics/ports/SkFontHost.cpp @@ -0,0 +1,272 @@ +#include "SkFontHost.h" +#include "SkDescriptor.h" +#include "SkString.h" +#include <stdio.h> + +#ifdef ANDROID + #define kFontFilePrefix "/fonts/" +#else + // this is for our test apps that look directly into the src directory for the fonts + #define kFontFilePrefix "../fonts/" +#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; +}; + +// export for scalercontext subclass using freetype +void FontFaceRec_getFileName(const FontFaceRec* face, SkString* name); +void FontFaceRec_getFileName(const FontFaceRec* face, SkString* name) +{ + name->set(getenv("ANDROID_ROOT")); + name->append(kFontFilePrefix); + name->append(face->fFileName); +} + +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 { + SANS_FAMILY_INDEX, + CJK_FAMILY_INDEX, + MONO_FAMILY_INDEX, + SERIF_FAMILY_INDEX, + DROID_FAMILY_INDEX, + DROID_MORE_FAMILY_INDEX, + + FAMILY_INDEX_COUNT +}; + + + +static const FontFaceRec gSansFaces[] = { +// { "DejaVuSansCondensed.ttf", SANS_FAMILY_INDEX, 0, 0 }, +// { "DejaVuSansCondensed-Bold.ttf", SANS_FAMILY_INDEX, 1, 0 } + { "DroidSans_bd100.ttf", SANS_FAMILY_INDEX, 0, 0 }, + { "DroidSans-Bold_bd100.ttf", SANS_FAMILY_INDEX, 1, 0 } +}; + +static const FontFaceRec gCJKFaces[] = { + { "HeiTBig5_J.otf", CJK_FAMILY_INDEX, 0, 0 } +}; + +static const FontFaceRec gSerifFaces[] = { + { "DejaVuSerifCondensed.ttf", SERIF_FAMILY_INDEX, 0, 0 }, + { "DejaVuSerifCondensed-Bold.ttf", SERIF_FAMILY_INDEX, 1, 0 }, + { "DejaVuSerifCondensed-Oblique.ttf", SERIF_FAMILY_INDEX, 0, 1 }, + { "DejaVuSerifCondensed-BoldOblique.ttf", SERIF_FAMILY_INDEX, 1, 1 } +}; + +static const FontFaceRec gMonoFaces[] = { + { "DejaVuSansMono.ttf", MONO_FAMILY_INDEX, 0, 0 } +}; + +static const FontFaceRec gDroidFaces[] = { + { "DroidSansLight.ttf", DROID_FAMILY_INDEX, 0, 0 }, + { "DroidSans.ttf", DROID_FAMILY_INDEX, 1, 0 }, + { "DroidSansDemiBold.ttf", DROID_FAMILY_INDEX, 0, 1 }, + { "DroidSans-SemiBold.ttf", DROID_FAMILY_INDEX, 1, 1 } +}; + +static const FontFaceRec gDroidMoreFaces[] = { + { "DroidSans-Bold.ttf", DROID_MORE_FAMILY_INDEX, 0, 0 }, + { "DroidSansBlack.ttf", DROID_MORE_FAMILY_INDEX, 1, 0 }, + { "DroidSerifScotch.ttf", DROID_MORE_FAMILY_INDEX, 0, 1 } +}; + +// This table must be in the same order as the ..._FAMILY_INDEX enum specifies +static const FontFamilyRec gFamilies[] = { + { gSansFaces, SK_ARRAY_COUNT(gSansFaces) }, + { gCJKFaces, SK_ARRAY_COUNT(gCJKFaces) }, + { gMonoFaces, SK_ARRAY_COUNT(gMonoFaces) }, + { gSerifFaces, SK_ARRAY_COUNT(gSerifFaces) }, + { gDroidFaces, SK_ARRAY_COUNT(gDroidFaces) }, + { gDroidMoreFaces, SK_ARRAY_COUNT(gDroidMoreFaces) } +}; + +#define DEFAULT_FAMILY_INDEX SANS_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[] = { + { "arial", SANS_FAMILY_INDEX }, + { "courier", MONO_FAMILY_INDEX }, + { "courier new", MONO_FAMILY_INDEX }, + { "cursive", SERIF_FAMILY_INDEX }, + { "dejavu mono", MONO_FAMILY_INDEX }, + { "dejavu sans", SANS_FAMILY_INDEX }, + { "dejavu serif", SANS_FAMILY_INDEX }, + { "droid more", DROID_MORE_FAMILY_INDEX }, + { "droid sans", SANS_FAMILY_INDEX }, + { "fantasy", SERIF_FAMILY_INDEX }, + { "goudy", SERIF_FAMILY_INDEX }, + { "helvetica", SANS_FAMILY_INDEX }, + { "lucida grande", SANS_FAMILY_INDEX }, + { "lucida grande", MONO_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 } +}; + +//////////////////////////////////////////////////////////////////////////////////////// + +#include "SkTSearch.h" + +const FontFamilyRec* find_family_rec(const char target[]) +{ + size_t targetLen = strlen(target); + int index; + + // Search for the font by matching the entire name + index = SkStrLCSearch(&gMatches[0].fLCName, SK_ARRAY_COUNT(gMatches), target, targetLen, sizeof(gMatches[0])); + if (index >= 0) + return &gFamilies[gMatches[index].fFamilyIndex]; + + // Sniff for key words... + // If we converted target to lower-case, these would be simpler/faster... + + if (strstr(target, "sans") || strstr(target, "Sans")) + return &gFamilies[SANS_FAMILY_INDEX]; + + if (strstr(target, "serif") || strstr(target, "Serif")) + return &gFamilies[SERIF_FAMILY_INDEX]; + + if (strstr(target, "mono") || strstr(target, "Mono")) + return &gFamilies[MONO_FAMILY_INDEX]; + + // we give up, just give them the default font + return &gFamilies[DEFAULT_FAMILY_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); + } + + const FontFaceRec& fFace; +}; + +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 != nil && &((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 = &gFamilies[DEFAULT_FAMILY_INDEX].fFaces[DEFAULT_FAMILY_FACE_INDEX]; + + size_t size = sizeof(face); + if (buffer) + memcpy(buffer, &face, size); + return size; +} + +SkFontHost::ScalerContextID SkFontHost::FindScalerContextIDForUnichar(int32_t unichar) +{ + // when we have more than one fall-back font, will have to do a search based on unichar + // to know what index to return (0 means no fall-back can help) + + // we return the family index+1 to indicate succes, or 0 for failure (no fall-back font) + return (ScalerContextID)(CJK_FAMILY_INDEX + 1); +} + +SkScalerContext* SkFontHost::CreateScalerContextFromID(ScalerContextID id, const SkScalerContext::Rec& rec) +{ + SkASSERT((int)id > 0 && (int)id < FAMILY_INDEX_COUNT); + + const FontFaceRec* face = &gFamilies[(int)id - 1].fFaces[0]; + + 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); +} + diff --git a/libs/graphics/ports/SkFontHost_FONTPATH.cpp b/libs/graphics/ports/SkFontHost_FONTPATH.cpp new file mode 100644 index 0000000000..b8a8c5c64d --- /dev/null +++ b/libs/graphics/ports/SkFontHost_FONTPATH.cpp @@ -0,0 +1,85 @@ +#include "SkFontHost.h" +#include "SkString.h" +#include <stdio.h> + +//#define SK_FONTPATH "the complete file name for our font" + +struct FontFaceRec { + const char* fFileName; + uint8_t fFamilyIndex; + SkBool8 fBold; + SkBool8 fItalic; +}; + +struct FontFamilyRec { + const char* fLCName; + const FontFaceRec* fFaces; + int fFaceCount; +}; + +// export for scalercontext subclass using freetype +void FontFaceRec_getFileName(const FontFaceRec* face, SkString* name); +void FontFaceRec_getFileName(const FontFaceRec* face, SkString* name) +{ + name->set(SK_FONTPATH); +} + +static const FontFaceRec gArialFaces[] = { + { SK_FONTPATH, 0, 0, 0 } +}; + +static const FontFamilyRec gFamilies[] = { + { "font", gArialFaces, SK_ARRAY_COUNT(gArialFaces) }, +}; + +#define DEFAULT_FAMILY_INDEX 0 +#define DEFAULT_FAMILY_FACE_INDEX 0 + +/////////////////////////////////////////////////////////////////////////////////////////////// + +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); + } + + const FontFaceRec& fFace; +}; + +SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace, const char familyName[], SkTypeface::Style style) +{ + return SkNEW_ARGS(FontFaceRec_Typeface, (gFamilies[DEFAULT_FAMILY_INDEX].fFaces[DEFAULT_FAMILY_FACE_INDEX])); +} + +uint32_t SkFontHost::FlattenTypeface(const SkTypeface* tface, void* buffer) +{ + const FontFaceRec* face; + + if (tface) + face = &((const FontFaceRec_Typeface*)tface)->fFace; + else + face = &gFamilies[DEFAULT_FAMILY_INDEX].fFaces[DEFAULT_FAMILY_FACE_INDEX]; + + size_t size = sizeof(face); + if (buffer) + memcpy(buffer, &face, size); + return size; +} + +SkFontHost::ScalerContextID SkFontHost::FindScalerContextIDForUnichar(int32_t unichar) +{ + return kMissing_ScalerContextID; // don't do any font-chaining when we only have 1 font +} + +SkScalerContext* SkFontHost::CreateScalerContextFromID(ScalerContextID, const SkScalerContext::Rec&) +{ + SkASSERT(!"Should not be called, since we always return kMissing_ScalerContextID"); + return NULL; +} + diff --git a/libs/graphics/ports/SkFontHost_FreeType.cpp b/libs/graphics/ports/SkFontHost_FreeType.cpp new file mode 100644 index 0000000000..418dcd97e8 --- /dev/null +++ b/libs/graphics/ports/SkFontHost_FreeType.cpp @@ -0,0 +1,633 @@ +#include "SkScalerContext.h" +#include "SkBitmap.h" +#include "SkCanvas.h" +#include "SkDescriptor.h" +#include "SkFDot6.h" +#include "SkFontHost.h" +#include "SkString.h" +#include "SkThread.h" +#include "SkTemplates.h" + +#include <ft2build.h> +#include FT_FREETYPE_H +#include FT_OUTLINE_H +#include FT_SIZES_H + +//#define ENABLE_GLYPH_SPEW // for tracing calls to generateMetrics/generateImage + +#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 void generateMetrics(SkGlyph* glyph); + virtual void generateImage(const SkGlyph& glyph); + virtual void generatePath(const SkGlyph& glyph, SkPath* path); + virtual void generateLineHeight(SkPoint* ascent, SkPoint* descent); + +private: + SkFaceRec* fFaceRec; + FT_Face fFace; // reference to shared face in gFaceRecHead + FT_Size fFTSize; // our own copy + SkFixed fScaleX, fScaleY; + SkFixed fMatrix22[2][2]; + uint32_t fLoadGlyphFlags; + + FT_Error setupSize(); +}; + +/////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////// + +#include "SkStream.h" + +struct SkFaceRec { + SkFaceRec* fNext; + FT_Face fFace; + FT_StreamRec fFTStream; + SkFILEStream fSkStream; + uint32_t fRefCnt; + SkString fFileName; + + SkFaceRec(const char filename[]); + ~SkFaceRec() + { +// SkDEBUGF(("~SkFaceRec: closing %s\n", fFileName.c_str())); + } +}; + +extern "C" { + static unsigned long sk_stream_read(FT_Stream stream, + unsigned long offset, + unsigned char* buffer, + unsigned long count ) + { + SkFaceRec* rec = (SkFaceRec*)stream->descriptor.pointer; + SkStream* str = &rec->fSkStream; + + if (count) + { + if (!str->rewind()) + { + SkDEBUGF(("sk_stream_read(file:%s offset:%ld count:%ld): rewind failed\n", + rec->fFileName.c_str(), offset, count)); + return 0; + } + else + { + unsigned long ret; + if (offset) + { + ret = str->read(nil, offset); + if (ret != offset) { + SkDEBUGF(("sk_stream_read(file:%s offset:%d) read returned %d\n", + rec->fFileName.c_str(), offset, ret)); + return 0; + } + } + ret = str->read(buffer, count); + if (ret != count) { + SkDEBUGF(("sk_stream_read(file:%s offset:%d count:%d) read returned %d\n", + rec->fFileName.c_str(), offset, count, ret)); + return 0; + } + count = ret; + } + } + return count; + } + + static void sk_stream_close( FT_Stream stream) + { + } +} + +SkFaceRec::SkFaceRec(const char filename[]) : fSkStream(filename) +{ +// SkDEBUGF(("SkFaceRec: opening %s (%p)\n", filename, fSkStream.getSkFILE())); + + fFileName.set(filename); + + memset(&fFTStream, 0, sizeof(fFTStream)); + fFTStream.size = fSkStream.read(nil, 0); // find out how big the file is + fFTStream.descriptor.pointer = this; + fFTStream.read = sk_stream_read; + fFTStream.close = sk_stream_close; +} + +static SkFaceRec* ref_ft_face(const char filename[], size_t filenameLen) +{ + SkFaceRec* rec = gFaceRecHead; + while (rec) + { + if (rec->fFileName.equals(filename, filenameLen)) + { + SkASSERT(rec->fFace); + rec->fRefCnt += 1; + return rec; + } + rec = rec->fNext; + } + + rec = SkNEW_ARGS(SkFaceRec, (filename)); + + FT_Open_Args args; + memset(&args, 0, sizeof(args)); + 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 '%s'\n", filename); + 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 = nil; + 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"); +} + +/////////////////////////////////////////////////////////////////////////// + +struct FontFaceRec; +extern void FontFaceRec_getFileName(const FontFaceRec*, SkString*); + +SkScalerContext_FreeType::SkScalerContext_FreeType(const SkDescriptor* desc) + : SkScalerContext(desc), fFTSize(nil) +{ + 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 + { + SkString fileName; + const FontFaceRec** face = (const FontFaceRec**)desc->findEntry(kTypeface_SkDescriptorTag, nil); + FontFaceRec_getFileName(*face, &fileName); + fFaceRec = ref_ft_face(fileName.c_str(), fileName.size()); + fFace = fFaceRec ? fFaceRec->fFace : nil; + } + + // compute our factors from the record + + SkMatrix m; + + fRec.getSingleMatrix(&m); + + // 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[0][0] = SkScalarToFixed(SkScalarMul(m.getScaleX(), inv)); + fMatrix22[0][1] = -SkScalarToFixed(SkScalarMul(m.getSkewX(), inv)); + fMatrix22[1][0] = -SkScalarToFixed(SkScalarMul(m.getSkewY(), inv)); + fMatrix22[1][1] = SkScalarToFixed(SkScalarMul(m.getScaleY(), inv)); + } + else + { + fMatrix22[0][0] = fMatrix22[1][1] = SK_Fixed1; + fMatrix22[0][1] = fMatrix22[1][0] = 0; + } + + fScaleX = SkScalarToFixed(sx); + fScaleY = SkScalarToFixed(sy); + + // compute the flags we send to Load_Glyph + { + uint32_t flags = 0; + + if (!fRec.fUseHints) { + flags |= FT_LOAD_NO_HINTING; + } + if (!fRec.fDoAA) { + flags |= FT_LOAD_TARGET_MONO; + } + fLoadGlyphFlags = flags; + } + + // now create the FT_Size + + { + FT_Error err; + + err = FT_New_Size(fFace, &fFTSize); + if (err != 0) { + SkDEBUGF(("SkScalerContext_FreeType::FT_New_Size(%s): FT_Set_Char_Size(0x%x, 0x%x) returned 0x%x\n", + fFaceRec->fFileName.c_str(), fScaleX, fScaleY, err)); + fFace = nil; + return; + } + + err = FT_Activate_Size(fFTSize); + if (err != 0) { + SkDEBUGF(("SkScalerContext_FreeType::FT_Activate_Size(%s, 0x%x, 0x%x) returned 0x%x\n", + fFaceRec->fFileName.c_str(), fScaleX, fScaleY, err)); + fFTSize = nil; + } + + err = FT_Set_Char_Size( fFace, + SkFixedToFDot6(fScaleX), SkFixedToFDot6(fScaleY), + 72, 72); + if (err != 0) { + SkDEBUGF(("SkScalerContext_FreeType::FT_Set_Char_Size(%s, 0x%x, 0x%x) returned 0x%x\n", + fFaceRec->fFileName.c_str(), fScaleX, fScaleY, err)); + fFace = nil; + return; + } + +// SkDEBUGF(("FT_Set_Transform %p %p %p [%x %x %x %x]\n", this, fFace, fFTSize, +// fMatrix22[0][0], fMatrix22[0][1], fMatrix22[1][0], fMatrix22[1][1])); + // not sure if I need this here, given that I redo it in setup() + FT_Set_Transform( fFace, (FT_Matrix*)&fMatrix22, nil); + } +} + +SkScalerContext_FreeType::~SkScalerContext_FreeType() +{ + if (fFTSize != nil) + FT_Done_Size(fFTSize); + + SkAutoMutexAcquire ac(gFTMutex); + + if (fFace != nil) + unref_ft_face(fFace); + + if (--gFTCount == 0) + { +// SkDEBUGF(("FT_Done_FreeType\n")); + FT_Done_FreeType(gFTLibrary); + SkDEBUGCODE(gFTLibrary = nil;) + } +} + +/* 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() +{ + FT_Error err = FT_Activate_Size(fFTSize); + + if (err != 0) { + SkDEBUGF(("SkScalerContext_FreeType::FT_Activate_Size(%s, 0x%x, 0x%x) returned 0x%x\n", + fFaceRec->fFileName.c_str(), fScaleX, fScaleY, err)); + fFTSize = nil; + } + 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, (FT_Matrix*)&fMatrix22, nil); + } + return err; +} + +void SkScalerContext_FreeType::generateMetrics(SkGlyph* glyph) +{ +#if 0 + { + static bool gOnce = true; + if (gOnce) { + gOnce = false; + for (int i = 403; i < fFace->num_glyphs; i++) { + FT_Error err = FT_Load_Glyph( fFace, i, fLoadGlyphFlags ); + if (err != 0) { + printf("FT_Load_Glyph[%d] returned %d\n", i, err); + } + } + } + } +#endif + + SkAutoMutexAcquire ac(gFTMutex); + + FT_Error err; + + if (this->setupSize()) + goto ERROR; + + glyph->fGlyphID = SkToU16(FT_Get_Char_Index( fFace, glyph->fCharCode )); + + err = FT_Load_Glyph( fFace, glyph->fGlyphID, fLoadGlyphFlags ); + if (err != 0) + { + SkDEBUGF(("SkScalerContext_FreeType::generateMetrics(%s): FT_Load_Glyph(char:%d glyph:%d flags:%d) returned 0x%x\n", + fFaceRec->fFileName.c_str(), glyph->fCharCode, glyph->fGlyphID, fLoadGlyphFlags, err)); + ERROR: + glyph->fWidth = 0; + glyph->fHeight = 0; + glyph->fTop = 0; + glyph->fLeft = 0; + glyph->fAdvanceX = 0; + glyph->fAdvanceY = 0; + return; + } + + switch ( fFace->glyph->format ) + { + case FT_GLYPH_FORMAT_OUTLINE: + FT_BBox bbox; + + FT_Outline_Get_CBox(&fFace->glyph->outline, &bbox); + 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 (fRec.fUseHints) + { + glyph->fAdvanceX = SkFDot6ToFixed(fFace->glyph->advance.x); + glyph->fAdvanceY = -SkFDot6ToFixed(fFace->glyph->advance.y); + } + else + { + glyph->fAdvanceX = SkFixedMul(fMatrix22[0][0], fFace->glyph->linearHoriAdvance); + glyph->fAdvanceY = -SkFixedMul(fMatrix22[1][0], 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->fGlyphID, 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.fGlyphID, fLoadGlyphFlags); + if (err != 0) { + SkDEBUGF(("SkScalerContext_FreeType::generateImage: FT_Load_Glyph(char:%d glyph:%d width:%d height:%d rb:%d flags:%d) returned 0x%x\n", + glyph.fCharCode, glyph.fGlyphID, glyph.fWidth, glyph.fHeight, glyph.fRowBytes, fLoadGlyphFlags, err)); + ERROR: + memset(glyph.fImage, 0, glyph.fRowBytes * glyph.fHeight); + return; + } + + switch ( fFace->glyph->format ) { + case FT_GLYPH_FORMAT_OUTLINE: + { + FT_Outline* outline = &fFace->glyph->outline; + FT_BBox bbox; + FT_Bitmap target; + + FT_Outline_Get_CBox(outline, &bbox); + FT_Outline_Translate(outline, -(bbox.xMin & ~63), -(bbox.yMin & ~63)); + + target.width = glyph.fWidth; + target.rows = glyph.fHeight; + target.pitch = glyph.fRowBytes; + target.buffer = reinterpret_cast<uint8_t*>(glyph.fImage); + target.pixel_mode = fRec.fDoAA ? FT_PIXEL_MODE_GRAY : FT_PIXEL_MODE_MONO; + target.num_grays = 256; + + memset(glyph.fImage, 0, glyph.fRowBytes * 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 U8* src = (const U8*)fFace->glyph->bitmap.buffer; + U8* dst = (U8*)glyph.fImage; + unsigned srcRowBytes = fFace->glyph->bitmap.pitch; + unsigned dstRowBytes = glyph.fRowBytes; + 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) + +static int move_proc(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(FT_Vector* pt, void* ctx) +{ + SkPath* path = (SkPath*)ctx; + path->lineTo(ft2sk(pt->x), -ft2sk(pt->y)); + return 0; +} + +static int quad_proc(FT_Vector* pt0, 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(FT_Vector* pt0, FT_Vector* pt1, 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; + } + + U32 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.fGlyphID, flags); + + if (err != 0) { + SkDEBUGF(("SkScalerContext_FreeType::generatePath: FT_Load_Glyph(char:%d glyph:%d flags:%d) returned 0x%x\n", + glyph.fCharCode, glyph.fGlyphID, 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(char:%d glyph:%d flags:%d) returned 0x%x\n", + glyph.fCharCode, glyph.fGlyphID, flags, err)); + path->reset(); + return; + } + + path->close(); +} + +static void map_y_to_pt(const FT_Matrix& mat, SkFixed y, SkPoint* pt) +{ + SkFixed x = SkFixedMul(mat.xy, y); + y = SkFixedMul(mat.yy, y); + + pt->set(SkFixedToScalar(x), SkFixedToScalar(y)); +} + +void SkScalerContext_FreeType::generateLineHeight(SkPoint* ascent, SkPoint* descent) +{ + SkAutoMutexAcquire ac(gFTMutex); + + if (this->setupSize()) { + if (ascent) + ascent->set(0, 0); + if (descent) + descent->set(0, 0); + return; + } + + if (ascent) + { + SkFixed a = SkMulDiv(fScaleY, -fFace->ascender, fFace->units_per_EM); + map_y_to_pt(*(FT_Matrix*)fMatrix22, a, ascent); + } + if (descent) + { + SkFixed d = SkMulDiv(fScaleY, -fFace->descender, fFace->units_per_EM); + map_y_to_pt(*(FT_Matrix*)fMatrix22, d, descent); + } +} + +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// + +SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc) +{ + return SkNEW_ARGS(SkScalerContext_FreeType, (desc)); +} + diff --git a/libs/graphics/ports/SkFontHost_none.cpp b/libs/graphics/ports/SkFontHost_none.cpp new file mode 100644 index 0000000000..c9231cf5f5 --- /dev/null +++ b/libs/graphics/ports/SkFontHost_none.cpp @@ -0,0 +1,33 @@ +#include "SkFontHost.h" + +SkTypeface* SkFontHost::CreateTypeface( const SkTypeface* familyFace, + const char familyName[], + SkTypeface::Style style) +{ + SkASSERT(!"SkFontHost::CreateTypeface unimplemented"); + return nil; +} + +uint32_t SkFontHost::FlattenTypeface(const SkTypeface* tface, void* buffer) +{ + SkASSERT(!"SkFontHost::FlattenTypeface unimplemented"); + return 0; +} + +SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc) +{ + SkASSERT(!"SkFontHost::CreateScalarContext unimplemented"); + return nil; +} + +SkFontHost::ScalerContextID SkFontHost::FindScalerContextIDForUnichar(int32_t unichar) +{ + SkASSERT(!"SkFontHost::FindScalerContextIDForUnichar unimplemented"); + return kMissing_ScalerContextID; +} + +SkScalerContext* SkFontHost::CreateScalerContextFromID(SkFontHost::ScalerContextID id, const SkScalerContext::Rec& rec) +{ + SkASSERT(!"SkFontHost::CreateScalerContextFromID unimplemented"); + return nil; +} diff --git a/libs/graphics/ports/SkGlobals_global.cpp b/libs/graphics/ports/SkGlobals_global.cpp new file mode 100644 index 0000000000..2143b59fee --- /dev/null +++ b/libs/graphics/ports/SkGlobals_global.cpp @@ -0,0 +1,11 @@ +#include "SkGlobals.h" +#include "SkThread.h" + +static SkGlobals::BootStrap gBootStrap; + +SkGlobals::BootStrap& SkGlobals::GetBootStrap() +{ + return gBootStrap; +} + + diff --git a/libs/graphics/ports/SkImageDecoder_Factory.cpp b/libs/graphics/ports/SkImageDecoder_Factory.cpp new file mode 100644 index 0000000000..3cec85aa72 --- /dev/null +++ b/libs/graphics/ports/SkImageDecoder_Factory.cpp @@ -0,0 +1,28 @@ +#include "SkImageDecoder.h" +#include "SkStream.h" + +/* Each of these is in their respective SkImageDecoder_libXXX.cpp file +*/ +extern SkImageDecoder* SkImageDecoder_GIF_Factory(SkStream*); +extern SkImageDecoder* SkImageDecoder_JPEG_Factory(SkStream*); +extern SkImageDecoder* SkImageDecoder_PNG_Factory(SkStream*); + +typedef SkImageDecoder* (*SkImageDecoderFactoryProc)(SkStream*); + +static const SkImageDecoderFactoryProc gProc[] = { + SkImageDecoder_GIF_Factory, + SkImageDecoder_PNG_Factory, + SkImageDecoder_JPEG_Factory // JPEG must be last, as it doesn't have a sniffer/detector yet +}; + +SkImageDecoder* SkImageDecoder::Factory(SkStream* stream) +{ + for (unsigned i = 0; i < SK_ARRAY_COUNT(gProc); i++) { + SkImageDecoder* codec = gProc[i](stream); + if (codec) + return codec; + stream->rewind(); + } + return NULL; +} + diff --git a/libs/graphics/ports/SkOSEvent_android.cpp b/libs/graphics/ports/SkOSEvent_android.cpp new file mode 100644 index 0000000000..9c7360f7bf --- /dev/null +++ b/libs/graphics/ports/SkOSEvent_android.cpp @@ -0,0 +1,138 @@ +#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, nil); + + // 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/libs/graphics/ports/SkOSEvent_dummy.cpp b/libs/graphics/ports/SkOSEvent_dummy.cpp new file mode 100644 index 0000000000..1c6e1880b9 --- /dev/null +++ b/libs/graphics/ports/SkOSEvent_dummy.cpp @@ -0,0 +1,11 @@ +#include "SkEvent.h" + +void SkEvent::SignalNonEmptyQueue() +{ + +} + +void SkEvent::SignalQueueTimer(SkMSec delay) +{ + +} diff --git a/libs/graphics/ports/SkOSFile_stdio.cpp b/libs/graphics/ports/SkOSFile_stdio.cpp new file mode 100644 index 0000000000..686ae52e01 --- /dev/null +++ b/libs/graphics/ports/SkOSFile_stdio.cpp @@ -0,0 +1,83 @@ +#include "SkOSFile.h" + +#ifndef SK_BUILD_FOR_BREW + +#include <stdio.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; + + return (SkFILE*)::fopen(path, perm); +} + +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 == nil) + { + 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/libs/graphics/ports/SkThread_none.cpp b/libs/graphics/ports/SkThread_none.cpp new file mode 100644 index 0000000000..39ce355c7f --- /dev/null +++ b/libs/graphics/ports/SkThread_none.cpp @@ -0,0 +1,32 @@ +#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() +{ +} + +SkMutex::~SkMutex() +{ +} + +void SkMutex::acquire() +{ +} + +void SkMutex::release() +{ +} + diff --git a/libs/graphics/ports/SkTime_Unix.cpp b/libs/graphics/ports/SkTime_Unix.cpp new file mode 100644 index 0000000000..6a0dda1b28 --- /dev/null +++ b/libs/graphics/ports/SkTime_Unix.cpp @@ -0,0 +1,33 @@ +#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, nil); + return (SkMSec) (tv.tv_sec * 1000 + tv.tv_usec / 1000 ); // microseconds to milliseconds +} + +#endif diff --git a/libs/graphics/ports/SkXMLParser_empty.cpp b/libs/graphics/ports/SkXMLParser_empty.cpp new file mode 100644 index 0000000000..b808b49092 --- /dev/null +++ b/libs/graphics/ports/SkXMLParser_empty.cpp @@ -0,0 +1,19 @@ +// 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/libs/graphics/ports/SkXMLParser_expat.cpp b/libs/graphics/ports/SkXMLParser_expat.cpp new file mode 100644 index 0000000000..7e31947325 --- /dev/null +++ b/libs/graphics/ports/SkXMLParser_expat.cpp @@ -0,0 +1,132 @@ +#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(nil); + 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(nil, 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/libs/graphics/ports/SkXMLParser_tinyxml.cpp b/libs/graphics/ports/SkXMLParser_tinyxml.cpp new file mode 100644 index 0000000000..bbea8e27cc --- /dev/null +++ b/libs/graphics/ports/SkXMLParser_tinyxml.cpp @@ -0,0 +1,79 @@ +#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(nil, 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/libs/graphics/sgl/SkAlphaRuns.cpp b/libs/graphics/sgl/SkAlphaRuns.cpp new file mode 100644 index 0000000000..891774fff3 --- /dev/null +++ b/libs/graphics/sgl/SkAlphaRuns.cpp @@ -0,0 +1,168 @@ +#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(S16 runs[], U8 alpha[], int x, int count) +{ + SkASSERT(count > 0 && x >= 0); + +// SkAlphaRuns::BreakAt(runs, alpha, x); +// SkAlphaRuns::BreakAt(&runs[x], &alpha[x], count); + + S16* next_runs = runs + x; + U8* 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); + + S16* runs = fRuns; + U8* 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 S16* runs = fRuns; + const U8* alpha = fAlpha; + + while (*runs) + { + SkASSERT(*alpha <= max); + alpha += *runs; + runs += *runs; + } + } + + void SkAlphaRuns::dump() const + { + const S16* runs = fRuns; + const U8* 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 S16* runs = fRuns; + + while (*runs) + { + SkASSERT(*runs > 0); + count += *runs; + SkASSERT(count <= fWidth); + runs += *runs; + } + SkASSERT(count == fWidth); + } +#endif + diff --git a/libs/graphics/sgl/SkAntiRun.h b/libs/graphics/sgl/SkAntiRun.h new file mode 100644 index 0000000000..43d502fb0a --- /dev/null +++ b/libs/graphics/sgl/SkAntiRun.h @@ -0,0 +1,168 @@ +#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 U8 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) + { + S16 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); + } + + U8 getStartAlpha() const { return fStartAlpha; } + int getMiddleCount() const { return fMiddleCount; } + U8 getStopAlpha() const { return fStopAlpha; } + +private: + U8 fStartAlpha, fStopAlpha; + int fMiddleCount; + + enum { + kMask = (1 << kShift) - 1, + kMax = (1 << (8 - kShift)) - 1 + }; +}; +#endif + +//////////////////////////////////////////////////////////////////////////////////// + +class SkAlphaRuns { +public: + S16* fRuns; + U8* 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(S16 runs[], U8 alpha[], int x, int count); + static void BreakAt(S16 runs[], U8 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/libs/graphics/sgl/SkBitmap.cpp b/libs/graphics/sgl/SkBitmap.cpp new file mode 100644 index 0000000000..a2ea623e51 --- /dev/null +++ b/libs/graphics/sgl/SkBitmap.cpp @@ -0,0 +1,412 @@ +#include "SkBitmap.h" +#include "SkColorPriv.h" +#include "SkUtils.h" + +SkBitmap::SkBitmap() +{ + memset(this, 0, sizeof(*this)); +} + +SkBitmap::SkBitmap(const SkBitmap& src) +{ + src.fColorTable->safeRef(); + + memcpy(this, &src, sizeof(src)); + fFlags &= ~(kWeOwnThePixels_Flag | kWeOwnTheMipMap_Flag); +} + +SkBitmap::~SkBitmap() +{ + fColorTable->safeUnref(); + this->freePixels(); +} + +SkBitmap& SkBitmap::operator=(const SkBitmap& src) +{ + src.fColorTable->safeRef(); + fColorTable->safeUnref(); + + this->freePixels(); + memcpy(this, &src, sizeof(src)); + fFlags &= ~(kWeOwnThePixels_Flag | kWeOwnTheMipMap_Flag); + + return *this; +} + +void SkBitmap::swap(SkBitmap& other) +{ + SkTSwap<SkColorTable*>(fColorTable, other.fColorTable); + +#ifdef SK_SUPPORT_MIPMAP + SkTSwap<MipMap*>(fMipMap, other.fMipMap); +#endif + + SkTSwap<void*>(fPixels, other.fPixels); + SkTSwap<U16>(fWidth, other.fWidth); + SkTSwap<U16>(fHeight, other.fHeight); + SkTSwap<U16>(fRowBytes, other.fRowBytes); + SkTSwap<U8>(fConfig, other.fConfig); + SkTSwap<U8>(fFlags, other.fFlags); +} + +void SkBitmap::reset() +{ + fColorTable->safeUnref(); + this->freePixels(); + memset(this, 0, sizeof(*this)); +} + +void SkBitmap::setConfig(Config c, U16CPU width, U16CPU height, U16CPU rowBytes) +{ + this->freePixels(); + + if (rowBytes == 0) + { + switch (c) { + case kA1_Config: + rowBytes = (width + 7) >> 3; + break; + case kA8_Config: + case kIndex8_Config: + rowBytes = width; + break; + case kRGB_565_Config: + rowBytes = SkAlign4(width << 1); + break; + case kARGB_8888_Config: + rowBytes = width << 2; + break; + default: + SkASSERT(!"unknown config"); + break; + } + } + + fConfig = SkToU8(c); + fWidth = SkToU16(width); + fHeight = SkToU16(height); + fRowBytes = SkToU16(rowBytes); +} + +void SkBitmap::setPixels(void* p) +{ + this->freePixels(); + + fPixels = p; + fFlags &= ~(kWeOwnThePixels_Flag | kWeOwnTheMipMap_Flag); +} + +void SkBitmap::allocPixels() +{ + this->freePixels(); + + fPixels = (U32*)sk_malloc_throw(fHeight * fRowBytes); + fFlags |= kWeOwnThePixels_Flag; +} + +void SkBitmap::freePixels() +{ + if (fFlags & kWeOwnThePixels_Flag) + { + SkASSERT(fPixels); + sk_free(fPixels); + fPixels = NULL; + fFlags &= ~kWeOwnThePixels_Flag; + } +#ifdef SK_SUPPORT_MIPMAP + if (fFlags & kWeOwnTheMipMap_Flag) + { + sk_free(fMipMap); + fMipMap = NULL; + } +#endif +} + +bool SkBitmap::getOwnsPixels() const +{ + return SkToBool(fFlags & kWeOwnThePixels_Flag); +} + +void SkBitmap::setOwnsPixels(bool ownsPixels) +{ + if (ownsPixels) + fFlags |= kWeOwnThePixels_Flag; + else + fFlags &= ~kWeOwnThePixels_Flag; +} + +SkColorTable* SkBitmap::setColorTable(SkColorTable* ct) +{ + SkRefCnt_SafeAssign(fColorTable, ct); + return ct; +} + +bool SkBitmap::isOpaque() const +{ + switch (fConfig) { + case kA1_Config: + case kA8_Config: + case kARGB_8888_Config: + return (fFlags & kImageIsOpaque_Flag) != 0; + + case kIndex8_Config: + return (fColorTable->getFlags() & SkColorTable::kColorsAreOpaque_Flag) != 0; + + case kRGB_565_Config: + return true; + + default: + SkASSERT(!"unknown bitmap config"); + 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::eraseARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) +{ + if (fPixels == NULL || fConfig == kNo_Config) + return; + + int height = fHeight; + + this->setIsOpaque(a == 255); + + // make rgb premultiplied + if (a != 255) + { + r = SkAlphaMul(r, a); + g = SkAlphaMul(g, a); + b = SkAlphaMul(b, a); + } + + switch (fConfig) { + case kA1_Config: + { + U8* p = (uint8_t*)fPixels; + size_t count = (fWidth + 7) >> 3; + a = (a >> 7) ? 0xFF : 0; + SkASSERT(count <= fRowBytes); + while (--height >= 0) + { + memset(p, a, count); + p += fRowBytes; + } + } + break; + case kA8_Config: + memset(fPixels, a, fRowBytes * fWidth); + break; + case kIndex8_Config: + SkASSERT(!"Don't support writing to Index8 bitmaps"); + break; + case kRGB_565_Config: + // now erase the color-plane + { + U16* p = (uint16_t*)fPixels; + U16 v = SkPackRGB16(r >> (8 - SK_R16_BITS), + g >> (8 - SK_G16_BITS), + b >> (8 - SK_B16_BITS)); + + while (--height >= 0) + { + sk_memset16(p, v, fWidth); + p = (uint16_t*)((char*)p + fRowBytes); + } + } + 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, fWidth); + p = (uint32_t*)((char*)p + fRowBytes); + } + } + break; + } +} + +////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_SUPPORT_MIPMAP +static void downsampleby2_proc32(SkBitmap* dst, int x, int y, const SkBitmap& src) +{ + x <<= 1; + y <<= 1; + const U32* p = src.getAddr32(x, y); + U32 c, ag, rb; + + c = *p; ag = (c >> 8) & 0xFF00FF; rb = c & 0xFF00FF; + if (x < (int)src.width() - 1) + p += 1; + c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF; + + if (y < (int)src.height() - 1) + p = src.getAddr32(x, y + 1); + c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF; + if (x < (int)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_R16B16_MASK_IN_PLACE) | ((c & SK_G16_MASK_IN_PLACE) << 16); +} + +static inline U16CPU pack16(uint32_t c) +{ + return (c & SK_R16B16_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 U16* p = src.getAddr16(x, y); + U32 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) = SkToU16(pack16(c >> 2)); +} + +void SkBitmap::buildMipMap(bool forceRebuild) +{ +#ifdef SK_SUPPORT_MIPMAP + if (!forceRebuild && fMipMap) + return; + + if (fFlags & kWeOwnTheMipMap_Flag) + { + SkASSERT(fMipMap); + sk_free(fMipMap); + fMipMap = NULL; + fFlags &= ~kWeOwnTheMipMap_Flag; + } + + int shift; + void (*proc)(SkBitmap* dst, int x, int y, const SkBitmap& src); + + switch (this->getConfig()) { + case kARGB_8888_Config: + shift = 2; + proc = downsampleby2_proc32; + break; + case kRGB_565_Config: + shift = 1; + proc = downsampleby2_proc16; + break; + case kIndex8_Config: + case kA8_Config: +// shift = 0; break; + default: + return; // don't build mipmaps for these configs + } + + // whip through our loop to compute the exact size needed + size_t size; + { + unsigned width = this->width(); + unsigned height = this->height(); + size = 0; + for (int i = 1; i < kMaxMipLevels; i++) + { + width = (width + 1) >> 1; + height = (height + 1) >> 1; + size += width * height << shift; + } + } + + MipMap* mm = (MipMap*)sk_malloc_throw(sizeof(MipMap) + size); + U8* addr = (U8*)(mm + 1); + + unsigned width = this->width(); + unsigned height = this->height(); + unsigned rowBytes = this->rowBytes(); + SkBitmap srcBM(*this), dstBM; + + mm->fLevel[0].fPixels = this->getPixels(); + mm->fLevel[0].fWidth = SkToU16(width); + mm->fLevel[0].fHeight = SkToU16(height); + mm->fLevel[0].fRowBytes = SkToU16(rowBytes); + mm->fLevel[0].fConfig = SkToU8(this->getConfig()); + mm->fLevel[0].fShift = SkToU8(shift); + + for (int i = 1; i < kMaxMipLevels; i++) + { + width = (width + 1) >> 1; + height = (height + 1) >> 1; + rowBytes = width << shift; + + mm->fLevel[i].fPixels = addr; + mm->fLevel[i].fWidth = SkToU16(width); + mm->fLevel[i].fHeight = SkToU16(height); + mm->fLevel[i].fRowBytes = SkToU16(rowBytes); + mm->fLevel[i].fConfig = SkToU8(this->getConfig()); + mm->fLevel[i].fShift = SkToU8(shift); + + dstBM.setConfig(this->getConfig(), 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 == (U8*)mm->fLevel[1].fPixels + size); + + fMipMap = mm; + fFlags |= kWeOwnTheMipMap_Flag; +#endif +} + +unsigned SkBitmap::countMipLevels() const +{ +#ifdef SK_SUPPORT_MIPMAP + return fMipMap ? kMaxMipLevels : 0; +#else + return 0; +#endif +} + +const SkBitmap::MipLevel* SkBitmap::getMipLevel(unsigned level) const +{ + SkASSERT(level < this->countMipLevels()); + + return &fMipMap->fLevel[level]; +} +#endif + + diff --git a/libs/graphics/sgl/SkBitmapSampler.cpp b/libs/graphics/sgl/SkBitmapSampler.cpp new file mode 100644 index 0000000000..28b83f0914 --- /dev/null +++ b/libs/graphics/sgl/SkBitmapSampler.cpp @@ -0,0 +1,281 @@ +#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, SkPaint::FilterType ftype, + SkShader::TileMode tmx, SkShader::TileMode tmy) + : fBitmap(bm), fFilterType(ftype), 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); +} + +class SkNullBitmapSampler : public SkBitmapSampler { +public: + SkNullBitmapSampler(const SkBitmap& bm, SkPaint::FilterType ft, + SkShader::TileMode tmx, SkShader::TileMode tmy) + : SkBitmapSampler(bm, ft, 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 + +static void assert_valid_pmcolor(uint32_t c) +{ + SkASSERT(SkGetPackedA32(c) >= SkGetPackedR32(c)); + SkASSERT(SkGetPackedA32(c) >= SkGetPackedG32(c)); + SkASSERT(SkGetPackedA32(c) >= SkGetPackedB32(c)); +} + +#include "SkFilterProc.h" + +class ARGB32_Bilinear_Sampler : public SkBitmapSampler { +public: + ARGB32_Bilinear_Sampler(const SkBitmap& bm, SkShader::TileMode tmx, SkShader::TileMode tmy) + : SkBitmapSampler(bm, SkPaint::kBilinear_FilterType, tmx, tmy) + { + fProcTable = SkGetBilinearFilterProcTable(); + } + + virtual SkPMColor sample(SkFixed x, SkFixed y) const + { + int ix = x >> 16; + int iy = y >> 16; + + ix = fTileProcX(ix, fMaxX); + iy = fTileProcY(iy, fMaxY); + + const uint32_t *p00, *p01, *p10, *p11; + + p00 = p01 = fBitmap.getAddr32(ix, iy); + if (ix < fMaxX) + p01 += 1; + p10 = p00; + p11 = p01; + if (iy < fMaxY) + { + p10 = (const uint32_t*)((const char*)p10 + fBitmap.rowBytes()); + p11 = (const uint32_t*)((const char*)p11 + fBitmap.rowBytes()); + } + + uint32_t c00 = *p00; + uint32_t c01 = *p01; + uint32_t c10 = *p10; + uint32_t c11 = *p11; + + assert_valid_pmcolor(c00); + assert_valid_pmcolor(c01); + assert_valid_pmcolor(c01); + assert_valid_pmcolor(c11); + + SkFilterProc proc = SkGetBilinearFilterProc(fProcTable, x, y); + uint32_t c = SkPackARGB32( proc(SkGetPackedA32(c00), SkGetPackedA32(c01), SkGetPackedA32(c10), SkGetPackedA32(c11)), + proc(SkGetPackedR32(c00), SkGetPackedR32(c01), SkGetPackedR32(c10), SkGetPackedR32(c11)), + proc(SkGetPackedG32(c00), SkGetPackedG32(c01), SkGetPackedG32(c10), SkGetPackedG32(c11)), + proc(SkGetPackedB32(c00), SkGetPackedB32(c01), SkGetPackedB32(c10), SkGetPackedB32(c11))); + + assert_valid_pmcolor(c); + return c; + } + +private: + const SkFilterProc* fProcTable; +}; + +class Index8_Bilinear_Sampler : public SkBitmapSampler { +public: + Index8_Bilinear_Sampler(const SkBitmap& bm, SkShader::TileMode tmx, SkShader::TileMode tmy) + : SkBitmapSampler(bm, SkPaint::kBilinear_FilterType, tmx, tmy) + { + fProcTable = SkGetBilinearFilterProcTable(); + } + + virtual SkPMColor sample(SkFixed x, SkFixed y) const + { + int ix = x >> 16; + int iy = y >> 16; + + ix = fTileProcX(ix, fMaxX); + iy = fTileProcY(iy, fMaxY); + + const U8 *p00, *p01, *p10, *p11; + + p00 = p01 = fBitmap.getAddr8(ix, iy); + if (ix < fMaxX) + p01 += 1; + p10 = p00; + p11 = p01; + if (iy < fMaxY) + { + p10 = (const U8*)((const char*)p10 + fBitmap.rowBytes()); + p11 = (const U8*)((const char*)p11 + fBitmap.rowBytes()); + } + + const SkPMColor* colors = fBitmap.getColorTable()->lockColors(); + + uint32_t c00 = colors[*p00]; + uint32_t c01 = colors[*p01]; + uint32_t c10 = colors[*p10]; + uint32_t c11 = colors[*p11]; + + assert_valid_pmcolor(c00); + assert_valid_pmcolor(c01); + assert_valid_pmcolor(c01); + assert_valid_pmcolor(c11); + + SkFilterProc proc = SkGetBilinearFilterProc(fProcTable, x, y); + uint32_t c = SkPackARGB32( proc(SkGetPackedA32(c00), SkGetPackedA32(c01), SkGetPackedA32(c10), SkGetPackedA32(c11)), + proc(SkGetPackedR32(c00), SkGetPackedR32(c01), SkGetPackedR32(c10), SkGetPackedR32(c11)), + proc(SkGetPackedG32(c00), SkGetPackedG32(c01), SkGetPackedG32(c10), SkGetPackedG32(c11)), + proc(SkGetPackedB32(c00), SkGetPackedB32(c01), SkGetPackedB32(c10), SkGetPackedB32(c11))); + + assert_valid_pmcolor(c); + + fBitmap.getColorTable()->unlockColors(false); + + return c; + } + +private: + const SkFilterProc* fProcTable; +}; + +///////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// + +SkBitmapSampler* SkBitmapSampler::Create(const SkBitmap& bm, SkPaint::FilterType ftype, + SkShader::TileMode tmx, SkShader::TileMode tmy) +{ + switch (bm.getConfig()) { + case SkBitmap::kARGB_8888_Config: + switch (ftype) { + case SkPaint::kNo_FilterType: + 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 SkPaint::kBilinear_FilterType: + return SkNEW_ARGS(ARGB32_Bilinear_Sampler, (bm, tmx, tmy)); + + default: + SkASSERT(!"unknown filter type"); + } + break; + case SkBitmap::kRGB_565_Config: + // we ignore ftype, since we haven't implemented bilinear for 16bit bitmaps yet + 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: + switch (ftype) { + case SkPaint::kNo_FilterType: + 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 SkPaint::kBilinear_FilterType: + return SkNEW_ARGS(Index8_Bilinear_Sampler, (bm, tmx, tmy)); + default: // to avoid warnings + break; + } + break; + default: + SkASSERT(!"unknown device"); + } + return SkNEW_ARGS(SkNullBitmapSampler, (bm, ftype, tmx, tmy)); +} + diff --git a/libs/graphics/sgl/SkBitmapSampler.h b/libs/graphics/sgl/SkBitmapSampler.h new file mode 100644 index 0000000000..27a18559cd --- /dev/null +++ b/libs/graphics/sgl/SkBitmapSampler.h @@ -0,0 +1,150 @@ +#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&, SkPaint::FilterType, SkShader::TileMode tmx, SkShader::TileMode tmy); + virtual ~SkBitmapSampler() {} + + const SkBitmap& getBitmap() const { return fBitmap; } + SkPaint::FilterType getFilterType() const { return fFilterType; } + SkShader::TileMode getTileModeX() const { return fTileModeX; } + SkShader::TileMode getTileModeY() const { return fTileModeY; } + + // override this in your subclass + virtual SkPMColor sample(SkFixed x, SkFixed y) const = 0; + + // This is the factory for finding an optimal subclass + static SkBitmapSampler* Create(const SkBitmap&, SkPaint::FilterType, + SkShader::TileMode tmx, SkShader::TileMode tmy); + +protected: + const SkBitmap& fBitmap; + uint16_t fMaxX, fMaxY; + SkPaint::FilterType fFilterType; + 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/libs/graphics/sgl/SkBitmapSamplerTemplate.h b/libs/graphics/sgl/SkBitmapSamplerTemplate.h new file mode 100644 index 0000000000..19530a01cc --- /dev/null +++ b/libs/graphics/sgl/SkBitmapSamplerTemplate.h @@ -0,0 +1,99 @@ +/* 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, SkPaint::kNo_FilterType, tmx, tmy) + { + } + + virtual SkPMColor sample(SkFixed x, SkFixed y) const + { + x = fTileProcX(SkFixedRound(x), fMaxX); + y = fTileProcY(SkFixedRound(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, SkPaint::kNo_FilterType, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode) + { + } + + virtual SkPMColor sample(SkFixed x, SkFixed y) const + { + x = do_clamp(SkFixedRound(x), fMaxX); + y = do_clamp(SkFixedRound(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, SkPaint::kNo_FilterType, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode) + { + } + + virtual SkPMColor sample(SkFixed x, SkFixed y) const + { + x = do_repeat_pow2(SkFixedRound(x), fMaxX); + y = do_repeat_pow2(SkFixedRound(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, SkPaint::kNo_FilterType, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode) + { + } + + virtual SkPMColor sample(SkFixed x, SkFixed y) const + { + x = do_repeat_mod(SkFixedRound(x), fMaxX); + y = do_repeat_mod(SkFixedRound(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, SkPaint::kNo_FilterType, SkShader::kMirror_TileMode, SkShader::kMirror_TileMode) + { + } + + virtual SkPMColor sample(SkFixed x, SkFixed y) const + { + x = do_mirror_pow2(SkFixedRound(x), fMaxX); + y = do_mirror_pow2(SkFixedRound(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, SkPaint::kNo_FilterType, SkShader::kMirror_TileMode, SkShader::kMirror_TileMode) + { + } + + virtual SkPMColor sample(SkFixed x, SkFixed y) const + { + x = do_mirror_mod(SkFixedRound(x), fMaxX); + y = do_mirror_mod(SkFixedRound(y), fMaxY); + return BITMAP_PIXEL_TO_PMCOLOR(fBitmap, x, y); + } +}; + +#undef BITMAP_CLASSNAME_PREFIX +#undef BITMAP_PIXEL_TO_PMCOLOR diff --git a/libs/graphics/sgl/SkBitmapShader.cpp b/libs/graphics/sgl/SkBitmapShader.cpp new file mode 100644 index 0000000000..b70f8bb587 --- /dev/null +++ b/libs/graphics/sgl/SkBitmapShader.cpp @@ -0,0 +1,476 @@ +#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 transferOwnershipOfPixels, + SkPaint::FilterType filterType, + TileMode tmx, TileMode tmy) + : +#ifdef SK_SUPPORT_MIPMAP + fMipLevel(0), fMipSrcBitmap(src), +#endif + fOrigSrcBitmap(src) + +{ + if (transferOwnershipOfPixels) + { + fOrigSrcBitmap.setOwnsPixels(src.getOwnsPixels()); + ((SkBitmap*)&src)->setOwnsPixels(false); + // do the same for mipmap ownership??? + } + fFilterType = SkToU8(filterType); + fTileModeX = SkToU8(tmx); + fTileModeY = SkToU8(tmy); +} + +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; + + uint32_t flags = fOrigSrcBitmap.isOpaque() ? kOpaqueAlpha_Flag : 0; + + if (flags == kOpaqueAlpha_Flag && paint.getAlpha() != 0xFF) + flags = kConstAlpha_Flag; + + fFlags = SkToU8(flags); + +#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); + } + else + { + fMipLevel = 0; + fMipSrcBitmap = fOrigSrcBitmap; + } +#endif + return true; +} + +/////////////////////////////////////////////////////////////////////////// + +#include "SkColorPriv.h" +#include "SkBitmapSampler.h" + +class Sampler_BitmapShader : public SkBitmapShader { +public: + Sampler_BitmapShader(const SkBitmap& src, + bool transferOwnershipOfPixels, + SkPaint::FilterType ftype, + TileMode tmx, TileMode tmy) + : SkBitmapShader(src, transferOwnershipOfPixels, ftype, 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->getFilterType(), + this->getTileModeX(), this->getTileModeY()); + return true; + } + return false; + } + + 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), SkIntToScalar(y), &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 + { + SkScalar dstX = SkIntToScalar(x); + SkScalar dstY = SkIntToScalar(y); + + for (int i = 0; i < count; i++) + { + proc(inv, dstX, dstY, &srcPt); + uint32_t c = sampler->sample(SkScalarToFixed(srcPt.fX), SkScalarToFixed(srcPt.fY)); + + if (scale != 256) + c = SkAlphaMulQ(c, scale); + dstC[i] = c; + dstX += SK_Scalar1; + } + } + } + +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.postScale(SK_Scalar1 / src.width(), SK_Scalar1 / src.height(), 0, 0); + 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 transferOwnershipOfPixels, + SkPaint::FilterType ft, TileMode tmx, TileMode tmy) + : Sampler_BitmapShader(src, transferOwnershipOfPixels, ft, tmx, tmy) + { + } + + virtual uint32_t getFlags() + { + uint32_t flags = this->INHERITED::getFlags(); + + if (this->getPaintAlpha() == 0xFF && this->getInverseClass() != kPerspective_MatrixClass) + flags |= SkShader::kHasSpan16_Flag; + else + flags &= ~SkShader::kHasSpan16_Flag; + + return flags; + } +private: + typedef Sampler_BitmapShader INHERITED; +}; + +/////////////////////////////////////////////////////////////////////////// + +#define NOFILTER_BITMAP_SHADER_CLASS Index8_NoFilter_ClampTile_BitmapShader +#define NOFILTER_BITMAP_SHADER_TILEMODE SkShader::kClamp_TileMode +#define NOFILTER_BITMAP_SHADER_TILEPROC(x, max) SkClampMax((x >> 16), max) +#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() +#include "SkBitmapShaderTemplate.h" + +#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 +#include "SkBitmapShaderTemplate.h" + +#define NOFILTER_BITMAP_SHADER_CLASS U16_NoFilter_ClampTile_BitmapShader +#define NOFILTER_BITMAP_SHADER_TILEMODE SkShader::kClamp_TileMode +#define NOFILTER_BITMAP_SHADER_TILEPROC(x, max) SkClampMax((x >> 16), max) +#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)) +#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 +#include "SkBitmapShaderTemplate.h" + +#define NOFILTER_BITMAP_SHADER_CLASS U32_NoFilter_ClampTile_BitmapShader +#define NOFILTER_BITMAP_SHADER_TILEMODE SkShader::kClamp_TileMode +#define NOFILTER_BITMAP_SHADER_TILEPROC(x, max) SkClampMax((x >> 16), max) +#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))) +#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 +#include "SkBitmapShaderTemplate.h" + +/////////////////////////////////////////////////////////////////////////////////////////////////////// + +#define Pack4Bytes(c00, c01, c10, c11) (((c00) << 24) | ((c01) << 16) | ((c10) << 8) | (c11)) +/* Each long stores 4 coefficients, each in a byte. + coeff >> 24 -> [0][0] + coeff >> 16 -> [0][1] + coeff >> 8 -> [1][0] + coeff >> 0 -> [1][1] +*/ +static const uint32_t gBilerpPackedCoeff[] = { + /* y == 0 */ + Pack4Bytes(16, 0, 0, 0), // x == 0 + Pack4Bytes(12, 4, 0, 0), // x == 1/4 + Pack4Bytes( 8, 8, 0, 0), // x == 1/2 + Pack4Bytes( 4, 12, 0, 0), // x == 3/4 + + /* y == 1/4 */ + Pack4Bytes(12, 0, 4, 0), + Pack4Bytes( 9, 3, 3, 1), + Pack4Bytes( 6, 6, 2, 2), + Pack4Bytes( 3, 9, 1, 3), + + /* y == 1/2 */ + Pack4Bytes( 8, 0, 8, 0), + Pack4Bytes( 6, 2, 6, 2), + Pack4Bytes( 4, 4, 4, 4), + Pack4Bytes( 2, 6, 2, 6), + + /* y == 3/4 */ + Pack4Bytes( 4, 0, 12, 0), + Pack4Bytes( 3, 1, 9, 3), + Pack4Bytes( 2, 2, 6, 6), + Pack4Bytes( 1, 3, 3, 9) +}; + +// extract the high two bits in the fractional part of the fixed +#define SK_BILERP_GET_BITS(x) (((x) >> 14) & 3) + +static inline uint32_t sk_find_bilerp_coeff(const uint32_t coeff[], SkFixed fx, SkFixed fy) +{ +#ifdef SK_DEBUG + uint32_t c = coeff[(SK_BILERP_GET_BITS(fy) << 2) | SK_BILERP_GET_BITS(fx)]; + SkASSERT((c >> 24) + ((c >> 16) & 0xFF) + ((c >> 8) & 0xFF) + (c & 0xFF) == 16); +#endif + return coeff[(SK_BILERP_GET_BITS(fy) << 2) | SK_BILERP_GET_BITS(fx)]; +} + +static inline uint32_t expand_rgb_16(U16CPU c, U16CPU rbMask) +{ + return ((c & SK_G16_MASK_IN_PLACE) << 16) | (c & rbMask); +} + +static inline U16CPU compact_rgb_16(uint32_t c, U16CPU rbMask) +{ + return ((c >> 16) & SK_G16_MASK_IN_PLACE) | (c & rbMask); +} + +static inline U16CPU sk_bilerp16(U16CPU c00, U16CPU c01, U16CPU c10, U16CPU c11, uint32_t coeff, U16CPU rbMask) +{ +// U16CPU rbMask = SK_R16B16_MASK_IN_PLACE; + + c00 = expand_rgb_16(c00, rbMask) * (coeff >> 24) + + expand_rgb_16(c01, rbMask) * ((coeff >> 16) & 0xFF) + + expand_rgb_16(c10, rbMask) * ((coeff >> 8) & 0xFF) + + expand_rgb_16(c11, rbMask) * (coeff & 0xFF); + + return compact_rgb_16(c00 >> 4, rbMask); +} + +// this wacky line is to force the compiler to put this contant into a register +// rather than try to construct it each time it is referenced in the inner-loop +extern const uint16_t gRBMask_Bilerp_BitmapShader; + +#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" + +// we define it below all the includes, so they won't try to inline the value +// (which doesn't fit in an immediate register load) +const uint16_t gRBMask_Bilerp_BitmapShader = SK_R16B16_MASK_IN_PLACE; + +/////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////// + +#include "SkTemplatesPriv.h" + +SkShader* SkShader::CreateBitmapShader(const SkBitmap& src, + bool transferOwnershipOfPixels, + SkPaint::FilterType filterType, + TileMode tmx, TileMode tmy, + void* storage, size_t storageSize) +{ + SkShader* shader = NULL; + + if (filterType == SkPaint::kNo_FilterType) + { + switch (src.getConfig()) { + case SkBitmap::kIndex8_Config: + if (kClamp_TileMode == tmx && kClamp_TileMode == tmy) + SK_PLACEMENT_NEW_ARGS(shader, Index8_NoFilter_ClampTile_BitmapShader, storage, storageSize, (src, transferOwnershipOfPixels)); + else if (kRepeat_TileMode == tmx && kRepeat_TileMode == tmy) + SK_PLACEMENT_NEW_ARGS(shader, Index8_NoFilter_RepeatTile_BitmapShader, storage, storageSize, (src, transferOwnershipOfPixels)); + break; + case SkBitmap::kRGB_565_Config: + if (kClamp_TileMode == tmx && kClamp_TileMode == tmy) + SK_PLACEMENT_NEW_ARGS(shader, U16_NoFilter_ClampTile_BitmapShader, storage, storageSize, (src, transferOwnershipOfPixels)); + else if (kRepeat_TileMode == tmx && kRepeat_TileMode == tmy) + SK_PLACEMENT_NEW_ARGS(shader, U16_NoFilter_RepeatTile_BitmapShader, storage, storageSize, (src, transferOwnershipOfPixels)); + break; + case SkBitmap::kARGB_8888_Config: + if (kClamp_TileMode == tmx && kClamp_TileMode == tmy) + SK_PLACEMENT_NEW_ARGS(shader, U32_NoFilter_ClampTile_BitmapShader, storage, storageSize, (src, transferOwnershipOfPixels)); + else if (kRepeat_TileMode == tmx && kRepeat_TileMode == tmy) + SK_PLACEMENT_NEW_ARGS(shader, U32_NoFilter_RepeatTile_BitmapShader, storage, storageSize, (src, transferOwnershipOfPixels)); + break; + default: + break; + } + } + else if (filterType == SkPaint::kBilinear_FilterType + && kClamp_TileMode == tmx + && kClamp_TileMode == tmy) + { + switch (src.getConfig()) { + case SkBitmap::kIndex8_Config: + SK_PLACEMENT_NEW_ARGS(shader, Index8_Bilerp_BitmapShader, storage, storageSize, (src, transferOwnershipOfPixels)); + break; + case SkBitmap::kRGB_565_Config: + SK_PLACEMENT_NEW_ARGS(shader, U16_Bilerp_BitmapShader, storage, storageSize, (src, transferOwnershipOfPixels)); + break; + default: + break; + } + } + + // 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, transferOwnershipOfPixels, filterType, tmx, tmy)); + return shader; +} + +SkShader* SkShader::CreateBitmapShader(const SkBitmap& src, + bool transferOwnershipOfPixels, + SkPaint::FilterType filterType, + TileMode tmx, TileMode tmy) +{ + return SkShader::CreateBitmapShader(src, transferOwnershipOfPixels, filterType, tmx, tmy, NULL, 0); +} + diff --git a/libs/graphics/sgl/SkBitmapShader.h b/libs/graphics/sgl/SkBitmapShader.h new file mode 100644 index 0000000000..6727ec4c28 --- /dev/null +++ b/libs/graphics/sgl/SkBitmapShader.h @@ -0,0 +1,51 @@ +#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 transferOwnershipOfPixels, + SkPaint::FilterType, TileMode tx, TileMode ty); + + virtual bool setContext(const SkBitmap&, const SkPaint& paint, const SkMatrix&); + virtual uint32_t getFlags() { return fFlags; } + +protected: + const SkBitmap& getSrcBitmap() const + { +#ifdef SK_SUPPORT_MIPMAP + return fMipSrcBitmap; +#else + return fOrigSrcBitmap; +#endif + } + SkPaint::FilterType getFilterType() const { return (SkPaint::FilterType)fFilterType; } + 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: +#ifdef SK_SUPPORT_MIPMAP + SkFixed fMipLevel; + SkBitmap fMipSrcBitmap; // the chosen level (in setContext) +#endif + SkBitmap fOrigSrcBitmap; + U8 fFilterType; + U8 fTileModeX; + U8 fTileModeY; + U8 fFlags; + + typedef SkShader INHERITED; +}; + +#endif diff --git a/libs/graphics/sgl/SkBitmapShader16BilerpTemplate.h b/libs/graphics/sgl/SkBitmapShader16BilerpTemplate.h new file mode 100644 index 0000000000..fa64a61bb6 --- /dev/null +++ b/libs/graphics/sgl/SkBitmapShader16BilerpTemplate.h @@ -0,0 +1,126 @@ + + +class BILERP_BITMAP16_SHADER_CLASS : public HasSpan16_Sampler_BitmapShader { +public: + BILERP_BITMAP16_SHADER_CLASS(const SkBitmap& src, bool transferOwnershipOfPixels) + : HasSpan16_Sampler_BitmapShader(src, transferOwnershipOfPixels, SkPaint::kBilinear_FilterType, + SkShader::kClamp_TileMode, SkShader::kClamp_TileMode) + { + } + + virtual void shadeSpanOpaque16(int x, int y, U16 dstC[], int count) + { + SkASSERT(count > 0); + SkASSERT(this->getInverseClass() != kPerspective_MatrixClass); + SkASSERT(this->getPaintAlpha() == 0xFF); + + const SkMatrix& inv = this->getTotalInverse(); + const SkBitmap& srcBitmap = this->getSrcBitmap(); + unsigned srcMaxX = srcBitmap.width() - 1; + unsigned srcMaxY = srcBitmap.height() - 1; + unsigned srcRB = srcBitmap.rowBytes(); + SkFixed fx, fy, dx, dy; + + // now init fx, fy, dx, dy + { + SkPoint srcPt; + this->getInverseMapPtProc()(inv, SkIntToScalar(x), SkIntToScalar(y), &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()); + } + } + + BILERP_BITMAP16_SHADER_PREAMBLE(srcBitmap); + + const U32* coeff_table = gBilerpPackedCoeff; + const BILERP_BITMAP16_SHADER_TYPE* srcPixels = (const BILERP_BITMAP16_SHADER_TYPE*)srcBitmap.getPixels(); + U16CPU rbMask = gRBMask_Bilerp_BitmapShader; + + if (dy == 0) + { + fy = SkClampMax(fy, srcMaxY << 16); + coeff_table += SK_BILERP_GET_BITS(fy) << 2; // jump the table to the correct section (so we can just use fx to index it) + + unsigned y = fy >> 16; + SkASSERT((int)y >= 0 && y <= srcMaxY); + // pre-bias srcPixels since y won't change + srcPixels = (const BILERP_BITMAP16_SHADER_TYPE*)((const char*)srcPixels + y * srcRB); + // now make y the step from one row to the next + y = srcRB; + if (y == srcMaxY) + y = 0; + + do { + unsigned fx_clamped = SkClampMax(fx, srcMaxX << 16); + unsigned x = fx_clamped >> 16; + SkASSERT((int)x >= 0 && x <= srcMaxX); + + const BILERP_BITMAP16_SHADER_TYPE *p00, *p01, *p10, *p11; + + p00 = p01 = srcPixels + x; + if (x < srcMaxX) + p01 += 1; + p10 = (const BILERP_BITMAP16_SHADER_TYPE*)((const char*)p00 + y); + p11 = (const BILERP_BITMAP16_SHADER_TYPE*)((const char*)p01 + y); + + *dstC++ = SkToU16(sk_bilerp16( BILERP_BITMAP16_SHADER_PIXEL(*p00), + BILERP_BITMAP16_SHADER_PIXEL(*p01), + BILERP_BITMAP16_SHADER_PIXEL(*p10), + BILERP_BITMAP16_SHADER_PIXEL(*p11), + coeff_table[SK_BILERP_GET_BITS(fx_clamped)], + rbMask)); + + fx += dx; + } while (--count != 0); + } + else + { + do { + unsigned x = SkClampMax(fx, srcMaxX << 16) >> 16; + unsigned y = SkClampMax(fy, srcMaxY << 16) >> 16; + + SkASSERT((int)x >= 0 && x <= srcMaxX); + SkASSERT((int)y >= 0 && y <= srcMaxY); + + const BILERP_BITMAP16_SHADER_TYPE *p00, *p01, *p10, *p11; + + p00 = p01 = ((const BILERP_BITMAP16_SHADER_TYPE*)((const char*)srcPixels + y * srcRB)) + x; + if (x < srcMaxX) + p01 += 1; + p10 = p00; + p11 = p01; + if (y < srcMaxY) + { + p10 = (const BILERP_BITMAP16_SHADER_TYPE*)((const char*)p10 + srcRB); + p11 = (const BILERP_BITMAP16_SHADER_TYPE*)((const char*)p11 + srcRB); + } + + *dstC++ = SkToU16(sk_bilerp16( BILERP_BITMAP16_SHADER_PIXEL(*p00), + BILERP_BITMAP16_SHADER_PIXEL(*p01), + BILERP_BITMAP16_SHADER_PIXEL(*p10), + BILERP_BITMAP16_SHADER_PIXEL(*p11), + sk_find_bilerp_coeff(coeff_table, fx, fy), + rbMask)); + + 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/libs/graphics/sgl/SkBitmapShaderTemplate.h b/libs/graphics/sgl/SkBitmapShaderTemplate.h new file mode 100644 index 0000000000..2e90b84377 --- /dev/null +++ b/libs/graphics/sgl/SkBitmapShaderTemplate.h @@ -0,0 +1,231 @@ + +#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, bool transferOwnershipOfPixels) + : HasSpan16_Sampler_BitmapShader(src, transferOwnershipOfPixels, SkPaint::kNo_FilterType, + NOFILTER_BITMAP_SHADER_TILEMODE, NOFILTER_BITMAP_SHADER_TILEMODE) + { + } + +#ifdef NOFILTER_BITMAP_SHADER_USE_UNITINVERSE + virtual bool setContext(const SkBitmap& device, const SkPaint& paint, const SkMatrix& matrix) + { + if (this->INHERITED::setContext(device, paint, matrix)) { + this->computeUnitInverse(); + return true; + } + return false; + } +#endif + + virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count) + { + if (this->getInverseClass() == kPerspective_MatrixClass) + { + this->INHERITED::shadeSpan(x, y, dstC, count); + return; + } + + 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; + + // now init fx, fy, dx, dy + { + SkPoint srcPt; + invProc(inv, SkIntToScalar(x), SkIntToScalar(y), &srcPt); + + fx = SkScalarToFixed(srcPt.fX); + fy = SkScalarToFixed(srcPt.fY); +#ifndef NOFILTER_BITMAP_SHADER_USE_UNITINVERSE + fx += SK_Fixed1/2; + fy += SK_Fixed1/2; +#endif + + if (this->getInverseClass() == kFixedStepInX_MatrixClass) + (void)inv.fixedStepInX(SkIntToScalar(y), &dx, &dy); + else + { + dx = SkScalarToFixed(inv.getScaleX()); + dy = SkScalarToFixed(inv.getSkewY()); + } + } + + const NOFILTER_BITMAP_SHADER_TYPE* srcPixels = (const NOFILTER_BITMAP_SHADER_TYPE*)srcBitmap.getPixels(); + NOFILTER_BITMAP_SHADER_PREAMBLE(srcBitmap, srcRB); + +#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); + U32 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); + U32 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 shadeSpanOpaque16(int x, int y, U16 dstC[], int count) + { + SkASSERT(count > 0); + SkASSERT(this->getInverseClass() != kPerspective_MatrixClass); + SkASSERT(this->getFlags() & SkShader::kHasSpan16_Flag); + SkASSERT(this->getFlags() & (SkShader::kOpaqueAlpha_Flag | SkShader::kConstAlpha_Flag)); + +#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; + + // now init fx, fy, dx, dy + { + SkPoint srcPt; + invProc(inv, SkIntToScalar(x), SkIntToScalar(y), &srcPt); + + fx = SkScalarToFixed(srcPt.fX); + fy = SkScalarToFixed(srcPt.fY); +#ifndef NOFILTER_BITMAP_SHADER_USE_UNITINVERSE + fx += SK_Fixed1/2; + fy += SK_Fixed1/2; +#endif + + if (this->getInverseClass() == kFixedStepInX_MatrixClass) + (void)inv.fixedStepInX(SkIntToScalar(y), &dx, &dy); + else + { + dx = SkScalarToFixed(inv.getScaleX()); + dy = SkScalarToFixed(inv.getSkewY()); + } + } + + const NOFILTER_BITMAP_SHADER_TYPE* srcPixels = (const NOFILTER_BITMAP_SHADER_TYPE*)srcBitmap.getPixels(); + NOFILTER_BITMAP_SHADER_PREAMBLE16(srcBitmap, srcRB); + +#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 { + int ix = fx >> 16; + unsigned x = NOFILTER_BITMAP_SHADER_TILEPROC(ix, srcMaxX); + ix = fy >> 16; + unsigned y = NOFILTER_BITMAP_SHADER_TILEPROC(ix, 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 diff --git a/libs/graphics/sgl/SkBlitBWMaskTemplate.h b/libs/graphics/sgl/SkBlitBWMaskTemplate.h new file mode 100644 index 0000000000..70530f17fc --- /dev/null +++ b/libs/graphics/sgl/SkBlitBWMaskTemplate.h @@ -0,0 +1,120 @@ +#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 SkRect16& 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 SkRect16& 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 U8* 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 U8* 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/libs/graphics/sgl/SkBlitter.cpp b/libs/graphics/sgl/SkBlitter.cpp new file mode 100644 index 0000000000..5dcac42adc --- /dev/null +++ b/libs/graphics/sgl/SkBlitter.cpp @@ -0,0 +1,795 @@ +#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() +{ +} + +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 SkRect16& 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((U16*)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; + } + } +} + +/////////////////////////////////////////////////////////////////////////////////////// + +// this guy is not virtual, just a helper +void SkBlitter::blitMaskRegion(const SkMask& mask, const SkRegion& clip) +{ + if (clip.quickReject(mask.fBounds)) + return; + + SkRegion::Cliperator clipper(clip, mask.fBounds); + + if (!clipper.done()) + { + const SkRect16& cr = clipper.rect(); + do { + this->blitMask(mask, cr); + clipper.next(); + } while (!clipper.done()); + } +} + +/////////////////////////////////////////////////////////////////////////////////////// + +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; +} + +/////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////// + +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) +{ +} + +/////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////// + +static inline bool y_in_rect(int y, const SkRect16& rect) +{ + return (unsigned)(y - rect.fTop) < (unsigned)rect.height(); +} + +static inline bool x_in_rect(int x, const SkRect16& 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, (U8*)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, (U8*)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) +{ + SkRect16 r; + + r.set(left, y, left + width, y + height); + if (r.intersect(fClipRect)) + fBlitter->blitRect(r.fLeft, r.fTop, r.width(), r.height()); +} + +/////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////// + +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; + bool firstTime = true; + SkDEBUGCODE(const SkRect16& bounds = fRgn->getBounds();) + +//SkDebugf("rgnClip: x=%d y=%d: ", x, y); + + while (span.next(&left, &right)) + { + SkASSERT(left < right); + SkASSERT(left >= bounds.fLeft && right <= bounds.fRight); + + if (firstTime && x < left) + { +//SkDebugf("zap[%d %d] ", x, left); + SkAlphaRuns::Break((int16_t*)runs, (U8*)aa, 0, left - x); + ((U8*)aa)[0] = 0; // skip runs before the first left + ((int16_t*)runs)[0] = SkToS16(left - x); + } + firstTime = false; + + SkAlphaRuns::Break((int16_t*)runs, (U8*)aa, left - x, right - left); + ((U8*)aa)[right - x] = 0; // skip runs after right + ((int16_t*)runs)[right - x] = SkToS16(right - left); + +//SkDebugf("[%d %d] ", left, right); + } + ((int16_t*)runs)[right - x] = 0; + +//dump_runs(runs, aa); + + fBlitter->blitAntiH(x, y, aa, runs); +} + +void SkRgnClipBlitter::blitV(int x, int y, int height, SkAlpha alpha) +{ + SkRect16 bounds; + bounds.set(x, y, x + 1, y + height); + + SkRegion::Cliperator iter(*fRgn, bounds); + + while (!iter.done()) + { + const SkRect16& 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) +{ + SkRect16 bounds; + bounds.set(x, y, x + width, y + height); + + SkRegion::Cliperator iter(*fRgn, bounds); + + while (!iter.done()) + { + const SkRect16& r = iter.rect(); + SkASSERT(bounds.contains(r)); + + fBlitter->blitRect(r.fLeft, r.fTop, r.width(), r.height()); + iter.next(); + } +} + +/////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////// + +SkBlitter* SkBlitterClipper::apply(SkBlitter* blitter, const SkRegion* clip, const SkRect16* ir) +{ + if (clip) + { + const SkRect16& clipR = clip->getBounds(); + + if (clip->isEmpty() || ir && !SkRect16::Intersects(clipR, *ir)) + blitter = &fNullBlitter; + else if (clip->isRect()) + { + if (ir == nil || !clipR.contains(*ir)) + { + fRectBlitter.init(blitter, clipR); + blitter = &fRectBlitter; + } + } + else + { + fRgnBlitter.init(blitter, clip); + blitter = &fRgnBlitter; + } + } + return blitter; +} + +/////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////// + +#include "SkShader.h" +#include "SkColorPriv.h" + +class SkColorShader : public SkShader { +public: + virtual U32 getFlags() + { + // should I claim hasspan16 if my color isn't opaque? + return (SkGetPackedA32(fPMColor) == 255 ? kOpaqueAlpha_Flag : kConstAlpha_Flag) | kHasSpan16_Flag; + } + virtual bool setContext(const SkBitmap& device, const SkPaint& paint, const SkMatrix& matrix) + { + if (!this->INHERITED::setContext(device, paint, matrix)) + return false; + + SkColor c = paint.getColor(); + unsigned a = SkColorGetA(c); + unsigned r = SkColorGetR(c); + unsigned g = SkColorGetG(c); + unsigned b = SkColorGetB(c); + + if (a != 255) + { + a = SkAlpha255To256(a); + r = SkAlphaMul(r, a); + g = SkAlphaMul(g, a); + b = SkAlphaMul(b, a); + } + fPMColor = SkPackARGB32(a, r, g, b); + fColor16 = SkPixel32ToPixel16_ToU16(fPMColor); // only meaning full if a == 255 + return true; + } + virtual void shadeSpan(int x, int y, SkPMColor span[], int count) + { + sk_memset32(span, fPMColor, count); + } + virtual void shadeSpanOpaque16(int x, int y, U16 span[], int count) + { + SkASSERT(SkGetPackedA32(fPMColor) == 255); + sk_memset16(span, fColor16, count); + } +private: + SkPMColor fPMColor; + U16 fColor16; + + typedef SkShader INHERITED; +}; + +class Sk3DShader : public SkShader { +public: + Sk3DShader(SkShader* proxy) : fProxy(proxy) + { + proxy->safeRef(); + fMask = nil; + } + 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 == nil) + { + if (fProxy == nil) + 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 U8* alpha = fMask->getAddr(x, y); + const U8* mulp = alpha + size; + const U8* addp = mulp + size; + + if (fProxy) + { + for (int i = 0; i < count; i++) + { + if (alpha[i]) + { + U32 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; + } + } + } +private: + 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 SkRect16& 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(nil); + } + 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 = nil; + 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 != nil); + + SkBlitter* blitter = nil; + SkAutoRestoreShader restore(paint); + SkShader* shader = paint.getShader(); + + Sk3DShader* shader3D = nil; + if (paint.getMaskFilter() != nil && 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(); + } + + if (shader) + { + if (!shader->setContext(device, paint, matrix)) + return SkNEW(SkNullBlitter); + } + + 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::kRGB_565_Config: + if (shader) + { + if (mode) + SK_PLACEMENT_NEW_ARGS(blitter, SkRGB16_Shader_Xfermode_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 + 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; +} + diff --git a/libs/graphics/sgl/SkBlitter.h b/libs/graphics/sgl/SkBlitter.h new file mode 100644 index 0000000000..2031efa093 --- /dev/null +++ b/libs/graphics/sgl/SkBlitter.h @@ -0,0 +1,102 @@ +#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 antialias[], const S16 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 SkRect16& clip); + + // not virtual, just a helper + void blitMaskRegion(const SkMask& mask, const SkRegion& clip); + + static SkBlitter* Choose(const SkBitmap& device, + const SkMatrix& matrix, + const SkPaint& paint) + { + return Choose(device, matrix, paint, nil, 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: +}; + +class SkNullBlitter : public SkBlitter { +public: + virtual void blitH(int x, int y, int width); + virtual void blitAntiH(int x, int y, const SkAlpha antialias[], const S16 runs[]); + virtual void blitV(int x, int y, int height, SkAlpha alpha); + virtual void blitRect(int x, int y, int width, int height); +}; + +class SkRectClipBlitter : public SkBlitter { +public: + void init(SkBlitter* blitter, const SkRect16& 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 antialias[], const S16 runs[]); + virtual void blitV(int x, int y, int height, SkAlpha alpha); + virtual void blitRect(int x, int y, int width, int height); + +private: + SkBlitter* fBlitter; + SkRect16 fClipRect; +}; + +class SkRgnClipBlitter : public SkBlitter { +public: + void init(SkBlitter* blitter, const SkRegion* rgn) + { + SkASSERT(rgn && !rgn->isEmpty()); + fBlitter = blitter; + fRgn = rgn; + } + + // overrides + virtual void blitH(int x, int y, int width); + virtual void blitAntiH(int x, int y, const SkAlpha antialias[], const S16 runs[]); + virtual void blitV(int x, int y, int height, SkAlpha alpha); + virtual void blitRect(int x, int y, int width, int height); + +private: + SkBlitter* fBlitter; + const SkRegion* fRgn; +}; + +class SkBlitterClipper { +public: + SkBlitter* apply(SkBlitter* blitter, const SkRegion* clip, const SkRect16* bounds = nil); + +private: + SkNullBlitter fNullBlitter; + SkRectClipBlitter fRectBlitter; + SkRgnClipBlitter fRgnBlitter; +}; + +#endif diff --git a/libs/graphics/sgl/SkBlitter_A1.cpp b/libs/graphics/sgl/SkBlitter_A1.cpp new file mode 100644 index 0000000000..38e0e4f096 --- /dev/null +++ b/libs/graphics/sgl/SkBlitter_A1.cpp @@ -0,0 +1,46 @@ +#include "SkCoreBlitters.h" + +SkA1_Blitter::SkA1_Blitter(const SkBitmap& device, const SkPaint& paint) + : fDevice(device) +{ + fSrcA = SkToU8(SkColorGetA(paint.getColor())); +} + +void SkA1_Blitter::blitH(int x, int y, int width) +{ + SkASSERT(x >= 0 && y >= 0 && (unsigned)(x + width) <= fDevice.width()); + + if (fSrcA <= 0x7F) + return; + + U8* 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/libs/graphics/sgl/SkBlitter_A8.cpp b/libs/graphics/sgl/SkBlitter_A8.cpp new file mode 100644 index 0000000000..bb94050125 --- /dev/null +++ b/libs/graphics/sgl/SkBlitter_A8.cpp @@ -0,0 +1,365 @@ +#include "SkCoreBlitters.h" +#include "SkColorPriv.h" +#include "SkShader.h" +#include "SkXfermode.h" + +SkA8_Blitter::SkA8_Blitter(const SkBitmap& device, const SkPaint& paint) : fDevice(device) +{ + fSrcA = SkColorGetA(paint.getColor()); +} + +void SkA8_Blitter::blitH(int x, int y, int width) +{ + SkASSERT(x >= 0 && y >= 0 && (unsigned)(x + width) <= 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 S16 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 SkRect16& 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) <= fDevice.width() && (unsigned)(y + height) <= 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) : fDevice(device) +{ + fShader = paint.getShader(); + SkASSERT(fShader); + fShader->ref(); + + 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(); + fShader->unref(); + sk_free(fBuffer); +} + +void SkA8_Shader_Blitter::blitH(int x, int y, int width) +{ + SkASSERT(x >= 0 && y >= 0 && (unsigned)(x + width) <= 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(U32 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 S16 runs[]) +{ + SkASSERT(x >= 0 && y >= 0 && x < (int)fDevice.width() && y < (int)fDevice.height()); + + 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 SkRect16& 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/libs/graphics/sgl/SkBlitter_ARGB32.cpp b/libs/graphics/sgl/SkBlitter_ARGB32.cpp new file mode 100644 index 0000000000..66ff505417 --- /dev/null +++ b/libs/graphics/sgl/SkBlitter_ARGB32.cpp @@ -0,0 +1,465 @@ +#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) : fDevice(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); +} + +#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 && (unsigned)(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 S16 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); + + for (int i = 0; i < count; i++) + device[i] = sc + SkAlphaMulQ(device[i], dst_scale); + } + } + 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 SkRect16& clip) +{ + SkASSERT(mask.fBounds.contains(clip)); + + if (fSrcA == 0) + return; + + if (mask.fFormat == SkMask::kBW_Format) + { + if (fSrcA == 0xFF) + SkARGB32_BlitBW(fDevice, mask, clip, fPMColor); + else + 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; + + while (--height >= 0) + { + for (int i = width - 1; i >= 0; --i) + { + uint32_t color = srcColor; + + // scale our src by the alpha value + { + int aa = alpha[i]; + if (aa == 0) + continue; + + if (aa == 255) + { + if (fSrcA == 255) + { + device[i] = color; + continue; + } + } + else + color = SkAlphaMulQ(color, SkAlpha255To256(aa)); + } + device[i] = SkPMSrcOver(color, device[i]); + } + device = (uint32_t*)((char*)device + fDevice.rowBytes()); + alpha += mask.fRowBytes; + } +} + +////////////////////////////////////////////////////////////////////////////////////// + +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 && (unsigned)(x + width) <= fDevice.width() && (unsigned)(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 SkRect16& clip) +{ + SkASSERT(mask.fBounds.contains(clip)); + + SkPMColor black = (SkPMColor)(SK_A32_MASK << SK_A32_SHIFT); + + if (mask.fFormat == SkMask::kBW_Format) + { + SkARGB32_BlitBW(fDevice, mask, clip, black); + } + else + { + uint32_t* device = fDevice.getAddr32(clip.fLeft, clip.fTop); + const U8* 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++; + if (aa) + { + if (aa == 255) + *device = black; + else + *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); + } +} + +////////////////////////////////////////////////////////////////////////////////////////// + +SkARGB32_Shader_Blitter::SkARGB32_Shader_Blitter(const SkBitmap& device, const SkPaint& paint) : fDevice(device) +{ + fBuffer = (SkPMColor*)sk_malloc_throw(device.width() * (sizeof(SkPMColor))); + + fShader = paint.getShader(); + SkASSERT(fShader); + fShader->ref(); + + (fXfermode = paint.getXfermode())->safeRef(); +} + +SkARGB32_Shader_Blitter::~SkARGB32_Shader_Blitter() +{ + fXfermode->safeUnref(); + fShader->unref(); + sk_free(fBuffer); +} + +void SkARGB32_Shader_Blitter::blitH(int x, int y, int width) +{ + SkASSERT(x >= 0 && y >= 0 && (unsigned)(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 S16 runs[]) +{ + SkASSERT(x >= 0 && y >= 0 && x < (int)fDevice.width() && y < (int)fDevice.height()); + + 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], count, 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/libs/graphics/sgl/SkBlitter_RGB16.cpp b/libs/graphics/sgl/SkBlitter_RGB16.cpp new file mode 100644 index 0000000000..56b2b419f2 --- /dev/null +++ b/libs/graphics/sgl/SkBlitter_RGB16.cpp @@ -0,0 +1,674 @@ +#include "SkCoreBlitters.h" +#include "SkColorPriv.h" +#include "SkShader.h" +#include "SkUtils.h" +#include "SkXfermode.h" + +#ifdef SK_DEBUG + static unsigned RGB16Add(U16CPU a, U16CPU b) + { + SkASSERT(SkGetPackedR16(a) + SkGetPackedR16(b) <= SK_R16_MASK); + SkASSERT(SkGetPackedG16(a) + SkGetPackedG16(b) <= SK_G16_MASK); + SkASSERT(SkGetPackedB16(a) + SkGetPackedB16(b) <= SK_B16_MASK); + + return a + b; + } +#else + #define RGB16Add(a, b) (a + b) +#endif + +#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, U16 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 U16 +#include "SkBlitBWMaskTemplate.h" + +void SkRGB16_Black_Blitter::blitMask(const SkMask& mask, const SkRect16& clip) +{ + if (mask.fFormat == SkMask::kBW_Format) + { + SkRGB16_Black_BlitBW(fDevice, mask, clip); + } + else + { + U16* device = fDevice.getAddr16(clip.fLeft, clip.fTop); + const U8* 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++; + if (aa) + { + if (aa == 255) + *device = 0; + else + *device = SkToU16(SkAlphaMulRGB16(*device, SkAlpha255To256(255 - aa))); + } + device += 1; + } while (--w != 0); + device = (U16*)((char*)device + deviceRB); + alpha += maskRB; + } while (--height != 0); + } +} + +////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////// + +SkRGB16_Blitter::SkRGB16_Blitter(const SkBitmap& device, const SkPaint& paint) : fDevice(device) +{ + U32 color = paint.getColor(); + + fScale = SkAlpha255To256(SkColorGetA(color)); + + fRawColor16 = SkPackRGB16( SkColorGetR(color) >> (8 - SK_R16_BITS), + SkColorGetG(color) >> (8 - SK_G16_BITS), + SkColorGetB(color) >> (8 - SK_B16_BITS)); + + fColor16 = SkPackRGB16( SkAlphaMul(SkColorGetR(color), fScale) >> (8 - SK_R16_BITS), + SkAlphaMul(SkColorGetG(color), fScale) >> (8 - SK_G16_BITS), + SkAlphaMul(SkColorGetB(color), fScale) >> (8 - SK_B16_BITS)); +} + +void SkRGB16_Blitter::blitH(int x, int y, int width) +{ + SkASSERT(width > 0); + SkASSERT(x >= 0 && y >= 0 && (unsigned)(x + width) <= fDevice.width()); + + if (fScale == 0) + return; + + U16* device = fDevice.getAddr16(x, y); + unsigned srcColor = fColor16; + + if (fScale == 256) + { + sk_memset16(device, srcColor, width); + } + else + { + unsigned scale = 256 - fScale; + do { + *device = (U16)RGB16Add(srcColor, SkAlphaMulRGB16(*device, scale)); + device += 1; + } while (--width != 0); + } +} + +void SkRGB16_Blitter::blitAntiH(int x, int y, const SkAlpha antialias[], const S16 runs[]) +{ + if (fScale == 0) + return; + + U16* device = fDevice.getAddr16(x, y); + U16 srcColor = fColor16; + unsigned scale = fScale; + + if (scale == 256) + { + 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) + { + sk_memset16(device, srcColor, count); + } + else + { + unsigned src = SkAlphaMulRGB16(srcColor, SkAlpha255To256(aa)); + unsigned dst_scale = SkAlpha255To256(255 - aa); + do { + *device = (U16)RGB16Add(src, SkAlphaMulRGB16(*device, dst_scale)); + device += 1; + } while (--count != 0); + continue; + } + } + device += count; + } + } + else + { + for (;;) + { + int count = runs[0]; + SkASSERT(count >= 0); + if (count == 0) + return; + runs += count; + + unsigned aa = antialias[0]; + antialias += count; + if (aa) + { + unsigned src = SkAlphaMulRGB16(srcColor, SkAlpha255To256(aa)); + unsigned dst_scale = SkAlpha255To256(255 - SkAlphaMul(aa, scale)); + do { + *device = (U16)RGB16Add(src, SkAlphaMulRGB16(*device, dst_scale)); + device += 1; + } 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 , U16 color +#define SK_BLITBWMASK_BLIT8(mask, dst) solid_8_pixels(mask, dst, color) +#define SK_BLITBWMASK_GETADDR getAddr16 +#define SK_BLITBWMASK_DEVTYPE U16 +#include "SkBlitBWMaskTemplate.h" + +static inline void blend_8_pixels(U8CPU bw, U16 dst[], unsigned dst_scale, U16CPU srcColor) +{ + if (bw & 0x80) dst[0] = SkToU16(srcColor + SkAlphaMulRGB16(dst[0], dst_scale)); + if (bw & 0x40) dst[1] = SkToU16(srcColor + SkAlphaMulRGB16(dst[1], dst_scale)); + if (bw & 0x20) dst[2] = SkToU16(srcColor + SkAlphaMulRGB16(dst[2], dst_scale)); + if (bw & 0x10) dst[3] = SkToU16(srcColor + SkAlphaMulRGB16(dst[3], dst_scale)); + if (bw & 0x08) dst[4] = SkToU16(srcColor + SkAlphaMulRGB16(dst[4], dst_scale)); + if (bw & 0x04) dst[5] = SkToU16(srcColor + SkAlphaMulRGB16(dst[5], dst_scale)); + if (bw & 0x02) dst[6] = SkToU16(srcColor + SkAlphaMulRGB16(dst[6], dst_scale)); + if (bw & 0x01) dst[7] = SkToU16(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 U16 +#include "SkBlitBWMaskTemplate.h" + +void SkRGB16_Blitter::blitMask(const SkMask& mask, const SkRect16& clip) +{ + 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; + } + + U16* device = fDevice.getAddr16(clip.fLeft, clip.fTop); + const U8* alpha = mask.getAddr(clip.fLeft, clip.fTop); + int width = clip.width(); + int height = clip.height(); + unsigned maskRB = mask.fRowBytes; + U16 color16 = fRawColor16; + unsigned scale = fScale; + unsigned deviceRB = fDevice.rowBytes(); + + if (scale == 256) + { + while (--height >= 0) + { + for (int i = width - 1; i >= 0; --i) + { + unsigned aa = alpha[i]; + if (aa) + { + if (aa == 255) + device[i] = color16; + else + { + unsigned src_scale = SkAlpha255To256(aa); + unsigned dst_scale = SkAlpha255To256(255 - aa); + device[i] = (U16)RGB16Add(SkAlphaMulRGB16(color16, src_scale), SkAlphaMulRGB16(device[i], dst_scale)); + } + } + } + device = (U16*)((char*)device + deviceRB); + alpha += maskRB; + } + } + else // scale < 256 + { + while (--height >= 0) + { + for (int i = width - 1; i >= 0; --i) + { + unsigned aa = alpha[i]; + if (aa) + { + aa = SkAlphaMul(aa, scale); + unsigned src_scale = SkAlpha255To256(aa); + unsigned dst_scale = SkAlpha255To256(255 - aa); + device[i] = (U16)RGB16Add(SkAlphaMulRGB16(color16, src_scale), SkAlphaMulRGB16(device[i], dst_scale)); + } + } + device = (U16*)((char*)device + deviceRB); + alpha += maskRB; + } + } +} + +void SkRGB16_Blitter::blitV(int x, int y, int height, SkAlpha alpha) +{ + if (fScale == 0) + return; + + U16* device = fDevice.getAddr16(x, y); + U16 color16 = fColor16; + unsigned deviceRB = fDevice.rowBytes(); + + if (alpha + fScale == (255 + 256)) + { + do { + device[0] = color16; + device = (U16*)((char*)device + deviceRB); + } while (--height != 0); + } + else + { + unsigned scale = fScale; + + if (alpha < 255) + { + scale = SkAlphaMul(alpha, scale); + color16 = SkToU16(SkAlphaMulRGB16(fRawColor16, scale)); + } + scale = 256 - scale; + do { + *device = (U16)RGB16Add(color16, SkAlphaMulRGB16(device[0], scale)); + device = (U16*)((char*)device + deviceRB); + } while (--height != 0); + } +} + +void SkRGB16_Blitter::blitRect(int x, int y, int width, int height) +{ + SkASSERT(x >= 0 && y >= 0 && (unsigned)(x + width) <= fDevice.width() && (unsigned)(y + height) <= fDevice.height()); + + if (fScale == 0) + return; + + U16* device = fDevice.getAddr16(x, y); + unsigned deviceRB = fDevice.rowBytes(); + U16 color16 = fColor16; + + if (fScale == 256) + { + while (--height >= 0) + { + sk_memset16(device, color16, width); + device = (U16*)((char*)device + deviceRB); + } + } + else + { + unsigned dst_scale = 256 - fScale; // apply it to the dst + + while (--height >= 0) + { + for (int i = width - 1; i >= 0; --i) + device[i] = SkToU16(color16 + SkAlphaMulRGB16(device[i], dst_scale)); + device = (U16*)((char*)device + deviceRB); + } + } +} + +/////////////////////////////////////////////////////////////////////// + +#define BLEND_32_TO_16(srcA, src, dst) \ + SkToU16(SkPixel32ToPixel16(src) + SkAlphaMulRGB16(dst, 256 - SkAlpha255To256(srcA))) + +SkRGB16_Shader_Blitter::SkRGB16_Shader_Blitter(const SkBitmap& device, const SkPaint& paint) : fDevice(device) +{ + SkASSERT(paint.getXfermode() == NULL); + + fShader = paint.getShader(); + SkASSERT(fShader); + fShader->ref(); + + SkAutoUnref autoUnref(fShader); + fBuffer = (SkPMColor*)sk_malloc_throw(device.width() * sizeof(SkPMColor)); + (void)autoUnref.detach(); +} + +SkRGB16_Shader_Blitter::~SkRGB16_Shader_Blitter() +{ + fShader->unref(); + sk_free(fBuffer); +} + +void SkRGB16_Shader_Blitter::blitH(int x, int y, int width) +{ + SkASSERT(x >= 0 && y >= 0 && (unsigned)(x + width) <= fDevice.width()); + + U16* device = fDevice.getAddr16(x, y); + U32 flags = fShader->getFlags(); + + if (SkShader::CanCallShadeSpanOpaque16(flags)) + { + fShader->shadeSpanOpaque16(x, y, device, width); + return; + } + + // If we get here, we know we need the 32bit answer from the shader + + SkPMColor* span = fBuffer; + + fShader->shadeSpan(x, y, span, width); + if (flags & SkShader::kOpaqueAlpha_Flag) + { + for (int i = width - 1; i >= 0; --i) + device[i] = SkPixel32ToPixel16_ToU16(span[i]); + } + else + { + for (int i = 0; i < width; i++) + { + U32 src = span[i]; + if (src) + { + unsigned srcA = SkGetPackedA32(src); + if (srcA == 0xFF) + device[i] = SkPixel32ToPixel16_ToU16(src); + else + device[i] = BLEND_32_TO_16(srcA, src, device[i]); + } + } + } +} + +static inline U16 aa_blendS32D16(U32 src, U16CPU dst, int aa) +{ + SkASSERT((unsigned)aa <= 255); + + int src_scale = SkAlpha255To256(aa); + int sa = SkGetPackedA32(src); + int dst_scale = SkAlpha255To256(255 - SkAlphaMul(sa, src_scale)); + + int dr = (SkPacked32ToR16(src) * src_scale + SkGetPackedR16(dst) * dst_scale) >> 8; + int dg = (SkPacked32ToG16(src) * src_scale + SkGetPackedG16(dst) * dst_scale) >> 8; + int db = (SkPacked32ToB16(src) * src_scale + SkGetPackedB16(dst) * dst_scale) >> 8; + + return SkPackRGB16(dr, dg, db); +} + +static inline int count_nonzero_span(const S16 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 antialias[], const S16 runs[]) +{ +// aa_blendS32D16(0xd4d4d49c, 65526, 238); + + SkASSERT(x >= 0 && y >= 0 && x < (int)fDevice.width() && y < (int)fDevice.height()); + + SkShader* shader = fShader; + SkPMColor* span = fBuffer; + U16* device = fDevice.getAddr16(x, y); + U32 flags = fShader->getFlags(); + + if (SkShader::CanCallShadeSpanOpaque16(flags)) + { + U16* span16 = (U16*)span; + for (;;) + { + int count = *runs; + if (count == 0) + break; + + int aa = *antialias; + if (aa == 255) + shader->shadeSpanOpaque16(x, y, device, count); + else if (aa) + { + unsigned scale = SkAlpha255To256(aa); + shader->shadeSpanOpaque16(x, y, span16, count); + for (int i = 0; i < count; i++) + device[i] = SkToU16(SkBlendRGB16(span16[i], device[i], scale)); + } + device += count; + runs += count; + antialias += count; + x += count; + } + return; + } + + // If we get here, take the 32bit shadeSpan case + + int opaque = flags & SkShader::kOpaqueAlpha_Flag; + + for (;;) + { + int count = *runs; + if (count == 0) + break; + + int aa = *antialias; + if (aa == 0) + { + device += count; + runs += count; + antialias += count; + x += count; + continue; + } + + int nonZeroCount = count + count_nonzero_span(runs + count, antialias + count); + + shader->shadeSpan(x, y, span, nonZeroCount); + x += nonZeroCount; + SkPMColor* localSpan = span; + for (;;) + { + if (aa == 255) // no antialiasing + { + if (opaque) + { + for (int i = 0; i < count; i++) + device[i] = SkPixel32ToPixel16_ToU16(localSpan[i]); + } + else + { + for (int i = 0; i < count; i++) + { + U32 src = localSpan[i]; + if (src) + { + unsigned srcA = SkGetPackedA32(src); + if (srcA == 0xFF) + device[i] = SkPixel32ToPixel16_ToU16(src); + else + device[i] = BLEND_32_TO_16(srcA, src, device[i]); + } + } + } + } + else + { + for (int i = 0; i < count; i++) + { + if (localSpan[i]) + device[i] = aa_blendS32D16(localSpan[i], device[i], aa); + } + } + + 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) : fDevice(device) +{ + fShader = paint.getShader(); + SkASSERT(fShader); + fShader->ref(); + + fXfermode = paint.getXfermode(); + SkASSERT(fXfermode); + fXfermode->ref(); + + int width = device.width(); + fBuffer = (SkPMColor*)sk_malloc_throw((width + (SkAlign4(width) >> 2)) * sizeof(SkPMColor)); + fAAExpand = (U8*)(fBuffer + width); +} + +SkRGB16_Shader_Xfermode_Blitter::~SkRGB16_Shader_Xfermode_Blitter() +{ + fXfermode->unref(); + fShader->unref(); + sk_free(fBuffer); +} + +void SkRGB16_Shader_Xfermode_Blitter::blitH(int x, int y, int width) +{ + SkASSERT(x >= 0 && y >= 0 && (unsigned)(x + width) <= fDevice.width()); + + U16* 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 antialias[], const S16 runs[]) +{ + SkASSERT(x >= 0 && y >= 0 && x < (int)fDevice.width() && y < (int)fDevice.height()); + + SkShader* shader = fShader; + SkXfermode* mode = fXfermode; + SkPMColor* span = fBuffer; + U8* aaExpand = fAAExpand; + U16* device = fDevice.getAddr16(x, y); + + for (;;) + { + int count = *runs; + if (count == 0) + break; + + int aa = *antialias; + if (aa == 0) + { + device += count; + runs += count; + antialias += count; + x += count; + continue; + } + + int nonZeroCount = count + count_nonzero_span(runs + count, antialias + count); + + 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; + } + } +} + + diff --git a/libs/graphics/sgl/SkBlitter_Sprite.cpp b/libs/graphics/sgl/SkBlitter_Sprite.cpp new file mode 100644 index 0000000000..a740e356d9 --- /dev/null +++ b/libs/graphics/sgl/SkBlitter_Sprite.cpp @@ -0,0 +1,73 @@ +#include "SkSpriteBlitter.h" + +SkSpriteBlitter::SkSpriteBlitter(const SkBitmap& source) + : fSource(&source) +{ +} + +SkSpriteBlitter::~SkSpriteBlitter() +{ +} + +#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 S16 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 SkRect16& clip) +{ + SkASSERT(!"how did we get here?"); +} +#endif + +////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////// + +// returning nil 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 nil if it is set, forcing the client to take the slow shader case + (which does respect soft edges). + */ + + SkSpriteBlitter* blitter = nil; + SkXfermode* mode = paint.getXfermode(); + U8 alpha = paint.getAlpha(); + + switch (device.getConfig()) { + case SkBitmap::kRGB_565_Config: + blitter = SkSpriteBlitter::ChooseD16(source, mode, alpha, storage, storageSize); + break; + case SkBitmap::kARGB_8888_Config: + blitter = SkSpriteBlitter::ChooseD32(source, mode, alpha, storage, storageSize); + default: + break; + } + + if (blitter) + blitter->setup(device, left, top); + return blitter; +} + diff --git a/libs/graphics/sgl/SkCanvas.cpp b/libs/graphics/sgl/SkCanvas.cpp new file mode 100644 index 0000000000..75cb7bd1b0 --- /dev/null +++ b/libs/graphics/sgl/SkCanvas.cpp @@ -0,0 +1,623 @@ +#include "SkCanvas.h" +#include "SkDraw.h" +#include "SkBounder.h" +#include "SkUtils.h" + +struct MCRecLayer { + SkBitmap fBitmap; + int fX, fY; + SkPaint fPaint; + + MCRecLayer(const SkPaint& paint) : fPaint(paint) {} +}; + +struct SkCanvas::MCRec { + MCRec* fNext; + + SkMatrix fMatrix; + SkMatrix::MapPtProc fMapPtProc; + SkRegion fRegion; + + MCRecLayer* fLayer; // may be NULL + const SkBitmap* fCurrBitmap; // points to layer or prevLayer or pixels + + uint8_t fSetPaintBits; + uint8_t fClearPaintBits; + + MCRec() : fLayer(NULL) + { + } + MCRec(const MCRec& other) + : fMatrix(other.fMatrix), fRegion(other.fRegion), fLayer(NULL) + { + // don't bother initializing fNext + fMapPtProc = other.fMapPtProc; + fCurrBitmap = other.fCurrBitmap; + fSetPaintBits = other.fSetPaintBits; + fClearPaintBits = other.fClearPaintBits; + } + ~MCRec() + { + SkDELETE(fLayer); + } +}; + +class AutoPaintSetClear { +public: + AutoPaintSetClear(const SkPaint& paint, U32 setBits, U32 clearBits) : fPaint(paint) + { + fFlags = paint.getFlags(); + ((SkPaint*)&paint)->setFlags((fFlags | setBits) & ~clearBits); + } + ~AutoPaintSetClear() + { + ((SkPaint*)&fPaint)->setFlags(fFlags); + } +private: + const SkPaint& fPaint; + U32 fFlags; + + // illegal + AutoPaintSetClear(const AutoPaintSetClear&); + AutoPaintSetClear& operator=(const AutoPaintSetClear&); +}; + +class SkAutoBounderCommit { +public: + SkAutoBounderCommit(SkBounder* bounder) : fBounder(bounder) {} + ~SkAutoBounderCommit() { if (fBounder) fBounder->commit(); } +private: + SkBounder* fBounder; +}; + +//////////////////////////////////////////////////////////////////////////// + +SkCanvas::SkCanvas() + : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)), fBounder(NULL) +{ + fMCRec = (MCRec*)fMCStack.push_back(); + new (fMCRec) MCRec; + + fMCRec->fNext = NULL; + fMCRec->fMatrix.reset(); + fMCRec->fSetPaintBits = 0; + fMCRec->fClearPaintBits = 0; + fMCRec->fMapPtProc = NULL; // mark as dirty/unknown + + fMCRec->fLayer = NULL; + fMCRec->fCurrBitmap = &fBitmap; +} + +SkCanvas::SkCanvas(const SkBitmap& bitmap) + : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)), fBitmap(bitmap), fBounder(NULL) +{ + fMCRec = (MCRec*)fMCStack.push_back(); + new (fMCRec) MCRec; + + fMCRec->fNext = NULL; + fMCRec->fMatrix.reset(); + fMCRec->fSetPaintBits = 0; + fMCRec->fClearPaintBits = 0; + fMCRec->fMapPtProc = NULL; // mark as dirty/unknown + + fMCRec->fRegion.setRect(0, 0, bitmap.width(), bitmap.height()); + + fMCRec->fLayer = NULL; + fMCRec->fCurrBitmap = &fBitmap; +} + +SkCanvas::~SkCanvas() +{ +} + +SkBounder* SkCanvas::setBounder(SkBounder* bounder) +{ + SkRefCnt_SafeAssign(fBounder, bounder); + return bounder; +} + +void SkCanvas::getPixels(SkBitmap* bitmap) const +{ + if (bitmap) + *bitmap = fBitmap; +} + +void SkCanvas::setPixels(const SkBitmap& bitmap) +{ + unsigned prevWidth = fBitmap.width(); + unsigned prevHeight = fBitmap.height(); + + fBitmap = bitmap; + + /* Now we update our initial region to have the bounds of the new bitmap, + and then intersect all of the clips in our stack with these bounds, + to ensure that we can't draw outside of the bitmap's bounds (and trash + memory). + + NOTE: this is only a partial-fix, since if the new bitmap 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 bitmap bounds. + */ + + if (prevWidth != bitmap.width() || prevHeight != bitmap.height()) + { + SkRect16 r; + r.set(0, 0, bitmap.width(), bitmap.height()); + + SkDeque::Iter iter(fMCStack); + MCRec* rec = (MCRec*)iter.next(); + + SkASSERT(rec); + rec->fRegion.setRect(r); + + while ((rec = (MCRec*)iter.next()) != NULL) + (void)rec->fRegion.op(r, SkRegion::kIntersect_Op); + } +} + +bool SkCanvas::isBitmapOpaque() const +{ + SkBitmap::Config c = fBitmap.getConfig(); + + return c != SkBitmap::kA8_Config && c != SkBitmap::kARGB_8888_Config; +} + +/////////////////////////////////////////////////////////////////////////////////////////// + +U32 SkCanvas::getPaintSetBits() const +{ + return fMCRec->fSetPaintBits; +} + +U32 SkCanvas::getPaintClearBits() const +{ + return fMCRec->fClearPaintBits; +} + +void SkCanvas::setPaintSetClearBits(U32 setBits, U32 clearBits) +{ + fMCRec->fSetPaintBits = SkToU8(setBits & SkPaint::kAllFlagMasks); + fMCRec->fClearPaintBits = SkToU8(clearBits & SkPaint::kAllFlagMasks); +} + +void SkCanvas::orPaintSetClearBits(U32 setBits, U32 clearBits) +{ + fMCRec->fSetPaintBits |= setBits; + fMCRec->fClearPaintBits |= clearBits; +} + +///////////////////////////////////////////////////////////////////////////// + +int SkCanvas::save() +{ + int saveCount = this->getSaveCount(); // record this before the actual save + + MCRec* newTop = (MCRec*)fMCStack.push_back(); + new (newTop) MCRec(*fMCRec); + + newTop->fNext = fMCRec; + fMCRec = newTop; + + return saveCount; +} + +int SkCanvas::saveLayer(const SkRect& bounds, const SkPaint& paint) +{ + // do this before we create the layer + int count = this->save(); + + SkRect r; + SkRect16 ir; + + fMCRec->fMatrix.mapRect(&r, bounds); + r.roundOut(&ir); + + if (ir.intersect(fMCRec->fRegion.getBounds())) + { + MCRecLayer* layer = SkNEW_ARGS(MCRecLayer, (paint)); + + layer->fBitmap.setConfig(SkBitmap::kARGB_8888_Config, ir.width(), ir.height()); + layer->fBitmap.allocPixels(); + layer->fBitmap.eraseARGB(0, 0, 0, 0); + layer->fX = ir.fLeft; + layer->fY = ir.fTop; + + fMCRec->fLayer = layer; + fMCRec->fCurrBitmap = &layer->fBitmap; + + fMCRec->fMatrix.postTranslate(-SkIntToScalar(ir.fLeft), -SkIntToScalar(ir.fTop)); + fMCRec->fMapPtProc = NULL; + + fMCRec->fRegion.op(ir, SkRegion::kIntersect_Op); + fMCRec->fRegion.translate(-ir.fLeft, -ir.fTop); + } + return count; +} + +#include "SkTemplates.h" + +void SkCanvas::restore() +{ + SkASSERT(!fMCStack.empty()); + + MCRecLayer* layer = fMCRec->fLayer; + SkAutoTDelete<MCRecLayer> ad(layer); + // now detach it from fMCRec + fMCRec->fLayer = NULL; + + // now do the normal restore() + fMCStack.pop_back(); + fMCRec = (MCRec*)fMCStack.back(); + + // now handle the layer if needed + if (layer) + this->drawSprite(layer->fBitmap, layer->fX, layer->fY, layer->fPaint); +} + +int SkCanvas::getSaveCount() const +{ + return fMCStack.count(); +} + +void SkCanvas::restoreToCount(int count) +{ + SkASSERT(fMCStack.count() >= count); + + while (fMCStack.count() > count) + this->restore(); +} + +void SkCanvas::clipDeviceRgn(const SkRegion& rgn) +{ + fMCRec->fRegion.op(rgn, SkRegion::kIntersect_Op); +} + +///////////////////////////////////////////////////////////////////////////// + +bool SkCanvas::translate(SkScalar dx, SkScalar dy) +{ + fMCRec->fMapPtProc = NULL; // mark as dirty/unknown + return fMCRec->fMatrix.preTranslate(dx, dy); +} + +bool SkCanvas::scale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) +{ + fMCRec->fMapPtProc = NULL; // mark as dirty/unknown + return fMCRec->fMatrix.preScale(sx, sy, px, py); +} + +bool SkCanvas::rotate(SkScalar degrees, SkScalar px, SkScalar py) +{ + fMCRec->fMapPtProc = NULL; // mark as dirty/unknown + return fMCRec->fMatrix.preRotate(degrees, px, py); +} + +bool SkCanvas::skew(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) +{ + fMCRec->fMapPtProc = NULL; // mark as dirty/unknown + return fMCRec->fMatrix.preSkew(sx, sy, px, py); +} + +bool SkCanvas::concat(const SkMatrix& matrix) +{ + fMCRec->fMapPtProc = NULL; // mark as dirty/unknown + return fMCRec->fMatrix.preConcat(matrix); +} + +////////////////////////////////////////////////////////////////////////////// + +void SkCanvas::clipRect(const SkRect& rect) +{ + if (fMCRec->fMatrix.rectStaysRect()) + { + SkRect r; + SkRect16 ir; + + fMCRec->fMatrix.mapRect(&r, rect); + r.round(&ir); + fMCRec->fRegion.op(ir, SkRegion::kIntersect_Op); + } + else + { + SkPath path; + + path.addRect(rect); + this->clipPath(path); + } +} + +void SkCanvas::clipRect(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) +{ + SkRect r; + + r.set(left, top, right, bottom); + this->clipRect(r); +} + +void SkCanvas::clipPath(const SkPath& path) +{ + SkPath devPath; + + path.transform(fMCRec->fMatrix, &devPath); + fMCRec->fRegion.setPath(devPath, &fMCRec->fRegion); +} + +bool SkCanvas::quickReject(const SkRect& rect, bool antialiased) const +{ + if (fMCRec->fRegion.isEmpty() || rect.isEmpty()) + return true; + + SkRect r; + SkRect16 ir; + + fMCRec->fMatrix.mapRect(&r, rect); + if (antialiased) + r.roundOut(&ir); + else + r.round(&ir); + + return fMCRec->fRegion.quickReject(ir); +} + +bool SkCanvas::quickReject(const SkPath& path, bool antialiased) const +{ + if (fMCRec->fRegion.isEmpty() || path.isEmpty()) + return true; + + if (fMCRec->fMatrix.rectStaysRect()) + { + SkRect r; + path.computeBounds(&r, SkPath::kExact_BoundsType); + return this->quickReject(r, antialiased); + } + + SkPath dstPath; + SkRect r; + SkRect16 ir; + + path.transform(fMCRec->fMatrix, &dstPath); + dstPath.computeBounds(&r, SkPath::kExact_BoundsType); + if (antialiased) + r.roundOut(&ir); + else + r.round(&ir); + + return fMCRec->fRegion.quickReject(ir); +} + +////////////////////////////////////////////////////////////////////////////// + +void SkCanvas::drawRGB(U8CPU r, U8CPU g, U8CPU b) +{ + SkPaint paint; + + paint.setARGB(0xFF, r, g, b); + this->drawPaint(paint); +} + +void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) +{ + SkPaint paint; + + paint.setARGB(a, r, g, b); + this->drawPaint(paint); +} + +void SkCanvas::drawColor(SkColor c, SkPorterDuff::Mode mode) +{ + SkPaint paint; + + paint.setColor(c); + paint.setPorterDuffXfermode(mode); + this->drawPaint(paint); +} + +void SkCanvas::drawPaint(const SkPaint& paint) +{ + SkAutoBounderCommit ac(fBounder); + SkDraw draw(*this); + + draw.drawPaint(paint); +} + +void SkCanvas::drawLine(const SkPoint& start, const SkPoint& stop, const SkPaint& paint) +{ + AutoPaintSetClear force(paint, fMCRec->fSetPaintBits, fMCRec->fClearPaintBits); + SkAutoBounderCommit ac(fBounder); + SkDraw draw(*this); + + draw.drawLine(start, stop, paint); +} + +void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1, const SkPaint& paint) +{ + AutoPaintSetClear force(paint, fMCRec->fSetPaintBits, fMCRec->fClearPaintBits); + SkAutoBounderCommit ac(fBounder); + SkDraw draw(*this); + + SkPoint pts[2]; + pts[0].set(x0, y0); + pts[1].set(x1, y1); + draw.drawLine(pts[0], pts[1], paint); +} + +//#include <stdio.h> + +void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) +{ + AutoPaintSetClear force(paint, fMCRec->fSetPaintBits, fMCRec->fClearPaintBits); + SkAutoBounderCommit ac(fBounder); + SkDraw draw(*this); + +#if 0 + const SkScalar* m = (const SkScalar*)draw.fMatrix; +printf("drawRect(%g %g %g %g) matrix(%g %g %g %g %g %g)\n", + SkScalarToFloat(r.fLeft), SkScalarToFloat(r.fTop), SkScalarToFloat(r.fRight), SkScalarToFloat(r.fBottom), + SkScalarToFloat(m[0]), SkScalarToFloat(m[1]), SkScalarToFloat(m[2]), + SkScalarToFloat(m[3]), SkScalarToFloat(m[4]), SkScalarToFloat(m[5]), + SkScalarToFloat(m[6]), SkScalarToFloat(m[7]), SkScalarToFloat(m[8])); +#endif + + draw.drawRect(r, paint); +} + +void SkCanvas::drawRect(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::drawOval(const SkRect& oval, const SkPaint& paint) +{ + AutoPaintSetClear force(paint, fMCRec->fSetPaintBits, fMCRec->fClearPaintBits); + SkAutoBounderCommit ac(fBounder); + SkDraw draw(*this); + + SkPath path; + + path.addOval(oval); + draw.drawPath(path, paint, NULL, true); +} + +void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius, const SkPaint& paint) +{ + AutoPaintSetClear force(paint, fMCRec->fSetPaintBits, fMCRec->fClearPaintBits); + SkAutoBounderCommit ac(fBounder); + SkDraw draw(*this); + + SkPath path; + + path.addCircle(cx, cy, radius); + draw.drawPath(path, paint, NULL, true); +} + +void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry, const SkPaint& paint) +{ + AutoPaintSetClear force(paint, fMCRec->fSetPaintBits, fMCRec->fClearPaintBits); + SkAutoBounderCommit ac(fBounder); + SkDraw draw(*this); + + SkPath path; + + path.addRoundRect(r, rx, ry, SkPath::kCW_Direction); + draw.drawPath(path, paint, NULL, true); +} + +void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) +{ + AutoPaintSetClear force(paint, fMCRec->fSetPaintBits, fMCRec->fClearPaintBits); + SkAutoBounderCommit ac(fBounder); + SkDraw draw(*this); + + draw.drawPath(path, paint, NULL, false); +} + +void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint& paint) +{ + AutoPaintSetClear force(paint, fMCRec->fSetPaintBits, fMCRec->fClearPaintBits); + SkAutoBounderCommit ac(fBounder); + SkDraw draw(*this); + + draw.drawBitmap(bitmap, x, y, paint); +} + +void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y) +{ + SkPaint paint; + this->drawBitmap(bitmap, x, y, paint); +} + +void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y, const SkPaint& paint) +{ + AutoPaintSetClear force(paint, fMCRec->fSetPaintBits, fMCRec->fClearPaintBits); + SkAutoBounderCommit ac(fBounder); + SkDraw draw(*this); + + draw.drawSprite(bitmap, x, y, paint); +} + +void SkCanvas::drawText(const char text[], size_t byteLength, SkScalar x, SkScalar y, const SkPaint& paint) +{ + AutoPaintSetClear force(paint, fMCRec->fSetPaintBits, fMCRec->fClearPaintBits); + SkAutoBounderCommit ac(fBounder); + SkDraw draw(*this); + + draw.drawText((SkUnicodeWalkerProc)SkUTF8_NextUnichar, text, byteLength, x, y, paint); +} + +void SkCanvas::drawText16(const U16 text[], size_t numberOf16BitValues, SkScalar x, SkScalar y, const SkPaint& paint) +{ + AutoPaintSetClear force(paint, fMCRec->fSetPaintBits, fMCRec->fClearPaintBits); + SkAutoBounderCommit ac(fBounder); + SkDraw draw(*this); + + draw.drawText((SkUnicodeWalkerProc)SkUTF16_NextUnichar, (const char*)text, numberOf16BitValues << 1, x, y, paint); +} + +void SkCanvas::drawPosText(const char text[], size_t byteLength, const SkPoint pos[], const SkPaint& paint) +{ + AutoPaintSetClear force(paint, fMCRec->fSetPaintBits, fMCRec->fClearPaintBits); + SkAutoBounderCommit ac(fBounder); + SkDraw draw(*this); + + draw.drawPosText((SkUnicodeWalkerProc)SkUTF8_NextUnichar, text, byteLength, pos, paint); +} + +void SkCanvas::drawPosText16(const U16 text[], size_t numberOf16BitValues, const SkPoint pos[], const SkPaint& paint) +{ + AutoPaintSetClear force(paint, fMCRec->fSetPaintBits, fMCRec->fClearPaintBits); + SkAutoBounderCommit ac(fBounder); + SkDraw draw(*this); + + draw.drawPosText((SkUnicodeWalkerProc)SkUTF16_NextUnichar, (const char*)text, numberOf16BitValues << 1, pos, paint); +} + +void SkCanvas::drawTextOnPath(const char text[], size_t byteLength, const SkPath& path, SkScalar offset, const SkPaint& paint) +{ + AutoPaintSetClear force(paint, fMCRec->fSetPaintBits, fMCRec->fClearPaintBits); + SkAutoBounderCommit ac(fBounder); + SkDraw draw(*this); + + draw.drawTextOnPath((SkUnicodeWalkerProc)SkUTF8_NextUnichar, text, byteLength, path, offset, paint); +} + +void SkCanvas::drawText16OnPath(const U16 text[], size_t numberOf16BitValues, const SkPath& path, SkScalar offset, const SkPaint& paint) +{ + AutoPaintSetClear force(paint, fMCRec->fSetPaintBits, fMCRec->fClearPaintBits); + SkAutoBounderCommit ac(fBounder); + SkDraw draw(*this); + + draw.drawTextOnPath((SkUnicodeWalkerProc)SkUTF16_NextUnichar, (const char*)text, numberOf16BitValues << 1, path, offset, paint); +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////// + +const SkBitmap& SkCanvas::getCurrBitmap() const +{ + return *fMCRec->fCurrBitmap; +} + +SkMatrix::MapPtProc SkCanvas::getCurrMapPtProc() const +{ + if (fMCRec->fMapPtProc == NULL) + fMCRec->fMapPtProc = fMCRec->fMatrix.getMapPtProc(); + + return fMCRec->fMapPtProc; +} + +const SkMatrix& SkCanvas::getTotalMatrix() const +{ + return fMCRec->fMatrix; +} + +const SkRegion& SkCanvas::getTotalClip() const +{ + return fMCRec->fRegion; +} + diff --git a/libs/graphics/sgl/SkColor.cpp b/libs/graphics/sgl/SkColor.cpp new file mode 100644 index 0000000000..3831c575b0 --- /dev/null +++ b/libs/graphics/sgl/SkColor.cpp @@ -0,0 +1,118 @@ +#include "SkColor.h" +#include "SkColorPriv.h" + +SkPMColor SkPreMultiplyARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) +{ + if (a != 255) + { + unsigned scale = SkAlpha255To256(a); + r = SkAlphaMul(r, scale); + g = SkAlphaMul(g, scale); + b = SkAlphaMul(b, scale); + } + return SkPackARGB32(a, r, g, b); +} + +SkPMColor SkPreMultiplyColor(SkColor c) +{ + unsigned a = SkColorGetA(c); + unsigned r = SkColorGetR(c); + unsigned g = SkColorGetG(c); + unsigned b = SkColorGetB(c); + + return SkPreMultiplyARGB(a, r, g, b); +} + +////////////////////////////////////////////////////////////////////////////////////////////// + +static inline SkScalar ByteToScalar(U8CPU x) +{ + SkASSERT(x <= 255); + return SkIntToScalar(x) / 255; +} + +static inline SkScalar ByteDivToScalar(int numer, U8CPU denom) +{ + // cast to keep the answer signed + return SkIntToScalar(numer) / (int)denom; +} + +void SkRGBToHSV(U8CPU r, U8CPU g, U8CPU b, SkScalar hsv[3]) +{ + SkASSERT(hsv); + + unsigned min = SkMin32(r, SkMin32(g, b)); + unsigned max = SkMax32(r, SkMax32(g, b)); + unsigned delta = max - min; + + SkScalar v = ByteToScalar(max); + SkASSERT(v >= 0 && v <= SK_Scalar1); + + if (0 == delta) // we're a shade of gray + { + hsv[0] = hsv[1] = hsv[2] = v; + return; + } + + SkScalar s = ByteDivToScalar(delta, max); + SkASSERT(s >= 0 && s <= SK_Scalar1); + + SkScalar h; + if (r == max) + h = ByteDivToScalar(g - b, delta); + else if (g == max) + h = SkIntToScalar(2) + ByteDivToScalar(b - r, delta); + else // b == max + h = SkIntToScalar(4) + ByteDivToScalar(r - g, delta); + + h *= 60; + if (h < 0) + h += SkIntToScalar(360); + SkASSERT(h >= 0 && h < SkIntToScalar(360)); + + hsv[0] = h; + hsv[1] = s; + hsv[2] = v; +} + +static inline U8CPU UnitScalarToByte(SkScalar x) +{ + if (x < 0) + return 0; + if (x >= SK_Scalar1) + return 255; + return SkScalarToFixed(x) >> 8; +} + +SkColor SkHSVToColor(U8CPU a, const SkScalar hsv[3]) +{ + SkASSERT(hsv); + + U8CPU s = UnitScalarToByte(hsv[1]); + U8CPU v = UnitScalarToByte(hsv[2]); + + if (0 == s) // shade of gray + return SkColorSetARGB(a, v, v, v); + + SkFixed hx = (hsv[0] < 0 || hsv[0] >= SkIntToScalar(360)) ? 0 : SkScalarToFixed(hsv[0]/60); + SkFixed f = hx & 0xFFFF; + + unsigned v_scale = SkAlpha255To256(v); + unsigned p = SkAlphaMul(255 - s, v_scale); + unsigned q = SkAlphaMul(255 - (s * f >> 16), v_scale); + unsigned t = SkAlphaMul(255 - (s * (SK_Fixed1 - f) >> 16), v_scale); + + unsigned r, g, b; + + SkASSERT((unsigned)(hx >> 16) < 6); + switch (hx >> 16) { + case 0: r = v; g = t; b = p; break; + case 1: r = q; g = v; b = p; break; + case 2: r = p; g = v; b = t; break; + case 3: r = p; g = q; b = v; break; + case 4: r = t; g = p; b = v; break; + default: r = v; g = p; b = q; break; + } + return SkColorSetARGB(a, r, g, b); +} + diff --git a/libs/graphics/sgl/SkColorFilter.cpp b/libs/graphics/sgl/SkColorFilter.cpp new file mode 100644 index 0000000000..dc89eb4e33 --- /dev/null +++ b/libs/graphics/sgl/SkColorFilter.cpp @@ -0,0 +1,36 @@ +#include "SkColorFilter.h" +#include "SkShader.h" + +void SkColorFilter::filterSpan(const SkPMColor src[], int count, SkPMColor result[]) +{ + memcpy(result, src, count * sizeof(SkPMColor)); +} + +////////////////////////////////////////////////////////////////////////////////////// + +SkFilterShader::SkFilterShader(SkShader* shader, SkColorFilter* filter) +{ + fShader = shader; shader->ref(); + fFilter = filter; filter->ref(); +} + +SkFilterShader::~SkFilterShader() +{ + fFilter->unref(); + fShader->unref(); +} + +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); +} + diff --git a/libs/graphics/sgl/SkColorTable.cpp b/libs/graphics/sgl/SkColorTable.cpp new file mode 100644 index 0000000000..2904a8feba --- /dev/null +++ b/libs/graphics/sgl/SkColorTable.cpp @@ -0,0 +1,99 @@ +#include "SkBitmap.h" +#include "SkTemplates.h" + +SkColorTable::SkColorTable() : fColors(nil), f16BitCache(nil), fCount(0), fFlags(0) +{ + 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::setColors(const SkPMColor src[], int count) +{ + SkASSERT(fColorLockCount == 0); + SkASSERT((unsigned)count <= 256); + + if (fCount != count) + { + if (count == 0) + { + sk_free(fColors); + fColors = nil; + } + else + { + // allocate new array before freeing old, in case the alloc fails (throws) + SkPMColor* table = (SkPMColor*)sk_malloc_throw(count * sizeof(SkPMColor)); + sk_free(fColors); + fColors = table; + + if (src) + memcpy(fColors, src, count * sizeof(SkPMColor)); + } + fCount = SkToU16(count); + } + else + { + if (src) + memcpy(fColors, src, count * sizeof(SkPMColor)); + } + + this->inval16BitCache(); +} + +void SkColorTable::inval16BitCache() +{ + SkASSERT(f16BitCacheLockCount == 0); + if (f16BitCache) + { + sk_free(f16BitCache); + f16BitCache = nil; + } +} + +#include "SkColorPriv.h" + +static inline void build_16bitcache(U16 dst[], const SkPMColor src[], int count) +{ + while (--count >= 0) + *dst++ = SkPixel32ToPixel16_ToU16(*src++); +} + +const U16* SkColorTable::lock16BitCache() +{ + if (fFlags & kColorsAreOpaque_Flag) + { + if (f16BitCache == nil) // build the cache + { + f16BitCache = (U16*)sk_malloc_throw(fCount * sizeof(U16)); + build_16bitcache(f16BitCache, fColors, fCount); + } + } + else // our colors have alpha, so no cache + { + this->inval16BitCache(); + if (f16BitCache) + { + sk_free(f16BitCache); + f16BitCache = nil; + } + } + + SkDEBUGCODE(f16BitCacheLockCount += 1); + return f16BitCache; +} + + diff --git a/libs/graphics/sgl/SkCoreBlitters.h b/libs/graphics/sgl/SkCoreBlitters.h new file mode 100644 index 0000000000..ebf339a50b --- /dev/null +++ b/libs/graphics/sgl/SkCoreBlitters.h @@ -0,0 +1,177 @@ +#ifndef SkCoreBlitters_DEFINED +#define SkCoreBlitters_DEFINED + +#include "SkBlitter.h" + +class SkA8_Blitter : public SkBlitter { +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 S16 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 SkRect16&); + +private: + const SkBitmap& fDevice; + unsigned fSrcA; + + // illegal + SkA8_Blitter& operator=(const SkA8_Blitter&); +}; + +class SkA8_Shader_Blitter : public SkBlitter { +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 S16 runs[]); + virtual void blitMask(const SkMask&, const SkRect16&); + +private: + const SkBitmap& fDevice; + SkShader* fShader; + SkXfermode* fXfermode; + SkPMColor* fBuffer; + U8* fAAExpand; + + typedef SkBlitter INHERITED; + + // illegal + SkA8_Shader_Blitter& operator=(const SkA8_Shader_Blitter&); +}; + +//////////////////////////////////////////////////////////////// + +class SkARGB32_Blitter : public SkBlitter { +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 S16 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 SkRect16&); + +protected: + const SkBitmap& fDevice; + +private: + SkColor fPMColor; + unsigned fSrcA, fSrcR, fSrcG, fSrcB; + + // illegal + SkARGB32_Blitter& operator=(const SkARGB32_Blitter&); +}; + +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 SkRect16&); +}; + +class SkARGB32_Shader_Blitter : public SkBlitter { +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 S16 runs[]); + +private: + const SkBitmap& fDevice; + SkShader* fShader; + SkXfermode* fXfermode; + SkPMColor* fBuffer; + + typedef SkBlitter INHERITED; + + // illegal + SkARGB32_Shader_Blitter& operator=(const SkARGB32_Shader_Blitter&); +}; + +//////////////////////////////////////////////////////////////// + +class SkRGB16_Blitter : public SkBlitter { +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 S16 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 SkRect16&); + +protected: + const SkBitmap& fDevice; + +private: + unsigned fScale; + U16 fColor16; + U16 fRawColor16; + + // illegal + SkRGB16_Blitter& operator=(const SkRGB16_Blitter&); +}; + +class SkRGB16_Black_Blitter : public SkRGB16_Blitter { +public: + SkRGB16_Black_Blitter(const SkBitmap& device, const SkPaint& paint) + : SkRGB16_Blitter(device, paint) {} + virtual void blitMask(const SkMask&, const SkRect16&); +}; + +class SkRGB16_Shader_Blitter : public SkBlitter { +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 S16 runs[]); + +private: + const SkBitmap& fDevice; + SkShader* fShader; + SkPMColor* fBuffer; + + typedef SkBlitter INHERITED; + + // illegal + SkRGB16_Shader_Blitter& operator=(const SkRGB16_Shader_Blitter&); +}; + +class SkRGB16_Shader_Xfermode_Blitter : public SkBlitter { +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 S16 runs[]); + +private: + const SkBitmap& fDevice; + SkShader* fShader; + SkXfermode* fXfermode; + SkPMColor* fBuffer; + U8* fAAExpand; + + typedef SkBlitter INHERITED; + + // illegal + SkRGB16_Shader_Xfermode_Blitter& operator=(const SkRGB16_Shader_Xfermode_Blitter&); +}; + +///////////////////////////////////////////////////////////////////////////// + +class SkA1_Blitter : public SkBlitter { +public: + SkA1_Blitter(const SkBitmap& device, const SkPaint& paint); + virtual void blitH(int x, int y, int width); + +private: + const SkBitmap& fDevice; + U8 fSrcA; + + // illegal + SkA1_Blitter& operator=(const SkA1_Blitter&); +}; + + +#endif + diff --git a/libs/graphics/sgl/SkDeque.cpp b/libs/graphics/sgl/SkDeque.cpp new file mode 100644 index 0000000000..d2ccfeb992 --- /dev/null +++ b/libs/graphics/sgl/SkDeque.cpp @@ -0,0 +1,389 @@ +#include "SkDeque.h" + +#define INIT_ELEM_COUNT 1 // should we let the caller set this in the constructor? + +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 = nil; + fBegin = fEnd = nil; + fStop = (char*)this + size; + } +}; + +SkDeque::SkDeque(size_t elemSize) : fElemSize(elemSize), fInitialStorage(nil), fCount(0) +{ + fFront = fBack = nil; +} + +SkDeque::SkDeque(size_t elemSize, void* storage, size_t storageSize) + : fElemSize(elemSize), fInitialStorage(storage), fCount(0) +{ + SkASSERT(storageSize == 0 || storage != nil); + + if (storageSize >= sizeof(Head) + elemSize) + { + fFront = (Head*)storage; + fFront->init(storageSize); + } + else + { + fFront = nil; + } + 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 (front == nil) + return nil; + + if (front->fBegin == nil) + { + front = front->fNext; + if (front == nil) + return nil; + } + SkASSERT(front->fBegin); + return front->fBegin; +} + +const void* SkDeque::back() const +{ + Head* back = fBack; + + if (back == nil) + return nil; + + if (back->fEnd == nil) // marked as deleted + { + back = back->fPrev; + if (back == nil) + return nil; + } + SkASSERT(back->fEnd); + return back->fEnd - fElemSize; +} + +void* SkDeque::push_front() +{ + fCount += 1; + + if (fFront == nil) + { + 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 (first->fBegin == nil) + { + 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 (fBack == nil) + { + 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 (last->fBegin == nil) + { + 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 != nil); + + if (first->fBegin == nil) // we were marked empty from before + { + first = first->fNext; + first->fPrev = nil; + sk_free(fFront); + fFront = first; + SkASSERT(first != nil); // 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 = nil; // mark as empty +} + +void SkDeque::pop_back() +{ + SkASSERT(fCount > 0); + fCount -= 1; + + Head* last = fBack; + + SkASSERT(last != nil); + + if (last->fEnd == nil) // we were marked empty from before + { + last = last->fPrev; + last->fNext = nil; + sk_free(fBack); + fBack = last; + SkASSERT(last != nil); // 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 = nil; // mark as empty +} + +/////////////////////////////////////////////////////////////////////////////// + +SkDeque::Iter::Iter(const SkDeque& d) : fElemSize(d.fElemSize) +{ + fHead = d.fFront; + while (fHead != nil && fHead->fBegin == nil) + fHead = fHead->fNext; + fPos = fHead ? fHead->fBegin : nil; +} + +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 != nil && fHead->fBegin == nil); + next = fHead ? fHead->fBegin : nil; + } + fPos = next; + } + return pos; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_DEBUG + +SK_SET_BASIC_TRAITS(void); +SK_SET_BASIC_TRAITS(bool); +SK_SET_BASIC_TRAITS(char); +SK_SET_BASIC_TRAITS(unsigned char); +SK_SET_BASIC_TRAITS(short); +SK_SET_BASIC_TRAITS(unsigned short); +SK_SET_BASIC_TRAITS(int); +SK_SET_BASIC_TRAITS(unsigned int); +SK_SET_BASIC_TRAITS(long); +SK_SET_BASIC_TRAITS(unsigned long); +SK_SET_BASIC_TRAITS(float); +SK_SET_BASIC_TRAITS(double); + +#include <new> + +static size_t gTestClassCount; + +//#define SPEW_LIFETIME + +class TestClass { +public: + TestClass() + { + ++gTestClassCount; +#ifdef SPEW_LIFETIME + SkDebugf("gTestClassCount=%d\n", gTestClassCount); +#endif + } + TestClass(int value) : fValue(value) + { + ++gTestClassCount; +#ifdef SPEW_LIFETIME + SkDebugf("gTestClassCount=%d\n", gTestClassCount); +#endif + } + TestClass(const TestClass& src) : fValue(src.fValue) + { + ++gTestClassCount; +#ifdef SPEW_LIFETIME + SkDebugf("gTestClassCount=%d\n", gTestClassCount); +#endif + } + ~TestClass() + { + --gTestClassCount; +#ifdef SPEW_LIFETIME + SkDebugf("~gTestClassCount=%d\n", gTestClassCount); +#endif + } + int fValue; +}; + +SK_SET_TYPE_TRAITS(TestClass, false, false, false, true); + +void SkDeque::UnitTest() +{ + { + SkTDeque<int> d1; + SkTDeque<int> d2; + SkTDeque<TestClass> d3; + SkSTDeque<5, TestClass> d4; + + int i; + + SkASSERT(d1.empty()); + SkASSERT(d2.empty()); + SkASSERT(d3.empty()); + SkASSERT(d4.empty()); + + for (i = 0; i < 100; i++) + { + d1.push_front(i); + SkASSERT(*d1.front() == i); + SkASSERT(*d1.back() == 0); + + d2.push_back(i); + SkASSERT(*d2.back() == i); + SkASSERT(*d2.front() == 0); + + d3.push_front(TestClass(i)); + d3.push_back(TestClass(-i)); + SkASSERT(d3.front()->fValue == i); + SkASSERT(d3.back()->fValue == -i); + + d4.push_front()->fValue = i; + d4.push_back()->fValue = -i; + SkASSERT(d4.front()->fValue == i); + SkASSERT(d4.back()->fValue == -i); + } + + SkASSERT(d1.count() == 100); + SkASSERT(d2.count() == 100); + SkASSERT(d3.count() == 200); + SkASSERT(d4.count() == 200); + + { + SkTDeque<int>::Iter iter(d2); + int* curr; + + i = 0; + while ((curr = iter.next()) != nil) { + SkASSERT(*curr == i); + i += 1; + } + SkASSERT(i == 100); + } + + for (i = 0; i < 50; i++) + { + d1.pop_front(); d1.pop_back(); + d2.pop_front(); d2.pop_back(); + d3.pop_front(); d3.pop_back(); + d4.pop_front(); d4.pop_back(); + } + + SkASSERT(d1.count() == 0); + SkASSERT(d2.count() == 0); + SkASSERT(d3.count() == 100); + SkASSERT(d4.count() == 100); + } + int counter = gTestClassCount; + SkASSERT(counter == 0); +} + +#endif + diff --git a/libs/graphics/sgl/SkDraw.cpp b/libs/graphics/sgl/SkDraw.cpp new file mode 100644 index 0000000000..9b1ed985ef --- /dev/null +++ b/libs/graphics/sgl/SkDraw.cpp @@ -0,0 +1,1139 @@ +#include "SkDraw.h" +#include "SkBlitter.h" +#include "SkBounder.h" +#include "SkCanvas.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 "SkTextLayout.h" + +/** Helper for allocating small blitters on the stack. +*/ + +#define kBlitterStorageLongCount 40 + +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, false, paint->getFilterType(), + 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 SkCanvas& canvas) +{ + fDevice = &canvas.getCurrBitmap(); + fMatrix = &canvas.getTotalMatrix(); + fClip = &canvas.getTotalClip(); + fBounder = canvas.getBounder(); + fMapPtProc = canvas.getCurrMapPtProc(); + + SkDEBUGCODE(this->validate();) +} + +void SkDraw::drawPaint(const SkPaint& paint) +{ + SkDEBUGCODE(this->validate();) + + if (fClip->isEmpty()) + return; + + SkRect16 devRect; + devRect.set(0, 0, fDevice->width(), fDevice->height()); + + if (fBounder && !fBounder->doIRect(devRect, *fClip)) + return; + + SkAutoBlitterChoose blitter(*fDevice, *fMatrix, paint); + SkScan::FillDevRect(devRect, fClip, blitter.get()); +} + +void SkDraw::drawLine(const SkPoint& start, const SkPoint& stop, const SkPaint& paint) +{ + SkDEBUGCODE(this->validate();) + + if (fClip->isEmpty() || + (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) // nothing to draw + return; + + if (!paint.getPathEffect() && !paint.getMaskFilter() && + !paint.getRasterizer() && paint.getStrokeWidth() == 0) // hairline + { + SkPoint pts[2]; + fMapPtProc(*fMatrix, start.fX, start.fY, &pts[0]); + fMapPtProc(*fMatrix, stop.fX, stop.fY, &pts[1]); + + if (fBounder && !fBounder->doHairline(pts[0], pts[1], paint, *fClip)) + return; + + SkAutoBlitterChoose blitter(*fDevice, *fMatrix, paint); + + if (paint.isAntiAliasOn()) + SkScan::AntiHairLine(pts[0], pts[1], fClip, blitter.get()); + else + SkScan::HairLine(pts[0], pts[1], fClip, blitter.get()); + } + else + { + SkPath path; + // temporarily mark the paint as framing + SkAutoPaintStyleRestore restore(paint, SkPaint::kStroke_Style); + + path.moveTo(start.fX, start.fY); + path.lineTo(stop.fX, stop.fY); + this->drawPath(path, paint); + } +} + +void SkDraw::drawRect(const SkRect& rect, const SkPaint& paint) +{ + SkDEBUGCODE(this->validate();) + + if (fClip->isEmpty() || + (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) // nothing to draw + return; + + if (paint.getPathEffect() || paint.getMaskFilter() || paint.getRasterizer() || + !fMatrix->rectStaysRect() || (paint.getStyle() != SkPaint::kFill_Style && (paint.getStrokeWidth() / 2) > 0)) + { + SkPath tmp; + tmp.addRect(rect); + tmp.setFillType(SkPath::kWinding_FillType); + this->drawPath(tmp, paint); + return; + } + + SkRect devRect; + fMapPtProc(*fMatrix, rect.fLeft, rect.fTop, (SkPoint*)&devRect.fLeft); + fMapPtProc(*fMatrix, rect.fRight, rect.fBottom, (SkPoint*)&devRect.fRight); + devRect.sort(); + + if (fBounder && !fBounder->doRect(devRect, paint, *fClip)) + return; + + SkAutoBlitterChoose blitter(*fDevice, *fMatrix, paint); + + if (paint.getStyle() == SkPaint::kFill_Style) + SkScan::FillRect(devRect, fClip, blitter.get()); + else + { + if (paint.isAntiAliasOn()) + SkScan::AntiHairRect(devRect, fClip, blitter.get()); + else + SkScan::HairRect(devRect, fClip, blitter.get()); + } +} + +void SkDraw::drawDevMask(const SkMask& srcM, const SkPaint& paint) +{ + 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, *fClip)) + return; + + SkAutoBlitterChoose blitter(*fDevice, *fMatrix, paint); + + blitter->blitMaskRegion(*mask, *fClip); +} + +void SkDraw::drawPath(const SkPath& origSrcPath, const SkPaint& paint, const SkMatrix* prePathMatrix, bool srcPathIsMutable) +{ + SkDEBUGCODE(this->validate();) + + if (fClip->isEmpty() || + (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) // nothing to draw + 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 (!srcPathIsMutable) + { + result = &tmpPath; + srcPathIsMutable = true; + } + pathPtr->transform(*prePathMatrix, result); + pathPtr = result; + } + else + { + tmpMatrix.setConcat(*matrix, *prePathMatrix); + matrix = &tmpMatrix; + } + } + // at this point we're done with prePathMatrix + SkDEBUGCODE(prePathMatrix = (const SkMatrix*)0x50FF8001;) + + 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 = srcPathIsMutable ? pathPtr : &tmpPath; + + // transform the path into device space + if (!pathPtr->transform(*matrix, devPathPtr)) + return; + + SkAutoBlitterChoose blitter(*fDevice, *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, *fClip, doFill)) + return; + + if (doFill) + { + if (paint.isAntiAliasOn()) + SkScan::AntiFillPath(*devPathPtr, fClip, blitter.get()); + else + SkScan::FillPath(*devPathPtr, fClip, blitter.get()); + } + else // hairline + { + if (paint.isAntiAliasOn()) + 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::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint& paint) +{ + SkDEBUGCODE(this->validate();) + + if (fClip->isEmpty() || + bitmap.width() == 0 || bitmap.height() == 0 || bitmap.getConfig() == SkBitmap::kNo_Config || + (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) // nothing to draw + return; + + SkAutoPaintStyleRestore restore(paint, SkPaint::kFill_Style); + + SkMatrix matrix = *fMatrix; + matrix.preTranslate(x, y); + + if (NULL == paint.getColorFilter() && just_translate(matrix)) + { + int ix = SkScalarRound(matrix.getTranslateX()); + int iy = SkScalarRound(matrix.getTranslateY()); + U32 storage[kBlitterStorageLongCount]; + SkBlitter* blitter = SkBlitter::ChooseSprite(*fDevice, paint, bitmap, ix, iy, storage, sizeof(storage)); + if (blitter) + { + SkAutoTPlacementDelete<SkBlitter> ad(blitter, storage); + + SkRect16 ir; + ir.set(ix, iy, ix + bitmap.width(), iy + bitmap.height()); + + if (fBounder && !fBounder->doIRect(ir, *fClip)) + return; + + SkRegion::Cliperator iter(*fClip, ir); + const SkRect16& cr = iter.rect(); + + for (; !iter.done(); iter.next()) + { + SkASSERT(!cr.isEmpty()); + #if 0 + LOGI("blitRect(%d %d %d %d) [%d %d %p]\n", cr.fLeft, cr.fTop, cr.width(), cr.height(), + bitmap.width(), bitmap.height(), bitmap.getPixels()); + #endif + blitter->blitRect(cr.fLeft, cr.fTop, cr.width(), cr.height()); + } + return; + } + } + + SkAutoBitmapShaderInstall install(bitmap, &paint); + + // save our state + const SkMatrix* saveMatrix = fMatrix; + SkMatrix::MapPtProc saveProc = fMapPtProc; + + // jam in the new temp state + fMatrix = &matrix; + fMapPtProc = matrix.getMapPtProc(); + + // call ourself with a rect + { + SkRect r; + r.set(0, 0, SkIntToScalar(bitmap.width()), SkIntToScalar(bitmap.height())); + // is this ok if paint has a rasterizer? <reed> + this->drawRect(r, paint); + } + + // restore our state + fMapPtProc = saveProc; + fMatrix = saveMatrix; +} + +void SkDraw::drawSprite(const SkBitmap& bitmap, int x, int y, const SkPaint& paint) +{ + SkDEBUGCODE(this->validate();) + + SkRect16 bounds; + bounds.set(x, y, x + bitmap.width(), y + bitmap.height()); + + if (fClip->quickReject(bounds) || + bitmap.getConfig() == SkBitmap::kNo_Config || + (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) + { + return; // nothing to draw + } + + SkAutoPaintStyleRestore restore(paint, SkPaint::kFill_Style); + + if (NULL == paint.getColorFilter()) + { + uint32_t storage[kBlitterStorageLongCount]; + SkBlitter* blitter = SkBlitter::ChooseSprite(*fDevice, paint, bitmap, x, y, storage, sizeof(storage)); + + if (blitter) + { + SkAutoTPlacementDelete<SkBlitter> ad(blitter, storage); + + if (fBounder && !fBounder->doIRect(bounds, *fClip)) + return; + + SkRegion::Cliperator iter(*fClip, bounds); + const SkRect16& 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); + + // save our state + const SkMatrix* saveMatrix = fMatrix; + SkMatrix::MapPtProc saveProc = fMapPtProc; + 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); + + // jam in the new temp state + matrix.reset(); + fMatrix = &matrix; + fMapPtProc = matrix.getMapPtProc(); + + // call ourself with a rect + { + // is this OK if paint has a rasterizer? <reed> + this->drawRect(r, paint); + } + + // restore our state + fMapPtProc = saveProc; + fMatrix = saveMatrix; +} + +/////////////////////////////////////////////////////////////////////////////////// + +#include "SkScalerContext.h" +#include "SkGlyphCache.h" +#include "SkUtils.h" + +static void measure_text(SkGlyphCache* cache, SkUnicodeWalkerProc textProc, const char text[], + size_t byteLength, SkVector* stopVector) +{ + SkFixed x = 0, y = 0; + const char* stop = text + byteLength; + + while (text < stop) + { + const SkGlyph& glyph = cache->getMetrics(textProc(&text)); + x += glyph.fAdvanceX; + y += glyph.fAdvanceY; + } + stopVector->set(SkFixedToScalar(x), SkFixedToScalar(y)); + + SkASSERT(text == stop); +} + +static void measure_layout(SkGlyphCache* cache, const SkTextLayout::Rec rec[], int count, + const SkMatrix& matrix, SkVector* stopVector) +{ + SkFixed x = 0, y = 0; + + for (int i = 0; i < count; i++) + { + // should pass glyphID to the cache, when we have that + const SkGlyph& glyph = cache->getMetrics(rec[i].charCode()); + SkVector adv; + adv.set(rec[i].fDeltaAdvance, 0); + matrix.mapVectors(&adv, 1); + x += glyph.fAdvanceX + SkScalarToFixed(adv.fX); + y += glyph.fAdvanceY + SkScalarToFixed(adv.fY); + } + stopVector->set(SkFixedToScalar(x), SkFixedToScalar(y)); +} + +void SkDraw::drawText_asPaths(SkUnicodeWalkerProc textProc, const char text[], size_t byteLength, SkScalar x, SkScalar y, const SkPaint& paint) +{ + SkDEBUGCODE(this->validate();) + + SkTextToPathIter iter(textProc, text, byteLength, paint, true, true); + + SkMatrix matrix; + matrix.setScale(iter.getPathScale(), iter.getPathScale(), 0, 0); + 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(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(SkDraw* draw, const SkPaint& paint, SkScalar width, const SkPoint& start) +{ + U32 flags = paint.getFlags(); + + if (flags & (SkPaint::kUnderlineText_Mask | SkPaint::kStrikeThruText_Mask)) + { + 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_Mask) + { + SkScalar offset = start.fY + SkScalarMul(textSize, kStdUnderline_Offset); + r.fTop = offset; + r.fBottom = offset + height; + draw_paint_rect(draw, paint, r, textSize); + } + if (flags & SkPaint::kStrikeThruText_Mask) + { + SkScalar offset = start.fY + SkScalarMul(textSize, kStdStrikeThru_Offset); + r.fTop = offset; + r.fBottom = offset + height; + draw_paint_rect(draw, paint, r, textSize); + } + } +} + +#if defined _WIN32 && _MSC_VER >= 1300 // disable warning : local variable used without having been initialized +#pragma warning ( push ) +#pragma warning ( disable : 4701 ) +#endif + +static inline void draw_one_glyph(const SkGlyph& glyph, int left, int top, SkBounder* bounder, + const SkRegion& clip, SkBlitter* blitter, SkGlyphCache* cache) +{ + SkMask mask; + + int right = left + glyph.fWidth; + int bottom = top + glyph.fHeight; + + mask.fBounds.set(left, top, right, bottom); + + if (bounder == NULL && clip.quickContains(left, top, right, bottom)) + { + uint8_t* aa = (uint8_t*)glyph.fImage; + if (aa == NULL) + aa = (uint8_t*)cache->findImage(glyph.fCharCode); + + if (aa) + { + mask.fRowBytes = glyph.fRowBytes; + mask.fFormat = glyph.fMaskFormat; + mask.fImage = aa; + blitter->blitMask(mask, mask.fBounds); + } + } + else + { + SkRegion::Cliperator clipper(clip, mask.fBounds); + if (!clipper.done()) + { + const SkRect16& cr = clipper.rect(); + const uint8_t* aa = (const uint8_t*)glyph.fImage; + if (NULL == aa) + aa = (const uint8_t*)cache->findImage(glyph.fCharCode); + + if (aa && (bounder == NULL || bounder->doIRect(cr, clip))) + { + mask.fRowBytes = glyph.fRowBytes; + mask.fFormat = glyph.fMaskFormat; + mask.fImage = (uint8_t*)aa; + do { + blitter->blitMask(mask, cr); + clipper.next(); + } while (!clipper.done()); + } + } + } +} + +void SkDraw::drawText(SkUnicodeWalkerProc textProc, const char text[], size_t byteLength, SkScalar x, SkScalar y, const SkPaint& paint) +{ + SkASSERT(byteLength == 0 || text != NULL); + + SkDEBUGCODE(this->validate();) + + if (text == NULL || byteLength == 0 || + fClip->isEmpty() || + (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) // nothing to draw + return; + + SkASSERT(textProc); + + SkScalar underlineWidth = 0; + SkPoint underlineStart; + + if (paint.getFlags() & (SkPaint::kUnderlineText_Mask | SkPaint::kStrikeThruText_Mask)) + { + underlineWidth = paint.privateMeasureText(textProc, text, byteLength, NULL, NULL); + + 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.isLinearTextOn() || + (fMatrix->getType() & SkMatrix::kPerspective_Mask)) + { + this->drawText_asPaths(textProc, text, byteLength, x, y, paint); + handle_aftertext(this, paint, underlineWidth, underlineStart); + return; + } + + SkAutoGlyphCache autoCache(paint, fMatrix); + SkGlyphCache* cache = autoCache.getCache(); + SkAutoBlitterChoose blitter(*fDevice, *fMatrix, paint); + SkTextLayout* layout = paint.getTextLayout(); + + // transform our starting point + { + SkPoint loc; + fMapPtProc(*fMatrix, x, y, &loc); + x = loc.fX; + y = loc.fY; + } + + if (paint.getTextAlign() != SkPaint::kLeft_Align) // need to measure first + { + SkVector stop; + + if (layout) + { + SkAutoSTMalloc<32, SkTextLayout::Rec> storage(byteLength); + SkTextLayout::Rec* rec = storage.get(); + + int count = layout->layout(paint, text, byteLength, textProc, rec); + measure_layout(cache, rec, count, *fMatrix, &stop); + } + else + measure_text(cache, textProc, 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; + } + + // add a half now so we can trunc rather than round in the loop + SkFixed fx = SkScalarToFixed(x) + SK_FixedHalf; + SkFixed fy = SkScalarToFixed(y) + SK_FixedHalf; + const char* stop = text + byteLength; + const SkRegion& clip = *fClip; + SkBounder* bounder = fBounder; + SkBlitter* blit = blitter.get(); + + if (layout) + { + SkAutoSTMalloc<32, SkTextLayout::Rec> storage(byteLength); + SkTextLayout::Rec* rec = storage.get(); + + int count = layout->layout(paint, text, byteLength, textProc, rec); + for (int i = 0; i < count; i++) + { + // should pass rec[i].glyphID when we have it + const SkGlyph& glyph = cache->getMetrics(rec[i].charCode()); + SkVector advance; + + advance.set(rec[i].fDeltaAdvance, 0); + fMatrix->mapVectors(&advance, 1); + SkFixed dx = SkScalarToFixed(advance.fX); + SkFixed dy = SkScalarToFixed(advance.fY); + + if (glyph.fWidth) { + draw_one_glyph( glyph, + SkFixedFloor(fx + (dx >> 1)) + glyph.fLeft, + SkFixedFloor(fy + (dy >> 1)) + glyph.fTop, + fBounder, *fClip, blit, cache); + } + fx += glyph.fAdvanceX + dx; + fy += glyph.fAdvanceY + dy; + } + } + else // no layout object + { + while (text < stop) + { + const SkGlyph& glyph = cache->getMetrics(textProc(&text)); + + if (glyph.fWidth) { + draw_one_glyph( glyph, + SkFixedFloor(fx) + glyph.fLeft, SkFixedFloor(fy) + glyph.fTop, + bounder, clip, blit, cache); + } + fx += glyph.fAdvanceX; + fy += glyph.fAdvanceY; + } + } + + if (underlineWidth) + { + autoCache.release(); // release this now to free up the RAM + handle_aftertext(this, paint, underlineWidth, underlineStart); + } +} + +typedef void (*AlignProc)(const SkPoint&, const SkGlyph&, SkPoint16*); + +static void leftAlignProc(const SkPoint& loc, const SkGlyph& glyph, SkPoint16* dst) +{ + dst->set(SkScalarRound(loc.fX) + glyph.fLeft, + SkScalarRound(loc.fY) + glyph.fTop); +} + +static void centerAlignProc(const SkPoint& loc, const SkGlyph& glyph, SkPoint16* dst) +{ + dst->set(SkFixedRound(SkScalarToFixed(loc.fX) - (glyph.fAdvanceX >> 1)) + glyph.fLeft, + SkFixedRound(SkScalarToFixed(loc.fY) - (glyph.fAdvanceY >> 1)) + glyph.fTop); +} + +static void rightAlignProc(const SkPoint& loc, const SkGlyph& glyph, SkPoint16* dst) +{ + dst->set(SkFixedRound(SkScalarToFixed(loc.fX) - glyph.fAdvanceX) + glyph.fLeft, + SkFixedRound(SkScalarToFixed(loc.fY) - glyph.fAdvanceY) + glyph.fTop); +} + +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]; +} + +void SkDraw::drawPosText(SkUnicodeWalkerProc textProc, const char text[], size_t byteLength, + const SkPoint pos[], const SkPaint& paint) +{ + SkASSERT(byteLength == 0 || text != NULL); + + SkDEBUGCODE(this->validate();) + + if (text == NULL || byteLength == 0 || + fClip->isEmpty() || + (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) // nothing to draw + return; + + SkASSERT(textProc); + + if (paint.isLinearTextOn() || + (fMatrix->getType() & SkMatrix::kPerspective_Mask)) + { +// this->drawText_asPaths(textProc, text, byteLength, x, y, paint); + return; + } + + SkAutoGlyphCache autoCache(paint, fMatrix); + SkGlyphCache* cache = autoCache.getCache(); + SkAutoBlitterChoose blitter(*fDevice, *fMatrix, paint); + + const char* stop = text + byteLength; + const SkRegion& clip = *fClip; + SkBounder* bounder = fBounder; + SkBlitter* blit = blitter.get(); + SkMatrix::MapPtProc mapPtProc = fMapPtProc; + const SkMatrix& matrix = *fMatrix; + AlignProc alignProc = pick_align_proc(paint.getTextAlign()); + + while (text < stop) + { + const SkGlyph& glyph = cache->getMetrics(textProc(&text)); + + if (glyph.fWidth) + { + SkPoint loc; + mapPtProc(matrix, pos->fX, pos->fY, &loc); + + SkPoint16 devLoc; + alignProc(loc, glyph, &devLoc); + + draw_one_glyph( glyph, devLoc.fX, devLoc.fY, + bounder, clip, blit, cache); + } + pos += 1; + } +} + +#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, SkScalar offset, SkScalar scale) +{ + for (int i = 0; i < count; i++) + { + SkPoint pos; + SkVector tangent; + + SkScalar sx = SkScalarMul(src[i].fX, scale) + offset; + SkScalar sy = SkScalarMul(src[i].fY, scale); + + meas.getPosTan(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 offset, SkScalar scale) +{ + 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, offset, scale); + 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, offset, scale); + dst->quadTo(dstP[0], dstP[1]); + break; + case SkPath::kCubic_Verb: + morphpoints(dstP, &srcP[1], 3, meas, offset, scale); + dst->cubicTo(dstP[0], dstP[1], dstP[2]); + break; + case SkPath::kClose_Verb: + dst->close(); + break; + default: + SkASSERT(!"unknown verb"); + break; + } + } +} + +void SkDraw::drawTextOnPath(SkUnicodeWalkerProc textProc, const char text[], size_t byteLength, + const SkPath& follow, SkScalar offset, const SkPaint& paint) +{ + SkASSERT(byteLength == 0 || text != NULL); + + if (text == NULL || byteLength == 0 || + fClip->getBounds().isEmpty() || + (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) // nothing to draw + return; + + SkTextToPathIter iter(textProc, text, byteLength, paint, true, true); + SkPathMeasure meas(follow, false); + + if (paint.getTextAlign() != SkPaint::kLeft_Align) // need to measure first + { + SkScalar pathLen = meas.getLength(); + if (paint.getTextAlign() == SkPaint::kCenter_Align) + pathLen = SkScalarHalf(pathLen); + offset += pathLen; + } + + const SkPath* iterPath; + SkScalar xpos; + while ((iterPath = iter.next(&xpos)) != NULL) + { + SkPath tmp; + morphpath(&tmp, *iterPath, meas, offset + xpos, iter.getPathScale()); + this->drawPath(tmp, iter.getPaint()); + } +} + +//////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_DEBUG + +void SkDraw::validate() const +{ + SkASSERT(fDevice != NULL); + SkASSERT(fMatrix != NULL); + SkASSERT(fClip != NULL); + + const SkRect16& cr = fClip->getBounds(); + SkRect16 br; + + br.set(0, 0, fDevice->width(), fDevice->height()); + SkASSERT(br.contains(cr)); +} + +#endif + +////////////////////////////////////////////////////////////////////////////////////////// + +bool SkBounder::doIRect(const SkRect16& r, const SkRegion& clip) +{ + SkRect16 rr; + return rr.intersect(clip.getBounds(), r) && this->onIRect(rr); +} + +bool SkBounder::doHairline(const SkPoint& pt0, const SkPoint& pt1, const SkPaint& paint, const SkRegion& clip) +{ + SkRect16 r; + SkScalar v0, v1; + + v0 = pt0.fX; + v1 = pt1.fX; + if (v0 > v1) + SkTSwap<SkScalar>(v0, v1); + r.fLeft = SkToS16(SkScalarFloor(v0)); + r.fRight = SkToS16(SkScalarCeil(v1)); + + v0 = pt0.fY; + v1 = pt1.fY; + if (v0 > v1) + SkTSwap<SkScalar>(v0, v1); + r.fTop = SkToS16(SkScalarFloor(v0)); + r.fBottom = SkToS16(SkScalarCeil(v1)); + + if (paint.isAntiAliasOn()) + r.inset(-1, -1); + + return this->doIRect(r, clip); +} + +bool SkBounder::doRect(const SkRect& rect, const SkPaint& paint, const SkRegion& clip) +{ + SkRect16 r; + + if (paint.getStyle() == SkPaint::kFill_Style) + rect.round(&r); + else + { + int rad = -1; + rect.roundOut(&r); + if (paint.isAntiAliasOn()) + rad = -2; + r.inset(rad, rad); + } + return this->doIRect(r, clip); +} + +bool SkBounder::doPath(const SkPath& path, const SkPaint& paint, const SkRegion& clip, bool doFill) +{ + SkRect bounds; + SkRect16 r; + + path.computeBounds(&bounds, SkPath::kFast_BoundsType); + + if (doFill) + bounds.round(&r); + else // hairline + bounds.roundOut(&r); + + if (paint.isAntiAliasOn()) + r.inset(-1, -1); + + return this->doIRect(r, clip); +} + +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 SkRect16* clipBounds, + SkMaskFilter* filter, const SkMatrix* filterMatrix, + SkRect16* bounds) +{ + if (devPath.isEmpty()) + return false; + + SkPoint16 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 && !SkRect16::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)) + { + SkRect16 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.fDevice = &bm; + draw.fClip = &clipRgn; + draw.fMatrix = &matrix; + draw.fMapPtProc = matrix.getMapPtProc(); + draw.fBounder = NULL; + paint.setAntiAliasOn(true); + draw.drawPath(devPath, paint); +} + +bool SkDraw::DrawToMask(const SkPath& devPath, const SkRect16* 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/libs/graphics/sgl/SkDraw.h b/libs/graphics/sgl/SkDraw.h new file mode 100644 index 0000000000..488fa793f0 --- /dev/null +++ b/libs/graphics/sgl/SkDraw.h @@ -0,0 +1,89 @@ +#ifndef SkDraw_DEFINED +#define SkDraw_DEFINED + +#include "SkBitmap.h" +#include "SkMask.h" +#include "SkMatrix.h" +#include "SkPaint.h" +#include "SkRect.h" + +class SkBounder; +class SkCanvas; +class SkPath; +class SkRegion; + +class SkDraw { +public: + SkDraw() {} + SkDraw(const SkCanvas&); + + void drawPaint(const SkPaint&); + void drawLine(const SkPoint& start, const SkPoint& stop, const SkPaint&); + void drawRect(const SkRect&, const SkPaint&); + /* To save on mallocs, we allow a flag that tells us that srcPath is mutable, so that we don't have to + make copies of it as we transform it. + */ + void drawPath(const SkPath& srcPath, const SkPaint&, const SkMatrix* prePathMatrix, bool srcPathIsMutable); + void drawBitmap(const SkBitmap&, SkScalar x, SkScalar y, const SkPaint&); + void drawSprite(const SkBitmap& bitmap, int x, int y, const SkPaint& paint); + void drawText(SkUnicodeWalkerProc, const char text[], size_t byteLength, SkScalar x, SkScalar y, const SkPaint& paint); + void drawPosText(SkUnicodeWalkerProc, const char text[], size_t byteLength, const SkPoint pos[], const SkPaint& paint); + void drawTextOnPath(SkUnicodeWalkerProc, const char text[], size_t byteLength, const SkPath& follow, + SkScalar offset, const SkPaint& paint); + + void drawPath(const SkPath& src, const SkPaint& paint) + { + this->drawPath(src, paint, NULL, false); + } + + /** Helper function that creates a mask from a path and an optional maskfilter. + Note however, that the resulting mask will not have been actually filtered, + that must be done afterwards (by calling filterMask). The maskfilter is provided + solely to assist in computing the mask's bounds (if the mode requests that). + */ + static bool DrawToMask(const SkPath& devPath, const SkRect16* clipBounds, + SkMaskFilter* filter, const SkMatrix* filterMatrix, + SkMask* mask, SkMask::CreateMode mode); + +private: + void drawText_asPaths(SkUnicodeWalkerProc, const char text[], size_t byteLength, SkScalar x, SkScalar y, const SkPaint&); + void drawDevMask(const SkMask& mask, const SkPaint&); + +#ifdef SK_DEBUG + void validate() const; +#endif + +public: + const SkBitmap* fDevice; // required + const SkMatrix* fMatrix; // required + const SkRegion* fClip; // required + SkMatrix::MapPtProc fMapPtProc; // required + SkBounder* fBounder; // optional +}; + +class SkTextToPathIter { +public: + SkTextToPathIter(SkUnicodeWalkerProc, const char text[], size_t length, const SkPaint&, + bool applyStrokeAndPathEffects, bool forceLinearTextOn); + ~SkTextToPathIter(); + + const SkPaint& getPaint() const { return fPaint; } + SkScalar getPathScale() const { return fScale; } + + const SkPath* next(SkScalar* xpos); //!< returns nil when there are no more paths + +private: + SkGlyphCache* fCache; + SkPaint fPaint; + SkScalar fScale, fPrevAdvance; + const char* fText; + const char* fStop; + SkUnicodeWalkerProc fTextProc; + + const SkPath* fPath; // returned in next + SkScalar fXPos; // accumulated xpos, returned in next +}; + +#endif + + diff --git a/libs/graphics/sgl/SkEdge.cpp b/libs/graphics/sgl/SkEdge.cpp new file mode 100644 index 0000000000..d5e5590635 --- /dev/null +++ b/libs/graphics/sgl/SkEdge.cpp @@ -0,0 +1,429 @@ +#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 pts[2], const SkRect16* clip, int shift) +{ + SkFDot6 x0, y0, x1, y1; + + { +#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); +#else + shift = 10 - shift; + x0 = pts[0].fX >> shift; + y0 = pts[0].fY >> shift; + x1 = pts[1].fX >> shift; + y1 = pts[1].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 (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 = SkToS16(top); + fLastY = SkToS16(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 = SkToS16(top); + fLastY = SkToS16(bot - 1); + + return 1; +} + +void SkEdge::chopLineWithClip(const SkRect16& clip) +{ + int top = fFirstY; + + SkASSERT(top < clip.fBottom); + + // clip the line to the top + if (top < clip.fTop) + { + SkASSERT(fLastY >= clip.fTop); + fX += fDX * (clip.fTop - top); + fFirstY = clip.fTop; + } +} + +///////////////////////////////////////////////////////////////////////// + +static inline SkFDot6 cheap_distance(SkFDot6 dx, SkFDot6 dy) +{ + dx = SkAbs32(dx); + dy = SkAbs32(dy); + // return max + min/2 + if (dx > dy) + dx += dy >> 1; + else + dx = dy + (dx >> 1); + return dx; +} + +static inline int diff_to_shift(SkFDot6 dx, SkFDot6 dy) +{ + // cheap calc of distance from center of p0-p2 to the center of the curve + SkFDot6 dist = cheap_distance(dx, dy); + + // shift down dist (it is currently in dot6) + // down by 5 should give us 1/2 pixel accuracy (assuming our dist is accurate...) + // this is chosen by heuristic: make it as big as possible (to minimize segments) + // ... but small enough so that our curves still look smooth + dist >>= 5; + + // each subdivision (shift value) cuts this dist (error) by 1/4 + return (32 - SkCLZ(dist)) >> 1; +} + +int SkQuadraticEdge::setQuadratic(const SkPoint pts[3], const SkRect16* clip, int shift) +{ + SkFDot6 x0, y0, x1, y1, x2, y2; + + { +#ifdef SK_SCALAR_IS_FLOAT + float scale = float(1 << (shift + 6)); + x0 = int(pts[0].fX * scale); + y0 = int(pts[0].fY * scale); + x1 = int(pts[1].fX * scale); + y1 = int(pts[1].fY * scale); + x2 = int(pts[2].fX * scale); + y2 = int(pts[2].fY * scale); +#else + shift = 10 - shift; + x0 = pts[0].fX >> shift; + y0 = pts[0].fY >> shift; + x1 = pts[1].fX >> shift; + y1 = pts[1].fY >> shift; + x2 = pts[2].fX >> shift; + y2 = pts[2].fY >> shift; +#endif + } + + int winding = 1; + if (y0 > y2) + { + SkTSwap(x0, x2); + SkTSwap(y0, y2); + winding = -1; + } + SkASSERT(y0 <= y1 && y1 <= y2); + + int top = SkFDot6Round(y0); + int bot = SkFDot6Round(y2); + + // are we a zero-height quad (line)? + if (top == bot) + return 0; + // are we completely above or below the clip? + if (clip && (top >= clip->fBottom || bot <= clip->fTop)) + return 0; + + // compute number of steps needed (1 << shift) + { + SkFDot6 dx = ((x1 << 1) - x0 - x2) >> 2; + SkFDot6 dy = ((y1 << 1) - y0 - y2) >> 2; + shift = diff_to_shift(dx, dy); + } + // need at least 1 subdivision for our bias trick + if (shift == 0) + shift = 1; + + fWinding = SkToS8(winding); + fCurveShift = SkToU8(shift); + fCurveCount = SkToS16(1 << shift); + + SkFixed A = SkFDot6ToFixed(x0 - x1 - x1 + x2); + SkFixed B = SkFDot6ToFixed(x1 - x0 + x1 - x0); + + fQx = SkFDot6ToFixed(x0); + fQDx = B + (A >> shift); // biased by shift + fQDDx = A >> (shift - 1); // biased by shift + + A = SkFDot6ToFixed(y0 - y1 - y1 + y2); + B = SkFDot6ToFixed(y1 - y0 + y1 - y0); + + fQy = SkFDot6ToFixed(y0); + fQDy = B + (A >> shift); // biased by shift + fQDDy = A >> (shift - 1); // biased by shift + + fQLastX = SkFDot6ToFixed(x2); + fQLastY = SkFDot6ToFixed(y2); + + if (clip) + { + do { + for (;!this->updateQuadratic();) + ; + } while (!this->intersectsClip(*clip)); + this->chopLineWithClip(*clip); + return 1; + } + return this->updateQuadratic(); +} + +int SkQuadraticEdge::updateQuadratic() +{ + int success; + int count = fCurveCount; + SkFixed oldx = fQx; + SkFixed oldy = fQy; + SkFixed newx, newy; + int shift = fCurveShift; + + SkASSERT(count > 0); + + do { + if (--count > 0) + { + newx = oldx + (fQDx >> shift); + fQDx += fQDDx; + newy = oldy + (fQDy >> shift); + fQDy += 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; + fCurveCount = SkToS16(count); + return success; +} + +///////////////////////////////////////////////////////////////////////// + +/* f(1/3) = (8a + 12b + 6c + d) / 27 + f(2/3) = (a + 6b + 12c + 8d) / 27 + + f(1/3)-b = (8a - 15b + 6c + d) / 27 + f(2/3)-c = (a + 6b - 15c + 8d) / 27 + + use 16/512 to approximate 1/27 +*/ +static SkFDot6 cubic_delta_from_line(SkFDot6 a, SkFDot6 b, SkFDot6 c, SkFDot6 d) +{ + SkFDot6 oneThird = ((a << 3) - ((b << 4) - b) + 6*c + d) * 19 >> 9; + SkFDot6 twoThird = (a + 6*b - ((c << 4) - c) + (d << 3)) * 19 >> 9; + + return SkMax32(SkAbs32(oneThird), SkAbs32(twoThird)); +} + +int SkCubicEdge::setCubic(const SkPoint pts[4], const SkRect16* clip, int shift) +{ + SkFDot6 x0, y0, x1, y1, x2, y2, x3, y3; + + { +#ifdef SK_SCALAR_IS_FLOAT + float scale = float(1 << (shift + 6)); + x0 = int(pts[0].fX * scale); + y0 = int(pts[0].fY * scale); + x1 = int(pts[1].fX * scale); + y1 = int(pts[1].fY * scale); + x2 = int(pts[2].fX * scale); + y2 = int(pts[2].fY * scale); + x3 = int(pts[3].fX * scale); + y3 = int(pts[3].fY * scale); +#else + shift = 10 - shift; + x0 = pts[0].fX >> shift; + y0 = pts[0].fY >> shift; + x1 = pts[1].fX >> shift; + y1 = pts[1].fY >> shift; + x2 = pts[2].fX >> shift; + y2 = pts[2].fY >> shift; + x3 = pts[3].fX >> shift; + y3 = pts[3].fY >> shift; +#endif + } + + int winding = 1; + if (y0 > y3) + { + SkTSwap(x0, x3); + SkTSwap(x1, x2); + SkTSwap(y0, y3); + SkTSwap(y1, y2); + winding = -1; + } + + int top = SkFDot6Round(y0); + int bot = SkFDot6Round(y3); + + // are we a zero-height cubic (line)? + if (top == bot) + return 0; + + // are we completely above or below the clip? + if (clip && (top >= clip->fBottom || bot <= clip->fTop)) + return 0; + + // compute number of steps needed (1 << shift) + { + // Can't use (center of curve - center of baseline), since center-of-curve + // need not be the max delta from the baseline (it could even be coincident) + // so we try just looking at the two off-curve points + SkFDot6 dx = cubic_delta_from_line(x0, x1, x2, x3); + SkFDot6 dy = cubic_delta_from_line(y0, y1, y2, y3); + // add 1 (by observation) + shift = diff_to_shift(dx, dy) + 1; + } + // need at least 1 subdivision for our bias trick + SkASSERT(shift > 0); + + fWinding = SkToS8(winding); + fCurveShift = SkToU8(shift); + fCurveCount = SkToS16(-1 << shift); + + SkFixed B = SkFDot6ToFixed(3 * (x1 - x0)); + SkFixed C = SkFDot6ToFixed(3 * (x0 - x1 - x1 + x2)); + SkFixed D = SkFDot6ToFixed(x3 + 3 * (x1 - x2) - x0); + + fCx = SkFDot6ToFixed(x0); + fCDx = B + (C >> shift) + (D >> 2*shift); // biased by shift + fCDDx = 2*C + (3*D >> (shift - 1)); // biased by 2*shift + fCDDDx = 3*D >> (shift - 1); // biased by 2*shift + + B = SkFDot6ToFixed(3 * (y1 - y0)); + C = SkFDot6ToFixed(3 * (y0 - y1 - y1 + y2)); + D = SkFDot6ToFixed(y3 + 3 * (y1 - y2) - y0); + + fCy = SkFDot6ToFixed(y0); + fCDy = B + (C >> shift) + (D >> 2*shift); // biased by shift + fCDDy = 2*C + (3*D >> (shift - 1)); // biased by 2*shift + fCDDDy = 3*D >> (shift - 1); // biased by 2*shift + + fCLastX = SkFDot6ToFixed(x3); + fCLastY = SkFDot6ToFixed(y3); + + if (clip) + { + do { + for (;!this->updateCubic();) + ; + } while (!this->intersectsClip(*clip)); + this->chopLineWithClip(*clip); + return 1; + } + return this->updateCubic(); +} + +int SkCubicEdge::updateCubic() +{ + int success; + int count = fCurveCount; + SkFixed oldx = fCx; + SkFixed oldy = fCy; + SkFixed newx, newy; + int shift = fCurveShift; + + SkASSERT(count < 0); + + do { + if (++count < 0) + { + newx = oldx + (fCDx >> shift); + fCDx += fCDDx >> shift; + fCDDx += fCDDDx; + + newy = oldy + (fCDy >> shift); + fCDy += fCDDy >> shift; + fCDDy += fCDDDy; + } + else // last segment + { + // SkDebugf("LastX err=%d, LastY err=%d\n", (oldx + (fCDx >> shift) - fLastX), (oldy + (fCDy >> shift) - fLastY)); + newx = fCLastX; + newy = fCLastY; + } + success = this->updateLine(oldx, oldy, newx, newy); + oldx = newx; + oldy = newy; + } while (count < 0 && !success); + + fCx = newx; + fCy = newy; + fCurveCount = SkToS16(count); + return success; +} + + + diff --git a/libs/graphics/sgl/SkEdge.h b/libs/graphics/sgl/SkEdge.h new file mode 100644 index 0000000000..73add2a141 --- /dev/null +++ b/libs/graphics/sgl/SkEdge.h @@ -0,0 +1,77 @@ +#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; + S16 fFirstY; + S16 fLastY; + S16 fCurveCount; // only used by kQuad(+) and kCubic(-) + U8 fCurveShift; + S8 fWinding; // 1 or -1 + + int setLine(const SkPoint pts[2], const SkRect16* clip, int shiftUp); + inline int updateLine(SkFixed ax, SkFixed ay, SkFixed bx, SkFixed by); + void chopLineWithClip(const SkRect16& clip); + + inline bool intersectsClip(const SkRect16& 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 SkRect16* 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 SkRect16* clip, int shiftUp); + int updateCubic(); +}; + +#endif diff --git a/libs/graphics/sgl/SkFP.h b/libs/graphics/sgl/SkFP.h new file mode 100644 index 0000000000..eb311b8e58 --- /dev/null +++ b/libs/graphics/sgl/SkFP.h @@ -0,0 +1,70 @@ +#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 S32 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/libs/graphics/sgl/SkFilterProc.cpp b/libs/graphics/sgl/SkFilterProc.cpp new file mode 100644 index 0000000000..b51fa931bd --- /dev/null +++ b/libs/graphics/sgl/SkFilterProc.cpp @@ -0,0 +1,38 @@ +#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; +} + diff --git a/libs/graphics/sgl/SkFilterProc.h b/libs/graphics/sgl/SkFilterProc.h new file mode 100644 index 0000000000..a9c7223c3e --- /dev/null +++ b/libs/graphics/sgl/SkFilterProc.h @@ -0,0 +1,22 @@ +#ifndef SkFilter_DEFINED +#define SkFilter_DEFINED + +#include "SkMath.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]; +} + +#endif + + diff --git a/libs/graphics/sgl/SkGeometry.cpp b/libs/graphics/sgl/SkGeometry.cpp new file mode 100644 index 0000000000..fe47cff526 --- /dev/null +++ b/libs/graphics/sgl/SkGeometry.cpp @@ -0,0 +1,1013 @@ +#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 + +//////////////////////////////////////////////////////////////////////// + +#if defined(SK_SCALAR_IS_FIXED) && !defined(SK_CPU_HAS_CONDITIONAL_INSTR) + 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 // scalar-is-float or we have fast if/then instructions + static int is_not_monotonic(SkScalar a, SkScalar b, SkScalar c, SkScalar d) + { + int neg = 0, pos = 0; + + if (a < b) neg = 1; + if (a > b) pos = 1; + if (b < c) neg = 1; + if (b > c) pos = 1; + if (c < d) neg = 1; + if (c > d) pos = 1; + + return neg & pos; + } + static int is_not_monotonic(SkScalar a, SkScalar b, SkScalar c) + { + int neg = 0, pos = 0; + + if (a < b) neg = 1; + if (a > b) pos = 1; + if (b < c) neg = 1; + if (b > c) pos = 1; + + return neg & pos; + } +#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) +{ + if (numer < 0) + { + numer = -numer; + denom = -denom; + } + + if (denom == 0 || numer == 0 || numer >= denom) + return 0; + + if (ratio) + { + 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]) +{ + SkScalar* r = roots; + + if (A == 0) + return valid_unit_divide(-C, B, 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 SkScalarMul(SkScalarMul(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 * (SkScalarMul(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]) +{ +#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 tValue; + int roots = SkFindQuadExtrema(src[0].fY, src[1].fY, src[2].fY, &tValue); + + if (dst) + { + if (roots == 0) // nothing to chop + { + memcpy(dst, src, 3*sizeof(SkPoint)); + // check if valid_unit_divide gave up but we're still not monotonic + // can happen if valid_unit_divide can't see the t-value (underflow) + // e.g. SkPoint s[3] = { 0, 26398, 0, 26331, 0, 20621428 }; + if (is_not_monotonic(src[0].fY, src[1].fY, src[2].fY)) + force_quad_monotonic_in_y(dst); + } + else + { + SkChopQuadAt(src, dst, tValue); + flatten_double_quad_extrema(&dst[0].fY); + } + } + return roots; +} + +// 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 SkScalarMul(SkScalarMul(SkScalarMul(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 SkScalarMul(SkScalarMul(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 SkScalarMul(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 == nil, 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 == nil) + 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 +*/ +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. +*/ +static bool quad_pt2OffCurve(const SkPoint quad[3], SkScalar x, SkScalar y, SkPoint* offCurve) +{ + SkScalar t; + SkPoint tmp[5]; + + if (SkScalarAbs(x) < SkScalarAbs(y)) + t = quad_solve(quad[0].fX, quad[1].fX, quad[2].fX, x); + else + t = quad_solve(quad[0].fY, quad[1].fY, quad[2].fY, y); + + if (t > 0) + { + SkChopQuadAt(quad, tmp, t); + *offCurve = tmp[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[]) +{ + // check for (effectively) coincident vectors + { + SkScalar dot = SkScalarMul(uStart.fX, uStop.fX) + SkScalarMul(uStart.fY, uStop.fY); + if (SkScalarAbs(dot - SK_Scalar1) <= SK_ScalarNearlyZero) + return 0; + } + // rotate unitStop so that unitStart is at (1,0) + SkScalar x = SkScalarMul(uStop.fX, uStart.fX) + SkScalarMul(uStop.fY, uStart.fY); + SkScalar y = SkScalarMul(uStop.fY, uStart.fX) - SkScalarMul(uStop.fX, uStart.fY); + + if (dir == kCCW_SkRotationDirection) + y = -y; + + // what octant (quadratic curve) is [xy] in? + int oct = 0; + bool sameSign = true; + + if (y < 0) + oct += 4; + if ((x < 0) != (y < 0)) + { + oct += 2; + sameSign = false; + } + if ((SkScalarAbs(x) < SkScalarAbs(y)) == sameSign) + oct += 1; + + if (SkScalarAbs(y) >= SK_Scalar1 || x <= -SK_Scalar1) + 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; + } + wholeCount += 1; + + // now handle counter-clockwise and the initial unitStart rotation + SkMatrix matrix; + matrix.setSinCos(uStart.fY, uStart.fX, 0, 0); + if (dir == kCCW_SkRotationDirection) + matrix.preScale(SK_Scalar1, -SK_Scalar1, 0, 0); + if (userMatrix) + matrix.postConcat(*userMatrix); + matrix.mapPoints(quadPoints, wholeCount); + return wholeCount; +} + + +///////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////// + +#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/libs/graphics/sgl/SkGeometry.h b/libs/graphics/sgl/SkGeometry.h new file mode 100644 index 0000000000..a76dfc2b8e --- /dev/null +++ b/libs/graphics/sgl/SkGeometry.h @@ -0,0 +1,146 @@ +#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 = nil); +void SkEvalQuadAtHalf(const SkPoint src[3], SkPoint* pt, SkVector* tangent = nil); + +/** 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 == nil, 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 == nil, 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* locOrNil, SkVector* tangentOrNil, SkVector* curvatureOrNil); + +/** 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 == nil, 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] = nil); + +/////////////////////////////////////////////////////////////////////////////////////////// + +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 nil, 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/libs/graphics/sgl/SkGlobals.cpp b/libs/graphics/sgl/SkGlobals.cpp new file mode 100644 index 0000000000..a53cc1d88b --- /dev/null +++ b/libs/graphics/sgl/SkGlobals.cpp @@ -0,0 +1,75 @@ +#include "SkGlobals.h" +#include "SkThread.h" + +SkGlobals::Rec::~Rec() +{ +} + +SkGlobals::Rec* SkGlobals::Find(U32 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 == nil) // no create proc, just return not found + return nil; + + // 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 == nil && (rec = create_proc()) != nil) + { + 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 = nil; + bootstrap.fMutex.release(); +} + + diff --git a/libs/graphics/sgl/SkGlyphCache.cpp b/libs/graphics/sgl/SkGlyphCache.cpp new file mode 100644 index 0000000000..60d0ba3ee7 --- /dev/null +++ b/libs/graphics/sgl/SkGlyphCache.cpp @@ -0,0 +1,234 @@ +#include "SkGlyphCache.h" +#include "SkPaint.h" +#include "SkTemplates.h" + +////////////////////////////////////////////////////////////////////// + +#define kMinGlphAlloc (sizeof(SkGlyph) * 64) +#define kMinImageAlloc (24 * 64) // this guy should be pointsize-dependent IMHO + +/* We use this local function instead of the static class method to work around + a bug in gcc98 +*/ +void SkDescriptor_Free(SkDescriptor* desc); +void SkDescriptor_Free(SkDescriptor* desc) +{ + SkDescriptor::Free(desc); +} + +SkGlyphCache::SkGlyphCache(const SkDescriptor* desc) + : fGlyphAlloc(kMinGlphAlloc), fImageAlloc(kMinImageAlloc) +{ + fNext = NULL; + fDesc = desc->copy(); + SkAutoTCallProc<SkDescriptor, SkDescriptor_Free> autoFree(fDesc); + fScalerContext = SkScalerContext::Create(desc); + memset(fHash, 0, sizeof(fHash)); + + fScalerContext->getLineHeight(&fAbove, &fBelow); + (void)autoFree.detach(); +} + +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); +} + +const SkGlyph& SkGlyphCache::lookupMetrics(SkUnichar charCode) +{ + 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]->fCharCode < charCode) + lo = mid + 1; + else + hi = mid; + } + glyph = gptr[hi]; + if (glyph->fCharCode == charCode) + goto DONE; + + // check if we need to bump hi before falling though to the allocator + if (glyph->fCharCode < charCode) + hi += 1; + } + + // not found, but hi tells us where to inser the new glyph + + glyph = (SkGlyph*)fGlyphAlloc.alloc(sizeof(SkGlyph), SkChunkAlloc::kThrow_AllocFailType); + glyph->fCharCode = SkToU16(charCode); + glyph->fImage = NULL; + glyph->fPath = NULL; + fScalerContext->getMetrics(glyph); + *fGlyphArray.insert(hi) = glyph; + +DONE: + fHash[charCode & kHashMask] = glyph; + return *glyph; +} + +const void* SkGlyphCache::findImage(SkUnichar uni) +{ + // cast away the constness, so we can update fImage if needed + SkGlyph* glyph = (SkGlyph*)&this->getMetrics(uni); + + if (glyph->fWidth) + { + if (glyph->fImage == NULL) + { + size_t size = glyph->computeImageSize(); + glyph->fImage = fImageAlloc.alloc(size, SkChunkAlloc::kReturnNil_AllocFailType); + fScalerContext->getImage(*glyph); + } + } + return glyph->fImage; +} + +const SkPath* SkGlyphCache::findPath(SkUnichar uni) +{ + // cast away the constness, so we can update fImage if needed + SkGlyph* glyph = (SkGlyph*)&this->getMetrics(uni); + + if (glyph->fWidth) + { + if (glyph->fPath == NULL) + { + glyph->fPath = SkNEW(SkPath); + fScalerContext->getPath(*glyph, glyph->fPath); + } + } + return glyph->fPath; +} + +void SkGlyphCache::getLineHeight(SkPoint* above, SkPoint* below) +{ + if (above) + *above = fAbove; + if (below) + *below = fBelow; +} + +///////////////////////////////////////////////////////////////// + +SkGlyphCache* SkGlyphCache::DetachCache(const SkPaint& paint, const SkMatrix* matrix) +{ + return paint.detachCache(matrix); +} + +#include "SkGlobals.h" +#include "SkThread.h" + +#define SkGlyphCache_GlobalsTag SkSetFourByteTag('g', 'l', 'f', 'c') + +class SkGlyphCache_Globals : public SkGlobals::Rec { +public: + SkMutex fMutex; + SkGlyphCache* fHead; +}; + +#ifdef SK_USE_RUNTIME_GLOBALS + static SkGlobals::Rec* create_globals() + { + SkGlyphCache_Globals* rec = SkNEW(SkGlyphCache_Globals); + rec->fHead = NULL; + 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 + +SkGlyphCache* SkGlyphCache::DetachCache(const SkDescriptor* desc) +{ + SkASSERT(desc); + + SkGlyphCache_Globals& globals = FIND_GC_GLOBALS(); + + globals.fMutex.acquire(); + SkGlyphCache* cache = globals.fHead; + SkGlyphCache* prev = NULL; + + while (cache) + { + SkGlyphCache* next = cache->fNext; + + if (*cache->fDesc == *desc) + { + if (prev) + prev->fNext = next; + else + globals.fHead = next; + cache->fNext = NULL; + break; + } + prev = cache; + cache = next; + } + globals.fMutex.release(); + + if (cache == NULL) + cache = SkNEW_ARGS(SkGlyphCache, (desc)); + return cache; +} + +void SkGlyphCache::AttachCache(SkGlyphCache* cache) +{ + SkASSERT(cache); + SkASSERT(cache->fNext == NULL); + + SkGlyphCache_Globals& globals = GET_GC_GLOBALS(); + + globals.fMutex.acquire(); + + cache->fNext = globals.fHead; + globals.fHead = cache; + + globals.fMutex.release(); +} + +bool SkGlyphCache::FreeCache(size_t bytesNeeded) +{ + SkGlyphCache_Globals& globals = FIND_GC_GLOBALS(); + + globals.fMutex.acquire(); + SkGlyphCache* cache = globals.fHead; + bool didSomething = (cache != NULL); + + while (cache) + { + SkGlyphCache* next = cache->fNext; + SkDELETE(cache); + cache = next; + } + + globals.fHead = NULL; + globals.fMutex.release(); + + return didSomething; +} + diff --git a/libs/graphics/sgl/SkGlyphCache.h b/libs/graphics/sgl/SkGlyphCache.h new file mode 100644 index 0000000000..20a7aced1d --- /dev/null +++ b/libs/graphics/sgl/SkGlyphCache.h @@ -0,0 +1,89 @@ +#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 { +public: + const SkGlyph& getMetrics(SkUnichar charCode) + { + int hash = charCode & kHashMask; + SkGlyph* glyph = fHash[hash]; + + if (glyph != nil && glyph->fCharCode == charCode) + return *glyph; + return this->lookupMetrics(charCode); + } + + + const void* findImage(SkUnichar); + const SkPath* findPath(SkUnichar); + void getLineHeight(SkPoint* above, SkPoint* below); + + static SkGlyphCache* DetachCache(const SkPaint&, const SkMatrix* matrix); + static SkGlyphCache* DetachCache(const SkDescriptor*); + static void AttachCache(SkGlyphCache*); + static bool FreeCache(size_t bytesNeeded); + +private: + SkGlyphCache(const SkDescriptor*); + ~SkGlyphCache(); + + const SkGlyph& lookupMetrics(SkUnichar charCode); + + SkGlyphCache* fNext; + SkDescriptor* fDesc; + SkScalerContext* fScalerContext; + + enum { + kHashBits = 6, + kHashCount = 1 << kHashBits, + kHashMask = kHashCount - 1 + }; + SkGlyph* fHash[kHashCount]; + SkTDArray<SkGlyph*> fGlyphArray; + SkChunkAlloc fGlyphAlloc; + SkChunkAlloc fImageAlloc; + + SkPoint fAbove, fBelow; +}; + +class SkAutoGlyphCache { +public: + SkAutoGlyphCache(SkGlyphCache* cache) : fCache(cache) {} + SkAutoGlyphCache(const SkDescriptor* desc) + { + fCache = SkGlyphCache::DetachCache(desc); + } + SkAutoGlyphCache(const SkPaint& paint, const SkMatrix* matrix) + { + fCache = SkGlyphCache::DetachCache(paint, matrix); + } + ~SkAutoGlyphCache() + { + if (fCache) + SkGlyphCache::AttachCache(fCache); + } + + SkGlyphCache* getCache() const { return fCache; } + + void release() + { + if (fCache) + { + SkGlyphCache::AttachCache(fCache); + fCache = nil; + } + } +private: + SkGlyphCache* fCache; +}; + +#endif + diff --git a/libs/graphics/sgl/SkGraphics.cpp b/libs/graphics/sgl/SkGraphics.cpp new file mode 100644 index 0000000000..82c9a6864d --- /dev/null +++ b/libs/graphics/sgl/SkGraphics.cpp @@ -0,0 +1,143 @@ +#include "SkGraphics.h" + +#include "Sk64.h" +#include "SkBlitter.h" +#include "SkCanvas.h" +#include "SkDeque.h" +#include "SkDOM.h" +#include "SkFloat.h" +#include "SkGeometry.h" +#include "SkGlobals.h" +#include "SkMath.h" +#include "SkMatrix.h" +#include "SkPath.h" +#include "SkPathEffect.h" +#include "SkPathMeasure.h" +#include "SkRefCnt.h" +#include "SkShader.h" +#include "SkStream.h" +#include "SkTSearch.h" +#include "SkUtils.h" +#include "SkXfermode.h" + +#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 + +void SkGraphics::Init(bool runUnitTests) +{ + SkGlobals::Init(); + +#ifdef BUILD_EMBOSS_TABLE + SkEmbossMask_BuildTable(); +#endif +#ifdef BUILD_RADIALGRADIENT_TABLE + SkRadialGradient_BuildTable(); +#endif + +#ifdef SK_SUPPORT_UNITTEST + if (runUnitTests == false) + return; + int i; + + static const struct { + const char* fTypeName; + size_t fSizeOf; + } gTypeSize[] = { + typesizeline(char), + typesizeline(short), + typesizeline(int), + typesizeline(long), + typesizeline(size_t), + typesizeline(void*), + + typesizeline(S8), + typesizeline(U8), + typesizeline(S16), + typesizeline(U16), + typesizeline(S32), + typesizeline(U32), + typesizeline(S8CPU), + typesizeline(U8CPU), + typesizeline(S16CPU), + typesizeline(U16CPU), + + typesizeline(SkPoint), + typesizeline(SkRect), + typesizeline(SkMatrix), + typesizeline(SkPath), + typesizeline(SkRefCnt), + + typesizeline(SkPaint), + typesizeline(SkCanvas), + typesizeline(SkBlitter), + typesizeline(SkShader), + typesizeline(SkXfermode), + typesizeline(SkPathEffect) + }; + + { + char test = (char)(0-1); // use this subtract to avoid truncation warnings (in VC7 at least) + if (test < 0) + SkDebugf("SkGraphics: char is signed\n"); + else + SkDebugf("SkGraphics: char is unsigned\n"); + } + for (i = 0; i < (int)SK_ARRAY_COUNT(gTypeSize); i++) + SkDebugf("SkGraphics: sizeof(%s) = %d\n", gTypeSize[i].fTypeName, gTypeSize[i].fSizeOf); + + static const struct { + const char* fTypeName; + void (*fUnitTest)(); + } gUnitTests[] = { + unittestline(Sk64), + unittestline(SkMath), + unittestline(SkUtils), + unittestline(SkString), + unittestline(SkFloat), + unittestline(SkMatrix), + unittestline(SkGeometry), + unittestline(SkDeque), + unittestline(SkPath), + unittestline(SkPathMeasure) + }; + + for (i = 0; i < (int)SK_ARRAY_COUNT(gUnitTests); i++) + { + SkDebugf("SkGraphics: Running UnitTest for %s\n", gUnitTests[i].fTypeName); + gUnitTests[i].fUnitTest(); + SkDebugf("SkGraphics: End UnitTest for %s\n", gUnitTests[i].fTypeName); + } + SkQSort_UnitTest(); + +#endif +} + +//////////////////////////////////////////////////////////////////////////// + +#include "SkGlyphCache.h" +#include "SkImageDecoder.h" + +void SkGraphics::Term() +{ + SkBitmapRef::PurgeCacheAll(); + SkGraphics::FreeCaches(SK_MaxS32); + SkGlobals::Term(); +} + +bool SkGraphics::FreeCaches(size_t bytesNeeded) +{ + bool didSomething = SkBitmapRef::PurgeCacheOne(); + + return SkGlyphCache::FreeCache(bytesNeeded) || didSomething; +} + + diff --git a/libs/graphics/sgl/SkMaskFilter.cpp b/libs/graphics/sgl/SkMaskFilter.cpp new file mode 100644 index 0000000000..a23b44e45f --- /dev/null +++ b/libs/graphics/sgl/SkMaskFilter.cpp @@ -0,0 +1,69 @@ +#include "SkMaskFilter.h" +#include "SkBlitter.h" +#include "SkBounder.h" +#include "SkBuffer.h" +#include "SkDraw.h" +#include "SkRegion.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; +} + +uint8_t* SkMask::AllocImage(size_t size) +{ + return (uint8_t*)sk_malloc_throw(SkAlign4(size)); +} + +void SkMask::FreeImage(uint8_t* image) +{ + sk_free(image); +} + +bool SkMaskFilter::filterMask(SkMask*, const SkMask&, const SkMatrix&, SkPoint16*) +{ + 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, clip))) + { + const SkRect16& cr = clipper.rect(); + do { + blitter->blitMask(dstM, cr); + clipper.next(); + } while (!clipper.done()); + } + + return true; +} + + diff --git a/libs/graphics/sgl/SkPaint.cpp b/libs/graphics/sgl/SkPaint.cpp new file mode 100644 index 0000000000..23216845fa --- /dev/null +++ b/libs/graphics/sgl/SkPaint.cpp @@ -0,0 +1,868 @@ +#include "SkPaint.h" +#include "SkColorFilter.h" +#include "SkFontHost.h" +#include "SkMaskFilter.h" +#include "SkPathEffect.h" +#include "SkRasterizer.h" +#include "SkShader.h" +#include "SkScalerContext.h" +#include "SkStroke.h" +#include "SkTextLayout.h" +#include "SkTypeface.h" +#include "SkXfermode.h" + +#define SK_DefaultTextSize SkIntToScalar(12) + +SkPaint::SkPaint() +{ + fTypeface = NULL; + fTextSize = SK_DefaultTextSize; + fTextScaleX = SK_Scalar1; + fTextSkewX = 0; + + fPathEffect = NULL; + fShader = NULL; + fXfermode = NULL; + fMaskFilter = NULL; + fColorFilter = NULL; + fTextLayout = NULL; + fRasterizer = NULL; + + fColor = SK_ColorBLACK; + fWidth = 0; + fMiterLimit = SK_DefaultMiterLimit; + fFlags = 0; + fCapType = kDefault_Cap; + fJoinType = kDefault_Join; + fFilterType = kNo_FilterType; + fTextAlign = kLeft_Align; + fStyle = kFill_Style; +} + +SkPaint::SkPaint(const SkPaint& src) +{ + memcpy(this, &src, sizeof(src)); + + fTypeface->safeRef(); + fPathEffect->safeRef(); + fShader->safeRef(); + fXfermode->safeRef(); + fMaskFilter->safeRef(); + fColorFilter->safeRef(); + fTextLayout->safeRef(); + fRasterizer->safeRef(); +} + +SkPaint::~SkPaint() +{ + fTypeface->safeUnref(); + fPathEffect->safeUnref(); + fShader->safeUnref(); + fXfermode->safeUnref(); + fMaskFilter->safeUnref(); + fColorFilter->safeUnref(); + fTextLayout->safeUnref(); + fRasterizer->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.fTextLayout->safeRef(); + src.fRasterizer->safeRef(); + + fTypeface->safeUnref(); + fPathEffect->safeUnref(); + fShader->safeUnref(); + fXfermode->safeUnref(); + fMaskFilter->safeUnref(); + fColorFilter->safeUnref(); + fTextLayout->safeUnref(); + fRasterizer->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(U32 flags) +{ + fFlags = SkToU8(flags); +} + +void SkPaint::setAntiAliasOn(bool doAA) +{ + this->setFlags(SkSetClear32(fFlags, doAA, kAntiAlias_Shift)); +} + +void SkPaint::setLinearTextOn(bool doLinearText) +{ + this->setFlags(SkSetClear32(fFlags, doLinearText, kLinearText_Shift)); +} + +void SkPaint::setUnderlineTextOn(bool doUnderline) +{ + this->setFlags(SkSetClear32(fFlags, doUnderline, kUnderlineText_Shift)); +} + +void SkPaint::setStrikeThruTextOn(bool doStrikeThru) +{ + this->setFlags(SkSetClear32(fFlags, doStrikeThru, kStrikeThruText_Shift)); +} + +void SkPaint::setFakeBoldTextOn(bool doFakeBold) +{ + this->setFlags(SkSetClear32(fFlags, doFakeBold, kFakeBoldText_Shift)); +} + +void SkPaint::setStyle(Style style) +{ + SkASSERT((unsigned)style < kStyleCount); + fStyle = style; +} + +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) +{ + SkASSERT(width >= 0); + fWidth = width; +} + +void SkPaint::setStrokeMiter(SkScalar limit) +{ + SkASSERT(limit >= 0); + fMiterLimit = limit; +} + +void SkPaint::setStrokeCap(Cap ct) +{ + SkASSERT((unsigned)ct < kCapCount); + fCapType = SkToU8(ct); +} + +void SkPaint::setStrokeJoin(Join jt) +{ + SkASSERT((unsigned)jt < kJoinCount); + fJoinType = SkToU8(jt); +} + +void SkPaint::setFilterType(FilterType ft) +{ + SkASSERT((unsigned)ft < kFilterTypeCount); + fFilterType = SkToU8(ft); +} + +////////////////////////////////////////////////////////////////// + +void SkPaint::setTextAlign(Align align) +{ + SkASSERT((unsigned)align < kAlignCount); + fTextAlign = SkToU8(align); +} + +void SkPaint::setTextSize(SkScalar ts) +{ + SkASSERT(ts > 0); + fTextSize = ts; +} + +void SkPaint::setTextScaleX(SkScalar scaleX) +{ + fTextScaleX = scaleX; +} + +void SkPaint::setTextSkewX(SkScalar skewX) +{ + fTextSkewX = skewX; +} + +/////////////////////////////////////////////////////////////////////////////////////// + +SkTextLayout* SkPaint::setTextLayout(SkTextLayout* layout) +{ + SkRefCnt_SafeAssign(fTextLayout, layout); + return layout; +} + +SkTypeface* SkPaint::setTypeface(SkTypeface* font) +{ + SkRefCnt_SafeAssign(fTypeface, font); + return font; +} + +SkRasterizer* SkPaint::setRasterizer(SkRasterizer* r) +{ + SkRefCnt_SafeAssign(fRasterizer, r); + return r; +} + +/////////////////////////////////////////////////////////////////////////////////////// + +#include "SkGlyphCache.h" +#include "SkUtils.h" + +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 SkScalar measure_text(const SkPaint& paint, SkGlyphCache* cache, + SkUnicodeWalkerProc proc, const char* text, size_t byteLength, + int* count) +{ + SkASSERT(count); + + SkFixed x = 0; + int n; + SkTextLayout* layout = paint.getTextLayout(); + + if (layout) + { + SkAutoSTMalloc<32, SkTextLayout::Rec> storage(byteLength); + SkTextLayout::Rec* rec = storage.get(); + + n = layout->layout(paint, text, byteLength, proc, rec); + for (int i = 0; i < n; i++) + { + // should pass rec[i].glyphID when we have it + x += cache->getMetrics(rec[i].charCode()).fAdvanceX + SkScalarToFixed(rec[i].fDeltaAdvance); + } + } + else + { + const char* stop = (const char*)text + byteLength; + for (n = 0; text < stop; n++) + { + x += cache->getMetrics(proc(&text)).fAdvanceX; + } + SkASSERT(text == stop); + } + *count = n; + return SkFixedToScalar(x); +} + +SkScalar SkPaint::privateMeasureText(SkUnicodeWalkerProc textProc, + const char* text, size_t byteLength, + SkScalar* above, SkScalar* below) const +{ + SkASSERT(text != NULL || byteLength == 0); + + SkScalar scale = 0; + SkAutoRestorePaintTextSizeAndFrame restore(this); + + if (this->isLinearTextOn()) + { + scale = fTextSize / kCanonicalTextSizeForPaths; + // this gets restored by restore + ((SkPaint*)this)->setTextSize(SkIntToScalar(kCanonicalTextSizeForPaths)); + } + + SkAutoGlyphCache autoCache(*this, NULL); + SkGlyphCache* cache = autoCache.getCache(); + + if (above || below) + { + SkPoint abovePt, belowPt; + cache->getLineHeight(&abovePt, &belowPt); + if (scale) + { + abovePt.fY = SkScalarMul(abovePt.fY, scale); + belowPt.fY = SkScalarMul(belowPt.fY, scale); + } + if (above) + *above = abovePt.fY; + if (below) + *below = belowPt.fY; + } + + SkScalar width = 0; + + if (byteLength) + { + int count; + width = measure_text(*this, cache, textProc, text, byteLength, &count); + + if (scale) + width = SkScalarMul(width, scale); + } + return width; +} + +SkScalar SkPaint::measureText(const char utf8[], size_t length, + SkScalar* above, SkScalar* below) const +{ + return this->privateMeasureText((SkUnicodeWalkerProc)SkUTF8_NextUnichar, utf8, length, above, below); +} + +SkScalar SkPaint::measureText16(const U16 utf16[], size_t numberOf16BitValues, + SkScalar* above, SkScalar* below) const +{ + return this->privateMeasureText((SkUnicodeWalkerProc)SkUTF16_NextUnichar, (const char*)utf16, numberOf16BitValues << 1, above, below); +} + +//////////////////////////////////////////////////////////////////////////////////////////// + +int SkPaint::privateGetTextWidths(const char text[], size_t byteLength, + SkScalar widths[], SkUnicodeWalkerProc proc) const +{ + SkASSERT(text != NULL && byteLength > 0); + + SkAutoRestorePaintTextSizeAndFrame restore(this); + SkScalar scale = 0; + + if (this->isLinearTextOn()) + { + scale = fTextSize / kCanonicalTextSizeForPaths; + // this gets restored by restore + ((SkPaint*)this)->setTextSize(SkIntToScalar(kCanonicalTextSizeForPaths)); + } + + SkAutoGlyphCache autoCache(*this, NULL); + SkGlyphCache* cache = autoCache.getCache(); + + SkScalar* w = widths; + const char* stop = (const char*)text + byteLength; + if (scale) { + while (text < stop) + *w++ = SkScalarMul(SkFixedToScalar(cache->getMetrics(proc(&text)).fAdvanceX), scale); + } + else { + while (text < stop) + *w++ = SkFixedToScalar(cache->getMetrics(proc(&text)).fAdvanceX); + } + return w - widths; // count +} + +int SkPaint::getTextWidths(const char text[], size_t byteLength, SkScalar widths[]) const +{ + if (0 == byteLength) + return 0; + + if (NULL == widths) + return SkUTF8_CountUnichars(text, byteLength); + + int count = this->privateGetTextWidths(text, byteLength, widths, (SkUnicodeWalkerProc)SkUTF8_NextUnichar); + SkASSERT(SkUTF8_CountUnichars(text, byteLength) == count); + return count; +} + +int SkPaint::getTextWidths16(const uint16_t text[], size_t numberOf16BitValues, SkScalar widths[]) const +{ + if (0 == numberOf16BitValues) + return 0; + + if (NULL == widths) + return SkUTF16_CountUnichars(text, numberOf16BitValues); + + int count = this->privateGetTextWidths((const char*)text, numberOf16BitValues << 1, widths, (SkUnicodeWalkerProc)SkUTF16_NextUnichar); + SkASSERT(SkUTF16_CountUnichars(text, numberOf16BitValues) == count); + return count; +} + +//////////////////////////////////////////////////////////////////////////////////////////// + +SkScalar SkPaint::ascent() const +{ + SkScalar above; + (void)this->measureText(NULL, 0, &above, NULL); + return above; +} + +SkScalar SkPaint::descent() const +{ + SkScalar below; + (void)this->measureText(NULL, 0, NULL, &below); + return below; +} + +#include "SkDraw.h" + +void SkPaint::privateGetTextPath(SkUnicodeWalkerProc textProc, const char text[], size_t length, SkScalar x, SkScalar y, SkPath* path) const +{ + SkASSERT(length == 0 || text != NULL); + if (text == NULL || length == 0 || path == NULL) + return; + SkASSERT(textProc); + + SkTextToPathIter iter(textProc, text, length, *this, false, true); + SkMatrix matrix; + SkScalar prevXPos = 0; + + matrix.setScale(iter.getPathScale(), iter.getPathScale(), 0, 0); + 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; + } +} + +void SkPaint::getTextPath(const char text[], size_t length, SkScalar x, SkScalar y, SkPath* path) const +{ + this->privateGetTextPath((SkUnicodeWalkerProc)SkUTF8_NextUnichar, text, length, x, y, path); +} + +void SkPaint::getText16Path(const U16 text[], size_t numberOf16BitValues, SkScalar x, SkScalar y, SkPath* path) const +{ + this->privateGetTextPath((SkUnicodeWalkerProc)SkUTF16_NextUnichar, (const char*)text, numberOf16BitValues << 1, x, y, path); +} + +static void add_flattenable(SkDescriptor* desc, U32 tag, U32 len, SkFlattenable* obj) +{ + SkFlattenable::Factory fact = obj->getFactory(); + SkASSERT(fact); + + SkWBuffer buffer(desc->addEntry(tag, sizeof(void*) + len, NULL), sizeof(void*) + len); + + buffer.writePtr((const void*)fact); + obj->flatten(buffer); + SkASSERT(buffer.pos() == buffer.size()); +} + +/* + * 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 }; + +void SkScalerContext::MakeRec(const SkPaint& paint, const SkMatrix* deviceMatrix, Rec* rec) +{ + SkASSERT(deviceMatrix == NULL || (deviceMatrix->getType() & SkMatrix::kPerspective_Mask) == 0); + + 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.isFakeBoldTextOn()) + { + 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; + } + + if (style != SkPaint::kFill_Style && strokeWidth > 0) + { + rec->fFrameWidth = strokeWidth; + rec->fMiterLimit = paint.getStrokeMiter(); + rec->fFrameAndFill = SkToU8(style == SkPaint::kStrokeAndFill_Style); + rec->fStrokeJoin = SkToU8(paint.getStrokeJoin()); + } + else + { + rec->fFrameWidth = 0; + rec->fMiterLimit = 0; + rec->fFrameAndFill = false; + rec->fStrokeJoin = 0; + } + + rec->fUseHints = SkToU8(!paint.isLinearTextOn()); + rec->fDoAA = SkToU8(paint.isAntiAliasOn()); +} + +SkGlyphCache* SkPaint::detachCache(const SkMatrix* deviceMatrix) const +{ + SkScalerContext::Rec rec; + + SkScalerContext::MakeRec(*this, deviceMatrix, &rec); + + size_t descSize = sizeof(rec); + int entryCount = 2; // scalerrec + typeface + SkTypeface* tf = this->getTypeface(); + size_t tfSize = 0; + SkPathEffect* pe = this->getPathEffect(); + size_t peLen = 0; + SkMaskFilter* mf = this->getMaskFilter(); + size_t mfLen = 0; + SkRasterizer* ra = this->getRasterizer(); + size_t raLen = 0; + + // we always do this, even if tf is NULL + tfSize = SkFontHost::FlattenTypeface(tf, NULL); + descSize += tfSize; + + if (pe) + { + if (pe->getFactory()) + { + SkWBuffer buffer; + pe->flatten(buffer); + peLen = buffer.pos(); + descSize += sizeof(SkFlattenable::Factory) + peLen; + entryCount += 1; + rec.fDoAA = true; // force antialiasing when we do the scan conversion + } + else + pe = NULL; + } + if (mf) + { + if (mf->getFactory()) + { + SkWBuffer buffer; + mf->flatten(buffer); + mfLen = buffer.pos(); + descSize += sizeof(SkFlattenable::Factory) + mfLen; + entryCount += 1; + rec.fDoAA = true; // force antialiasing with maskfilters + } + else + mf = NULL; + } + if (ra) + { + if (ra->getFactory()) + { + SkWBuffer buffer; + ra->flatten(buffer); + raLen = buffer.pos(); + descSize += sizeof(SkFlattenable::Factory) + raLen; + entryCount += 1; + rec.fDoAA = true; // force antialiasing when we do the scan conversion + } + else + ra = NULL; + } + descSize += SkDescriptor::ComputeOverhead(entryCount); + + SkAutoDescriptor ad(descSize); + SkDescriptor* desc = ad.getDesc(); + + desc->init(); + desc->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec); + + // we always do this, even if tf is NULL + { + SkDEBUGCODE(size_t tfSize2 = ) SkFontHost::FlattenTypeface(tf, desc->addEntry(kTypeface_SkDescriptorTag, tfSize, NULL)); + SkASSERT(tfSize2 == tfSize); + } + + if (pe) + add_flattenable(desc, kPathEffect_SkDescriptorTag, peLen, pe); + if (mf) + add_flattenable(desc, kMaskFilter_SkDescriptorTag, mfLen, mf); + if (ra) + add_flattenable(desc, kRasterizer_SkDescriptorTag, raLen, ra); + + SkASSERT(descSize == desc->getLength()); + desc->computeChecksum(); + + return SkGlyphCache::DetachCache(desc); +} + +////////////////////////////////////////////////////////////////// + +SkShader* SkPaint::setShader(SkShader* shader) +{ + SkRefCnt_SafeAssign(fShader, shader); + return shader; +} + +SkColorFilter* SkPaint::setColorFilter(SkColorFilter* filter) +{ + SkRefCnt_SafeAssign(fColorFilter, filter); + return filter; +} + +SkXfermode* SkPaint::setXfermode(SkXfermode* mode) +{ + SkRefCnt_SafeAssign(fXfermode, mode); + return mode; +} + +SkXfermode* SkPaint::setPorterDuffXfermode(SkPorterDuff::Mode mode) +{ + fXfermode->safeUnref(); + fXfermode = SkPorterDuff::CreateXfermode(mode); + return fXfermode; +} + +SkPathEffect* SkPaint::setPathEffect(SkPathEffect* effect) +{ + SkRefCnt_SafeAssign(fPathEffect, effect); + return effect; +} + +SkMaskFilter* SkPaint::setMaskFilter(SkMaskFilter* filter) +{ + SkRefCnt_SafeAssign(fMaskFilter, filter); + return filter; +} + +//////////////////////////////////////////////////////////////////////////////////////// + +bool SkPaint::getFillPath(const SkPath& src, SkPath* dst) const +{ + SkPath effectPath, strokePath; + const SkPath* path = &src; + + SkScalar width = this->getStrokeWidth(); + + switch (this->getStyle()) { + case SkPaint::kFill_Style: + width = -1; // mark it as no-stroke + break; + case SkPaint::kStrokeAndFill_Style: + if (width == 0) + width = -1; // mark it as no-stroke + break; + case SkPaint::kStroke_Style: + break; + default: + SkASSERT(!"unknown paint style"); + } + + if (this->getPathEffect()) + { + // lie to the pathEffect if our style is strokeandfill, so that it treats us as just fill + if (this->getStyle() == SkPaint::kStrokeAndFill_Style) + width = -1; // mark it as no-stroke + + if (this->getPathEffect()->filterPath(&effectPath, src, &width)) + path = &effectPath; + + // restore the width if we earlier had to lie, and if we're still set to no-stroke + // note: if we're now stroke (width >= 0), then the pathEffect asked for that change + // and we want to respect that (i.e. don't overwrite their setting for width) + if (this->getStyle() == SkPaint::kStrokeAndFill_Style && width < 0) + { + width = this->getStrokeWidth(); + if (width == 0) + width = -1; + } + } + + if (width > 0 && !path->isEmpty()) + { + SkStroke stroker(*this, width); + stroker.strokePath(*path, &strokePath); + path = &strokePath; + } + + if (path == &src) + *dst = src; + else + { + SkASSERT(path == &effectPath || path == &strokePath); + dst->swap(*(SkPath*)path); + } + + return width != 0; // return true if we're filled, or false if we're hairline (width == 0) +} + +//////////////////////////////////////////////////////////////////////////////////////// + +static bool has_thick_frame(const SkPaint& paint) +{ + return paint.getStrokeWidth() > 0 && paint.getStyle() != SkPaint::kFill_Style; +} + +SkTextToPathIter::SkTextToPathIter( SkUnicodeWalkerProc textProc, + const char text[], size_t length, + const SkPaint& paint, + bool applyStrokeAndPathEffects, + bool forceLinearTextOn) + : fPaint(paint), fTextProc(textProc) +{ + if (forceLinearTextOn) + fPaint.setLinearTextOn(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.isLinearTextOn() && !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 = SkGlyphCache::DetachCache(fPaint, 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(measure_text(paint, fCache, textProc, text, length, &count), fScale); + if (paint.getTextAlign() == SkPaint::kCenter_Align) + width = SkScalarHalf(width); + xOffset = -width; + } + fXPos = xOffset; // + SkScalarHalf(paint.getTextTracking()); do we need to return the textlayout's first deltaAdvance? + fPrevAdvance = 0; + + fText = text; + fStop = text + length; +} + +SkTextToPathIter::~SkTextToPathIter() +{ + SkGlyphCache::AttachCache(fCache); +} + +const SkPath* SkTextToPathIter::next(SkScalar* xpos) +{ + while (fText < fStop) + { + const SkGlyph& glyph = fCache->getMetrics(fTextProc(&fText)); + + fXPos += fPrevAdvance; + fPrevAdvance = SkScalarMul(SkFixedToScalar(glyph.fAdvanceX), fScale); // + fPaint.getTextTracking(); + + if (glyph.fWidth) + { + if (xpos) + *xpos = fXPos; + return fCache->findPath(glyph.fCharCode); + } + } + return NULL; +} + +///////////////////////////////////////////////////////////////////////////////////////////////////// + +SkTypeface* SkTypeface::Create(const char name[], Style style) +{ + return SkFontHost::CreateTypeface(NULL, name, style); +} + +SkTypeface* SkTypeface::CreateFromTypeface(const SkTypeface* family, Style style) +{ + return SkFontHost::CreateTypeface(family, NULL, style); +} + diff --git a/libs/graphics/sgl/SkPath.cpp b/libs/graphics/sgl/SkPath.cpp new file mode 100644 index 0000000000..18e4fb80a3 --- /dev/null +++ b/libs/graphics/sgl/SkPath.cpp @@ -0,0 +1,1139 @@ +#include "SkPath.h" +#include "SkMath.h" + +/* + Stores the verbs and points as they are given to us, with exceptions: + - we only record "Close" if it was immediately preceeded by Line | Quad | Cubic + - we insert a Move(0,0) if Line | Quad | Cubic is our first command + + The iterator does more cleanup, especially if forceClose == true + 1. if we encounter Close, return a cons'd up Line() first (if the curr-pt != start-pt) + 2. if we encounter Move without a preceeding Close, and forceClose is true, goto #1 + 3. if we encounter Line | Quad | Cubic after Close, cons up a Move +*/ + +//////////////////////////////////////////////////////////////////////////// + +SkPath::SkPath() : fFillType(kWinding_FillType) +{ +} + +SkPath::SkPath(const SkPath& src) +{ + *this = src; +} + +SkPath::~SkPath() +{ +} + +SkPath& SkPath::operator=(const SkPath& src) +{ + if (this != &src) + { + fPts = src.fPts; + fVerbs = src.fVerbs; + fFillType = src.fFillType; + } + return *this; +} + +void SkPath::swap(SkPath& other) +{ + SkASSERT(&other != nil); + + if (this != &other) + { + fPts.swap(other.fPts); + fVerbs.swap(other.fVerbs); + SkTSwap<U8>(fFillType, other.fFillType); + } +} + +void SkPath::reset() +{ + fPts.reset(); + fVerbs.reset(); +} + +bool SkPath::isEmpty() const +{ + int count = fVerbs.count(); + return count == 0 || (count == 1 && fVerbs[0] == kMove_Verb); +} + +bool SkPath::isRect(SkRect*) const +{ + SkASSERT(!"unimplemented"); + return false; +} + +int SkPath::getPoints(SkPoint copy[], int max) const +{ + 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 +{ + 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) +{ + int count = fPts.count(); + if (count == 0) + this->moveTo(x, y); + else + fPts[count - 1].set(x, y); +} + +void SkPath::computeBounds(SkRect* bounds, BoundsType bt) const +{ + SkASSERT(bounds); + + if (fPts.count() <= 1) + bounds->set(0, 0, 0, 0); + else if (true || bt == kFast_BoundsType) + bounds->set(fPts.begin(), fPts.count()); + else + { + SkASSERT(!"unimplemented"); + Iter iter(*this, false); + SkPoint pts[4]; + Verb verb; + + while ((verb = iter.next(pts)) != kDone_Verb) + { + switch (verb) { + case kLine_Verb: + case kQuad_Verb: + case kCubic_Verb: + break; + default: + break; + } + } + } +} + +////////////////////////////////////////////////////////////////////////////// +// Construction methods + +void SkPath::incReserve(U16CPU inc) +{ + fVerbs.setReserve(fVerbs.count() + inc); + fPts.setReserve(fPts.count() + inc); +} + +void SkPath::moveTo(SkScalar x, SkScalar y) +{ + 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); +} + +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) +{ + if (fVerbs.count() == 0) + { + fPts.append()->set(0, 0); + *fVerbs.append() = kMove_Verb; + } + fPts.append()->set(x, y); + *fVerbs.append() = kLine_Verb; +} + +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) +{ + 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; +} + +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) +{ + 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; +} + +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() +{ + int count = fVerbs.count(); + if (count > 0) + { + switch (fVerbs[count - 1]) { + case kLine_Verb: + case kQuad_Verb: + case kCubic_Verb: + *fVerbs.append() = kClose_Verb; + break; + default: + // don't add a close if the prev wasn't a primitive + break; + } + } +} + +//////////////////////////////////////////////////////////////////////////////////// + +void SkPath::addRect(const SkRect& rect, Direction dir) +{ + this->addRect(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, dir); +} + +void SkPath::addRect(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom, Direction dir) +{ + this->moveTo(left, top); + if (dir == kCCW_Direction) + { + this->lineTo(left, bottom); + this->lineTo(right, bottom); + this->lineTo(right, top); + } + else + { + this->lineTo(right, top); + this->lineTo(right, bottom); + this->lineTo(left, bottom); + } + this->close(); +} + +#define CUBIC_ARC_FACTOR ((SK_ScalarSqrt2 - SK_Scalar1) * 4 / 3) + +void SkPath::addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry, Direction dir) +{ + SkScalar w = rect.width(); + SkScalar halfW = SkScalarHalf(w); + SkScalar h = rect.height(); + SkScalar halfH = SkScalarHalf(h); + + if (halfW <= 0 || halfH <= 0) + return; + + bool skip_hori = rx >= halfW; + bool skip_vert = ry >= halfH; + + if (skip_hori && skip_vert) + { + this->addOval(rect, dir); + return; + } + if (skip_hori) + rx = halfW; + else if (skip_vert) + ry = halfH; + + SkScalar sx = SkScalarMul(rx, CUBIC_ARC_FACTOR); + SkScalar sy = SkScalarMul(ry, CUBIC_ARC_FACTOR); + + this->incReserve(17); + this->moveTo(rect.fRight - rx, rect.fTop); + if (dir == kCCW_Direction) + { + if (!skip_hori) + this->lineTo(rect.fLeft + rx, rect.fTop); // top + this->cubicTo(rect.fLeft + rx - sx, rect.fTop, + rect.fLeft, rect.fTop + ry - sy, + rect.fLeft, rect.fTop + ry); // top-left + if (!skip_vert) + this->lineTo(rect.fLeft, rect.fBottom - ry); // left + this->cubicTo(rect.fLeft, rect.fBottom - ry + sy, + rect.fLeft + rx - sx, rect.fBottom, + rect.fLeft + rx, rect.fBottom); // bot-left + if (!skip_hori) + this->lineTo(rect.fRight - rx, rect.fBottom); // bottom + this->cubicTo(rect.fRight - rx + sx, rect.fBottom, + rect.fRight, rect.fBottom - ry + sy, + rect.fRight, rect.fBottom - ry); // bot-right + if (!skip_vert) + this->lineTo(rect.fRight, rect.fTop + ry); + this->cubicTo(rect.fRight, rect.fTop + ry - sy, + rect.fRight - rx + sx, rect.fTop, + rect.fRight - rx, rect.fTop); // top-right + } + else + { + this->cubicTo(rect.fRight - rx + sx, rect.fTop, + rect.fRight, rect.fTop + ry - sy, + rect.fRight, rect.fTop + ry); // top-right + if (!skip_vert) + this->lineTo(rect.fRight, rect.fBottom - ry); + this->cubicTo(rect.fRight, rect.fBottom - ry + sy, + rect.fRight - rx + sx, rect.fBottom, + rect.fRight - rx, rect.fBottom); // bot-right + if (!skip_hori) + this->lineTo(rect.fLeft + rx, rect.fBottom); // bottom + this->cubicTo(rect.fLeft + rx - sx, rect.fBottom, + rect.fLeft, rect.fBottom - ry + sy, + rect.fLeft, rect.fBottom - ry); // bot-left + if (!skip_vert) + this->lineTo(rect.fLeft, rect.fTop + ry); // left + this->cubicTo(rect.fLeft, rect.fTop + ry - sy, + rect.fLeft + rx - sx, rect.fTop, + rect.fLeft + rx, rect.fTop); // top-left + if (!skip_hori) + this->lineTo(rect.fRight - rx, rect.fTop); // top + } + this->close(); +} + +void SkPath::addOval(const SkRect& oval, Direction dir) +{ + SkScalar cx = oval.centerX(); + SkScalar cy = oval.centerY(); + SkScalar rx = SkScalarHalf(oval.width()); + SkScalar ry = SkScalarHalf(oval.height()); +#if 1 // these seem faster than using quads (1/2 the number of edges to process) + SkScalar sx = SkScalarMul(rx, CUBIC_ARC_FACTOR); + SkScalar sy = SkScalarMul(ry, CUBIC_ARC_FACTOR); + + this->incReserve(13); + this->moveTo(cx + rx, cy); + if (dir == kCCW_Direction) + { + this->cubicTo(cx + rx, cy - sy, cx + sx, cy - ry, cx, cy - ry); + this->cubicTo(cx - sx, cy - ry, cx - rx, cy - sy, cx - rx, cy); + this->cubicTo(cx - rx, cy + sy, cx - sx, cy + ry, cx, cy + ry); + this->cubicTo(cx + sx, cy + ry, cx + rx, cy + sy, cx + rx, cy); + } + else + { + this->cubicTo(cx + rx, cy + sy, cx + sx, cy + ry, cx, cy + ry); + this->cubicTo(cx - sx, cy + ry, cx - rx, cy + sy, cx - rx, cy); + this->cubicTo(cx - rx, cy - sy, cx - sx, cy - ry, cx, cy - ry); + this->cubicTo(cx + sx, cy - ry, cx + rx, cy - sy, cx + rx, cy); + } +#else + SkScalar sx = SkScalarMul(rx, SK_ScalarTanPIOver8); + SkScalar sy = SkScalarMul(ry, SK_ScalarTanPIOver8); + SkScalar mx = SkScalarMul(rx, SK_ScalarRoot2Over2); + SkScalar my = SkScalarMul(ry, SK_ScalarRoot2Over2); + + this->incReserve(16); + this->moveTo(cx + rx, cy); + if (dir == kCCW_Direction) + { + this->quadTo(cx + rx, cy - sy, cx + mx, cy - my); + this->quadTo(cx + sx, cy - ry, cx + 0, cy - ry); + this->quadTo(cx - sx, cy - ry, cx - mx, cy - my); + this->quadTo(cx - rx, cy - sy, cx - rx, cy - 0); + this->quadTo(cx - rx, cy + sy, cx - mx, cy + my); + this->quadTo(cx - sx, cy + ry, cx - 0, cy + ry); + this->quadTo(cx + sx, cy + ry, cx + mx, cy + my); + this->quadTo(cx + rx, cy + sy, cx + rx, cy + 0); + } + else + { + this->quadTo(cx + rx, cy + sy, cx + mx, cy + my); + this->quadTo(cx + sx, cy + ry, cx - 0, cy + ry); + this->quadTo(cx - sx, cy + ry, cx - mx, cy + my); + this->quadTo(cx - rx, cy + sy, cx - rx, cy - 0); + this->quadTo(cx - rx, cy - sy, cx - mx, cy - my); + this->quadTo(cx - sx, cy - ry, cx + 0, cy - ry); + this->quadTo(cx + sx, cy - ry, cx + mx, cy - my); + this->quadTo(cx + rx, cy - sy, cx + rx, cy + 0); + } +#endif + this->close(); +} + +void SkPath::addCircle(SkScalar x, SkScalar y, SkScalar r, Direction dir) +{ + if (r > 0) + { + SkRect rect; + rect.set(x - r, y - r, x + r, y + r); + this->addOval(rect, dir); + } +} + +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::TypeMask mask = matrix.getType(); + + while ((verb = iter.next(pts)) != kDone_Verb) + { + switch (verb) { + case kMove_Verb: + matrix.mapPoints(&pts[0], &pts[0], 1, mask); + this->moveTo(pts[0]); + break; + case kLine_Verb: + matrix.mapPoints(&pts[1], &pts[1], 1, mask); + this->lineTo(pts[1]); + break; + case kQuad_Verb: + matrix.mapPoints(&pts[1], &pts[1], 2, mask); + this->quadTo(pts[1], pts[2]); + break; + case kCubic_Verb: + matrix.mapPoints(&pts[1], &pts[1], 3, mask); + this->cubicTo(pts[1], pts[2], pts[3]); + break; + case kClose_Verb: + this->close(); + break; + default: + SkASSERT(!"unknown verb"); + } + } +} + +//////////////////////////////////////////////////////////////////////////////////// + +static const U8 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; + + const U8* 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; + + const U8* 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]]; + } +} + +//////////////////////////////////////////////////////////////////////////////////// + +bool SkPath::offset(SkScalar dx, SkScalar dy, SkPath* dst) const +{ + SkMatrix matrix; + + matrix.setTranslate(dx, dy); + return 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]); +} + +bool SkPath::transform(const SkMatrix& matrix, SkPath* dst) const +{ + if (dst == nil) + 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); + return matrix.mapPoints(dst->fPts.begin(), dst->fPts.count()); + } + + else + { + if (this != dst) + { + dst->fVerbs = fVerbs; + dst->fPts.setCount(fPts.count()); + dst->fFillType = fFillType; + } + return matrix.mapPoints(dst->fPts.begin(), fPts.begin(), fPts.count()); + } +} + +//////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////// + +enum NeedMoveToState { + kAfterClose_NeedMoveToState, + kAfterCons_NeedMoveToState, + kAfterPrefix_NeedMoveToState +}; + +SkPath::Iter::Iter() +{ +#ifdef SK_DEBUG + fPts = nil; + 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 = nil; + fVerbStop = nil; + 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 == nil || 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 (nil == 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[]] +*/ + +#include "SkBuffer.h" + +U32 SkPath::flatten(void* storage) const +{ + if (storage) + { + SkWBuffer buffer(storage); + + buffer.write32(fPts.count()); + buffer.write32(fVerbs.count()); + buffer.write32(fFillType); + buffer.write(fPts.begin(), sizeof(SkPoint) * fPts.count()); + buffer.write(fVerbs.begin(), fVerbs.count()); + buffer.padToAlign4(); + } + return 3 * sizeof(int32_t) + sizeof(SkPoint) * fPts.count() + SkAlign4(fVerbs.count()); +} + +void SkPath::unflatten(const void* storage) +{ + SkRBuffer buffer(storage); + + 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()); + buffer.skipToAlign4(); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#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) + stream->write(" ", 1); // can skip the separater if data[i] is negative + 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 + +#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 U8* 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/libs/graphics/sgl/SkPathEffect.cpp b/libs/graphics/sgl/SkPathEffect.cpp new file mode 100644 index 0000000000..cb09db13b3 --- /dev/null +++ b/libs/graphics/sgl/SkPathEffect.cpp @@ -0,0 +1,181 @@ +#include "SkPathEffect.h" +#include "SkPath.h" +#include "SkBuffer.h" + +SkFlattenable::Factory SkFlattenable::getFactory() +{ + return NULL; +} + +void SkFlattenable::flatten(SkWBuffer&) +{ +} + +////////////////////////////////////////////////////////////////////////////////// + +bool SkPathEffect::filterPath(SkPath*, const SkPath&, SkScalar*) +{ + return false; +} + +static SkFlattenable* create_null_patheffect(SkRBuffer&) +{ + return SkNEW(SkPathEffect); +} + +SkFlattenable::Factory SkPathEffect::getFactory() +{ + return create_null_patheffect; +} + +////////////////////////////////////////////////////////////////////////////////// + +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(SkWBuffer& buffer) +{ + buffer.writePtr((void*)fPE0->getFactory()); + buffer.writePtr((void*)fPE1->getFactory()); + fPE0->flatten(buffer); + fPE1->flatten(buffer); +} + +SkPairPathEffect::SkPairPathEffect(SkRBuffer& buffer) +{ + Factory factory0 = (Factory)buffer.readPtr(); + Factory factory1 = (Factory)buffer.readPtr(); + + fPE0 = (SkPathEffect*)factory0(buffer); + fPE1 = (SkPathEffect*)factory1(buffer); +} + +////////////////////////////////////////////////////////////////////////////////// + +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); +} + +SkFlattenable* SkComposePathEffect::CreateProc(SkRBuffer& buffer) +{ + return SkNEW_ARGS(SkComposePathEffect, (buffer)); +} + +SkFlattenable::Factory SkComposePathEffect::getFactory() +{ + return SkComposePathEffect::CreateProc; +} + +////////////////////////////////////////////////////////////////////////////////// + +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); +} + +SkFlattenable* SkSumPathEffect::CreateProc(SkRBuffer& buffer) +{ + return SkNEW_ARGS(SkSumPathEffect, (buffer)); +} + +SkFlattenable::Factory SkSumPathEffect::getFactory() +{ + return SkSumPathEffect::CreateProc; +} + +///////////////////////////////////////////////////////////////////////////////// + +#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(SkRBuffer& buffer) +{ + return SkNEW_ARGS(SkStrokePathEffect, (buffer)); +} + +void SkStrokePathEffect::flatten(SkWBuffer& buffer) +{ + this->INHERITED::flatten(buffer); + + buffer.writeScalar(fWidth); + buffer.writeScalar(fMiter); + buffer.write8(fStyle); + buffer.write8(fJoin); + buffer.write8(fCap); + buffer.padToAlign4(); +} + +SkStrokePathEffect::SkStrokePathEffect(SkRBuffer& buffer) + : SkPathEffect(buffer) +{ + fWidth = buffer.readScalar(); + fMiter = buffer.readScalar(); + fStyle = buffer.readU8(); + fJoin = buffer.readU8(); + fCap = buffer.readU8(); + buffer.skipToAlign4(); +} + + diff --git a/libs/graphics/sgl/SkPathMeasure.cpp b/libs/graphics/sgl/SkPathMeasure.cpp new file mode 100644 index 0000000000..6191724ee2 --- /dev/null +++ b/libs/graphics/sgl/SkPathMeasure.cpp @@ -0,0 +1,623 @@ +#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 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 quad_too_curvy(const SkPoint pts[3]) +{ +#if 0 + SkPoint mid; + SkEvalQuadAtHalf(pts, &mid); + return cheap_dist_exceeds_limit(mid, + SkScalarAve(pts[0].fX, pts[2].fX), + SkScalarAve(pts[0].fY, pts[2].fY)); +#else + // 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; +#endif +} + +static bool cubic_too_curvy(const SkPoint pts[4]) +{ + SkPoint third; + + // test 1/3 + SkEvalCubicAt(pts, SK_Scalar1/3, &third, nil, nil); + if (cheap_dist_exceeds_limit(third, + SkScalarInterp(pts[0].fX, pts[3].fX, SK_Scalar1/3), + SkScalarInterp(pts[0].fY, pts[2].fY, SK_Scalar1/3))) + return true; + + // test 2/3 + SkEvalCubicAt(pts, SK_Scalar1*2/3, &third, nil, nil); + return cheap_dist_exceeds_limit(third, + SkScalarInterp(pts[0].fX, pts[3].fX, SK_Scalar1*2/3), + SkScalarInterp(pts[0].fY, pts[2].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) + { + // SkDebugf("seg dist=%g t=%d p=%d\n", seg->fDistance, seg->fTValue, seg->fPtIndex); + + 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->setUnit(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, nil); + 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 = nil; + 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 nil 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 == nil) + 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 == nil) + { + 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, nil); + 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/libs/graphics/sgl/SkProcSpriteBlitter.cpp b/libs/graphics/sgl/SkProcSpriteBlitter.cpp new file mode 100644 index 0000000000..9f0592feaf --- /dev/null +++ b/libs/graphics/sgl/SkProcSpriteBlitter.cpp @@ -0,0 +1,38 @@ +#if 0 // experimental + +class SkProcSpriteBlitter : public SkSpriteBlitter { +public: + typedef void (*Proc)(void* dst, const void* src, int count, const U32 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 U32* ctable = nil; + + 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; + U8 fSrcShift, fDstShift; +}; + +#endif diff --git a/libs/graphics/sgl/SkRasterizer.cpp b/libs/graphics/sgl/SkRasterizer.cpp new file mode 100644 index 0000000000..5e3dc995ae --- /dev/null +++ b/libs/graphics/sgl/SkRasterizer.cpp @@ -0,0 +1,45 @@ +#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(SkRBuffer&) {} + +bool SkRasterizer::rasterize(const SkPath& fillPath, const SkMatrix& matrix, + const SkRect16* clipBounds, SkMaskFilter* filter, + SkMask* mask, SkMask::CreateMode mode) +{ + SkRect16 storage; + + if (clipBounds && filter && SkMask::kJustRenderImage_CreateMode != mode) + { + SkPoint16 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 SkRect16* clipBounds, + SkMask* mask, SkMask::CreateMode mode) +{ + SkPath devPath; + + fillPath.transform(matrix, &devPath); + return SkDraw::DrawToMask(devPath, clipBounds, NULL, NULL, mask, mode); +} + diff --git a/libs/graphics/sgl/SkRefCnt.cpp b/libs/graphics/sgl/SkRefCnt.cpp new file mode 100644 index 0000000000..7c3d59c430 --- /dev/null +++ b/libs/graphics/sgl/SkRefCnt.cpp @@ -0,0 +1,36 @@ +#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 = nil; + return true; + } + return false; +} + +SkRefCnt* SkAutoUnref::detach() +{ + SkRefCnt* obj = fObj; + + fObj = nil; + return obj; +} diff --git a/libs/graphics/sgl/SkRegion_path.cpp b/libs/graphics/sgl/SkRegion_path.cpp new file mode 100644 index 0000000000..ab45c8f493 --- /dev/null +++ b/libs/graphics/sgl/SkRegion_path.cpp @@ -0,0 +1,478 @@ +#include "SkRegionPriv.h" +#include "SkBlitter.h" +#include "SkScan.h" +#include "SkTDArray.h" +#include "SkPath.h" + +class SkRgnBuilder : public SkBlitter { +public: + virtual ~SkRgnBuilder(); + + void init(int maxHeight, int maxTransitions); + void done() { (void)this->collapsWithPrev(); } + + int computeRunCount() const; + void copyToRect(SkRect16*) const; + void copyToRgn(S16 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 { + S16 fLastY; + S16 fXCount; + + S16* firstX() const { return (S16*)(this + 1); } + Scanline* nextScanline() const { return (Scanline*)((S16*)(this + 1) + fXCount); } + }; + S16* fStorage; + Scanline* fCurrScanline; + Scanline* fPrevScanline; + S16* fCurrXPtr; // points at next avialable x[] in fCurrScanline + S16 fTop; // first Y value + + bool collapsWithPrev() + { + if (fPrevScanline != nil && + fPrevScanline->fLastY + 1 == fCurrScanline->fLastY && + fPrevScanline->fXCount == fCurrScanline->fXCount && + !memcmp(fPrevScanline->firstX(), + fCurrScanline->firstX(), + fCurrScanline->fXCount * sizeof(S16))) + { + // update the height of fPrevScanline + fPrevScanline->fLastY = fCurrScanline->fLastY; + return true; + } + return false; + } +}; + +SkRgnBuilder::~SkRgnBuilder() +{ + sk_free(fStorage); +} + +void SkRgnBuilder::init(int maxHeight, int maxTransitions) +{ + int count = maxHeight * (3 + maxTransitions); + + // add maxTransitions to have slop for working buffer + fStorage = (S16*)sk_malloc_throw((count + 3 + maxTransitions) * sizeof(S16)); + + fCurrScanline = nil; // signal empty collection + fPrevScanline = nil; // signal first scanline +} + +void SkRgnBuilder::blitH(int x, int y, int width) +{ + if (fCurrScanline == nil) // first time + { + fTop = SkToS16(y); + fCurrScanline = (Scanline*)fStorage; + fCurrScanline->fLastY = SkToS16(y); + fCurrXPtr = fCurrScanline->firstX(); + } + else + { + SkASSERT(y >= fCurrScanline->fLastY); + + if (y > fCurrScanline->fLastY) + { + // if we get here, we're done with fCurrScanline + fCurrScanline->fXCount = SkToS16((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 = SkToS16(y - 1); + fCurrScanline->fXCount = 0; + fCurrScanline = fCurrScanline->nextScanline(); + } + // setup for the new curr line + fCurrScanline->fLastY = SkToS16(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] = SkToS16(x + width); + else + { + fCurrXPtr[0] = SkToS16(x); + fCurrXPtr[1] = SkToS16(x + width); + fCurrXPtr += 2; + } +} + +int SkRgnBuilder::computeRunCount() const +{ + if (fCurrScanline == nil) + return 0; + + const S16* line = fStorage; + const S16* stop = (const S16*)fCurrScanline; + + return 2 + (int)(stop - line); +} + +void SkRgnBuilder::copyToRect(SkRect16* r) const +{ + SkASSERT(fCurrScanline != nil); + SkASSERT((const S16*)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(S16 runs[]) const +{ + SkASSERT(fCurrScanline != nil); + SkASSERT((const S16*)fCurrScanline - fStorage > 4); + + const Scanline* line = (const Scanline*)fStorage; + const Scanline* stop = fCurrScanline; + + *runs++ = fTop; + do { + *runs++ = SkToS16(line->fLastY + 1); + int count = line->fXCount; + if (count) + { + memcpy(runs, line->firstX(), count * sizeof(S16)); + runs += count; + } + *runs++ = kRunTypeSentinel; + line = line->nextScanline(); + } while (line < stop); + SkASSERT(line == stop); + *runs = kRunTypeSentinel; +} + +static int count_path_runtype_values(const SkPath& path, int* itop, int* ibot) +{ + static const U8 gPathVerbToInitialPointCount[] = { + 0, // kMove_Verb + 1, // kLine_Verb + 2, // kQuad_Verb + 3, // kCubic_Verb + 0, // kClose_Verb + 0 // kDone_Verb + }; + + static const U8 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) + { + int ptCount = gPathVerbToInitialPointCount[verb]; + if (ptCount) + { + maxEdges += gPathVerbToMaxEdges[verb]; + for (int i = ptCount - 1; i >= 0; --i) + { + if (top > pts[i].fY) + top = pts[i].fY; + else if (bot < pts[i].fY) + bot = pts[i].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 (path.isEmpty() || clip && clip->isEmpty()) + 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 = clip->count_runtype_values(&clipTop, &clipBot); + + int top = SkMax32(pathTop, clipTop); + int bot = SkMin32(pathBot, clipBot); + + if (top >= bot) + return this->setEmpty(); + + SkRgnBuilder builder; + + builder.init(bot - top, SkMax32(pathTransitions, clipTransitions)); + SkScan::FillPath(path, clip, &builder); + builder.done(); + + int count = builder.computeRunCount(); + if (count == 0) + { + return this->setEmpty(); + } + else if (count == kRectRegionRuns) + { + builder.copyToRect(&fBounds); + this->setRect(fBounds); + } + else + { + SkRegion tmp; + + tmp.fRunHead = RunHead::Alloc(count); + builder.copyToRgn(tmp.fRunHead->runs()); + compute_run_bounds(tmp.fRunHead->runs(), count, &tmp.fBounds); + this->swap(tmp); + } + SkDEBUGCODE(this->validate();) + return true; +} + +///////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////// + +struct SkPrivPoint { + int fX, fY; + + friend int operator==(const SkPrivPoint& a, const SkPrivPoint& b) + { + return a.fX == b.fX && a.fY == b.fY; + } + friend int operator!=(const SkPrivPoint& a, const SkPrivPoint& b) + { + return a.fX != b.fX || a.fY != b.fY; + } +}; + +struct SkPrivLine { + SkPrivPoint fP0, fP1; + int fWinding; + SkPrivLine* fNext, *fPrev; + + void set(int x, int y0, int y1) + { + fP0.fX = x; + fP0.fY = y0; + fP1.fX = x; + fP1.fY = y1; + fWinding = y1 > y0; // 1: up, 0: down + fNext = fPrev = nil; + } + + void detach() + { + fNext->fPrev = fPrev; + fPrev->fNext = fNext; + } + + void attachNext(SkPrivLine* next) + { + next->fNext = fNext; + next->fPrev = this; + fNext->fPrev = next; + fNext = next; + } +}; + +static SkPrivLine* find_match(SkPrivLine* ctr, SkPrivLine* skip) +{ + const SkPrivPoint pt = skip->fP1; + int winding = skip->fWinding; + + SkPrivLine* start = ctr; + + SkPrivLine* closest_pos = nil; + int dist_pos = 0x7FFFFFFF; + SkPrivLine* closest_neg = nil; + int dist_neg = -0x7FFFFFFF; + + do { + // find a Line one the same scan as pt + if (ctr != skip && (ctr->fP0.fY == pt.fY || ctr->fP1.fY == pt.fY)) + { + int dist = ctr->fP0.fX - pt.fX; // keep the sign + + if (dist == 0) + { + if (winding == ctr->fWinding) // quick accept + goto FOUND; + else + goto NEXT; // quick reject + } + + if (dist < 0) + { + if (dist > dist_neg) + { + dist_neg = dist; + closest_neg = ctr; + } + } + else + { + if (dist < dist_pos) + { + dist_pos = dist; + closest_pos = ctr; + } + } + } + NEXT: + ctr = ctr->fNext; + } while (ctr != start); + + SkASSERT(closest_pos != nil || closest_neg != nil); + + if (closest_pos == nil) + ctr = closest_neg; + else if (closest_neg == nil) + ctr = closest_pos; + else + { + if (closest_neg->fP0.fY != pt.fY) + ctr = closest_pos; + else if (closest_pos->fP0.fY != pt.fY) + ctr = closest_neg; + else + { + if (closest_pos->fWinding != closest_neg->fWinding) + { + if (closest_pos->fWinding == winding) + ctr = closest_pos; + else + ctr = closest_neg; + } + else + { + if (winding == 0) + ctr = closest_pos; + else + ctr = closest_neg; + } + } + } + +FOUND: + SkASSERT(ctr && ctr->fP0.fY == pt.fY); + return ctr; +} + +static void LinesToPath(SkPrivLine lines[], int count, SkPath* path) +{ + SkASSERT(count > 1); + + // turn the array into a linked list + lines[0].fNext = &lines[1]; + lines[0].fPrev = &lines[count - 1]; + for (int i = 1; i < count - 1; i++) + { + lines[i].fNext = &lines[i+1]; + lines[i].fPrev = &lines[i-1]; + } + lines[count - 1].fNext = &lines[0]; + lines[count - 1].fPrev = &lines[count - 2]; + + SkPrivLine* head = lines; + + // loop through looking for contours + while (count > 0) + { + SkPrivLine* ctr = head; + SkPrivLine* first = ctr; + head = head->fNext; + + path->moveTo(SkIntToScalar(ctr->fP0.fX), SkIntToScalar(ctr->fP0.fY)); + do { + SkPrivLine* next = find_match(head, ctr); + + if (ctr->fP1 != next->fP0) + { + path->lineTo(SkIntToScalar(ctr->fP1.fX), SkIntToScalar(ctr->fP1.fY)); // Vertical + path->lineTo(SkIntToScalar(next->fP0.fX), SkIntToScalar(next->fP0.fY)); // Horzontal + } + if (head == next) + head = head->fNext; + next->detach(); + count -= 1; + ctr = next; + } while (ctr != first); + path->close(); + ctr->detach(); + count -= 1; + } +// SkASSERT(count == 0); +} + +bool SkRegion::getBoundaryPath(SkPath* path) const +{ + if (this->isEmpty()) + return false; + + const SkRect16& 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<SkPrivLine> lines; + + for (const SkRect16& r = iter.rect(); !iter.done(); iter.next()) + { + SkPrivLine* line = lines.append(2); + line[0].set(r.fLeft, r.fBottom, r.fTop); + line[1].set(r.fRight, r.fTop, r.fBottom); + } + + LinesToPath(lines.begin(), lines.count(), path); + return true; +} + + diff --git a/libs/graphics/sgl/SkScalerContext.cpp b/libs/graphics/sgl/SkScalerContext.cpp new file mode 100644 index 0000000000..9e44bdcfcd --- /dev/null +++ b/libs/graphics/sgl/SkScalerContext.cpp @@ -0,0 +1,498 @@ +#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" + +#define ComputeBWRowBytes(width) (((unsigned)(width) + 7) >> 3) + +static uint16_t compute_rowbytes(unsigned format, unsigned width) +{ + if (format == SkMask::kBW_Format) + width = ComputeBWRowBytes(width); + return SkToU16(width); +} + +size_t SkGlyph::computeImageSize() const +{ + size_t size = fRowBytes * 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) + { + SkRBuffer buffer(data, len); + SkFlattenable::Factory fact = (SkFlattenable::Factory)buffer.readPtr(); + SkASSERT(fact); + obj = fact(buffer); + SkASSERT(buffer.pos() == buffer.size()); + } + return obj; +} + +SkScalerContext::SkScalerContext(const SkDescriptor* desc) + : fPathEffect(NULL), fMaskFilter(NULL) +{ + memset(fAuxContext, 0, sizeof(fAuxContext)); + + 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 aa %d join %d\n", + rec->fFrameWidth, rec->fMiterLimit, rec->fUseHints, rec->fFrameAndFill, + rec->fDoAA, 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(); + + for (unsigned i = 0; i < SK_ARRAY_COUNT(fAuxContext); i++) + delete fAuxContext[i]; +} + +static void glyph2mask(const SkGlyph& glyph, SkMask* mask) +{ + SkASSERT(&glyph && mask); + + mask->fImage = (uint8_t*)glyph.fImage; + mask->fBounds.set(glyph.fLeft, glyph.fTop, + glyph.fLeft + glyph.fWidth, + glyph.fTop + glyph.fHeight); + mask->fRowBytes = glyph.fRowBytes; + mask->fFormat = glyph.fMaskFormat; +} + +void SkScalerContext::getMetrics(SkGlyph* glyph) +{ + this->generateMetrics(glyph); + // assume that we're handling this glyph + glyph->fUseAuxContext = false; + + if (0 == glyph->fGlyphID) + { + SkFontHost::ScalerContextID id = SkFontHost::FindScalerContextIDForUnichar(glyph->fCharCode); + if (SK_UnknownAuxScalerContextID != id) + { + SkASSERT(id > 0 && (unsigned)id <= SK_ARRAY_COUNT(fAuxContext)); + SkScalerContext* ctx = fAuxContext[id - 1]; + if (NULL == ctx) + { + ctx = SkFontHost::CreateScalerContextFromID(id, fRec); + SkASSERT(ctx); + fAuxContext[id - 1] = ctx; + } + ctx->generateMetrics(glyph); + glyph->fUseAuxContext = true; + } + } + + if (0 == glyph->fWidth) + 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; + SkRect16 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 = SkToU8(fRec.fDoAA ? SkMask::kA8_Format : SkMask::kBW_Format); + + if (fMaskFilter) + { + SkMask src, dst; + SkMatrix matrix; + + glyph2mask(*glyph, &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; + } + } + + glyph->fRowBytes = compute_rowbytes(glyph->fMaskFormat, glyph->fWidth); +} + +//#define PLAY_WITH_GAMMA + +#ifdef PLAY_WITH_GAMMA +static SkFixed interp(SkFixed a, SkFixed b, int scale) // scale is [0..255] +{ + return a + ((b - a) * scale >> 8); +} + +static void filter_image(uint8_t image[], size_t size) +{ + static uint8_t gGammaTable[256]; + static bool gInit; + + if (!gInit) + { + for (int i = 0; i < 256; i++) + { + SkFixed n = i * 257; + n += n >> 15; + SkASSERT(n >= 0 && n <= SK_Fixed1); + + // n = SkFixedSqrt(n); + n = interp(SkFixedMul(n, n), n, 0xDD); + + n = n * 255 >> 16; + // SkDebugf("morph %d -> %d\n", i, n); + gGammaTable[i] = SkToU8(n); + } + gInit = true; + } + + const uint8_t* table = gGammaTable; + uint8_t* stop = image + size; + while (image < stop) + { + *image = table[*image]; + image += 1; + } +} +#endif + +void SkScalerContext::getImage(const SkGlyph& origGlyph) +{ + const SkGlyph* glyph = &origGlyph; + + SkGlyph tmpGlyph; + if (fMaskFilter) // restore the prefilter bounds + { + tmpGlyph.fCharCode = origGlyph.fCharCode; + + // 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; + + mask.fFormat = SkMask::kA8_Format; + mask.fRowBytes = glyph->fRowBytes; + mask.fBounds.set(glyph->fLeft, + glyph->fTop, + glyph->fLeft + glyph->fWidth, + glyph->fTop + glyph->fHeight); + mask.fImage = (uint8_t*)glyph->fImage; + memset(glyph->fImage, 0, glyph->fRowBytes * glyph->fHeight); + + 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 (fRec.fDoAA) + { + config = SkBitmap::kA8_Config; + paint.setAntiAliasOn(true); + } + else + { + config = SkBitmap::kA1_Config; + paint.setAntiAliasOn(false); + } + + clip.setRect(0, 0, glyph->fWidth, glyph->fHeight); + matrix.setTranslate(-SkIntToScalar(glyph->fLeft), -SkIntToScalar(glyph->fTop)); + bm.setConfig(config, glyph->fWidth, glyph->fHeight); + bm.setPixels(glyph->fImage); + memset(glyph->fImage, 0, bm.height() * bm.rowBytes()); + + draw.fClip = &clip; + draw.fMatrix = &matrix; + draw.fDevice = &bm; + draw.fBounder = NULL; + draw.drawPath(devPath, paint); + } + } + else + { + SkScalerContext* ctx = this; + if (glyph->fUseAuxContext) + { + SkFontHost::ScalerContextID id = SkFontHost::FindScalerContextIDForUnichar(glyph->fCharCode); + SkASSERT(id > 0 && (unsigned)id <= SK_ARRAY_COUNT(fAuxContext)); + ctx = fAuxContext[id - 1]; + SkASSERT(ctx); + } + ctx->generateImage(*glyph); + } + + if (fMaskFilter) + { + SkMask srcM, dstM; + SkMatrix matrix; + + SkASSERT(SkMask::k3D_Format != glyph->fMaskFormat); // the src glyph image shouldn't be 3D + glyph2mask(*glyph, &srcM); + fRec.getMatrixFrom2x2(&matrix); + + if (fMaskFilter->filterMask(&dstM, srcM, matrix, NULL)) + { + if (true) // hack until I can figure out why the assert below sometimes fails + { + int width = SkFastMin32(origGlyph.fWidth, dstM.fBounds.width()); + int height = SkFastMin32(origGlyph.fHeight, dstM.fBounds.height()); + int srcRB = origGlyph.fRowBytes; + int dstRB = 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; + + while (--height >= 0) + { + memcpy(dst, src, width); + src += srcRB; + dst += dstRB; + } + } + else + { + SkASSERT(origGlyph.fWidth == dstM.fBounds.width()); + SkASSERT(origGlyph.fTop == dstM.fBounds.fTop); + SkASSERT(origGlyph.fLeft == dstM.fBounds.fLeft); + SkASSERT(origGlyph.fHeight == dstM.fBounds.height()); + SkASSERT(origGlyph.computeImageSize() == dstM.computeTotalImageSize()); + + memcpy(glyph->fImage, dstM.fImage, dstM.computeTotalImageSize()); + } + SkMask::FreeImage(dstM.fImage); + } + } +} + +void SkScalerContext::getPath(const SkGlyph& glyph, SkPath* path) +{ + this->internalGetPath(glyph, NULL, path, NULL); +} + +void SkScalerContext::getLineHeight(SkPoint* above, SkPoint* below) +{ + this->generateLineHeight(above, below); + + // apply any mods due to effects (e.g. stroking, etc.)... +} + +/////////////////////////////////////////////////////////////////////// + +void SkScalerContext::internalGetPath(const SkGlyph& glyph, SkPath* fillPath, SkPath* devPath, SkMatrix* fillToDevMatrix) +{ + SkPath path; + + this->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(fRec.fFrameAndFill != 0); + 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); + } +} + + +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, 0, 0); + if (fPreSkewX) + m->postSkew(fPreSkewX, 0, 0, 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/libs/graphics/sgl/SkScan.cpp b/libs/graphics/sgl/SkScan.cpp new file mode 100644 index 0000000000..b8534e6342 --- /dev/null +++ b/libs/graphics/sgl/SkScan.cpp @@ -0,0 +1,32 @@ +#include "SkScan.h" +#include "SkBlitter.h" +#include "SkRegion.h" + +void SkScan::FillRect(const SkRect& rect, const SkRegion* clip, SkBlitter* blitter) +{ + SkRect16 r; + + rect.round(&r); + SkScan::FillDevRect(r, clip, blitter); +} + +void SkScan::FillDevRect(const SkRect16& r, const SkRegion* clip, SkBlitter* blitter) +{ + if (!r.isEmpty()) + { + if (clip) + { + SkRegion::Cliperator cliper(*clip, r); + const SkRect16& rr = cliper.rect(); + + while (!cliper.done()) + { + blitter->blitRect(rr.fLeft, rr.fTop, rr.width(), rr.height()); + cliper.next(); + } + } + else + blitter->blitRect(r.fLeft, r.fTop, r.width(), r.height()); + } +} + diff --git a/libs/graphics/sgl/SkScan.h b/libs/graphics/sgl/SkScan.h new file mode 100644 index 0000000000..c8b62c8d71 --- /dev/null +++ b/libs/graphics/sgl/SkScan.h @@ -0,0 +1,30 @@ +#ifndef SkScan_DEFINED +#define SkScan_DEFINED + +#include "SkRect.h" + +class SkRegion; +class SkBlitter; +class SkPath; + +class SkScan { +public: + static void FillDevRect(const SkRect16&, const SkRegion* clip, SkBlitter*); + static void FillRect(const SkRect&, const SkRegion* clip, SkBlitter*); + static void FillPath(const SkPath&, const SkRegion* clip, SkBlitter*); + + 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 AntiFillRect(const SkRect&, const SkRegion* clip, SkBlitter*); + 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*); +}; + +#endif diff --git a/libs/graphics/sgl/SkScanPriv.h b/libs/graphics/sgl/SkScanPriv.h new file mode 100644 index 0000000000..2fce606165 --- /dev/null +++ b/libs/graphics/sgl/SkScanPriv.h @@ -0,0 +1,25 @@ +#ifndef SkScanPriv_DEFINED +#define SkScanPriv_DEFINED + +#include "SkScan.h" +#include "SkBlitter.h" + +class SkScanClipper { +public: + SkScanClipper(SkBlitter* blitter, const SkRegion* clip, const SkRect16& bounds); + + SkBlitter* getBlitter() const { return fBlitter; } + const SkRect16* getClipRect() const { return fClipRect; } + +private: + SkRectClipBlitter fRectBlitter; + SkRgnClipBlitter fRgnBlitter; + SkBlitter* fBlitter; + const SkRect16* fClipRect; +}; + +void sk_fill_path(const SkPath& path, const SkRect16* clipRect, SkBlitter* blitter, + const SkRect16& ir, int shiftEdgesUp); + +#endif + diff --git a/libs/graphics/sgl/SkScan_AntiPath.cpp b/libs/graphics/sgl/SkScan_AntiPath.cpp new file mode 100644 index 0000000000..592edff8d1 --- /dev/null +++ b/libs/graphics/sgl/SkScan_AntiPath.cpp @@ -0,0 +1,209 @@ +#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 SuperBlitter : public SkBlitter { +public: + SuperBlitter(SkBlitter* realBlitter, const SkRegion* clip, const SkRect16& ir); + virtual ~SuperBlitter() + { + sk_free(fRuns.fRuns); + } + void flush(); + + virtual void blitH(int x, int y, int width); + virtual void blitAntiH(int x, int y, const SkAlpha antialias[], const S16 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?"); + } + +private: + SkBlitter* fRealBlitter; + int fCurrIY; + int fWidth, fLeft, fSuperLeft; + SkAlphaRuns fRuns; + + SkDEBUGCODE(int fCurrX;) + SkDEBUGCODE(int fCurrY;) +}; + +SuperBlitter::SuperBlitter(SkBlitter* realBlitter, const SkRegion* clip, const SkRect16& ir) +{ + fRealBlitter = realBlitter; + + int width = ir.width(); + + // extra one to store the zero at the end + fRuns.fRuns = (S16*)sk_malloc_throw((width + 1 + (width + 2)/2) * sizeof(S16)); + fRuns.fAlpha = (U8*)(fRuns.fRuns + width + 1); + fRuns.reset(width); + + fLeft = ir.fLeft; + fSuperLeft = ir.fLeft << SHIFT; + fWidth = ir.width(); + fCurrIY = -1; + SkDEBUGCODE(fCurrX = -1; fCurrY = -1;) +} + +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; +#if 0 // I should just need to assert + SkASSERT(x >= 0); +#else + // hack, until I figure out why my cubics (I think) go beyond the bounds + if (x < 0) + { + width += x; + x = 0; + } +#endif + +#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 +} + +////////////////////////////////////////////////////////////////////////////////////////// + +static int overflows_short(int value) +{ + return value - (short)value; +} + +void SkScan::AntiFillPath(const SkPath& path, const SkRegion* clip, SkBlitter* blitter) +{ + if (clip && clip->isEmpty()) + return; + + SkRect r; + SkRect16 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)) + return; + + SkScanClipper clipper(blitter, clip, ir); + const SkRect16* clipRect = clipper.getClipRect(); + + blitter = clipper.getBlitter(); + if (blitter == nil) // clipped out + return; + + SuperBlitter superBlit(blitter, clip, ir); + SkRect16 superIR, superRect, *superClipRect = nil; + + superIR.set(ir.fLeft << SHIFT, ir.fTop << SHIFT, + ir.fRight << SHIFT, ir.fBottom << SHIFT); + + if (clipRect) + { + superRect.set( clipRect->fLeft << SHIFT, clipRect->fTop << SHIFT, + clipRect->fRight << SHIFT, clipRect->fBottom << SHIFT); + superClipRect = &superRect; + } + + sk_fill_path(path, superClipRect, &superBlit, superIR, SHIFT); + superBlit.flush(); +} diff --git a/libs/graphics/sgl/SkScan_Antihair.cpp b/libs/graphics/sgl/SkScan_Antihair.cpp new file mode 100644 index 0000000000..81f503bece --- /dev/null +++ b/libs/graphics/sgl/SkScan_Antihair.cpp @@ -0,0 +1,397 @@ +#include "SkScan.h" +#include "SkBlitter.h" +#include "SkRegion.h" +#include "SkFDot6.h" + +#define HLINE_STACK_BUFFER 100 + +//#define TEST_GAMMA + +#ifdef TEST_GAMMA + static U8 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, U8 alpha) +{ + SkASSERT(count > 0); + + S16 runs[HLINE_STACK_BUFFER + 1]; + U8 aa[HLINE_STACK_BUFFER]; + + aa[0] = ApplyGamma(gGammaTable, alpha); + do { + int n = count; + if (n > HLINE_STACK_BUFFER) + n = HLINE_STACK_BUFFER; + + runs[0] = SkToS16(n); + runs[n] = 0; + blitter->blitAntiH(x, y, aa, runs); + x += n; + count -= n; + } while (count > 0); +} + +static void hline(int x, int stopx, SkFixed fy, SkFixed /*slope*/, SkBlitter* blitter) +{ + SkASSERT(x < stopx); + int count = stopx - x; + fy += SK_Fixed1/2; + + int y = fy >> 16; + U8 a = (U8)(fy >> 8); + + // lower line + if (a) + call_hline_blitter(blitter, x, y, count, a); + + // upper line + a = (U8)(255 - a); + if (a) + call_hline_blitter(blitter, x, y - 1, count, a); +} + +static void horish(int x, int stopx, SkFixed fy, SkFixed dy, SkBlitter* blitter) +{ + SkASSERT(x < stopx); + +#ifdef TEST_GAMMA + const U8* gamma = gGammaTable; +#endif + S16 runs[2]; + U8 aa[1]; + + runs[0] = 1; + runs[1] = 0; + + fy += SK_Fixed1/2; + do { + int lower_y = fy >> 16; + U8 a = (U8)(fy >> 8); + + if (a) + { + aa[0] = ApplyGamma(gamma, a); + blitter->blitAntiH(x, lower_y, aa, runs); + // the clipping blitters might edit runs, but should not affect us + SkASSERT(runs[0] == 1); + SkASSERT(runs[1] == 0); + } + a = (U8)(255 - a); + if (a) + { + aa[0] = ApplyGamma(gamma, a); + blitter->blitAntiH(x, lower_y - 1, aa, runs); + // the clipping blitters might edit runs, but should not affect us + SkASSERT(runs[0] == 1); + SkASSERT(runs[1] == 0); + } + fy += dy; + } while (++x < stopx); +} + +static void vline(int y, int stopy, SkFixed fx, SkFixed /*slope*/, SkBlitter* blitter) +{ + SkASSERT(y < stopy); + fx += SK_Fixed1/2; + + int x = fx >> 16; + int a = (U8)(fx >> 8); + + if (a) + blitter->blitV(x, y, stopy - y, ApplyGamma(gGammaTable, a)); + a = 255 - a; + if (a) + blitter->blitV(x - 1, y, stopy - y, ApplyGamma(gGammaTable, a)); +} + +static void vertish(int y, int stopy, SkFixed fx, SkFixed dx, SkBlitter* blitter) +{ + SkASSERT(y < stopy); +#ifdef TEST_GAMMA + const U8* gamma = gGammaTable; +#endif + S16 runs[3]; + U8 aa[2]; + + runs[0] = 1; + runs[2] = 0; + + fx += SK_Fixed1/2; + do { + int x = fx >> 16; + U8 a = (U8)(fx >> 8); + + aa[0] = ApplyGamma(gamma, 255 - a); + aa[1] = ApplyGamma(gamma, a); + // the clippng blitters might overwrite this guy, so we have to reset it each time + runs[1] = 1; + blitter->blitAntiH(x - 1, y, aa, runs); + // the clipping blitters might edit runs, but should not affect us + SkASSERT(runs[0] == 1); + SkASSERT(runs[2] == 0); + fx += dx; + } while (++y < stopy); +} + +typedef void (*LineProc)(int istart, int istop, SkFixed fstart, SkFixed slope, SkBlitter*); + +static inline SkFixed fastfixdiv(SkFDot6 a, SkFDot6 b) +{ + SkASSERT((a << 16 >> 16) == a); + SkASSERT(b != 0); + return (a << 16) / b; +} +static inline SkFDot6 fastfixmul(SkFixed fixed, SkFDot6 b) +{ + SkASSERT(SkAbs32(fixed) <= SK_Fixed1 && SkAbs32(b) <= SkIntToFDot6(511)); + return (fixed * b + 0x8000) >> 16; +} + +static void do_anti_hairline(SkFDot6 x0, SkFDot6 y0, SkFDot6 x1, SkFDot6 y1, + const SkRect16* clip, SkBlitter* blitter) +{ + // check that we're no larger than 511 pixels (so we can do a faster div). + // if we are, subdivide and call again + + if (SkAbs32(x1 - x0) > SkIntToFDot6(511) || SkAbs32(y1 - y0) > SkIntToFDot6(511)) + { + int hx = (x0 + x1) >> 1; + int hy = (y0 + y1) >> 1; + do_anti_hairline(x0, y0, hx, hy, clip, blitter); + do_anti_hairline(hx, hy, x1, y1, clip, blitter); + return; + } + + int istart, istop; + SkFixed fstart, slope; + LineProc proc; + + if (SkAbs32(x1 - x0) > SkAbs32(y1 - y0)) // mostly horizontal + { + if (x0 > x1) // we want to go left-to-right + { + SkTSwap<SkFDot6>(x0, x1); + SkTSwap<SkFDot6>(y0, y1); + } + istart = SkFDot6Round(x0); + istop = SkFDot6Round(x1); + if (istart == istop) // too short to draw + return; + + if (y0 == y1) // completely horizontal, take fast case + { + slope = 0; + fstart = SkFDot6ToFixed(y0); + proc = hline; + } + else + { + slope = fastfixdiv(y1 - y0, x1 - x0); + SkASSERT(slope >= -SK_Fixed1 && slope <= SK_Fixed1); + fstart = SkFDot6ToFixed(y0 + fastfixmul(slope, (32 - x0) & 63)); + proc = horish; + } + + if (clip) + { + if (istart >= clip->fRight || istop <= clip->fLeft) + return; + if (istart < clip->fLeft) + { + fstart += slope * (clip->fLeft - istart); + istart = clip->fLeft; + } + if (istop > clip->fRight) + istop = clip->fRight; + SkASSERT(istart <= istop); + if (istart == istop) + return; + // now test if our Y values are completely inside the clip + int top, bottom; + if (slope >= 0) // T2B + { + top = SkFixedFloor(fstart - SK_FixedHalf); + bottom = SkFixedCeil(fstart + (istop - istart - 1) * slope + SK_FixedHalf); + } + else // B2T + { + bottom = SkFixedCeil(fstart + SK_FixedHalf); + top = SkFixedFloor(fstart + (istop - istart - 1) * slope - SK_FixedHalf); + } + if (top >= clip->fBottom || bottom <= clip->fTop) + return; + if (clip->fTop <= top && clip->fBottom >= bottom) + clip = nil; + } + } + else // mostly vertical + { + if (y0 > y1) // we want to go top-to-bottom + { + SkTSwap<SkFDot6>(x0, x1); + SkTSwap<SkFDot6>(y0, y1); + } + istart = SkFDot6Round(y0); + istop = SkFDot6Round(y1); + if (istart == istop) // too short to draw + return; + + if (x0 == x1) + { + slope = 0; + fstart = SkFDot6ToFixed(x0); + proc = vline; + } + else + { + slope = fastfixdiv(x1 - x0, y1 - y0); + SkASSERT(slope <= SK_Fixed1 && slope >= -SK_Fixed1); + fstart = SkFDot6ToFixed(x0 + fastfixmul(slope, (32 - y0) & 63)); + proc = vertish; + } + + if (clip) + { + if (istart >= clip->fBottom || istop <= clip->fTop) + return; + if (istart < clip->fTop) + { + fstart += slope * (clip->fTop - istart); + istart = clip->fTop; + } + if (istop > clip->fBottom) + istop = clip->fBottom; + SkASSERT(istart <= istop); + if (istart == istop) + return; + // now test if our X values are completely inside the clip + int left, right; + if (slope >= 0) // L2R + { + left = SkFixedFloor(fstart - SK_FixedHalf); + right = SkFixedCeil(fstart + (istop - istart - 1) * slope + SK_FixedHalf); + } + else // R2L + { + right = SkFixedCeil(fstart + SK_FixedHalf); + left = SkFixedFloor(fstart + (istop - istart - 1) * slope - SK_FixedHalf); + } + if (left >= clip->fRight || right <= clip->fLeft) + return; + if (clip->fLeft <= left && clip->fRight >= right) + clip = nil; + } + } + + SkRectClipBlitter rectClipper; + if (clip) + { + rectClipper.init(blitter, *clip); + blitter = &rectClipper; + } + proc(istart, istop, fstart, slope, blitter); +} + +void SkScan::AntiHairLine(const SkPoint& pt0, const SkPoint& pt1, + const SkRegion* clip, SkBlitter* blitter) +{ + if (clip && clip->isEmpty()) + return; + + SkASSERT(clip == nil || !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); + SkRect16 ir; + + ir.set( SkFDot6Round(left) - 1, + SkFDot6Round(top) - 1, + SkFDot6Round(right) + 1, + SkFDot6Round(bottom) + 1); + + if (clip->quickReject(ir)) + return; + if (!clip->quickContains(ir)) + { + SkRegion::Cliperator iter(*clip, ir); + const SkRect16* 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, nil, blitter); +} + +void SkScan::AntiHairRect(const SkRect& rect, const SkRegion* clip, SkBlitter* blitter) +{ + if (clip) + { + SkRect16 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 = nil; + } + + 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); +} + + diff --git a/libs/graphics/sgl/SkScan_Hairline.cpp b/libs/graphics/sgl/SkScan_Hairline.cpp new file mode 100644 index 0000000000..2af9e12163 --- /dev/null +++ b/libs/graphics/sgl/SkScan_Hairline.cpp @@ -0,0 +1,254 @@ +#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; + SkRect16 ir; + SkPoint pts[2]; + + pts[0] = pt0; + pts[1] = pt1; + r.set(pts, 2); + r.roundOut(&ir); + + if (clip->quickReject(ir)) + return; + if (clip->quickContains(ir)) + clip = nil; + else + { + blitter = clipper.apply(blitter, clip); + } + } + + SkFDot6 dx = x1 - x0; + SkFDot6 dy = y1 - y0; + + if (SkAbs32(dx) > SkAbs32(dy)) // mostly horizontal + { + if (x0 > x1) // we want to go left-to-right + { + SkTSwap<SkFDot6>(x0, x1); + SkTSwap<SkFDot6>(y0, y1); + } + int ix0 = SkFDot6Round(x0); + int ix1 = SkFDot6Round(x1); + if (ix0 == ix1) // too short to draw + return; + + SkFixed slope = SkFixedDiv(dy, dx); + SkFixed startY = SkFDot6ToFixed(y0 + SkFixedMul(slope, (32 - x0) & 63)); + + horiline(ix0, ix1, startY, slope, blitter); + } + else // mostly vertical + { + if (y0 > y1) // we want to go top-to-bottom + { + SkTSwap<SkFDot6>(x0, x1); + SkTSwap<SkFDot6>(y0, y1); + } + int iy0 = SkFDot6Round(y0); + int iy1 = SkFDot6Round(y1); + if (iy0 == iy1) // too short to draw + return; + + SkFixed slope = SkFixedDiv(dx, dy); + SkFixed startX = SkFDot6ToFixed(x0 + SkFixedMul(slope, (32 - y0) & 63)); + + vertline(iy0, iy1, startX, slope, blitter); + } +} + +void SkScan::HairRect(const SkRect& rect, const SkRegion* clip, SkBlitter* blitter) +{ + SkPoint p0, p1; + + p0.set(rect.fLeft, rect.fTop); + p1.set(rect.fRight, rect.fTop); + SkScan::HairLine(p0, p1, clip, blitter); + p0.set(rect.fRight, rect.fBottom); + SkScan::HairLine(p0, p1, clip, blitter); + p1.set(rect.fLeft, rect.fBottom); + SkScan::HairLine(p0, p1, clip, blitter); + p0.set(rect.fLeft, rect.fTop); + SkScan::HairLine(p0, p1, clip, blitter); +} + +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "SkPath.h" +#include "SkGeometry.h" + +static bool quad_too_curvy(const SkPoint pts[3]) +{ + return true; +} + +static void hairquad(const SkPoint pts[3], const SkRegion* clip, SkBlitter* blitter, int level, + void (*lineproc)(const SkPoint&, const SkPoint&, const SkRegion* clip, SkBlitter*)) +{ +#if 1 + if (level > 0 && quad_too_curvy(pts)) + { + SkPoint tmp[5]; + + SkChopQuadAtHalf(pts, tmp); + hairquad(tmp, clip, blitter, level - 1, lineproc); + hairquad(&tmp[2], clip, blitter, level - 1, lineproc); + } + else + lineproc(pts[0], pts[2], clip, blitter); +#else + lineproc(pts[0], pts[1], clip, blitter); + lineproc(pts[1], pts[2], clip, blitter); +#endif +} + +static bool cubic_too_curvy(const SkPoint pts[4]) +{ + return true; +} + +static void haircubic(const SkPoint pts[4], const SkRegion* clip, SkBlitter* blitter, int level, + void (*lineproc)(const SkPoint&, const SkPoint&, const SkRegion*, SkBlitter*)) +{ + if (level > 0 && cubic_too_curvy(pts)) + { + SkPoint tmp[7]; + + SkChopCubicAt(pts, tmp, SK_Scalar1/2); + haircubic(tmp, clip, blitter, level - 1, lineproc); + haircubic(&tmp[3], clip, blitter, level - 1, lineproc); + } + else + lineproc(pts[0], pts[3], clip, blitter); +} + +#define kMaxCubicSubdivideLevel 6 +#define kMaxQuadSubdivideLevel 5 + +static void hair_path(const SkPath& path, const SkRegion* clip, SkBlitter* blitter, + void (*lineproc)(const SkPoint&, const SkPoint&, const SkRegion*, SkBlitter*)) +{ + if (path.isEmpty()) + return; + + const SkRect16* clipR = nil; + + if (clip) + { + SkRect bounds; + SkRect16 ibounds; + + path.computeBounds(&bounds, SkPath::kFast_BoundsType); + bounds.roundOut(&ibounds); + ibounds.inset(-1, -1); + + if (clip->quickReject(ibounds)) + return; + + if (clip->quickContains(ibounds)) + clip = nil; + else + clipR = &clip->getBounds(); + } + + SkPath::Iter iter(path, false); + SkPoint pts[4]; + SkPath::Verb verb; + + while ((verb = iter.next(pts)) != SkPath::kDone_Verb) + { + switch (verb) { + case SkPath::kLine_Verb: + lineproc(pts[0], pts[1], clip, blitter); + break; + case SkPath::kQuad_Verb: + hairquad(pts, clip, blitter, kMaxQuadSubdivideLevel, lineproc); + break; + case SkPath::kCubic_Verb: + haircubic(pts, clip, blitter, kMaxCubicSubdivideLevel, lineproc); + break; + default: + break; + } + } +} + +void SkScan::HairPath(const SkPath& path, const SkRegion* clip, SkBlitter* blitter) +{ + hair_path(path, clip, blitter, SkScan::HairLine); +} + +void SkScan::AntiHairPath(const SkPath& path, const SkRegion* clip, SkBlitter* blitter) +{ + hair_path(path, clip, blitter, SkScan::AntiHairLine); +} + +//////////////////////////////////////////////////////////////////////////////// + +void SkScan::FrameRect(const SkRect& r, SkScalar diameter, const SkRegion* clip, SkBlitter* blitter) +{ + SkASSERT(diameter > 0); + + if (r.isEmpty()) + return; + + SkScalar radius = diameter / 2; + SkRect outer, tmp; + + outer.set( r.fLeft - radius, r.fTop - radius, + r.fRight + radius, r.fBottom + radius); + + if (r.width() <= diameter || r.height() <= diameter) + { + SkScan::FillRect(outer, clip, blitter); + return; + } + + tmp.set(outer.fLeft, outer.fTop, outer.fRight, outer.fTop + diameter); + SkScan::FillRect(tmp, clip, blitter); + tmp.fTop = outer.fBottom - diameter; + tmp.fBottom = outer.fBottom; + SkScan::FillRect(tmp, clip, blitter); + + tmp.set(outer.fLeft, outer.fTop + diameter, outer.fLeft + diameter, outer.fBottom - diameter); + SkScan::FillRect(tmp, clip, blitter); + tmp.fLeft = outer.fRight - diameter; + tmp.fRight = outer.fRight; + SkScan::FillRect(tmp, clip, blitter); +} + diff --git a/libs/graphics/sgl/SkScan_Path.cpp b/libs/graphics/sgl/SkScan_Path.cpp new file mode 100644 index 0000000000..2518bb5e87 --- /dev/null +++ b/libs/graphics/sgl/SkScan_Path.cpp @@ -0,0 +1,430 @@ +#include "SkScanPriv.h" +#include "SkBlitter.h" +#include "SkEdge.h" +#include "SkGeometry.h" +#include "SkPath.h" +#include "SkRegion.h" +#include "SkTemplates.h" + +#define kEDGE_HEAD_Y SK_MinS16 +#define kEDGE_TAIL_Y SK_MaxS16 + +#ifdef SK_DEBUG + static void validate_sort(const SkEdge* edge) + { + int y = kEDGE_HEAD_Y; + + while (edge->fFirstY != SK_MaxS16) + { + edge->validate(); + SkASSERT(y <= edge->fFirstY); + + y = edge->fFirstY; + edge = edge->fNext; + } + } +#else + #define validate_sort(edge) +#endif + +static inline void remove_edge(SkEdge* edge) +{ + edge->fPrev->fNext = edge->fNext; + edge->fNext->fPrev = edge->fPrev; +} + +static inline void swap_edges(SkEdge* prev, SkEdge* next) +{ + SkASSERT(prev->fNext == next && next->fPrev == prev); + + // remove prev from the list + prev->fPrev->fNext = next; + next->fPrev = prev->fPrev; + + // insert prev after next + prev->fNext = next->fNext; + next->fNext->fPrev = prev; + next->fNext = prev; + prev->fPrev = next; +} + +static void backward_insert_edge_based_on_x(SkEdge* edge SkDECLAREPARAM(int, curr_y)) +{ + SkFixed x = edge->fX; + + for (;;) + { + SkEdge* prev = edge->fPrev; + + // add 1 to curr_y since we may have added new edges (built from curves) + // that start on the next scanline + SkASSERT(prev && prev->fFirstY <= curr_y + 1); + + if (prev->fX <= x) + break; + + swap_edges(prev, edge); + } +} + +static void insert_new_edges(SkEdge* newEdge, int curr_y) +{ + SkASSERT(newEdge->fFirstY >= curr_y); + + while (newEdge->fFirstY == curr_y) + { + SkEdge* next = newEdge->fNext; + backward_insert_edge_based_on_x(newEdge SkPARAM(curr_y)); + newEdge = next; + } +} + +#ifdef SK_DEBUG +static void validate_edges_for_y(const SkEdge* edge, int curr_y) +{ + while (edge->fFirstY <= curr_y) + { + SkASSERT(edge->fPrev && edge->fNext); + SkASSERT(edge->fPrev->fNext == edge); + SkASSERT(edge->fNext->fPrev == edge); + SkASSERT(edge->fFirstY <= edge->fLastY); + + SkASSERT(edge->fPrev->fX <= edge->fX); + edge = edge->fNext; + } +} +#else + #define validate_edges_for_y(edge, curr_y) +#endif + +#if defined _WIN32 && _MSC_VER >= 1300 // disable warning : local variable used without having been initialized +#pragma warning ( push ) +#pragma warning ( disable : 4701 ) +#endif + +static void walk_edges(SkEdge* prevHead, SkPath::FillType fillType, SkBlitter* blitter, + int stop_y) +{ + validate_sort(prevHead->fNext); + + int curr_y = prevHead->fNext->fFirstY; + int windingMask = (fillType == SkPath::kWinding_FillType) ? -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); + + 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); + } + + 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); + } +} + +#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 SkRect16* 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, 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]; +} + +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(nil)) != 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; +} + +void sk_fill_path(const SkPath& path, const SkRect16* clipRect, SkBlitter* blitter, + const SkRect16& ir, int shiftEdgesUp) +{ + SkASSERT(&path && blitter); + + size_t size; + int maxCount = worst_case_edge_count(path, &size); + + 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 = nil; + headEdge.fNext = edge; + headEdge.fFirstY = kEDGE_HEAD_Y; + headEdge.fX = SK_MinS32; + edge->fPrev = &headEdge; + + tailEdge.fPrev = last; + tailEdge.fNext = nil; + 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, path.getFillType(), blitter, stop_y); +} + +///////////////////////////////////////////////////////////////////////////////////// + +SkScanClipper::SkScanClipper(SkBlitter* blitter, const SkRegion* clip, const SkRect16& ir) +{ + fBlitter = nil; // nil means blit nothing + fClipRect = nil; + + if (clip) + { + fClipRect = &clip->getBounds(); + if (!SkRect16::Intersects(*fClipRect, ir)) // completely clipped out + return; + + if (clip->isRect()) + { + if (fClipRect->contains(ir)) + fClipRect = nil; + 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 && clip->isEmpty()) + return; + + SkRect r; + SkRect16 ir; + + path.computeBounds(&r, SkPath::kFast_BoundsType); + r.round(&ir); + if (ir.isEmpty()) + return; + + SkScanClipper clipper(blitter, clip, ir); + + blitter = clipper.getBlitter(); + if (blitter) + sk_fill_path(path, clipper.getClipRect(), blitter, ir, 0); +} + diff --git a/libs/graphics/sgl/SkShader.cpp b/libs/graphics/sgl/SkShader.cpp new file mode 100644 index 0000000000..6d1ac3efac --- /dev/null +++ b/libs/graphics/sgl/SkShader.cpp @@ -0,0 +1,370 @@ +#include "SkShader.h" +#include "SkPaint.h" + +SkShader::SkShader() : fLocalMatrix(nil) +{ +} + +SkShader::~SkShader() +{ + sk_free(fLocalMatrix); +} + +void SkShader::setLocalMatrix(const SkMatrix& matrix) +{ + if (matrix.isIdentity()) + { + if (fLocalMatrix) + { + sk_free(fLocalMatrix); + fLocalMatrix = nil; + } + } + else + { + if (fLocalMatrix == nil) + fLocalMatrix = (SkMatrix*)sk_malloc_throw(sizeof(SkMatrix)); + *fLocalMatrix = matrix; + } +} + +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)) + { + fInverseMapPtProc = fTotalInverse.getMapPtProc(); + fTotalInverseClass = (U8)SkShader::ComputeMatrixClass(fTotalInverse); + return true; + } + return false; +} + +U32 SkShader::getFlags() +{ + return 0; +} + +#include "SkColorPriv.h" + +void SkShader::shadeSpanOpaque16(int x, int y, U16 span16[], int count) +{ + SkASSERT(span16); + SkASSERT(count > 0); + SkASSERT(this->canCallShadeSpanOpaque16()); + + // basically, if we get here, the subclass screwed up + SkASSERT(!"kHasSpan16 flag is set, but shadeSpanOpaque16() 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, U8 alpha[], int count) +{ + SkASSERT(count > 0); + + SkPMColor colors[kTempColorCount]; + + while ((count -= kTempColorCount) >= 0) + { + this->shadeSpan(x, y, colors, kTempColorCount); + x += kTempColorCount; + + const U8* srcA = (const U8*)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 U8* srcA = (const U8*)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 U8* srcA = (const U8*)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, nil, nil)) + mc = kFixedStepInX_MatrixClass; + else + mc = kPerspective_MatrixClass; + } + return mc; +} + +//////////////////////////////////////////////////////////////////////////////////////// + +#if 0 +SkPairShader::SkPairShader(SkShader* s0, SkShader* s1) + : fShader0(s0), fShader1(s1) +{ + s0->safeRef(); + s1->safeRef(); +} + +SkPairShader::~SkPairShader() +{ + fShader1->safeUnref(); + fShader0->safeUnref(); +} + +U32 SkPairShader::getFlags() +{ + SkASSERT(fShader0 || fShader1); + + U32 flags = 0-1U; + + if (fShader0) + flags &= fShader0->getFlags(); + if (fShader1) + flags &= fShader1->getFlags(); + return flags; +} + +bool SkPairShader::setContext( const SkBitmap& device, + const SkPaint& paint, + const SkMatrix& matrix) +{ + if (fShader0 == nil && fShader1 == nil) + return false; + + const SkMatrix* localM = this->getLocalMatrix(); + SkMatrix tmp; + + if (localM) + { + tmp.setConcat(matrix, *localM); + localM = &tmp; + } + else + localM = &matrix; + + // wonder if some subclasses will want OR instead of AND? + + if (fShader0 && !fShader0->setContext(device, paint, *localM)) + return false; + if (fShader1 && !fShader1->setContext(device, paint, *localM)) + return false; + return true; +} + +//////////////////////////////////////////////////////////////////////////////////////// + +void SkComposeShader::shadeSpan(int x, int y, SkPMColor span[], int count) +{ + SkShader* s0 = this->getShader0(); + SkShader* s1 = this->getShader1(); + + if (s1) + s1->shadeSpan(x, y, span, count); + if (s0) + s0->shadeSpan(x, y, span, count); +} + +void SkComposeShader::shadeSpanOpaque16(int x, int y, U16 span[], int count) +{ + SkShader* s0 = this->getShader0(); + SkShader* s1 = this->getShader1(); + + if (s1) + s1->shadeSpanOpaque16(x, y, span, count); + if (s0) + s0->shadeSpanOpaque16(x, y, span, count); +} + +void SkComposeShader::shadeSpanAlpha(int x, int y, U8 span[], int count) +{ + SkShader* s0 = this->getShader0(); + SkShader* s1 = this->getShader1(); + + if (s1) + s1->shadeSpanAlpha(x, y, span, count); + if (s0) + s0->shadeSpanAlpha(x, y, span, count); +} + +//////////////////////////////////////////////////////////////////////////////////////// + +#include "SkXfermode.h" +#include "SkColorPriv.h" +#include "SkBitmap.h" + +SkSumShader::SkSumShader(SkShader* s0, SkShader* s1, U8CPU weight) + : SkPairShader(s0, s1), fBuffer(nil), fMode(nil), fWeight(SkToU8(weight)) +{ +} + +SkSumShader::SkSumShader(SkShader* s0, SkShader* s1, SkXfermode* mode) + : SkPairShader(s0, s1), fBuffer(nil), fMode(mode), fWeight(0xFF) +{ + mode->safeRef(); +} + +SkSumShader::~SkSumShader() +{ + fMode->safeUnref(); + sk_free(fBuffer); +} + +bool SkSumShader::setContext(const SkBitmap& device, + const SkPaint& paint, + const SkMatrix& matrix) +{ + if (!this->INHERITED::setContext(device, paint, matrix)) + return false; + + if (fBuffer == nil) + fBuffer = (SkPMColor*)sk_malloc_throw(device.width() * sizeof(SkPMColor)); + else + fBuffer = (SkPMColor*)sk_realloc_throw(fBuffer, device.width() * sizeof(SkPMColor)); + return true; +} + +void SkSumShader::shadeSpan(int x, int y, SkPMColor dst[], int count) +{ + SkShader* s0 = this->getShader0(); + SkShader* s1 = this->getShader1(); + SkASSERT(s0 || s1); + + if (s0 == nil) + s1->shadeSpan(x, y, dst, count); + else + { + s0->shadeSpan(x, y, dst, count); + if (s1) + { + SkPMColor* src = fBuffer; + s1->shadeSpan(x, y, src, count); + + if (fMode) + fMode->xfer32(dst, src, count, nil); + else + { + unsigned weight = fWeight; + for (int i = 0; i < count; i++) + dst[i] = SkBlendARGB32(src[i], dst[i], weight); + } + } + } +} + +void SkSumShader::shadeSpanOpaque16(int x, int y, U16 dst[], int count) +{ + SkShader* s0 = this->getShader0(); + SkShader* s1 = this->getShader1(); + SkASSERT(s0 || s1); + + if (s0 == nil) + s1->shadeSpanOpaque16(x, y, dst, count); + else + { + s0->shadeSpanOpaque16(x, y, dst, count); + if (s1) + { + if (fMode) + { + SkPMColor* src = fBuffer; + s1->shadeSpan(x, y, src, count); + fMode->xfer16(dst, src, count, nil); + } + else + { + U16* src = (U16*)fBuffer; + s1->shadeSpanOpaque16(x, y, src, count); + + unsigned scale = SkAlpha255To256(fWeight); + for (int i = 0; i < count; i++) + dst[i] = (U16)SkBlendRGB16(src[i], dst[i], scale); + } + } + } +} + +void SkSumShader::shadeSpanAlpha(int x, int y, U8 dst[], int count) +{ + SkShader* s0 = this->getShader0(); + SkShader* s1 = this->getShader1(); + SkASSERT(s0 || s1); + + if (s0 == nil) + s1->shadeSpanAlpha(x, y, dst, count); + else + { + s0->shadeSpanAlpha(x, y, dst, count); + if (s1) + { + if (fMode) + { + SkPMColor* src = fBuffer; + s1->shadeSpan(x, y, src, count); + fMode->xferA8(dst, src, count, nil); + } + else + { + U8* src = (U8*)fBuffer; + s1->shadeSpanAlpha(x, y, src, count); + + unsigned scale = SkAlpha255To256(fWeight); + for (int i = 0; i < count; i++) + dst[i] = (U8)SkAlphaBlend(src[i], dst[i], scale); + } + } + } +} +#endif diff --git a/libs/graphics/sgl/SkSinTable.h b/libs/graphics/sgl/SkSinTable.h new file mode 100644 index 0000000000..2c4c11d488 --- /dev/null +++ b/libs/graphics/sgl/SkSinTable.h @@ -0,0 +1,268 @@ +#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 U16 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/libs/graphics/sgl/SkSpriteBlitter.h b/libs/graphics/sgl/SkSpriteBlitter.h new file mode 100644 index 0000000000..b85690e29f --- /dev/null +++ b/libs/graphics/sgl/SkSpriteBlitter.h @@ -0,0 +1,41 @@ +#ifndef SkSpriteBlitter_DEFINED +#define SkSpriteBlitter_DEFINED + +#include "SkBlitter.h" +#include "SkBitmap.h" + +class SkXfermode; + +class SkSpriteBlitter : public SkBlitter { +public: + SkSpriteBlitter(const SkBitmap& source); + virtual ~SkSpriteBlitter(); + + void setup(const SkBitmap& device, int left, int top) + { + fDevice = &device; + fLeft = left; + fTop = top; + } + + // overrides +#ifdef SK_DEBUG + virtual void blitH(int x, int y, int width); + virtual void blitAntiH(int x, int y, const SkAlpha antialias[], const S16 runs[]); + virtual void blitV(int x, int y, int height, SkAlpha alpha); + virtual void blitMask(const SkMask&, const SkRect16& clip); +#endif + + static SkSpriteBlitter* ChooseD16(const SkBitmap& source, SkXfermode* mode, U8 alpha, + void* storage, size_t storageSize); + static SkSpriteBlitter* ChooseD32(const SkBitmap& source, SkXfermode* mode, U8 alpha, + void* storage, size_t storageSize); + +protected: + const SkBitmap* fDevice; + const SkBitmap* fSource; + int fLeft, fTop; +}; + +#endif + diff --git a/libs/graphics/sgl/SkSpriteBlitterTemplate.h b/libs/graphics/sgl/SkSpriteBlitterTemplate.h new file mode 100644 index 0000000000..23d2fbf872 --- /dev/null +++ b/libs/graphics/sgl/SkSpriteBlitterTemplate.h @@ -0,0 +1,60 @@ + +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; + SkSPRITE_DST_TYPE* dst = fDevice->SkSPRITE_DST_GETADDR(x, y); + const 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 + int w = width; + do { + SkSPRITE_SRC_TYPE sc = *s++; + SkSPRITE_BLIT_PIXEL(d, sc); + d += 1; + } while (--w != 0); + dst = (SkSPRITE_DST_TYPE*)((char*)dst + dstRB); + src = (const SkSPRITE_SRC_TYPE*)((const char*)src + srcRB); + SkSPRITE_NEXT_ROW + } 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 + diff --git a/libs/graphics/sgl/SkSpriteBlitter_ARGB32.cpp b/libs/graphics/sgl/SkSpriteBlitter_ARGB32.cpp new file mode 100644 index 0000000000..80ab5a5ce5 --- /dev/null +++ b/libs/graphics/sgl/SkSpriteBlitter_ARGB32.cpp @@ -0,0 +1,81 @@ +#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); \ + U32 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); + uint32_t* dst = fDevice->getAddr32(x, y); + const 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 = (uint32_t*)((char*)dst + dstRB); + src = (const uint32_t*)((const char*)src + srcRB); + } while (--height != 0); + } +}; + +////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////// + +#include "SkTemplatesPriv.h" + +SkSpriteBlitter* SkSpriteBlitter::ChooseD32(const SkBitmap& source, SkXfermode* mode, U8 alpha, + void* storage, size_t storageSize) +{ + SkSpriteBlitter* blitter = nil; + + switch (source.getConfig()) { + case SkBitmap::kARGB_8888_Config: + if (mode == nil) + { + if (alpha == 255) + { + 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/libs/graphics/sgl/SkSpriteBlitter_RGB16.cpp b/libs/graphics/sgl/SkSpriteBlitter_RGB16.cpp new file mode 100644 index 0000000000..ab65bae777 --- /dev/null +++ b/libs/graphics/sgl/SkSpriteBlitter_RGB16.cpp @@ -0,0 +1,300 @@ +#include "SkSpriteBlitter.h" +#include "SkTemplates.h" +#include "SkUtils.h" +#include "SkColorPriv.h" + +#define D16_S32A_Opaque_Pixel(dst, sc) \ +do { \ + if (sc) \ + { \ + unsigned srcA = SkGetPackedA32(sc); \ + unsigned result = SkPixel32ToPixel16(sc); \ + if (srcA != 0xFF) \ + result += SkAlphaMulRGB16(*dst, SkAlpha255To256(255 - srcA)); \ + *dst = SkToU16(result); \ + } \ +} while (0) + +#define SkSPRITE_CLASSNAME Sprite_D16_S32A_Opaque +#define SkSPRITE_ARGS +#define SkSPRITE_FIELDS +#define SkSPRITE_INIT +#define SkSPRITE_DST_TYPE uint16_t +#define SkSPRITE_SRC_TYPE uint32_t +#define SkSPRITE_DST_GETADDR getAddr16 +#define SkSPRITE_SRC_GETADDR getAddr32 +#define SkSPRITE_PREAMBLE(srcBM, x, y) +#define SkSPRITE_BLIT_PIXEL(dst, src) D16_S32A_Opaque_Pixel(dst, src) +#define SkSPRITE_NEXT_ROW +#define SkSPRITE_POSTAMBLE(srcBM) +#include "SkSpriteBlitterTemplate.h" + +static inline void D16_S32A_Blend_Pixel_helper(U16* dst, U32 sc, unsigned src_scale) +{ + 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); +} + +#define D16_S32A_Blend_Pixel(dst, sc, src_scale) do { if (sc) D16_S32A_Blend_Pixel_helper(dst, sc, src_scale); } while (0) + + +#define SkSPRITE_CLASSNAME Sprite_D16_S32A_Blend +#define SkSPRITE_ARGS , U8 alpha +#define SkSPRITE_FIELDS U8 fSrcAlpha; +#define SkSPRITE_INIT fSrcAlpha = alpha; +#define SkSPRITE_DST_TYPE uint16_t +#define SkSPRITE_SRC_TYPE uint32_t +#define SkSPRITE_DST_GETADDR getAddr16 +#define SkSPRITE_SRC_GETADDR getAddr32 +#define SkSPRITE_PREAMBLE(srcBM, x, y) unsigned src_scale = SkAlpha255To256(fSrcAlpha); +#define SkSPRITE_BLIT_PIXEL(dst, src) D16_S32A_Blend_Pixel(dst, src, src_scale) +#define SkSPRITE_NEXT_ROW +#define SkSPRITE_POSTAMBLE(srcBM) +#include "SkSpriteBlitterTemplate.h" + +////////////////////////////////////////////////////////////////////////////////////////////////// + +#define SkSPRITE_CLASSNAME Sprite_D16_S32_Opaque +#define SkSPRITE_ARGS +#define SkSPRITE_FIELDS +#define SkSPRITE_INIT +#define SkSPRITE_DST_TYPE uint16_t +#define SkSPRITE_SRC_TYPE uint32_t +#define SkSPRITE_DST_GETADDR getAddr16 +#define SkSPRITE_SRC_GETADDR getAddr32 +#define SkSPRITE_PREAMBLE(srcBM, x, y) +#define SkSPRITE_BLIT_PIXEL(dst, src) *dst = SkPixel32ToPixel16_ToU16(src) +#define SkSPRITE_NEXT_ROW +#define SkSPRITE_POSTAMBLE(srcBM) +#include "SkSpriteBlitterTemplate.h" + +#if 1 +#define D16_S32_Blend_Pixel(dst, sc, scale) \ +do { \ + U16 dc = *dst; \ + *dst = SkPackRGB16( SkAlphaBlend(SkPacked32ToR16(sc), SkGetPackedR16(dc), scale), \ + SkAlphaBlend(SkPacked32ToG16(sc), SkGetPackedG16(dc), scale), \ + SkAlphaBlend(SkPacked32ToB16(sc), SkGetPackedB16(dc), scale)); \ +} while (0) +#else +static inline void D16_S32_Blend_Pixel(uint16_t* dst, uint32_t sc, int scale) +{ + U16 dc = *dst; + *dst = SkPackRGB16( SkAlphaBlend(SkPacked32ToR16(sc), SkGetPackedR16(dc), scale), + SkAlphaBlend(SkPacked32ToG16(sc), SkGetPackedG16(dc), scale), + SkAlphaBlend(SkPacked32ToB16(sc), SkGetPackedB16(dc), scale)); +} +#endif + +#define SkSPRITE_CLASSNAME Sprite_D16_S32_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 uint32_t +#define SkSPRITE_DST_GETADDR getAddr16 +#define SkSPRITE_SRC_GETADDR getAddr32 +#define SkSPRITE_PREAMBLE(srcBM, x, y) int src_scale = SkAlpha255To256(fSrcAlpha); +#define SkSPRITE_BLIT_PIXEL(dst, src) D16_S32_Blend_Pixel(dst, src, src_scale) +#define SkSPRITE_NEXT_ROW +#define SkSPRITE_POSTAMBLE(srcBM) +#include "SkSpriteBlitterTemplate.h" + +////////////////////////////////////////////////////////////////////////////////////////////////// + +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) + { + uint16_t* dst = fDevice->getAddr16(x, y); + const 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 = SkToU16(SkBlendRGB16(sc, dc, scale)); \ + } while (0) + +#define SkSPRITE_CLASSNAME Sprite_D16_S16_Blend +#define SkSPRITE_ARGS , U8 alpha +#define SkSPRITE_FIELDS U8 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 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" + +////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////// + +#include "SkTemplatesPriv.h" + +SkSpriteBlitter* SkSpriteBlitter::ChooseD16(const SkBitmap& source, SkXfermode* mode, U8 alpha, + void* storage, size_t storageSize) +{ + SkSpriteBlitter* blitter = nil; + + switch (source.getConfig()) { + case SkBitmap::kARGB_8888_Config: + if (mode == nil) + { + bool srcIsOpaque = source.isOpaque(); + if (alpha == 255) + { + if (srcIsOpaque) + SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D16_S32_Opaque, storage, storageSize, (source)); + else + SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D16_S32A_Opaque, storage, storageSize, (source)); + } + else + { + if (srcIsOpaque) + SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D16_S32_Blend, storage, storageSize, (source, alpha)); + else + SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D16_S32A_Blend, storage, storageSize, (source, alpha)); + } + } + break; + case SkBitmap::kRGB_565_Config: +#ifdef SK_SUPPORT_16_8_BITMAP + if (source.getA8Plane()) + { + if (mode == nil) + { + if (alpha == 255) + SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D16_S816_Opaque, storage, storageSize, (source)); + else + SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D16_S816_Blend, storage, storageSize, (source, alpha)); + } + } + else +#endif + if (mode == nil) + { + if (alpha == 255) + 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 (mode == nil) + { + if (source.getColorTable()->getFlags() & SkColorTable::kColorsAreOpaque_Flag) + { + if (alpha == 255) + 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 (alpha == 255) + 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/libs/graphics/sgl/SkString.cpp b/libs/graphics/sgl/SkString.cpp new file mode 100644 index 0000000000..1859f98154 --- /dev/null +++ b/libs/graphics/sgl/SkString.cpp @@ -0,0 +1,602 @@ +#include "SkString.h" +#include "SkFixed.h" +#include "SkUtils.h" +#include <stdarg.h> + +bool SkStrStartsWith(const char string[], const char prefix[]) +{ + SkASSERT(string); + SkASSERT(prefix); + return !strncmp(string, prefix, strlen(prefix)); +} + +bool SkStrEndsWith(const char string[], const char suffix[]) +{ + SkASSERT(string); + SkASSERT(suffix); + size_t strLen = strlen(string); + size_t suffixLen = strlen(suffix); + return strLen >= suffixLen && + !strncmp(string + strLen - suffixLen, suffix, suffixLen); +} + +int SkStrStartsWithOneOf(const char string[], const char prefixes[]) +{ + int index = 0; + do { + const char* limit = strchr(prefixes, '\0'); + if (!strncmp(string, prefixes, limit - prefixes)) + return index; + prefixes = limit + 1; + index++; + } while (prefixes[0]); + return -1; +} + +char* SkStrAppendS32(char string[], int32_t dec) +{ + SkDEBUGCODE(char* start = string;) + + char buffer[SkStrAppendS32_MaxSize]; + char* p = buffer + sizeof(buffer); + bool neg = false; + + if (dec < 0) + { + neg = true; + dec = -dec; + } + do { + *--p = SkToU8('0' + dec % 10); + dec /= 10; + } while (dec != 0); + if (neg) + *--p = '-'; + + SkASSERT(p >= buffer); + char* stop = buffer + sizeof(buffer); + while (p < stop) + *string++ = *p++; + + SkASSERT(string - start <= SkStrAppendS32_MaxSize); + return string; +} + +char* SkStrAppendScalar(char string[], SkScalar value) +{ + SkDEBUGCODE(char* start = string;) + + SkFixed x = SkScalarToFixed(value); + + if (x < 0) + { + *string++ = '-'; + x = -x; + } + + unsigned frac = x & 0xFFFF; + x >>= 16; + if (frac == 0xFFFF) // need to do this to "round up", since 65535/65536 is closer to 1 than to .9999 + { + x += 1; + frac = 0; + } + string = SkStrAppendS32(string, x); + + // now handle the fractional part (if any) + if (frac) + { + static const uint16_t gTens[] = { 1000, 100, 10, 1 }; + const uint16_t* tens = gTens; + + x = SkFixedRound(frac * 10000); + SkASSERT(x < 10000); + *string++ = '.'; + do { + unsigned powerOfTen = *tens++; + *string++ = SkToU8('0' + x / powerOfTen); + x %= powerOfTen; + } while (x != 0); + } + + SkASSERT(string - start <= SkStrAppendScalar_MaxSize); + return string; +} + +//////////////////////////////////////////////////////////////////////////////////// + +#define kMaxRefCnt_SkString SK_MaxU16 + +// the 3 values are [length] [refcnt] [terminating zero data] +static const U16 gEmptyRec[3] = { 0, 0, 0 }; + +SkString::Rec* SkString::AllocRec(const char text[], U16CPU len) +{ + Rec* rec; + + if (len == 0) + rec = (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(sizeof(Rec) + 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 != (Rec*)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(((Rec*)gEmptyRec)->fLength == 0); + SkASSERT(((Rec*)gEmptyRec)->fRefCnt == 0); + SkASSERT(((Rec*)gEmptyRec)->data()[0] == 0); + + if (fRec != (Rec*)gEmptyRec) + { + SkASSERT(fRec->fLength > 0); + SkASSERT(fRec->fRefCnt > 0); + SkASSERT(fRec->data()[fRec->fLength] == 0); + } + SkASSERT(fStr == c_str()); +} +#endif + +/////////////////////////////////////////////////////////////////////// + +SkString::SkString() : fRec((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(nil, (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 != nil); + + 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 = (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 U16 src[]) +{ + int count = 0; + + while (src[count]) + count += 1; + + 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 (int i = 0; i < count; i++) + p[i] = SkToU8(src[i]); + p[count] = 0; + } + else + { + SkString tmp(count); + char* p = tmp.writable_str(); + + for (int 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, S32 dec) +{ + char buffer[SkStrAppendS32_MaxSize]; + char* stop = SkStrAppendS32(buffer, dec); + this->insert(offset, buffer, stop - buffer); +} + +void SkString::insertHex(size_t offset, U32 hex, int minDigits) +{ + minDigits = SkPin32(minDigits, 0, 8); + + static const char gHex[] = "0123456789ABCDEF"; + + char buffer[8]; + char* p = buffer + sizeof(buffer); + + do { + *--p = gHex[hex & 0xF]; + hex >>= 4; + minDigits -= 1; + } while (hex != 0); + while (--minDigits >= 0) + *--p = '0'; + + SkASSERT(p >= buffer); + this->insert(offset, p, buffer + sizeof(buffer) - p); +} + +void SkString::insertScalar(size_t offset, SkScalar value) +{ + char buffer[SkStrAppendScalar_MaxSize]; + char* stop = SkStrAppendScalar(buffer, value); + this->insert(offset, buffer, stop - buffer); +} + +/////////////////////////////////////////////////////////////////////////// + +//#include <stdarg.h> +#if defined(SK_BUILD_FOR_WIN) || defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_UNIX) + #include <stdio.h> +#endif + +void SkString::printf(const char format[], ...) +{ + static const size_t kBufferSize = 100; + + char buffer[kBufferSize + 1]; + +#ifdef SK_BUILD_FOR_WIN + va_list args; + va_start(args, format); + _vsnprintf(buffer, kBufferSize, format, args); + va_end(args); +#elif defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_UNIX) + va_list args; + va_start(args, format); + vsnprintf(buffer, kBufferSize, format, args); + va_end(args); +#else + buffer[0] = 0; +#endif + + this->set(buffer, strlen(buffer)); +} + +/////////////////////////////////////////////////////////////////////////// + +void SkString::remove(size_t offset, size_t length) +{ + size_t size = this->size(); + + if (offset < size) + { + if (offset + length > size) + length = size - offset; + if (length > 0) + { + SkASSERT(size > length); + SkString tmp(size - length); + char* dst = tmp.writable_str(); + const char* src = this->c_str(); + + if (offset) + { + SkASSERT(offset <= tmp.size()); + memcpy(dst, src, offset); + } + size_t tail = size - offset - length; + SkASSERT((S32)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 = (U16*)sk_malloc_throw((len + 1) * sizeof(U16)); + + U16* 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(nil, 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/libs/graphics/sgl/SkStroke.cpp b/libs/graphics/sgl/SkStroke.cpp new file mode 100644 index 0000000000..f0c66546b0 --- /dev/null +++ b/libs/graphics/sgl/SkStroke.cpp @@ -0,0 +1,586 @@ +#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->setUnit(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->setUnit(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 : nil); + fOuter.reversePathTo(fInner); + // cap the start + fCapper(&fOuter, fFirstPt, -fFirstNormal, fFirstOuterPt, fPrevIsLine ? &fInner : nil); + fOuter.close(); + } + } + fInner.reset(); + fSegmentCount = -1; +} + +//////////////////////////////////////////////////////////////////////////// + +SkPathStroker::SkPathStroker(SkScalar radius, SkScalar miterLimit, SkPaint::Cap cap, SkPaint::Join join) + : fRadius(radius) +{ + 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.setUnit(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 = (U8)p.getStrokeCap(); + fJoin = (U8)p.getStrokeJoin(); + fDoFill = SkToU8(p.getStyle() == SkPaint::kStrokeAndFill_Style); +} + +SkStroke::SkStroke(const SkPaint& p, SkScalar width) +{ + fWidth = width; + fMiterLimit = p.getStrokeMiter(); + fCap = (U8)p.getStrokeCap(); + fJoin = (U8)p.getStrokeJoin(); + fDoFill = SkToU8(p.getStyle() == SkPaint::kStrokeAndFill_Style); +} + +void SkStroke::setWidth(SkScalar width) +{ + SkASSERT(width >= 0); + fWidth = width; +} + +void SkStroke::setMiterLimit(SkScalar miterLimit) +{ + SkASSERT(miterLimit >= 0); + fMiterLimit = miterLimit; +} + +void SkStroke::setCap(SkPaint::Cap cap) +{ + SkASSERT((unsigned)cap < SkPaint::kCapCount); + fCap = SkToU8(cap); +} + +void SkStroke::setJoin(SkPaint::Join join) +{ + SkASSERT((unsigned)join < SkPaint::kJoinCount); + fJoin = SkToU8(join); +} + +void SkStroke::strokePath(const SkPath& src, SkPath* dst) const +{ + SkASSERT(&src != nil && dst != nil); + + dst->reset(); + if (SkScalarHalf(fWidth) <= 0) + return; + + SkPathStroker stroker(SkScalarHalf(fWidth), fMiterLimit, this->getCap(), this->getJoin()); + + SkPath::Iter iter(src, false); + SkPoint pts[4]; + SkPath::Verb verb, lastSegment = SkPath::kMove_Verb; + + while ((verb = iter.next(pts)) != SkPath::kDone_Verb) + { + switch (verb) { + case SkPath::kMove_Verb: + stroker.moveTo(pts[0]); + break; + case SkPath::kLine_Verb: + stroker.lineTo(pts[1]); + lastSegment = verb; + break; + case SkPath::kQuad_Verb: + stroker.quadTo(pts[1], pts[2]); + lastSegment = verb; + break; + case SkPath::kCubic_Verb: + stroker.cubicTo(pts[1], pts[2], pts[3]); + lastSegment = verb; + break; + case SkPath::kClose_Verb: + stroker.close(lastSegment == SkPath::kLine_Verb); + break; + default: + break; + } + } + stroker.done(dst, lastSegment == SkPath::kLine_Verb); + + if (fDoFill) + dst->addPath(src); +} + +void SkStroke::strokeLine(const SkPoint& p0, const SkPoint& p1, SkPath* dst) const +{ + SkPath tmp; + + tmp.moveTo(p0); + tmp.lineTo(p1); + this->strokePath(tmp, dst); +} + diff --git a/libs/graphics/sgl/SkStrokerPriv.cpp b/libs/graphics/sgl/SkStrokerPriv.cpp new file mode 100644 index 0000000000..4daf9326b0 --- /dev/null +++ b/libs/graphics/sgl/SkStrokerPriv.cpp @@ -0,0 +1,216 @@ +#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 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); + + outer->lineTo(pivot.fX + after.fX, pivot.fY + after.fY); + inner->lineTo(pivot.fX - after.fX, pivot.fY - after.fY); +} + +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, 0, 0); + matrix.postTranslate(pivot.fX, pivot.fY); + int count = SkBuildQuadArc(before, after, dir, &matrix, pts); + + if (count > 0) + { + 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); + inner->lineTo(pivot.fX - after.fX, pivot.fY - after.fY); + } +} + +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; + } + + /* 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; + } + + ccw = !is_clockwise(before, after); + if (ccw) + { + SkTSwap<SkPath*>(outer, inner); + before.negate(); + after.negate(); + } + + // 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)); + 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); + inner->lineTo(pivot.fX - after.fX, pivot.fY - after.fY); +} + +///////////////////////////////////////////////////////////////////////////// + +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/libs/graphics/sgl/SkStrokerPriv.h b/libs/graphics/sgl/SkStrokerPriv.h new file mode 100644 index 0000000000..60a2a1f392 --- /dev/null +++ b/libs/graphics/sgl/SkStrokerPriv.h @@ -0,0 +1,33 @@ +#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/libs/graphics/sgl/SkTSearch.cpp b/libs/graphics/sgl/SkTSearch.cpp new file mode 100644 index 0000000000..41118bb5f8 --- /dev/null +++ b/libs/graphics/sgl/SkTSearch.cpp @@ -0,0 +1,176 @@ +#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) +{ + SkASSERT(base != nil); + SkASSERT(count >= 0); + + if (count <= 0) + return ~0; + + 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); +} + +#define kLCBufferSize 32 + +int SkStrLCSearch(const char*const* base, int count, const char target[], size_t len, size_t elemSize) +{ + SkASSERT(target); + + char lcBuffer[kLCBufferSize + 1]; + char* lc; + + if (len <= kLCBufferSize) + lc = lcBuffer; + else + lc = (char*)sk_malloc_throw(len + 1); + + for (int i = (int)(len - 1); i >= 0; --i) + { + SkASSERT((target[i] & 0x80) == 0); // only works for ascii + lc[i] = (char)tolower(target[i]); + } + lc[len] = 0; + + int index = SkStrSearch(base, count, lc, len, elemSize); + + if (lc != lcBuffer) + sk_free(lc); + return index; +} + +int SkStrLCSearch(const char*const* base, int count, const char target[], size_t elemSize) { + return SkStrLCSearch(base, count, target, strlen(target), elemSize); +} + +////////////////////////////////////////////////////////////////////////////// + +#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/libs/graphics/sgl/SkTSort.h b/libs/graphics/sgl/SkTSort.h new file mode 100644 index 0000000000..bdfbf6daf4 --- /dev/null +++ b/libs/graphics/sgl/SkTSort.h @@ -0,0 +1,48 @@ +#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/libs/graphics/sgl/SkTemplatesPriv.h b/libs/graphics/sgl/SkTemplatesPriv.h new file mode 100644 index 0000000000..7c2e915421 --- /dev/null +++ b/libs/graphics/sgl/SkTemplatesPriv.h @@ -0,0 +1,67 @@ +#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 = nil; + return obj; + } +private: + T* fObj; + void* fStorage; +}; + +#endif diff --git a/libs/graphics/sgl/SkTextLayout.cpp b/libs/graphics/sgl/SkTextLayout.cpp new file mode 100644 index 0000000000..1ff426426d --- /dev/null +++ b/libs/graphics/sgl/SkTextLayout.cpp @@ -0,0 +1,66 @@ +#include "SkTextLayout.h" +#include "SkPaint.h" + +int SkTextLayout::layout(const SkPaint& paint, + const char* text, size_t byteLength, SkUnicodeWalkerProc proc, + Rec rec[]) +{ + const char* stop = text + byteLength; + Rec* recStart = rec; + + while (text < stop) + { + rec->fCharCode = proc(&text); + rec += 1; + // set private fields of Rec (when we use them) + } + + int count = rec - recStart; + if (count > 0) + this->onLayout(paint, recStart, count); + return count; +} + +///////////////////////////////////////////////////////////////////////////////////////////// + +class SkTrackingTextLayout : public SkTextLayout { +public: + SkTrackingTextLayout(SkScalar charExtra, SkScalar spaceExtra) + : fCharExtra(charExtra), fSpaceExtra(spaceExtra) {} + +protected: + // override + virtual void onLayout(const SkPaint& paint, Rec rec[], int count) + { + SkScalar ce = fCharExtra; + SkScalar se = fSpaceExtra; + + if (0 == se) // special case no space-extra (so we don't have to read charCode() + { + for (int i = 0; i < count; i++) + rec[i].fDeltaAdvance = ce; + } + else + { + for (int i = 0; i < count; i++) + { + SkScalar delta = ce; + if (32 == rec[i].charCode()) // do I need a fancier test? + delta += se; + rec[i].fDeltaAdvance = delta; + } + } + } + +private: + SkScalar fCharExtra, fSpaceExtra; +}; + +///////////////////////////////////////////////////////////////////////////////////////////// + +SkTextLayout* SkTextLayout::CreateTrackingLayout(SkScalar charExtra, SkScalar spaceExtra) +{ + return SkNEW_ARGS(SkTrackingTextLayout, (charExtra, spaceExtra)); +} + + diff --git a/libs/graphics/sgl/SkUtils.cpp b/libs/graphics/sgl/SkUtils.cpp new file mode 100644 index 0000000000..9cb7c65d0f --- /dev/null +++ b/libs/graphics/sgl/SkUtils.cpp @@ -0,0 +1,496 @@ +#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 + +/////////////////////////////////////////////////////////////////////////// + +#ifndef SK_MEMSET16_REDIRECT +void sk_memset16(uint16_t dst[], U16CPU 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) + { + U32* dst32 = (U32*)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; +} +#endif + +#ifndef SK_MEMSET32_REDIRECT +void sk_memset32(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); + } +} +#endif + +////////////////////////////////////////////////////////////////////////////// + +/* 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 U8*)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 U8* p = (const U8*)utf8; + int c = *p; + int hic = c << 24; + + assert_utf8_leadingbyte(c); + + if (hic < 0) + { + U32 mask = (U32)~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 U8* p = (const U8*)*ptr; + int c = *p; + int hic = c << 24; + + assert_utf8_leadingbyte(c); + + if (hic < 0) + { + U32 mask = (U32)~0x3F; + hic <<= 1; + do { + c = (c << 6) | (*++p & 0x3F); + mask <<= 5; + } while ((hic <<= 1) < 0); + c &= ~mask; + } + *ptr = (char*)p + 1; + return c; +} + +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; +} + +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; +} + +//////////////////////////////////////////////////////////////////////////////////// + +#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/libs/graphics/sgl/SkXfermode.cpp b/libs/graphics/sgl/SkXfermode.cpp new file mode 100644 index 0000000000..6f5a9a89f8 --- /dev/null +++ b/libs/graphics/sgl/SkXfermode.cpp @@ -0,0 +1,535 @@ +#include "SkXfermode.h" +#include "SkColorPriv.h" + +static inline U8CPU SkAlphaMulAlpha(U8CPU a, U8CPU b) +{ + unsigned ab = a * b; +#if 0 + return ab / 255; +#else + return ((ab << 8) + ab + 257) >> 16; +#endif +} + +void SkXfermode::xfer32(SkPMColor dst[], const SkPMColor src[], int count, const SkAlpha aa[]) +{ + // override in subclass +} + +void SkXfermode::xfer16(uint16_t dst[], const SkPMColor src[], int count, const SkAlpha aa[]) +{ + // override in subclass +} + +void SkXfermode::xferA8(SkAlpha dst[], const SkPMColor src[], int count, const SkAlpha aa[]) +{ + // override in subclass +} + +////////////////////////////////////////////////////////////////////////////////// + +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); +} + +void SkProcXfermode::xfer32(SkPMColor dst[], const SkPMColor src[], int count, const SkAlpha aa[]) +{ + SkASSERT(dst && src && count >= 0); + + SkXfermodeProc proc = fProc; + if (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 (a != 0) + { + SkPMColor dstC = dst[i]; + SkPMColor C = proc(src[i], dstC); + if (a != 0xFF) + C = SkFourByteInterp(C, dstC, a); + dst[i] = C; + } + } + } + } +} + +void SkProcXfermode::xfer16(uint16_t dst[], const SkPMColor src[], int count, const SkAlpha aa[]) +{ + SkASSERT(dst && src && count >= 0); + + SkXfermodeProc proc = fProc; + if (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 (a != 0) + { + SkPMColor dstC = SkPixel16ToPixel32(dst[i]); + SkPMColor C = proc(src[i], dstC); + if (a != 0xFF) + C = SkFourByteInterp(C, dstC, a); + dst[i] = SkPixel32ToPixel16_ToU16(C); + } + } + } + } +} + +void SkProcXfermode::xferA8(SkAlpha dst[], const SkPMColor src[], int count, const SkAlpha aa[]) +{ + SkASSERT(dst && src && count >= 0); + + SkXfermodeProc proc = fProc; + if (proc) + { + if (NULL == aa) + { + for (int i = count - 1; i >= 0; --i) + dst[i] = SkToU8(SkGetPackedA32(proc(src[i], (SkPMColor)(dst[i] << SK_A32_SHIFT)))); + } + else + { + for (int i = count - 1; i >= 0; --i) + { + unsigned a = aa[i]; + if (a != 0) + { + SkAlpha dstA = dst[i]; + unsigned A = SkGetPackedA32(proc(src[i], (SkPMColor)(dstA << SK_A32_SHIFT))); + if (a != 0xFF) + A = SkAlphaBlend(A, dstA, SkAlpha255To256(a)); + dst[i] = SkToU8(A); + } + } + } + } +} + +SkProcXfermode::SkProcXfermode(SkRBuffer& buffer) : SkXfermode(buffer) +{ + fProc = (SkXfermodeProc)buffer.readPtr(); +} + +void SkProcXfermode::flatten(SkWBuffer& buffer) +{ + this->INHERITED::flatten(buffer); + + buffer.writePtr((void*)fProc); +} + +SkFlattenable::Factory SkProcXfermode::getFactory() +{ + return CreateProc; +} + +SkFlattenable* SkProcXfermode::CreateProc(SkRBuffer& buffer) +{ + return SkNEW_ARGS(SkProcXfermode, (buffer)); +} + +////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////// + +// 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] this is the default mode +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); + + return SkPackARGB32(sa + da - SkAlphaMulAlpha(sa, da), + SkGetPackedR32(dst) + SkAlphaMulAlpha(255 - da, SkGetPackedR32(src)), + SkGetPackedG32(dst) + SkAlphaMulAlpha(255 - da, SkGetPackedG32(src)), + SkGetPackedB32(dst) + SkAlphaMulAlpha(255 - da, 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 src_scale = SkAlpha255To256(255 - sa); + unsigned dst_scale = SkAlpha255To256(da); + + return SkPackARGB32(da, + (dst_scale * SkGetPackedR32(src) + src_scale * SkGetPackedR32(dst)) >> 8, + (dst_scale * SkGetPackedG32(src) + src_scale * SkGetPackedG32(dst)) >> 8, + (dst_scale * SkGetPackedB32(src) + src_scale * SkGetPackedB32(dst)) >> 8); +} + +// 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 src_scale = SkAlpha255To256(sa); + unsigned dst_scale = SkAlpha255To256(255 - da); + + return SkPackARGB32(sa, + (dst_scale * SkGetPackedR32(src) + src_scale * SkGetPackedR32(dst)) >> 8, + (dst_scale * SkGetPackedG32(src) + src_scale * SkGetPackedG32(dst)) >> 8, + (dst_scale * SkGetPackedB32(src) + src_scale * SkGetPackedB32(dst)) >> 8); +} + +// 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 src_scale = SkAlpha255To256(255 - sa); + unsigned dst_scale = SkAlpha255To256(255 - da); + + return SkPackARGB32(sa + da - (SkAlphaMulAlpha(sa, da) << 1), + (dst_scale * SkGetPackedR32(src) + src_scale * SkGetPackedR32(dst)) >> 8, + (dst_scale * SkGetPackedG32(src) + src_scale * SkGetPackedG32(dst)) >> 8, + (dst_scale * SkGetPackedB32(src) + src_scale * SkGetPackedB32(dst)) >> 8); +} + + +// 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)); +} + +////////////////////////////////////////////////////////////////////////////////// + +#if 0 // maybe do these later + +#define SkPinToU8(value) SkFastMin32(value, 0xFF) + +// kAdd_Mode, //!< clamp [Sa + Da, Sc + Dc] +static SkPMColor add_modeproc(SkPMColor src, SkPMColor dst) +{ + return SkPackARGB32(SkPinToU8(SkGetPackedA32(src) + SkGetPackedA32(dst)), + SkPinToU8(SkGetPackedR32(src) + SkGetPackedR32(dst)), + SkPinToU8(SkGetPackedG32(src) + SkGetPackedG32(dst)), + SkPinToU8(SkGetPackedB32(src) + SkGetPackedB32(dst))); +} + +static U8CPU do_mul(U8CPU src, U8CPU dst, unsigned src_scale, unsigned dst_scale) +{ + return (src * dst_scale + dst * (SkAlpha255To256(src) + src_scale)) >> 8; +} + +// kMul_Mode, //!< clamp [Sa + Da - Sa * Da, Sc * Dc + Sc * (1 - Da) + (1 - Sa) * Dc] +static SkPMColor mul_modeproc(SkPMColor src, SkPMColor dst) +{ + unsigned sa = SkGetPackedA32(src); + unsigned src_scale = SkAlpha255To256(255 - sa); + + unsigned da = SkGetPackedA32(dst); + unsigned dst_scale = SkAlpha255To256(255 - da); + + return SkPackARGB32(sa + da - SkAlphaMul(SkAlpha255To256(sa), da), + do_mul(SkGetPackedR32(src), SkGetPackedR32(dst), src_scale, dst_scale), + do_mul(SkGetPackedG32(src), SkGetPackedG32(dst), src_scale, dst_scale), + do_mul(SkGetPackedB32(src), SkGetPackedB32(dst), src_scale, dst_scale)); +} +#endif + +////////////////////////////////////////////////////////////////////////////////// + +class SkClearXfermode : public SkProcXfermode { +public: + SkClearXfermode() : SkProcXfermode(clear_modeproc) {} + + virtual void xfer32(SkPMColor dst[], const SkPMColor[], int count, const 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 (a == 0xFF) + dst[i] = 0; + else if (a != 0) + dst[i] = SkAlphaMulQ(dst[i], SkAlpha255To256(255 - a)); + } + } + } + virtual void xferA8(SkAlpha dst[], const SkPMColor[], int count, const 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 (a == 0xFF) + dst[i] = 0; + else if (a != 0) + dst[i] = SkToU8(SkAlphaMul(dst[i], SkAlpha255To256(255 - a))); + } + } + } + + virtual Factory getFactory() { return CreateProc; } + // we have nothing to flatten(), so don't need to override it + +private: + SkClearXfermode(SkRBuffer& buffer) : SkProcXfermode(buffer) {} + + static SkFlattenable* CreateProc(SkRBuffer& buffer) + { + return SkNEW_ARGS(SkClearXfermode, (buffer)); + } +}; + +////////////////////////////////////////////////////////////////////////////////// + +class SkSrcXfermode : public SkProcXfermode { +public: + SkSrcXfermode() : SkProcXfermode(src_modeproc) {} + + virtual void xfer32(SkPMColor dst[], const SkPMColor src[], int count, const 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(SkAlpha dst[], const SkPMColor src[], int count, const 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 (a != 0) + { + 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; } + // we have nothing to flatten(), so don't need to override it + +private: + SkSrcXfermode(SkRBuffer& buffer) : SkProcXfermode(buffer) {} + + static SkFlattenable* CreateProc(SkRBuffer& buffer) + { + return SkNEW_ARGS(SkSrcXfermode, (buffer)); + } +}; + +////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "SkPorterDuff.h" + +static const SkXfermodeProc gPorterDuffModeProcs[] = { + clear_modeproc, + src_modeproc, + dst_modeproc, + srcover_modeproc, + dstover_modeproc, + srcin_modeproc, + dstin_modeproc, + srcout_modeproc, + dstout_modeproc, + srcatop_modeproc, + dstatop_modeproc, + xor_modeproc, + darken_modeproc, + lighten_modeproc +}; + +SkXfermode* SkPorterDuff::CreateXfermode(SkPorterDuff::Mode mode) +{ + SkASSERT(SK_ARRAY_COUNT(gPorterDuffModeProcs) == 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; + default: + return SkNEW_ARGS(SkProcXfermode, (gPorterDuffModeProcs[mode])); + } +} + +#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); + + for (unsigned i = 0; i < SK_ARRAY_COUNT(gPorterDuffModeProcs); i++) { + gPorterDuffModeProcs[i](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 = gPorterDuffModeProcs[mode]; + + return proc; +} + diff --git a/libs/graphics/svg/SkSVG.cpp b/libs/graphics/svg/SkSVG.cpp new file mode 100644 index 0000000000..190957a443 --- /dev/null +++ b/libs/graphics/svg/SkSVG.cpp @@ -0,0 +1,19 @@ +#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/libs/graphics/svg/SkSVGCircle.cpp b/libs/graphics/svg/SkSVGCircle.cpp new file mode 100644 index 0000000000..e964229f4c --- /dev/null +++ b/libs/graphics/svg/SkSVGCircle.cpp @@ -0,0 +1,36 @@ +#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/libs/graphics/svg/SkSVGCircle.h b/libs/graphics/svg/SkSVGCircle.h new file mode 100644 index 0000000000..4535a66283 --- /dev/null +++ b/libs/graphics/svg/SkSVGCircle.h @@ -0,0 +1,15 @@ +#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/libs/graphics/svg/SkSVGClipPath.cpp b/libs/graphics/svg/SkSVGClipPath.cpp new file mode 100644 index 0000000000..6671921ef8 --- /dev/null +++ b/libs/graphics/svg/SkSVGClipPath.cpp @@ -0,0 +1,31 @@ +#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/libs/graphics/svg/SkSVGClipPath.h b/libs/graphics/svg/SkSVGClipPath.h new file mode 100644 index 0000000000..a06103a8fe --- /dev/null +++ b/libs/graphics/svg/SkSVGClipPath.h @@ -0,0 +1,14 @@ +#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/libs/graphics/svg/SkSVGDefs.cpp b/libs/graphics/svg/SkSVGDefs.cpp new file mode 100644 index 0000000000..d0aaef507e --- /dev/null +++ b/libs/graphics/svg/SkSVGDefs.cpp @@ -0,0 +1,15 @@ +#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/libs/graphics/svg/SkSVGDefs.h b/libs/graphics/svg/SkSVGDefs.h new file mode 100644 index 0000000000..5a4f087bcd --- /dev/null +++ b/libs/graphics/svg/SkSVGDefs.h @@ -0,0 +1,14 @@ +#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/libs/graphics/svg/SkSVGElements.cpp b/libs/graphics/svg/SkSVGElements.cpp new file mode 100644 index 0000000000..08484365b0 --- /dev/null +++ b/libs/graphics/svg/SkSVGElements.cpp @@ -0,0 +1,79 @@ +#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(nil), fIsDef(false), fIsNotDef(true) { +} + +SkSVGElement::~SkSVGElement() { +} + +SkSVGElement* SkSVGElement::getGradient() { + return nil; +} + +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/libs/graphics/svg/SkSVGElements.h b/libs/graphics/svg/SkSVGElements.h new file mode 100644 index 0000000000..8fe7814d14 --- /dev/null +++ b/libs/graphics/svg/SkSVGElements.h @@ -0,0 +1,64 @@ +#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/libs/graphics/svg/SkSVGEllipse.cpp b/libs/graphics/svg/SkSVGEllipse.cpp new file mode 100644 index 0000000000..a4dfd023e1 --- /dev/null +++ b/libs/graphics/svg/SkSVGEllipse.cpp @@ -0,0 +1,38 @@ +#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/libs/graphics/svg/SkSVGEllipse.h b/libs/graphics/svg/SkSVGEllipse.h new file mode 100644 index 0000000000..b822e4df33 --- /dev/null +++ b/libs/graphics/svg/SkSVGEllipse.h @@ -0,0 +1,16 @@ +#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/libs/graphics/svg/SkSVGFeColorMatrix.cpp b/libs/graphics/svg/SkSVGFeColorMatrix.cpp new file mode 100644 index 0000000000..46225f8c5a --- /dev/null +++ b/libs/graphics/svg/SkSVGFeColorMatrix.cpp @@ -0,0 +1,15 @@ +#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/libs/graphics/svg/SkSVGFeColorMatrix.h b/libs/graphics/svg/SkSVGFeColorMatrix.h new file mode 100644 index 0000000000..408c50151c --- /dev/null +++ b/libs/graphics/svg/SkSVGFeColorMatrix.h @@ -0,0 +1,17 @@ +#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/libs/graphics/svg/SkSVGFilter.cpp b/libs/graphics/svg/SkSVGFilter.cpp new file mode 100644 index 0000000000..0463ee1074 --- /dev/null +++ b/libs/graphics/svg/SkSVGFilter.cpp @@ -0,0 +1,16 @@ +#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/libs/graphics/svg/SkSVGFilter.h b/libs/graphics/svg/SkSVGFilter.h new file mode 100644 index 0000000000..71bb45fd7f --- /dev/null +++ b/libs/graphics/svg/SkSVGFilter.h @@ -0,0 +1,18 @@ +#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_DEFINED
\ No newline at end of file diff --git a/libs/graphics/svg/SkSVGG.cpp b/libs/graphics/svg/SkSVGG.cpp new file mode 100644 index 0000000000..cf45054ff7 --- /dev/null +++ b/libs/graphics/svg/SkSVGG.cpp @@ -0,0 +1,7 @@ +#include "SkSVGG.h" + +DEFINE_SVG_NO_INFO(G) + +void SkSVGG::translate(SkSVGParser& parser, bool defState) { + INHERITED::translate(parser, defState); +} diff --git a/libs/graphics/svg/SkSVGG.h b/libs/graphics/svg/SkSVGG.h new file mode 100644 index 0000000000..3e9e5c91e3 --- /dev/null +++ b/libs/graphics/svg/SkSVGG.h @@ -0,0 +1,12 @@ +#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/libs/graphics/svg/SkSVGGradient.cpp b/libs/graphics/svg/SkSVGGradient.cpp new file mode 100644 index 0000000000..ba64c16323 --- /dev/null +++ b/libs/graphics/svg/SkSVGGradient.cpp @@ -0,0 +1,106 @@ +#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 = nil; + 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/libs/graphics/svg/SkSVGGradient.h b/libs/graphics/svg/SkSVGGradient.h new file mode 100644 index 0000000000..25421ce725 --- /dev/null +++ b/libs/graphics/svg/SkSVGGradient.h @@ -0,0 +1,20 @@ +#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/libs/graphics/svg/SkSVGGroup.cpp b/libs/graphics/svg/SkSVGGroup.cpp new file mode 100644 index 0000000000..a72dd8af6b --- /dev/null +++ b/libs/graphics/svg/SkSVGGroup.cpp @@ -0,0 +1,36 @@ +#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 != nil) + return result; + } + return nil; +} + +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/libs/graphics/svg/SkSVGGroup.h b/libs/graphics/svg/SkSVGGroup.h new file mode 100644 index 0000000000..ff4c342abd --- /dev/null +++ b/libs/graphics/svg/SkSVGGroup.h @@ -0,0 +1,19 @@ +#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/libs/graphics/svg/SkSVGImage.cpp b/libs/graphics/svg/SkSVGImage.cpp new file mode 100644 index 0000000000..bcf8a4f732 --- /dev/null +++ b/libs/graphics/svg/SkSVGImage.cpp @@ -0,0 +1,35 @@ +#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/libs/graphics/svg/SkSVGImage.h b/libs/graphics/svg/SkSVGImage.h new file mode 100644 index 0000000000..3a0a2a512a --- /dev/null +++ b/libs/graphics/svg/SkSVGImage.h @@ -0,0 +1,19 @@ +#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/libs/graphics/svg/SkSVGLine.cpp b/libs/graphics/svg/SkSVGLine.cpp new file mode 100644 index 0000000000..5a5e97874c --- /dev/null +++ b/libs/graphics/svg/SkSVGLine.cpp @@ -0,0 +1,21 @@ +#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/libs/graphics/svg/SkSVGLine.h b/libs/graphics/svg/SkSVGLine.h new file mode 100644 index 0000000000..f7a665b0f3 --- /dev/null +++ b/libs/graphics/svg/SkSVGLine.h @@ -0,0 +1,16 @@ +#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/libs/graphics/svg/SkSVGLinearGradient.cpp b/libs/graphics/svg/SkSVGLinearGradient.cpp new file mode 100644 index 0000000000..69f48231e0 --- /dev/null +++ b/libs/graphics/svg/SkSVGLinearGradient.cpp @@ -0,0 +1,35 @@ +#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/libs/graphics/svg/SkSVGLinearGradient.h b/libs/graphics/svg/SkSVGLinearGradient.h new file mode 100644 index 0000000000..fac7aa3a66 --- /dev/null +++ b/libs/graphics/svg/SkSVGLinearGradient.h @@ -0,0 +1,19 @@ +#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/libs/graphics/svg/SkSVGMask.cpp b/libs/graphics/svg/SkSVGMask.cpp new file mode 100644 index 0000000000..cb029d4a11 --- /dev/null +++ b/libs/graphics/svg/SkSVGMask.cpp @@ -0,0 +1,24 @@ +#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/libs/graphics/svg/SkSVGMask.h b/libs/graphics/svg/SkSVGMask.h new file mode 100644 index 0000000000..632061e441 --- /dev/null +++ b/libs/graphics/svg/SkSVGMask.h @@ -0,0 +1,20 @@ +#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/libs/graphics/svg/SkSVGMetadata.cpp b/libs/graphics/svg/SkSVGMetadata.cpp new file mode 100644 index 0000000000..3dd8e356f6 --- /dev/null +++ b/libs/graphics/svg/SkSVGMetadata.cpp @@ -0,0 +1,15 @@ +#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/libs/graphics/svg/SkSVGMetadata.h b/libs/graphics/svg/SkSVGMetadata.h new file mode 100644 index 0000000000..c8d02320d3 --- /dev/null +++ b/libs/graphics/svg/SkSVGMetadata.h @@ -0,0 +1,14 @@ +#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/libs/graphics/svg/SkSVGPaintState.cpp b/libs/graphics/svg/SkSVGPaintState.cpp new file mode 100644 index 0000000000..50ba21dcb5 --- /dev/null +++ b/libs/graphics/svg/SkSVGPaintState.cpp @@ -0,0 +1,446 @@ +#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(nil) { +} + +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 == nil) + 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 != nil) { + 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 != nil) { + 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 != nil); + 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/libs/graphics/svg/SkSVGParser.cpp b/libs/graphics/svg/SkSVGParser.cpp new file mode 100644 index 0000000000..a31f43611c --- /dev/null +++ b/libs/graphics/svg/SkSVGParser.cpp @@ -0,0 +1,428 @@ +#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(nil), 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 = nil; + 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 != nil) { + 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 == nil) // 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() : nil; + 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 S32 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 = nil, * fillState = nil; + 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[16]; + strcpy(idStr, "sk_matrix"); + char num[8]; + sprintf(num, "%d", gGeneratedMatrixID); + strcat(idStr, num); + _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 != nil); + 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 == nil) + 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 = nil; + 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 nil; + } + 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/libs/graphics/svg/SkSVGPath.cpp b/libs/graphics/svg/SkSVGPath.cpp new file mode 100644 index 0000000000..be7a45f9a3 --- /dev/null +++ b/libs/graphics/svg/SkSVGPath.cpp @@ -0,0 +1,28 @@ +#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 != nil) { + 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/libs/graphics/svg/SkSVGPath.h b/libs/graphics/svg/SkSVGPath.h new file mode 100644 index 0000000000..1cbff6d5e5 --- /dev/null +++ b/libs/graphics/svg/SkSVGPath.h @@ -0,0 +1,13 @@ +#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/libs/graphics/svg/SkSVGPolygon.cpp b/libs/graphics/svg/SkSVGPolygon.cpp new file mode 100644 index 0000000000..f7e41653de --- /dev/null +++ b/libs/graphics/svg/SkSVGPolygon.cpp @@ -0,0 +1,24 @@ +#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/libs/graphics/svg/SkSVGPolygon.h b/libs/graphics/svg/SkSVGPolygon.h new file mode 100644 index 0000000000..9cf86193a9 --- /dev/null +++ b/libs/graphics/svg/SkSVGPolygon.h @@ -0,0 +1,14 @@ +#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/libs/graphics/svg/SkSVGPolyline.cpp b/libs/graphics/svg/SkSVGPolyline.cpp new file mode 100644 index 0000000000..cc4c197bc9 --- /dev/null +++ b/libs/graphics/svg/SkSVGPolyline.cpp @@ -0,0 +1,34 @@ +#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/libs/graphics/svg/SkSVGPolyline.h b/libs/graphics/svg/SkSVGPolyline.h new file mode 100644 index 0000000000..50840b28d5 --- /dev/null +++ b/libs/graphics/svg/SkSVGPolyline.h @@ -0,0 +1,18 @@ +#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/libs/graphics/svg/SkSVGRadialGradient.cpp b/libs/graphics/svg/SkSVGRadialGradient.cpp new file mode 100644 index 0000000000..8883f3685a --- /dev/null +++ b/libs/graphics/svg/SkSVGRadialGradient.cpp @@ -0,0 +1,33 @@ +#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/libs/graphics/svg/SkSVGRadialGradient.h b/libs/graphics/svg/SkSVGRadialGradient.h new file mode 100644 index 0000000000..514523eae6 --- /dev/null +++ b/libs/graphics/svg/SkSVGRadialGradient.h @@ -0,0 +1,21 @@ +#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/libs/graphics/svg/SkSVGRect.cpp b/libs/graphics/svg/SkSVGRect.cpp new file mode 100644 index 0000000000..aff3a634ea --- /dev/null +++ b/libs/graphics/svg/SkSVGRect.cpp @@ -0,0 +1,26 @@ +#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/libs/graphics/svg/SkSVGRect.h b/libs/graphics/svg/SkSVGRect.h new file mode 100644 index 0000000000..e6202ec2c2 --- /dev/null +++ b/libs/graphics/svg/SkSVGRect.h @@ -0,0 +1,17 @@ +#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/libs/graphics/svg/SkSVGSVG.cpp b/libs/graphics/svg/SkSVGSVG.cpp new file mode 100644 index 0000000000..b56d2c877f --- /dev/null +++ b/libs/graphics/svg/SkSVGSVG.cpp @@ -0,0 +1,65 @@ +#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/libs/graphics/svg/SkSVGSVG.h b/libs/graphics/svg/SkSVGSVG.h new file mode 100644 index 0000000000..0326b46f73 --- /dev/null +++ b/libs/graphics/svg/SkSVGSVG.h @@ -0,0 +1,22 @@ +#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/libs/graphics/svg/SkSVGStop.cpp b/libs/graphics/svg/SkSVGStop.cpp new file mode 100644 index 0000000000..f4d7308acd --- /dev/null +++ b/libs/graphics/svg/SkSVGStop.cpp @@ -0,0 +1,15 @@ +#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/libs/graphics/svg/SkSVGStop.h b/libs/graphics/svg/SkSVGStop.h new file mode 100644 index 0000000000..e2fe273cc1 --- /dev/null +++ b/libs/graphics/svg/SkSVGStop.h @@ -0,0 +1,14 @@ +#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/libs/graphics/svg/SkSVGSymbol.cpp b/libs/graphics/svg/SkSVGSymbol.cpp new file mode 100644 index 0000000000..54581158e3 --- /dev/null +++ b/libs/graphics/svg/SkSVGSymbol.cpp @@ -0,0 +1,13 @@ +#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/libs/graphics/svg/SkSVGSymbol.h b/libs/graphics/svg/SkSVGSymbol.h new file mode 100644 index 0000000000..f63d683e8c --- /dev/null +++ b/libs/graphics/svg/SkSVGSymbol.h @@ -0,0 +1,13 @@ +#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/libs/graphics/svg/SkSVGText.cpp b/libs/graphics/svg/SkSVGText.cpp new file mode 100644 index 0000000000..e8d4cd14de --- /dev/null +++ b/libs/graphics/svg/SkSVGText.cpp @@ -0,0 +1,30 @@ +#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/libs/graphics/svg/SkSVGText.h b/libs/graphics/svg/SkSVGText.h new file mode 100644 index 0000000000..76703d5187 --- /dev/null +++ b/libs/graphics/svg/SkSVGText.h @@ -0,0 +1,23 @@ +#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/libs/graphics/svg/SkSVGUse.cpp b/libs/graphics/svg/SkSVGUse.cpp new file mode 100644 index 0000000000..4d2a6e4879 --- /dev/null +++ b/libs/graphics/svg/SkSVGUse.cpp @@ -0,0 +1,21 @@ +#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/libs/graphics/svg/SkSVGUse.h b/libs/graphics/svg/SkSVGUse.h new file mode 100644 index 0000000000..5ea9ac5257 --- /dev/null +++ b/libs/graphics/svg/SkSVGUse.h @@ -0,0 +1,19 @@ +#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/libs/graphics/text/ATextEntry.h b/libs/graphics/text/ATextEntry.h new file mode 100644 index 0000000000..0a584b721a --- /dev/null +++ b/libs/graphics/text/ATextEntry.h @@ -0,0 +1,20 @@ +#ifndef ATextEntry_DEFINED +#define ATextEntry_DEFINED + +#include "SkTypes.h" + +class SkCanavs; + +class ATextEntry { +public: + ATextEntry(); + ~ATextEntry(); + + void setUtf16(const U16 text[], size_t count); + void setSize(SkScalar width, SkScalar height); + void setSelection(int start, int stop); + void draw(SkCanvas*); + void handleKey(int key); +}; + +#endif diff --git a/libs/graphics/views/SkEvent.cpp b/libs/graphics/views/SkEvent.cpp new file mode 100644 index 0000000000..f7a9648100 --- /dev/null +++ b/libs/graphics/views/SkEvent.cpp @@ -0,0 +1,548 @@ +#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/libs/graphics/views/SkEventSink.cpp b/libs/graphics/views/SkEventSink.cpp new file mode 100644 index 0000000000..e2a45f0619 --- /dev/null +++ b/libs/graphics/views/SkEventSink.cpp @@ -0,0 +1,328 @@ +#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 = nil; + return rec; +} + +SkEventSink::SkEventSink() : fTagHead(nil) +{ + 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 = nil; + + 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) : nil; +} + +void SkEventSink::addTagList(SkTagList* rec) +{ + SkASSERT(rec); + SkASSERT(fTagHead == nil || SkTagList::Find(fTagHead, rec->fTag) == nil); + + 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 == nil) + 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 == nil) + 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) != nil; +} + +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 nil; +} + +//////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////// + +#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/libs/graphics/views/SkMetaData.cpp b/libs/graphics/views/SkMetaData.cpp new file mode 100644 index 0000000000..8692979dc5 --- /dev/null +++ b/libs/graphics/views/SkMetaData.cpp @@ -0,0 +1,388 @@ +#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/libs/graphics/views/SkTagList.cpp b/libs/graphics/views/SkTagList.cpp new file mode 100644 index 0000000000..42a35038df --- /dev/null +++ b/libs/graphics/views/SkTagList.cpp @@ -0,0 +1,54 @@ +#include "SkTagList.h" + +SkTagList::~SkTagList() +{ +} + +SkTagList* SkTagList::Find(SkTagList* rec, U8CPU tag) +{ + SkASSERT(tag < kSkTagListCount); + + while (rec != nil) + { + 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 = nil; + + while (rec != nil) + { + 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/libs/graphics/views/SkTagList.h b/libs/graphics/views/SkTagList.h new file mode 100644 index 0000000000..937c030f96 --- /dev/null +++ b/libs/graphics/views/SkTagList.h @@ -0,0 +1,34 @@ +#ifndef SkTagList_DEFINED +#define SkTagList_DEFINED + +#include "SkTypes.h" + +enum SkTagListEnum { + kListeners_SkTagList, + kViewLayout_SkTagList, + kViewArtist_SkTagList, + + kSkTagListCount +}; + +struct SkTagList { + SkTagList* fNext; + U16 fExtra16; + U8 fExtra8; + U8 fTag; + + SkTagList(U8CPU tag) : fTag(SkToU8(tag)) + { + SkASSERT(tag < kSkTagListCount); + fNext = nil; + 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/libs/graphics/views/SkTextBox.cpp b/libs/graphics/views/SkTextBox.cpp new file mode 100644 index 0000000000..01d5dbe803 --- /dev/null +++ b/libs/graphics/views/SkTextBox.cpp @@ -0,0 +1,195 @@ +#include "SkTextBox.h" +#include "SkGlyphCache.h" +#include "SkUtils.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, nil); + SkGlyphCache* cache = ac.getCache(); + SkFixed w = 0; + SkFixed limit = SkScalarToFixed(margin); + + 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); + + if (!currWS && prevWS) + word_start = prevText; + prevWS = currWS; + + w += cache->getMetrics(uni).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, spacing, height, before, after; + + 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; + + paint.measureText(nil, 0, &before, &after); + spacing = SkScalarMul(after - before, fSpacingMul) + fSpacingAdd; + height = fBox.height(); + + // compute Y position for first line + { + SkScalar textHeight = after - before; + + if (fMode == kLineBreak_Mode && fSpacingAlign != kStart_SpacingAlign) + { + int count = SkTextLineBreaker::CountLines(text, textStop - text, paint, marginWidth); + SkASSERT(count > 0); + textHeight += spacing * (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 - before; + } + + for (;;) + { + len = linebreak(text, textStop, paint, marginWidth); + if (y + after > 0) + canvas->drawText(text, len, x, y, paint); + text += len; + if (text >= textStop) + break; + y += spacing; + if (y + before >= height) + break; + } +} + diff --git a/libs/graphics/xml/SkBML_Verbs.h b/libs/graphics/xml/SkBML_Verbs.h new file mode 100644 index 0000000000..b23c4dec18 --- /dev/null +++ b/libs/graphics/xml/SkBML_Verbs.h @@ -0,0 +1,16 @@ +#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/libs/graphics/xml/SkBML_XMLParser.cpp b/libs/graphics/xml/SkBML_XMLParser.cpp new file mode 100644 index 0000000000..7901565a8f --- /dev/null +++ b/libs/graphics/xml/SkBML_XMLParser.cpp @@ -0,0 +1,175 @@ +#include "SkBML_XMLParser.h" +#include "SkBML_Verbs.h" +#include "SkStream.h" +#include "SkXMLWriter.h" + +static U8 rbyte(SkStream& s) +{ + U8 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] == nil) + 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 U8, so we get automatic wrap-around + U8 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/libs/graphics/xml/SkDOM.cpp b/libs/graphics/xml/SkDOM.cpp new file mode 100644 index 0000000000..431d78109f --- /dev/null +++ b/libs/graphics/xml/SkDOM.cpp @@ -0,0 +1,495 @@ +#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)) != nil) + if (this->addAttribute(name, value)) + return false; + + if ((node = dom.getFirstChild(node)) != nil) + do { + if (!this->parse(dom, node)) + return false; + } while ((node = dom.getNextSibling(node)) != nil); + + return !this->endElement(elemName); +} + +///////////////////////////////////////////////////////////////////////// + +struct SkDOMAttr { + const char* fName; + const char* fValue; +}; + +struct SkDOMNode { + const char* fName; + SkDOMNode* fFirstChild; + SkDOMNode* fNextSibling; + U16 fAttrCount; + U8 fType; + U8 fPad; + + const SkDOMAttr* attrs() const + { + return (const SkDOMAttr*)(this + 1); + } + SkDOMAttr* attrs() + { + return (SkDOMAttr*)(this + 1); + } +}; + +///////////////////////////////////////////////////////////////////////// + +#define kMinChunkSize 512 + +SkDOM::SkDOM() : fAlloc(kMinChunkSize), fRoot(nil) +{ +} + +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 != nil; 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 != nil; 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 nil; +} + +///////////////////////////////////////////////////////////////////////////////////// + +const SkDOM::Attr* SkDOM::getFirstAttr(const Node* node) const +{ + return node->fAttrCount ? node->attrs() : nil; +} + +const SkDOM::Attr* SkDOM::getNextAttr(const Node* node, const Attr* attr) const +{ + SkASSERT(node); + if (attr == nil) + return nil; + return (attr - node->attrs() + 1) < node->fAttrCount ? attr + 1 : nil; +} + +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 = nil; + + 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 = nil; + 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 = nil; + node->fAttrCount = SkToU16(attrCount); + node->fType = SkDOM::kElement_Type; + + if (fRoot == nil) + { + node->fNextSibling = nil; + 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 = nil; + 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 = nil; + fAlloc.reset(); + return nil; + } + 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)) != nil) + parser->addAttribute(name, value); + + node = dom.getFirstChild(node, nil); + while (node) + { + walk_dom(dom, node, parser); + node = dom.getNextSibling(node, nil); + } + + 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 == nil) + 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() == nil); + + 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 == nil); + + SkASSERT(dom.getFirstChild(root, "elem1")); + SkASSERT(!dom.getFirstChild(root, "subelem1")); + + dom.dump(); +#endif +} + +#endif + diff --git a/libs/graphics/xml/SkJS.cpp b/libs/graphics/xml/SkJS.cpp new file mode 100644 index 0000000000..06e7e834a6 --- /dev/null +++ b/libs/graphics/xml/SkJS.cpp @@ -0,0 +1,219 @@ +#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)) == nil) { + SkASSERT(0); + return; + } + if ((fContext = JS_NewContext(fRuntime, 0x1000)) == nil) { + SkASSERT(0); + return; + } + ; + if ((fGlobal = JS_NewObject(fContext, &global_class, NULL, NULL)) == nil) { + SkASSERT(0); + return; + } + if (JS_InitStandardClasses(fContext, fGlobal) == nil) { + 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); +} +#endif
\ No newline at end of file diff --git a/libs/graphics/xml/SkJSDisplayable.cpp b/libs/graphics/xml/SkJSDisplayable.cpp new file mode 100644 index 0000000000..6d5e10f40f --- /dev/null +++ b/libs/graphics/xml/SkJSDisplayable.cpp @@ -0,0 +1,455 @@ +#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(nil) {} + ~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(nil, 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(nil /* fMaker */, type, &infoCount); + if (info == nil) + 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(nil /* 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= nil; + 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(nil /* 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/libs/graphics/xml/SkParse.cpp b/libs/graphics/xml/SkParse.cpp new file mode 100644 index 0000000000..b7d312c1af --- /dev/null +++ b/libs/graphics/xml/SkParse.cpp @@ -0,0 +1,319 @@ +#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 nil; + + U32 n = 0; + int max_digits = 8; + int digit; + + while ((digit = to_hex(*str)) >= 0) + { + if (--max_digits < 0) + return nil; + 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 nil; + + 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 nil; + + 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 nil; + + int n = 0; + while (is_digit(*str)) + { + n = 10*n + *str - '0'; + if (n > 0x7FFF) + return nil; + 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 == nil) + 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 == nil) // last entry + entryLen = strlen(list); + else + entryLen = end - list; + + if (entryLen == len && memcmp(target, list, len) == 0) + return index; + if (end == nil) + 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/libs/graphics/xml/SkParseColor.cpp b/libs/graphics/xml/SkParseColor.cpp new file mode 100644 index 0000000000..75da7f4688 --- /dev/null +++ b/libs/graphics/xml/SkParseColor.cpp @@ -0,0 +1,529 @@ +#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 nil; + else + hi = mid; + } + return nil; +} + +// !!! 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) == nil) +// continue; +// do { +// if ((c = *str++) == '\0') +// goto goHome; +// } while (strchr(sep, c) != nil); +// 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 == nil) + 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 nil; + } +// } 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 == nil) +// return nil; + // !!! 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) != nil); + 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/libs/graphics/xml/SkXMLParser.cpp b/libs/graphics/xml/SkXMLParser.cpp new file mode 100644 index 0000000000..fa050b0f5f --- /dev/null +++ b/libs/graphics/xml/SkXMLParser.cpp @@ -0,0 +1,78 @@ +#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(nil), 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/libs/graphics/xml/SkXMLWriter.cpp b/libs/graphics/xml/SkXMLWriter.cpp new file mode 100644 index 0000000000..b0430d696d --- /dev/null +++ b/libs/graphics/xml/SkXMLWriter.cpp @@ -0,0 +1,325 @@ +#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[], S32 value) +{ + SkString tmp; + tmp.appendS32(value); + this->addAttribute(name, tmp.c_str()); +} + +void SkXMLWriter::addHexAttribute(const char name[], U32 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(nil, 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)) != nil) + w->addAttribute(name, value); + } + + node = dom.getFirstChild(node, nil); + while (node) + { + write_dom(dom, node, w, false); + node = dom.getNextSibling(node, nil); + } + + 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 + |