| /* libs/graphics/effects/SkShaderExtras.cpp |
| ** |
| ** Copyright 2006, The Android Open Source Project |
| ** |
| ** Licensed under the Apache License, Version 2.0 (the "License"); |
| ** you may not use this file except in compliance with the License. |
| ** You may obtain a copy of the License at |
| ** |
| ** http://www.apache.org/licenses/LICENSE-2.0 |
| ** |
| ** Unless required by applicable law or agreed to in writing, software |
| ** distributed under the License is distributed on an "AS IS" BASIS, |
| ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| ** See the License for the specific language governing permissions and |
| ** limitations under the License. |
| */ |
| |
| #include "SkComposeShader.h" |
| #include "SkColorFilter.h" |
| #include "SkColorPriv.h" |
| #include "SkXfermode.h" |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| SkComposeShader::SkComposeShader(SkShader* sA, SkShader* sB, SkXfermode* mode) { |
| fShaderA = sA; sA->ref(); |
| fShaderB = sB; sB->ref(); |
| // mode may be null |
| fMode = mode; |
| SkSafeRef(mode); |
| } |
| |
| SkComposeShader::SkComposeShader(SkFlattenableReadBuffer& buffer) : |
| INHERITED(buffer) { |
| fShaderA = static_cast<SkShader*>(buffer.readFlattenable()); |
| fShaderB = static_cast<SkShader*>(buffer.readFlattenable()); |
| fMode = static_cast<SkXfermode*>(buffer.readFlattenable()); |
| } |
| |
| SkComposeShader::~SkComposeShader() { |
| SkSafeUnref(fMode); |
| fShaderB->unref(); |
| fShaderA->unref(); |
| } |
| |
| void SkComposeShader::beginSession() { |
| this->INHERITED::beginSession(); |
| fShaderA->beginSession(); |
| fShaderB->beginSession(); |
| } |
| |
| void SkComposeShader::endSession() { |
| fShaderA->endSession(); |
| fShaderB->endSession(); |
| this->INHERITED::endSession(); |
| } |
| |
| class SkAutoAlphaRestore { |
| public: |
| SkAutoAlphaRestore(SkPaint* paint, uint8_t newAlpha) { |
| fAlpha = paint->getAlpha(); |
| fPaint = paint; |
| paint->setAlpha(newAlpha); |
| } |
| |
| ~SkAutoAlphaRestore() { |
| fPaint->setAlpha(fAlpha); |
| } |
| private: |
| SkPaint* fPaint; |
| uint8_t fAlpha; |
| }; |
| |
| void SkComposeShader::flatten(SkFlattenableWriteBuffer& buffer) { |
| this->INHERITED::flatten(buffer); |
| buffer.writeFlattenable(fShaderA); |
| buffer.writeFlattenable(fShaderB); |
| buffer.writeFlattenable(fMode); |
| } |
| |
| /* We call setContext on our two worker shaders. However, we |
| always let them see opaque alpha, and if the paint really |
| is translucent, then we apply that after the fact. |
| */ |
| bool SkComposeShader::setContext(const SkBitmap& device, |
| const SkPaint& paint, |
| const SkMatrix& matrix) { |
| if (!this->INHERITED::setContext(device, paint, matrix)) { |
| return false; |
| } |
| |
| // we preconcat our localMatrix (if any) with the device matrix |
| // before calling our sub-shaders |
| |
| SkMatrix tmpM; |
| |
| (void)this->getLocalMatrix(&tmpM); |
| tmpM.setConcat(matrix, tmpM); |
| |
| SkAutoAlphaRestore restore(const_cast<SkPaint*>(&paint), 0xFF); |
| |
| return fShaderA->setContext(device, paint, tmpM) && |
| fShaderB->setContext(device, paint, tmpM); |
| } |
| |
| // larger is better (fewer times we have to loop), but we shouldn't |
| // take up too much stack-space (each element is 4 bytes) |
| #define TMP_COLOR_COUNT 64 |
| |
| void SkComposeShader::shadeSpan(int x, int y, SkPMColor result[], int count) { |
| SkShader* shaderA = fShaderA; |
| SkShader* shaderB = fShaderB; |
| SkXfermode* mode = fMode; |
| unsigned scale = SkAlpha255To256(this->getPaintAlpha()); |
| |
| SkPMColor tmp[TMP_COLOR_COUNT]; |
| |
| if (NULL == mode) { // implied SRC_OVER |
| // TODO: when we have a good test-case, should use SkBlitRow::Proc32 |
| // for these loops |
| do { |
| int n = count; |
| if (n > TMP_COLOR_COUNT) { |
| n = TMP_COLOR_COUNT; |
| } |
| |
| shaderA->shadeSpan(x, y, result, n); |
| shaderB->shadeSpan(x, y, tmp, n); |
| |
| if (256 == scale) { |
| for (int i = 0; i < n; i++) { |
| result[i] = SkPMSrcOver(tmp[i], result[i]); |
| } |
| } else { |
| for (int i = 0; i < n; i++) { |
| result[i] = SkAlphaMulQ(SkPMSrcOver(tmp[i], result[i]), |
| scale); |
| } |
| } |
| |
| result += n; |
| x += n; |
| count -= n; |
| } while (count > 0); |
| } else { // use mode for the composition |
| do { |
| int n = count; |
| if (n > TMP_COLOR_COUNT) { |
| n = TMP_COLOR_COUNT; |
| } |
| |
| shaderA->shadeSpan(x, y, result, n); |
| shaderB->shadeSpan(x, y, tmp, n); |
| mode->xfer32(result, tmp, n, NULL); |
| |
| if (256 == scale) { |
| for (int i = 0; i < n; i++) { |
| result[i] = SkAlphaMulQ(result[i], scale); |
| } |
| } |
| |
| result += n; |
| x += n; |
| count -= n; |
| } while (count > 0); |
| } |
| } |
| |