blob: 15df3a37a587f253f0634558517a09e141bce63a [file] [log] [blame]
commit-bot@chromium.orgc5d9bb02014-04-08 15:19:34 +00001/*
2 * Copyright 2014 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "SkPictureShader.h"
9
10#include "SkBitmap.h"
11#include "SkBitmapProcShader.h"
12#include "SkCanvas.h"
13#include "SkMatrixUtils.h"
14#include "SkPicture.h"
15#include "SkReadBuffer.h"
16
17#if SK_SUPPORT_GPU
18#include "GrContext.h"
19#endif
20
21SkPictureShader::SkPictureShader(SkPicture* picture, TileMode tmx, TileMode tmy)
22 : fPicture(picture)
23 , fTmx(tmx)
24 , fTmy(tmy) {
25 SkSafeRef(fPicture);
26}
27
28SkPictureShader::SkPictureShader(SkReadBuffer& buffer)
29 : INHERITED(buffer) {
30 fTmx = static_cast<SkShader::TileMode>(buffer.read32());
31 fTmy = static_cast<SkShader::TileMode>(buffer.read32());
32 if (buffer.readBool()) {
33 fPicture = SkPicture::CreateFromBuffer(buffer);
34 } else {
35 fPicture = NULL;
36 }
37}
38
39SkPictureShader::~SkPictureShader() {
40 SkSafeUnref(fPicture);
41}
42
43SkPictureShader* SkPictureShader::Create(SkPicture* picture, TileMode tmx, TileMode tmy) {
44 return SkNEW_ARGS(SkPictureShader, (picture, tmx, tmy));
45}
46
47void SkPictureShader::flatten(SkWriteBuffer& buffer) const {
48 this->INHERITED::flatten(buffer);
49
50 buffer.write32(fTmx);
51 buffer.write32(fTmy);
52 buffer.writeBool(NULL != fPicture);
53 if (fPicture) {
54 fPicture->flatten(buffer);
55 }
56}
57
58bool SkPictureShader::buildBitmapShader(const SkMatrix& matrix) const {
59 if (!fPicture || (0 == fPicture->width() && 0 == fPicture->height())) {
60 return false;
61 }
62
63 SkMatrix m;
64 if (this->hasLocalMatrix()) {
65 m.setConcat(matrix, this->getLocalMatrix());
66 } else {
67 m = matrix;
68 }
69
70 // Use a rotation-invariant scale
71 SkPoint scale;
72 if (!SkDecomposeUpper2x2(m, NULL, &scale, NULL)) {
73 // Decomposition failed, use an approximation.
74 scale.set(SkScalarSqrt(m.getScaleX() * m.getScaleX() + m.getSkewX() * m.getSkewX()),
75 SkScalarSqrt(m.getScaleY() * m.getScaleY() + m.getSkewY() * m.getSkewY()));
76 }
77 SkSize scaledSize = SkSize::Make(scale.x() * fPicture->width(), scale.y() * fPicture->height());
78
79 SkISize tileSize = scaledSize.toRound();
80 if (tileSize.isEmpty()) {
81 return false;
82 }
83
84 // The actual scale, compensating for rounding.
85 SkSize tileScale = SkSize::Make(SkIntToScalar(tileSize.width()) / fPicture->width(),
86 SkIntToScalar(tileSize.height()) / fPicture->height());
87
88 if (!fCachedShader || tileScale != fCachedTileScale) {
89 SkBitmap bm;
90 if (!bm.allocN32Pixels(tileSize.width(), tileSize.height())) {
91 return false;
92 }
93 bm.eraseColor(SK_ColorTRANSPARENT);
94
95 SkCanvas canvas(bm);
96 canvas.scale(tileScale.width(), tileScale.height());
97 canvas.drawPicture(*fPicture);
98
99 fCachedShader.reset(CreateBitmapShader(bm, fTmx, fTmy));
100 fCachedTileScale = tileScale;
101 }
102
103 SkMatrix shaderMatrix = this->getLocalMatrix();
104 shaderMatrix.preScale(1 / tileScale.width(), 1 / tileScale.height());
105 fCachedShader->setLocalMatrix(shaderMatrix);
106
107 return true;
108}
109
110bool SkPictureShader::setContext(const SkBitmap& device,
111 const SkPaint& paint,
112 const SkMatrix& matrix) {
113 if (!this->buildBitmapShader(matrix)) {
114 return false;
115 }
116
117 if (!this->INHERITED::setContext(device, paint, matrix)) {
118 return false;
119 }
120
121 SkASSERT(fCachedShader);
122 if (!fCachedShader->setContext(device, paint, matrix)) {
123 this->INHERITED::endContext();
124 return false;
125 }
126
127 return true;
128}
129
130void SkPictureShader::endContext() {
131 SkASSERT(fCachedShader);
132 fCachedShader->endContext();
133
134 this->INHERITED::endContext();
135}
136
137uint32_t SkPictureShader::getFlags() {
138 if (NULL != fCachedShader) {
139 return fCachedShader->getFlags();
140 }
141 return 0;
142}
143
144SkShader::ShadeProc SkPictureShader::asAShadeProc(void** ctx) {
145 if (fCachedShader) {
146 return fCachedShader->asAShadeProc(ctx);
147 }
148 return NULL;
149}
150
151void SkPictureShader::shadeSpan(int x, int y, SkPMColor dstC[], int count) {
152 SkASSERT(fCachedShader);
153 fCachedShader->shadeSpan(x, y, dstC, count);
154}
155
156void SkPictureShader::shadeSpan16(int x, int y, uint16_t dstC[], int count) {
157 SkASSERT(fCachedShader);
158 fCachedShader->shadeSpan16(x, y, dstC, count);
159}
160
161#ifndef SK_IGNORE_TO_STRING
162void SkPictureShader::toString(SkString* str) const {
163 static const char* gTileModeName[SkShader::kTileModeCount] = {
164 "clamp", "repeat", "mirror"
165 };
166
167 str->appendf("PictureShader: [%d:%d] ",
168 fPicture ? fPicture->width() : 0,
169 fPicture ? fPicture->height() : 0);
170
171 str->appendf("(%s, %s)", gTileModeName[fTmx], gTileModeName[fTmy]);
172
173 this->INHERITED::toString(str);
174}
175#endif
176
177#if SK_SUPPORT_GPU
178GrEffectRef* SkPictureShader::asNewEffect(GrContext* context, const SkPaint& paint) const {
179 if (!this->buildBitmapShader(context->getMatrix())) {
180 return NULL;
181 }
182 SkASSERT(fCachedShader);
183 return fCachedShader->asNewEffect(context, paint);
184}
185#endif