blob: 1668618cf740bac454bd7af06774e5994d4443af [file] [log] [blame]
robertphillips@google.com53238bc2013-08-30 13:12:10 +00001/*
2 * Copyright 2013 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 "SkBitmapDevice.h"
9#include "SkConfig8888.h"
10#include "SkDraw.h"
11#include "SkRasterClip.h"
12#include "SkShader.h"
13
reed@google.com44699382013-10-31 17:28:30 +000014#define CHECK_FOR_ANNOTATION(paint) \
15 do { if (paint.getAnnotation()) { return; } } while (0)
robertphillips@google.com53238bc2013-08-30 13:12:10 +000016
17SkBitmapDevice::SkBitmapDevice(const SkBitmap& bitmap)
18 : fBitmap(bitmap) {
19 SkASSERT(SkBitmap::kARGB_4444_Config != bitmap.config());
20}
21
22SkBitmapDevice::SkBitmapDevice(const SkBitmap& bitmap, const SkDeviceProperties& deviceProperties)
23 : SkBaseDevice(deviceProperties)
24 , fBitmap(bitmap) {
25}
26
reed@google.com398337b2013-12-11 21:22:39 +000027SkBitmapDevice::SkBitmapDevice(SkBitmap::Config config, int width, int height, bool isOpaque) {
reed@google.com383a6972013-10-21 14:00:07 +000028 fBitmap.setConfig(config, width, height, 0, isOpaque ?
29 kOpaque_SkAlphaType : kPremul_SkAlphaType);
reed@google.com398337b2013-12-11 21:22:39 +000030 if (!fBitmap.allocPixels()) {
31 fBitmap.setConfig(config, 0, 0, 0, isOpaque ?
32 kOpaque_SkAlphaType : kPremul_SkAlphaType);
commit-bot@chromium.orgcd3b15c2013-12-04 17:06:49 +000033 }
reed@google.com398337b2013-12-11 21:22:39 +000034 if (!isOpaque) {
35 fBitmap.eraseColor(SK_ColorTRANSPARENT);
36 }
robertphillips@google.com53238bc2013-08-30 13:12:10 +000037}
38
39SkBitmapDevice::SkBitmapDevice(SkBitmap::Config config, int width, int height, bool isOpaque,
40 const SkDeviceProperties& deviceProperties)
reed@google.com398337b2013-12-11 21:22:39 +000041 : SkBaseDevice(deviceProperties) {
42
43 fBitmap.setConfig(config, width, height, 0, isOpaque ?
44 kOpaque_SkAlphaType : kPremul_SkAlphaType);
45 if (!fBitmap.allocPixels()) {
46 fBitmap.setConfig(config, 0, 0, 0, isOpaque ?
47 kOpaque_SkAlphaType : kPremul_SkAlphaType);
48 }
49 if (!isOpaque) {
50 fBitmap.eraseColor(SK_ColorTRANSPARENT);
51 }
robertphillips@google.com53238bc2013-08-30 13:12:10 +000052}
53
54SkBitmapDevice::~SkBitmapDevice() {
55}
56
57void SkBitmapDevice::replaceBitmapBackendForRasterSurface(const SkBitmap& bm) {
58 SkASSERT(bm.width() == fBitmap.width());
59 SkASSERT(bm.height() == fBitmap.height());
60 fBitmap = bm; // intent is to use bm's pixelRef (and rowbytes/config)
61 fBitmap.lockPixels();
62}
63
64SkBaseDevice* SkBitmapDevice::onCreateCompatibleDevice(SkBitmap::Config config,
65 int width, int height,
66 bool isOpaque,
67 Usage usage) {
commit-bot@chromium.orgcd3b15c2013-12-04 17:06:49 +000068 SkBitmapDevice* device = SkNEW_ARGS(SkBitmapDevice,(config, width, height,
69 isOpaque, this->getDeviceProperties()));
70 // Check if allocation failed and delete device if it did fail
71 if ((device->width() != width) || (device->height() != height)) {
72 SkDELETE(device);
73 device = NULL;
74 }
75 return device;
robertphillips@google.com53238bc2013-08-30 13:12:10 +000076}
77
78void SkBitmapDevice::lockPixels() {
79 if (fBitmap.lockPixelsAreWritable()) {
80 fBitmap.lockPixels();
81 }
82}
83
84void SkBitmapDevice::unlockPixels() {
85 if (fBitmap.lockPixelsAreWritable()) {
86 fBitmap.unlockPixels();
87 }
88}
89
robertphillips@google.com53238bc2013-08-30 13:12:10 +000090void SkBitmapDevice::clear(SkColor color) {
91 fBitmap.eraseColor(color);
92}
93
94const SkBitmap& SkBitmapDevice::onAccessBitmap() {
95 return fBitmap;
96}
97
98bool SkBitmapDevice::canHandleImageFilter(SkImageFilter*) {
99 return false;
100}
101
102bool SkBitmapDevice::filterImage(SkImageFilter* filter, const SkBitmap& src,
103 const SkMatrix& ctm, SkBitmap* result,
104 SkIPoint* offset) {
105 return false;
106}
107
108bool SkBitmapDevice::allowImageFilter(SkImageFilter*) {
109 return true;
110}
111
112bool SkBitmapDevice::onReadPixels(const SkBitmap& bitmap,
113 int x, int y,
114 SkCanvas::Config8888 config8888) {
115 SkASSERT(SkBitmap::kARGB_8888_Config == bitmap.config());
116 SkASSERT(!bitmap.isNull());
skia.committer@gmail.come0c170f2013-08-31 07:01:28 +0000117 SkASSERT(SkIRect::MakeWH(this->width(), this->height()).contains(SkIRect::MakeXYWH(x, y,
118 bitmap.width(),
robertphillips@google.com53238bc2013-08-30 13:12:10 +0000119 bitmap.height())));
120
121 SkIRect srcRect = SkIRect::MakeXYWH(x, y, bitmap.width(), bitmap.height());
122 const SkBitmap& src = this->accessBitmap(false);
123
124 SkBitmap subset;
125 if (!src.extractSubset(&subset, srcRect)) {
126 return false;
127 }
128 if (SkBitmap::kARGB_8888_Config != subset.config()) {
129 // It'd be preferable to do this directly to bitmap.
130 subset.copyTo(&subset, SkBitmap::kARGB_8888_Config);
131 }
132 SkAutoLockPixels alp(bitmap);
133 uint32_t* bmpPixels = reinterpret_cast<uint32_t*>(bitmap.getPixels());
134 SkCopyBitmapToConfig8888(bmpPixels, bitmap.rowBytes(), config8888, subset);
135 return true;
136}
137
138void SkBitmapDevice::writePixels(const SkBitmap& bitmap,
139 int x, int y,
140 SkCanvas::Config8888 config8888) {
141 if (bitmap.isNull() || bitmap.getTexture()) {
142 return;
143 }
144 const SkBitmap* sprite = &bitmap;
145 // check whether we have to handle a config8888 that doesn't match SkPMColor
146 if (SkBitmap::kARGB_8888_Config == bitmap.config() &&
147 SkCanvas::kNative_Premul_Config8888 != config8888 &&
148 kPMColorAlias != config8888) {
149
150 // We're going to have to convert from a config8888 to the native config
151 // First we clip to the device bounds.
152 SkBitmap dstBmp = this->accessBitmap(true);
153 SkIRect spriteRect = SkIRect::MakeXYWH(x, y,
154 bitmap.width(), bitmap.height());
155 SkIRect devRect = SkIRect::MakeWH(dstBmp.width(), dstBmp.height());
156 if (!spriteRect.intersect(devRect)) {
157 return;
158 }
159
160 // write directly to the device if it has pixels and is SkPMColor
161 bool drawSprite;
162 if (SkBitmap::kARGB_8888_Config == dstBmp.config() && !dstBmp.isNull()) {
163 // we can write directly to the dst when doing the conversion
164 dstBmp.extractSubset(&dstBmp, spriteRect);
165 drawSprite = false;
166 } else {
167 // we convert to a temporary bitmap and draw that as a sprite
168 dstBmp.setConfig(SkBitmap::kARGB_8888_Config,
169 spriteRect.width(),
170 spriteRect.height());
171 if (!dstBmp.allocPixels()) {
172 return;
173 }
174 drawSprite = true;
175 }
176
177 // copy pixels to dstBmp and convert from config8888 to native config.
178 SkAutoLockPixels alp(bitmap);
179 uint32_t* srcPixels = bitmap.getAddr32(spriteRect.fLeft - x,
180 spriteRect.fTop - y);
181 SkCopyConfig8888ToBitmap(dstBmp,
182 srcPixels,
183 bitmap.rowBytes(),
184 config8888);
185
186 if (drawSprite) {
187 // we've clipped the sprite when we made a copy
188 x = spriteRect.fLeft;
189 y = spriteRect.fTop;
190 sprite = &dstBmp;
191 } else {
192 return;
193 }
194 }
195
196 SkPaint paint;
197 paint.setXfermodeMode(SkXfermode::kSrc_Mode);
198 SkRasterClip clip(SkIRect::MakeWH(fBitmap.width(), fBitmap.height()));
199 SkDraw draw;
200 draw.fRC = &clip;
201 draw.fClip = &clip.bwRgn();
202 draw.fBitmap = &fBitmap; // canvas should have already called accessBitmap
203 draw.fMatrix = &SkMatrix::I();
204 this->drawSprite(draw, *sprite, x, y, paint);
205}
206
207///////////////////////////////////////////////////////////////////////////////
208
209void SkBitmapDevice::drawPaint(const SkDraw& draw, const SkPaint& paint) {
210 draw.drawPaint(paint);
211}
212
213void SkBitmapDevice::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode, size_t count,
214 const SkPoint pts[], const SkPaint& paint) {
reed@google.com44699382013-10-31 17:28:30 +0000215 CHECK_FOR_ANNOTATION(paint);
robertphillips@google.com53238bc2013-08-30 13:12:10 +0000216 draw.drawPoints(mode, count, pts, paint);
217}
218
219void SkBitmapDevice::drawRect(const SkDraw& draw, const SkRect& r, const SkPaint& paint) {
reed@google.com44699382013-10-31 17:28:30 +0000220 CHECK_FOR_ANNOTATION(paint);
robertphillips@google.com53238bc2013-08-30 13:12:10 +0000221 draw.drawRect(r, paint);
222}
223
224void SkBitmapDevice::drawOval(const SkDraw& draw, const SkRect& oval, const SkPaint& paint) {
reed@google.com44699382013-10-31 17:28:30 +0000225 CHECK_FOR_ANNOTATION(paint);
robertphillips@google.com53238bc2013-08-30 13:12:10 +0000226
227 SkPath path;
228 path.addOval(oval);
229 // call the VIRTUAL version, so any subclasses who do handle drawPath aren't
230 // required to override drawOval.
231 this->drawPath(draw, path, paint, NULL, true);
232}
233
234void SkBitmapDevice::drawRRect(const SkDraw& draw, const SkRRect& rrect, const SkPaint& paint) {
reed@google.com44699382013-10-31 17:28:30 +0000235 CHECK_FOR_ANNOTATION(paint);
robertphillips@google.com53238bc2013-08-30 13:12:10 +0000236
robertphillips@google.com50a76002013-11-12 01:16:56 +0000237#ifdef SK_IGNORE_BLURRED_RRECT_OPT
238 SkPath path;
239
240 path.addRRect(rrect);
241 // call the VIRTUAL version, so any subclasses who do handle drawPath aren't
242 // required to override drawRRect.
243 this->drawPath(draw, path, paint, NULL, true);
244#else
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000245 draw.drawRRect(rrect, paint);
robertphillips@google.com50a76002013-11-12 01:16:56 +0000246#endif
robertphillips@google.com53238bc2013-08-30 13:12:10 +0000247}
248
249void SkBitmapDevice::drawPath(const SkDraw& draw, const SkPath& path,
250 const SkPaint& paint, const SkMatrix* prePathMatrix,
251 bool pathIsMutable) {
reed@google.com44699382013-10-31 17:28:30 +0000252 CHECK_FOR_ANNOTATION(paint);
robertphillips@google.com53238bc2013-08-30 13:12:10 +0000253 draw.drawPath(path, paint, prePathMatrix, pathIsMutable);
254}
255
256void SkBitmapDevice::drawBitmap(const SkDraw& draw, const SkBitmap& bitmap,
257 const SkMatrix& matrix, const SkPaint& paint) {
258 draw.drawBitmap(bitmap, matrix, paint);
259}
260
261void SkBitmapDevice::drawBitmapRect(const SkDraw& draw, const SkBitmap& bitmap,
262 const SkRect* src, const SkRect& dst,
263 const SkPaint& paint,
264 SkCanvas::DrawBitmapRectFlags flags) {
265 SkMatrix matrix;
266 SkRect bitmapBounds, tmpSrc, tmpDst;
267 SkBitmap tmpBitmap;
268
269 bitmapBounds.isetWH(bitmap.width(), bitmap.height());
270
271 // Compute matrix from the two rectangles
272 if (src) {
273 tmpSrc = *src;
274 } else {
275 tmpSrc = bitmapBounds;
276 }
277 matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
278
279 const SkRect* dstPtr = &dst;
280 const SkBitmap* bitmapPtr = &bitmap;
281
282 // clip the tmpSrc to the bounds of the bitmap, and recompute dstRect if
283 // needed (if the src was clipped). No check needed if src==null.
284 if (src) {
285 if (!bitmapBounds.contains(*src)) {
286 if (!tmpSrc.intersect(bitmapBounds)) {
287 return; // nothing to draw
288 }
289 // recompute dst, based on the smaller tmpSrc
290 matrix.mapRect(&tmpDst, tmpSrc);
291 dstPtr = &tmpDst;
292 }
293
294 // since we may need to clamp to the borders of the src rect within
295 // the bitmap, we extract a subset.
296 SkIRect srcIR;
297 tmpSrc.roundOut(&srcIR);
298 if (!bitmap.extractSubset(&tmpBitmap, srcIR)) {
299 return;
300 }
301 bitmapPtr = &tmpBitmap;
302
303 // Since we did an extract, we need to adjust the matrix accordingly
304 SkScalar dx = 0, dy = 0;
305 if (srcIR.fLeft > 0) {
306 dx = SkIntToScalar(srcIR.fLeft);
307 }
308 if (srcIR.fTop > 0) {
309 dy = SkIntToScalar(srcIR.fTop);
310 }
311 if (dx || dy) {
312 matrix.preTranslate(dx, dy);
313 }
314
315 SkRect extractedBitmapBounds;
316 extractedBitmapBounds.isetWH(bitmapPtr->width(), bitmapPtr->height());
317 if (extractedBitmapBounds == tmpSrc) {
318 // no fractional part in src, we can just call drawBitmap
319 goto USE_DRAWBITMAP;
320 }
321 } else {
322 USE_DRAWBITMAP:
323 // We can go faster by just calling drawBitmap, which will concat the
324 // matrix with the CTM, and try to call drawSprite if it can. If not,
325 // it will make a shader and call drawRect, as we do below.
326 this->drawBitmap(draw, *bitmapPtr, matrix, paint);
327 return;
328 }
329
330 // construct a shader, so we can call drawRect with the dst
331 SkShader* s = SkShader::CreateBitmapShader(*bitmapPtr,
332 SkShader::kClamp_TileMode,
333 SkShader::kClamp_TileMode);
334 if (NULL == s) {
335 return;
336 }
337 s->setLocalMatrix(matrix);
338
339 SkPaint paintWithShader(paint);
340 paintWithShader.setStyle(SkPaint::kFill_Style);
341 paintWithShader.setShader(s)->unref();
342
343 // Call ourself, in case the subclass wanted to share this setup code
344 // but handle the drawRect code themselves.
345 this->drawRect(draw, *dstPtr, paintWithShader);
346}
347
348void SkBitmapDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap,
349 int x, int y, const SkPaint& paint) {
350 draw.drawSprite(bitmap, x, y, paint);
351}
352
353void SkBitmapDevice::drawText(const SkDraw& draw, const void* text, size_t len,
354 SkScalar x, SkScalar y, const SkPaint& paint) {
355 draw.drawText((const char*)text, len, x, y, paint);
356}
357
358void SkBitmapDevice::drawPosText(const SkDraw& draw, const void* text, size_t len,
359 const SkScalar xpos[], SkScalar y,
360 int scalarsPerPos, const SkPaint& paint) {
361 draw.drawPosText((const char*)text, len, xpos, y, scalarsPerPos, paint);
362}
363
364void SkBitmapDevice::drawTextOnPath(const SkDraw& draw, const void* text,
365 size_t len, const SkPath& path,
366 const SkMatrix* matrix,
367 const SkPaint& paint) {
368 draw.drawTextOnPath((const char*)text, len, path, matrix, paint);
369}
370
robertphillips@google.com53238bc2013-08-30 13:12:10 +0000371void SkBitmapDevice::drawVertices(const SkDraw& draw, SkCanvas::VertexMode vmode,
372 int vertexCount,
373 const SkPoint verts[], const SkPoint textures[],
374 const SkColor colors[], SkXfermode* xmode,
375 const uint16_t indices[], int indexCount,
376 const SkPaint& paint) {
377 draw.drawVertices(vmode, vertexCount, verts, textures, colors, xmode,
378 indices, indexCount, paint);
379}
380
381void SkBitmapDevice::drawDevice(const SkDraw& draw, SkBaseDevice* device,
382 int x, int y, const SkPaint& paint) {
383 const SkBitmap& src = device->accessBitmap(false);
384 draw.drawSprite(src, x, y, paint);
385}
386
387///////////////////////////////////////////////////////////////////////////////
388
389bool SkBitmapDevice::filterTextFlags(const SkPaint& paint, TextFlags* flags) {
390 if (!paint.isLCDRenderText() || !paint.isAntiAlias()) {
391 // we're cool with the paint as is
392 return false;
393 }
394
395 if (SkBitmap::kARGB_8888_Config != fBitmap.config() ||
396 paint.getRasterizer() ||
397 paint.getPathEffect() ||
398 paint.isFakeBoldText() ||
399 paint.getStyle() != SkPaint::kFill_Style ||
400 !SkXfermode::IsMode(paint.getXfermode(), SkXfermode::kSrcOver_Mode)) {
401 // turn off lcd
402 flags->fFlags = paint.getFlags() & ~SkPaint::kLCDRenderText_Flag;
403 flags->fHinting = paint.getHinting();
404 return true;
405 }
406 // we're cool with the paint as is
407 return false;
408}