michael@0: michael@0: /* michael@0: * Copyright 2006 The Android Open Source Project michael@0: * michael@0: * Use of this source code is governed by a BSD-style license that can be michael@0: * found in the LICENSE file. michael@0: */ michael@0: michael@0: michael@0: #include "SkComposeShader.h" michael@0: #include "SkColorFilter.h" michael@0: #include "SkColorPriv.h" michael@0: #include "SkColorShader.h" michael@0: #include "SkReadBuffer.h" michael@0: #include "SkWriteBuffer.h" michael@0: #include "SkXfermode.h" michael@0: #include "SkString.h" michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: SkComposeShader::SkComposeShader(SkShader* sA, SkShader* sB, SkXfermode* mode) { michael@0: fShaderA = sA; sA->ref(); michael@0: fShaderB = sB; sB->ref(); michael@0: // mode may be null michael@0: fMode = mode; michael@0: SkSafeRef(mode); michael@0: } michael@0: michael@0: SkComposeShader::SkComposeShader(SkReadBuffer& buffer) : michael@0: INHERITED(buffer) { michael@0: fShaderA = buffer.readShader(); michael@0: if (NULL == fShaderA) { michael@0: fShaderA = SkNEW_ARGS(SkColorShader, (0)); michael@0: } michael@0: fShaderB = buffer.readShader(); michael@0: if (NULL == fShaderB) { michael@0: fShaderB = SkNEW_ARGS(SkColorShader, (0)); michael@0: } michael@0: fMode = buffer.readXfermode(); michael@0: } michael@0: michael@0: SkComposeShader::~SkComposeShader() { michael@0: SkSafeUnref(fMode); michael@0: fShaderB->unref(); michael@0: fShaderA->unref(); michael@0: } michael@0: michael@0: class SkAutoAlphaRestore { michael@0: public: michael@0: SkAutoAlphaRestore(SkPaint* paint, uint8_t newAlpha) { michael@0: fAlpha = paint->getAlpha(); michael@0: fPaint = paint; michael@0: paint->setAlpha(newAlpha); michael@0: } michael@0: michael@0: ~SkAutoAlphaRestore() { michael@0: fPaint->setAlpha(fAlpha); michael@0: } michael@0: private: michael@0: SkPaint* fPaint; michael@0: uint8_t fAlpha; michael@0: }; michael@0: #define SkAutoAlphaRestore(...) SK_REQUIRE_LOCAL_VAR(SkAutoAlphaRestore) michael@0: michael@0: void SkComposeShader::flatten(SkWriteBuffer& buffer) const { michael@0: this->INHERITED::flatten(buffer); michael@0: buffer.writeFlattenable(fShaderA); michael@0: buffer.writeFlattenable(fShaderB); michael@0: buffer.writeFlattenable(fMode); michael@0: } michael@0: michael@0: /* We call setContext on our two worker shaders. However, we michael@0: always let them see opaque alpha, and if the paint really michael@0: is translucent, then we apply that after the fact. michael@0: michael@0: We need to keep the calls to setContext/endContext balanced, since if we michael@0: return false, our endContext() will not be called. michael@0: */ michael@0: bool SkComposeShader::setContext(const SkBitmap& device, michael@0: const SkPaint& paint, michael@0: const SkMatrix& matrix) { michael@0: if (!this->INHERITED::setContext(device, paint, matrix)) { michael@0: return false; michael@0: } michael@0: michael@0: // we preconcat our localMatrix (if any) with the device matrix michael@0: // before calling our sub-shaders michael@0: michael@0: SkMatrix tmpM; michael@0: michael@0: tmpM.setConcat(matrix, this->getLocalMatrix()); michael@0: michael@0: SkAutoAlphaRestore restore(const_cast(&paint), 0xFF); michael@0: michael@0: bool setContextA = fShaderA->setContext(device, paint, tmpM); michael@0: bool setContextB = fShaderB->setContext(device, paint, tmpM); michael@0: if (!setContextA || !setContextB) { michael@0: if (setContextB) { michael@0: fShaderB->endContext(); michael@0: } michael@0: else if (setContextA) { michael@0: fShaderA->endContext(); michael@0: } michael@0: this->INHERITED::endContext(); michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: void SkComposeShader::endContext() { michael@0: fShaderB->endContext(); michael@0: fShaderA->endContext(); michael@0: this->INHERITED::endContext(); michael@0: } michael@0: michael@0: // larger is better (fewer times we have to loop), but we shouldn't michael@0: // take up too much stack-space (each element is 4 bytes) michael@0: #define TMP_COLOR_COUNT 64 michael@0: michael@0: void SkComposeShader::shadeSpan(int x, int y, SkPMColor result[], int count) { michael@0: SkShader* shaderA = fShaderA; michael@0: SkShader* shaderB = fShaderB; michael@0: SkXfermode* mode = fMode; michael@0: unsigned scale = SkAlpha255To256(this->getPaintAlpha()); michael@0: michael@0: SkPMColor tmp[TMP_COLOR_COUNT]; michael@0: michael@0: if (NULL == mode) { // implied SRC_OVER michael@0: // TODO: when we have a good test-case, should use SkBlitRow::Proc32 michael@0: // for these loops michael@0: do { michael@0: int n = count; michael@0: if (n > TMP_COLOR_COUNT) { michael@0: n = TMP_COLOR_COUNT; michael@0: } michael@0: michael@0: shaderA->shadeSpan(x, y, result, n); michael@0: shaderB->shadeSpan(x, y, tmp, n); michael@0: michael@0: if (256 == scale) { michael@0: for (int i = 0; i < n; i++) { michael@0: result[i] = SkPMSrcOver(tmp[i], result[i]); michael@0: } michael@0: } else { michael@0: for (int i = 0; i < n; i++) { michael@0: result[i] = SkAlphaMulQ(SkPMSrcOver(tmp[i], result[i]), michael@0: scale); michael@0: } michael@0: } michael@0: michael@0: result += n; michael@0: x += n; michael@0: count -= n; michael@0: } while (count > 0); michael@0: } else { // use mode for the composition michael@0: do { michael@0: int n = count; michael@0: if (n > TMP_COLOR_COUNT) { michael@0: n = TMP_COLOR_COUNT; michael@0: } michael@0: michael@0: shaderA->shadeSpan(x, y, result, n); michael@0: shaderB->shadeSpan(x, y, tmp, n); michael@0: mode->xfer32(result, tmp, n, NULL); michael@0: michael@0: if (256 == scale) { michael@0: for (int i = 0; i < n; i++) { michael@0: result[i] = SkAlphaMulQ(result[i], scale); michael@0: } michael@0: } michael@0: michael@0: result += n; michael@0: x += n; michael@0: count -= n; michael@0: } while (count > 0); michael@0: } michael@0: } michael@0: michael@0: #ifndef SK_IGNORE_TO_STRING michael@0: void SkComposeShader::toString(SkString* str) const { michael@0: str->append("SkComposeShader: ("); michael@0: michael@0: str->append("ShaderA: "); michael@0: fShaderA->toString(str); michael@0: str->append(" ShaderB: "); michael@0: fShaderB->toString(str); michael@0: str->append(" Xfermode: "); michael@0: fMode->toString(str); michael@0: michael@0: this->INHERITED::toString(str); michael@0: michael@0: str->append(")"); michael@0: } michael@0: #endif