robertphillips@google.com | 53238bc | 2013-08-30 13:12:10 +0000 | [diff] [blame] | 1 | /* |
| 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.com | 4469938 | 2013-10-31 17:28:30 +0000 | [diff] [blame] | 14 | #define CHECK_FOR_ANNOTATION(paint) \ |
| 15 | do { if (paint.getAnnotation()) { return; } } while (0) |
robertphillips@google.com | 53238bc | 2013-08-30 13:12:10 +0000 | [diff] [blame] | 16 | |
| 17 | SkBitmapDevice::SkBitmapDevice(const SkBitmap& bitmap) |
| 18 | : fBitmap(bitmap) { |
| 19 | SkASSERT(SkBitmap::kARGB_4444_Config != bitmap.config()); |
| 20 | } |
| 21 | |
| 22 | SkBitmapDevice::SkBitmapDevice(const SkBitmap& bitmap, const SkDeviceProperties& deviceProperties) |
| 23 | : SkBaseDevice(deviceProperties) |
| 24 | , fBitmap(bitmap) { |
| 25 | } |
| 26 | |
reed@google.com | 398337b | 2013-12-11 21:22:39 +0000 | [diff] [blame^] | 27 | SkBitmapDevice::SkBitmapDevice(SkBitmap::Config config, int width, int height, bool isOpaque) { |
reed@google.com | 383a697 | 2013-10-21 14:00:07 +0000 | [diff] [blame] | 28 | fBitmap.setConfig(config, width, height, 0, isOpaque ? |
| 29 | kOpaque_SkAlphaType : kPremul_SkAlphaType); |
reed@google.com | 398337b | 2013-12-11 21:22:39 +0000 | [diff] [blame^] | 30 | if (!fBitmap.allocPixels()) { |
| 31 | fBitmap.setConfig(config, 0, 0, 0, isOpaque ? |
| 32 | kOpaque_SkAlphaType : kPremul_SkAlphaType); |
commit-bot@chromium.org | cd3b15c | 2013-12-04 17:06:49 +0000 | [diff] [blame] | 33 | } |
reed@google.com | 398337b | 2013-12-11 21:22:39 +0000 | [diff] [blame^] | 34 | if (!isOpaque) { |
| 35 | fBitmap.eraseColor(SK_ColorTRANSPARENT); |
| 36 | } |
robertphillips@google.com | 53238bc | 2013-08-30 13:12:10 +0000 | [diff] [blame] | 37 | } |
| 38 | |
| 39 | SkBitmapDevice::SkBitmapDevice(SkBitmap::Config config, int width, int height, bool isOpaque, |
| 40 | const SkDeviceProperties& deviceProperties) |
reed@google.com | 398337b | 2013-12-11 21:22:39 +0000 | [diff] [blame^] | 41 | : 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.com | 53238bc | 2013-08-30 13:12:10 +0000 | [diff] [blame] | 52 | } |
| 53 | |
| 54 | SkBitmapDevice::~SkBitmapDevice() { |
| 55 | } |
| 56 | |
| 57 | void 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 | |
| 64 | SkBaseDevice* SkBitmapDevice::onCreateCompatibleDevice(SkBitmap::Config config, |
| 65 | int width, int height, |
| 66 | bool isOpaque, |
| 67 | Usage usage) { |
commit-bot@chromium.org | cd3b15c | 2013-12-04 17:06:49 +0000 | [diff] [blame] | 68 | 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.com | 53238bc | 2013-08-30 13:12:10 +0000 | [diff] [blame] | 76 | } |
| 77 | |
| 78 | void SkBitmapDevice::lockPixels() { |
| 79 | if (fBitmap.lockPixelsAreWritable()) { |
| 80 | fBitmap.lockPixels(); |
| 81 | } |
| 82 | } |
| 83 | |
| 84 | void SkBitmapDevice::unlockPixels() { |
| 85 | if (fBitmap.lockPixelsAreWritable()) { |
| 86 | fBitmap.unlockPixels(); |
| 87 | } |
| 88 | } |
| 89 | |
robertphillips@google.com | 53238bc | 2013-08-30 13:12:10 +0000 | [diff] [blame] | 90 | void SkBitmapDevice::clear(SkColor color) { |
| 91 | fBitmap.eraseColor(color); |
| 92 | } |
| 93 | |
| 94 | const SkBitmap& SkBitmapDevice::onAccessBitmap() { |
| 95 | return fBitmap; |
| 96 | } |
| 97 | |
| 98 | bool SkBitmapDevice::canHandleImageFilter(SkImageFilter*) { |
| 99 | return false; |
| 100 | } |
| 101 | |
| 102 | bool SkBitmapDevice::filterImage(SkImageFilter* filter, const SkBitmap& src, |
| 103 | const SkMatrix& ctm, SkBitmap* result, |
| 104 | SkIPoint* offset) { |
| 105 | return false; |
| 106 | } |
| 107 | |
| 108 | bool SkBitmapDevice::allowImageFilter(SkImageFilter*) { |
| 109 | return true; |
| 110 | } |
| 111 | |
| 112 | bool 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.com | e0c170f | 2013-08-31 07:01:28 +0000 | [diff] [blame] | 117 | SkASSERT(SkIRect::MakeWH(this->width(), this->height()).contains(SkIRect::MakeXYWH(x, y, |
| 118 | bitmap.width(), |
robertphillips@google.com | 53238bc | 2013-08-30 13:12:10 +0000 | [diff] [blame] | 119 | 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 | |
| 138 | void 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 | |
| 209 | void SkBitmapDevice::drawPaint(const SkDraw& draw, const SkPaint& paint) { |
| 210 | draw.drawPaint(paint); |
| 211 | } |
| 212 | |
| 213 | void SkBitmapDevice::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode, size_t count, |
| 214 | const SkPoint pts[], const SkPaint& paint) { |
reed@google.com | 4469938 | 2013-10-31 17:28:30 +0000 | [diff] [blame] | 215 | CHECK_FOR_ANNOTATION(paint); |
robertphillips@google.com | 53238bc | 2013-08-30 13:12:10 +0000 | [diff] [blame] | 216 | draw.drawPoints(mode, count, pts, paint); |
| 217 | } |
| 218 | |
| 219 | void SkBitmapDevice::drawRect(const SkDraw& draw, const SkRect& r, const SkPaint& paint) { |
reed@google.com | 4469938 | 2013-10-31 17:28:30 +0000 | [diff] [blame] | 220 | CHECK_FOR_ANNOTATION(paint); |
robertphillips@google.com | 53238bc | 2013-08-30 13:12:10 +0000 | [diff] [blame] | 221 | draw.drawRect(r, paint); |
| 222 | } |
| 223 | |
| 224 | void SkBitmapDevice::drawOval(const SkDraw& draw, const SkRect& oval, const SkPaint& paint) { |
reed@google.com | 4469938 | 2013-10-31 17:28:30 +0000 | [diff] [blame] | 225 | CHECK_FOR_ANNOTATION(paint); |
robertphillips@google.com | 53238bc | 2013-08-30 13:12:10 +0000 | [diff] [blame] | 226 | |
| 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 | |
| 234 | void SkBitmapDevice::drawRRect(const SkDraw& draw, const SkRRect& rrect, const SkPaint& paint) { |
reed@google.com | 4469938 | 2013-10-31 17:28:30 +0000 | [diff] [blame] | 235 | CHECK_FOR_ANNOTATION(paint); |
robertphillips@google.com | 53238bc | 2013-08-30 13:12:10 +0000 | [diff] [blame] | 236 | |
robertphillips@google.com | 50a7600 | 2013-11-12 01:16:56 +0000 | [diff] [blame] | 237 | #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.com | a8e33a9 | 2013-11-08 18:02:53 +0000 | [diff] [blame] | 245 | draw.drawRRect(rrect, paint); |
robertphillips@google.com | 50a7600 | 2013-11-12 01:16:56 +0000 | [diff] [blame] | 246 | #endif |
robertphillips@google.com | 53238bc | 2013-08-30 13:12:10 +0000 | [diff] [blame] | 247 | } |
| 248 | |
| 249 | void SkBitmapDevice::drawPath(const SkDraw& draw, const SkPath& path, |
| 250 | const SkPaint& paint, const SkMatrix* prePathMatrix, |
| 251 | bool pathIsMutable) { |
reed@google.com | 4469938 | 2013-10-31 17:28:30 +0000 | [diff] [blame] | 252 | CHECK_FOR_ANNOTATION(paint); |
robertphillips@google.com | 53238bc | 2013-08-30 13:12:10 +0000 | [diff] [blame] | 253 | draw.drawPath(path, paint, prePathMatrix, pathIsMutable); |
| 254 | } |
| 255 | |
| 256 | void SkBitmapDevice::drawBitmap(const SkDraw& draw, const SkBitmap& bitmap, |
| 257 | const SkMatrix& matrix, const SkPaint& paint) { |
| 258 | draw.drawBitmap(bitmap, matrix, paint); |
| 259 | } |
| 260 | |
| 261 | void 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 | |
| 348 | void 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 | |
| 353 | void 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 | |
| 358 | void 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 | |
| 364 | void 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.com | 53238bc | 2013-08-30 13:12:10 +0000 | [diff] [blame] | 371 | void 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 | |
| 381 | void 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 | |
| 389 | bool 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 | } |