blob: c2b633e1a645338245e250e580e42dd6b52b478f [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"
reed@google.com76f10a32014-02-05 15:32:21 +000013#include "SkSurface.h"
robertphillips@google.com53238bc2013-08-30 13:12:10 +000014
reed@google.com44699382013-10-31 17:28:30 +000015#define CHECK_FOR_ANNOTATION(paint) \
16 do { if (paint.getAnnotation()) { return; } } while (0)
robertphillips@google.com53238bc2013-08-30 13:12:10 +000017
commit-bot@chromium.org15a14052014-02-16 00:59:25 +000018static bool valid_for_bitmap_device(const SkImageInfo& info,
19 SkAlphaType* newAlphaType) {
20 if (info.width() < 0 || info.height() < 0) {
21 return false;
22 }
23
24 // TODO: can we stop supporting kUnknown in SkBitmkapDevice?
25 if (kUnknown_SkColorType == info.colorType()) {
26 if (newAlphaType) {
27 *newAlphaType = kIgnore_SkAlphaType;
28 }
29 return true;
30 }
skia.committer@gmail.com969588f2014-02-16 03:01:56 +000031
commit-bot@chromium.org15a14052014-02-16 00:59:25 +000032 switch (info.alphaType()) {
33 case kPremul_SkAlphaType:
34 case kOpaque_SkAlphaType:
35 break;
36 default:
37 return false;
38 }
39
40 SkAlphaType canonicalAlphaType = info.alphaType();
41
42 switch (info.colorType()) {
43 case kAlpha_8_SkColorType:
44 break;
45 case kRGB_565_SkColorType:
46 canonicalAlphaType = kOpaque_SkAlphaType;
47 break;
commit-bot@chromium.org149e9a12014-04-09 20:45:29 +000048 case kN32_SkColorType:
commit-bot@chromium.org15a14052014-02-16 00:59:25 +000049 break;
50 default:
51 return false;
52 }
53
54 if (newAlphaType) {
55 *newAlphaType = canonicalAlphaType;
56 }
57 return true;
58}
59
60SkBitmapDevice::SkBitmapDevice(const SkBitmap& bitmap) : fBitmap(bitmap) {
61 SkASSERT(valid_for_bitmap_device(bitmap.info(), NULL));
robertphillips@google.com53238bc2013-08-30 13:12:10 +000062}
63
64SkBitmapDevice::SkBitmapDevice(const SkBitmap& bitmap, const SkDeviceProperties& deviceProperties)
65 : SkBaseDevice(deviceProperties)
commit-bot@chromium.org15a14052014-02-16 00:59:25 +000066 , fBitmap(bitmap)
67{
68 SkASSERT(valid_for_bitmap_device(bitmap.info(), NULL));
robertphillips@google.com53238bc2013-08-30 13:12:10 +000069}
70
commit-bot@chromium.org15a14052014-02-16 00:59:25 +000071SkBitmapDevice* SkBitmapDevice::Create(const SkImageInfo& origInfo,
72 const SkDeviceProperties* props) {
73 SkImageInfo info = origInfo;
74 if (!valid_for_bitmap_device(info, &info.fAlphaType)) {
75 return NULL;
76 }
robertphillips@google.com53238bc2013-08-30 13:12:10 +000077
commit-bot@chromium.org15a14052014-02-16 00:59:25 +000078 SkBitmap bitmap;
79
80 if (kUnknown_SkColorType == info.colorType()) {
81 if (!bitmap.setConfig(info)) {
82 return NULL;
83 }
84 } else {
85 if (!bitmap.allocPixels(info)) {
86 return NULL;
87 }
88 if (!bitmap.info().isOpaque()) {
89 bitmap.eraseColor(SK_ColorTRANSPARENT);
90 }
91 }
92
93 if (props) {
94 return SkNEW_ARGS(SkBitmapDevice, (bitmap, *props));
95 } else {
96 return SkNEW_ARGS(SkBitmapDevice, (bitmap));
97 }
robertphillips@google.com53238bc2013-08-30 13:12:10 +000098}
99
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +0000100SkImageInfo SkBitmapDevice::imageInfo() const {
101 return fBitmap.info();
102}
103
robertphillips@google.com53238bc2013-08-30 13:12:10 +0000104void SkBitmapDevice::replaceBitmapBackendForRasterSurface(const SkBitmap& bm) {
105 SkASSERT(bm.width() == fBitmap.width());
106 SkASSERT(bm.height() == fBitmap.height());
107 fBitmap = bm; // intent is to use bm's pixelRef (and rowbytes/config)
108 fBitmap.lockPixels();
109}
110
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000111SkBaseDevice* SkBitmapDevice::onCreateDevice(const SkImageInfo& info, Usage usage) {
112 return SkBitmapDevice::Create(info, &this->getDeviceProperties());
robertphillips@google.com53238bc2013-08-30 13:12:10 +0000113}
114
115void SkBitmapDevice::lockPixels() {
116 if (fBitmap.lockPixelsAreWritable()) {
117 fBitmap.lockPixels();
118 }
119}
120
121void SkBitmapDevice::unlockPixels() {
122 if (fBitmap.lockPixelsAreWritable()) {
123 fBitmap.unlockPixels();
124 }
125}
126
robertphillips@google.com53238bc2013-08-30 13:12:10 +0000127void SkBitmapDevice::clear(SkColor color) {
128 fBitmap.eraseColor(color);
129}
130
131const SkBitmap& SkBitmapDevice::onAccessBitmap() {
132 return fBitmap;
133}
134
commit-bot@chromium.orgae761f72014-02-05 22:32:02 +0000135bool SkBitmapDevice::canHandleImageFilter(const SkImageFilter*) {
robertphillips@google.com53238bc2013-08-30 13:12:10 +0000136 return false;
137}
138
commit-bot@chromium.orgae761f72014-02-05 22:32:02 +0000139bool SkBitmapDevice::filterImage(const SkImageFilter* filter, const SkBitmap& src,
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +0000140 const SkImageFilter::Context& ctx, SkBitmap* result,
robertphillips@google.com53238bc2013-08-30 13:12:10 +0000141 SkIPoint* offset) {
142 return false;
143}
144
commit-bot@chromium.orgae761f72014-02-05 22:32:02 +0000145bool SkBitmapDevice::allowImageFilter(const SkImageFilter*) {
robertphillips@google.com53238bc2013-08-30 13:12:10 +0000146 return true;
147}
148
reed@google.com9c135db2014-03-12 18:28:35 +0000149void* SkBitmapDevice::onAccessPixels(SkImageInfo* info, size_t* rowBytes) {
150 if (fBitmap.getPixels()) {
151 *info = fBitmap.info();
152 *rowBytes = fBitmap.rowBytes();
153 return fBitmap.getPixels();
154 }
155 return NULL;
156}
157
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000158static void rect_memcpy(void* dst, size_t dstRB, const void* src, size_t srcRB, size_t bytesPerRow,
159 int rowCount) {
160 SkASSERT(bytesPerRow <= srcRB);
161 SkASSERT(bytesPerRow <= dstRB);
162 for (int i = 0; i < rowCount; ++i) {
163 memcpy(dst, src, bytesPerRow);
164 dst = (char*)dst + dstRB;
165 src = (const char*)src + srcRB;
166 }
167}
168
commit-bot@chromium.org231f6b82014-03-25 13:38:44 +0000169#include "SkConfig8888.h"
reed@google.com7111d462014-03-25 16:20:24 +0000170
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000171static bool copy_pixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRowBytes,
172 const SkImageInfo& srcInfo, const void* srcPixels, size_t srcRowBytes) {
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000173 if (srcInfo.dimensions() != dstInfo.dimensions()) {
174 return false;
175 }
176 if (4 == srcInfo.bytesPerPixel() && 4 == dstInfo.bytesPerPixel()) {
reed@google.com7111d462014-03-25 16:20:24 +0000177 SkDstPixelInfo dstPI;
178 dstPI.fColorType = dstInfo.colorType();
179 dstPI.fAlphaType = dstInfo.alphaType();
180 dstPI.fPixels = dstPixels;
181 dstPI.fRowBytes = dstRowBytes;
182
183 SkSrcPixelInfo srcPI;
184 srcPI.fColorType = srcInfo.colorType();
185 srcPI.fAlphaType = srcInfo.alphaType();
186 srcPI.fPixels = srcPixels;
187 srcPI.fRowBytes = srcRowBytes;
188
189 return srcPI.convertPixelsTo(&dstPI, srcInfo.width(), srcInfo.height());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000190 }
191 if (srcInfo.colorType() == dstInfo.colorType()) {
192 switch (srcInfo.colorType()) {
193 case kRGB_565_SkColorType:
194 case kAlpha_8_SkColorType:
195 break;
196 case kARGB_4444_SkColorType:
197 if (srcInfo.alphaType() != dstInfo.alphaType()) {
198 return false;
199 }
200 break;
201 default:
202 return false;
203 }
204 rect_memcpy(dstPixels, dstRowBytes, srcPixels, srcRowBytes,
205 srcInfo.width() * srcInfo.bytesPerPixel(), srcInfo.height());
206 }
207 // TODO: add support for more conversions as needed
208 return false;
209}
210
211bool SkBitmapDevice::onWritePixels(const SkImageInfo& srcInfo, const void* srcPixels,
212 size_t srcRowBytes, int x, int y) {
reed@google.com0d30c512014-03-14 14:02:58 +0000213 // since we don't stop creating un-pixeled devices yet, check for no pixels here
214 if (NULL == fBitmap.getPixels()) {
215 return false;
216 }
217
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000218 SkImageInfo dstInfo = fBitmap.info();
219 dstInfo.fWidth = srcInfo.width();
220 dstInfo.fHeight = srcInfo.height();
221
222 void* dstPixels = fBitmap.getAddr(x, y);
223 size_t dstRowBytes = fBitmap.rowBytes();
224
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000225 if (copy_pixels(dstInfo, dstPixels, dstRowBytes, srcInfo, srcPixels, srcRowBytes)) {
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000226 fBitmap.notifyPixelsChanged();
227 return true;
228 }
229 return false;
230}
robertphillips@google.com53238bc2013-08-30 13:12:10 +0000231
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000232bool SkBitmapDevice::onReadPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRowBytes,
233 int x, int y) {
234 // since we don't stop creating un-pixeled devices yet, check for no pixels here
235 if (NULL == fBitmap.getPixels()) {
236 return false;
237 }
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000238
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000239 SkImageInfo srcInfo = fBitmap.info();
240
241 // perhaps can relax these in the future
242 if (4 != dstInfo.bytesPerPixel()) {
243 return false;
244 }
245 if (4 != srcInfo.bytesPerPixel()) {
246 return false;
247 }
248
249 srcInfo.fWidth = dstInfo.width();
250 srcInfo.fHeight = dstInfo.height();
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000251
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000252 const void* srcPixels = fBitmap.getAddr(x, y);
253 const size_t srcRowBytes = fBitmap.rowBytes();
254
255 return copy_pixels(dstInfo, dstPixels, dstRowBytes, srcInfo, srcPixels, srcRowBytes);
256}
257
robertphillips@google.com53238bc2013-08-30 13:12:10 +0000258///////////////////////////////////////////////////////////////////////////////
259
260void SkBitmapDevice::drawPaint(const SkDraw& draw, const SkPaint& paint) {
261 draw.drawPaint(paint);
262}
263
264void SkBitmapDevice::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode, size_t count,
265 const SkPoint pts[], const SkPaint& paint) {
reed@google.com44699382013-10-31 17:28:30 +0000266 CHECK_FOR_ANNOTATION(paint);
robertphillips@google.com53238bc2013-08-30 13:12:10 +0000267 draw.drawPoints(mode, count, pts, paint);
268}
269
270void SkBitmapDevice::drawRect(const SkDraw& draw, const SkRect& r, const SkPaint& paint) {
reed@google.com44699382013-10-31 17:28:30 +0000271 CHECK_FOR_ANNOTATION(paint);
robertphillips@google.com53238bc2013-08-30 13:12:10 +0000272 draw.drawRect(r, paint);
273}
274
275void SkBitmapDevice::drawOval(const SkDraw& draw, const SkRect& oval, const SkPaint& paint) {
reed@google.com44699382013-10-31 17:28:30 +0000276 CHECK_FOR_ANNOTATION(paint);
robertphillips@google.com53238bc2013-08-30 13:12:10 +0000277
278 SkPath path;
279 path.addOval(oval);
280 // call the VIRTUAL version, so any subclasses who do handle drawPath aren't
281 // required to override drawOval.
282 this->drawPath(draw, path, paint, NULL, true);
283}
284
285void SkBitmapDevice::drawRRect(const SkDraw& draw, const SkRRect& rrect, const SkPaint& paint) {
reed@google.com44699382013-10-31 17:28:30 +0000286 CHECK_FOR_ANNOTATION(paint);
robertphillips@google.com53238bc2013-08-30 13:12:10 +0000287
robertphillips@google.com50a76002013-11-12 01:16:56 +0000288#ifdef SK_IGNORE_BLURRED_RRECT_OPT
289 SkPath path;
290
291 path.addRRect(rrect);
292 // call the VIRTUAL version, so any subclasses who do handle drawPath aren't
293 // required to override drawRRect.
294 this->drawPath(draw, path, paint, NULL, true);
295#else
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000296 draw.drawRRect(rrect, paint);
robertphillips@google.com50a76002013-11-12 01:16:56 +0000297#endif
robertphillips@google.com53238bc2013-08-30 13:12:10 +0000298}
299
300void SkBitmapDevice::drawPath(const SkDraw& draw, const SkPath& path,
301 const SkPaint& paint, const SkMatrix* prePathMatrix,
302 bool pathIsMutable) {
reed@google.com44699382013-10-31 17:28:30 +0000303 CHECK_FOR_ANNOTATION(paint);
robertphillips@google.com53238bc2013-08-30 13:12:10 +0000304 draw.drawPath(path, paint, prePathMatrix, pathIsMutable);
305}
306
307void SkBitmapDevice::drawBitmap(const SkDraw& draw, const SkBitmap& bitmap,
308 const SkMatrix& matrix, const SkPaint& paint) {
309 draw.drawBitmap(bitmap, matrix, paint);
310}
311
312void SkBitmapDevice::drawBitmapRect(const SkDraw& draw, const SkBitmap& bitmap,
313 const SkRect* src, const SkRect& dst,
314 const SkPaint& paint,
315 SkCanvas::DrawBitmapRectFlags flags) {
316 SkMatrix matrix;
317 SkRect bitmapBounds, tmpSrc, tmpDst;
318 SkBitmap tmpBitmap;
319
320 bitmapBounds.isetWH(bitmap.width(), bitmap.height());
321
322 // Compute matrix from the two rectangles
323 if (src) {
324 tmpSrc = *src;
325 } else {
326 tmpSrc = bitmapBounds;
327 }
328 matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
329
330 const SkRect* dstPtr = &dst;
331 const SkBitmap* bitmapPtr = &bitmap;
332
333 // clip the tmpSrc to the bounds of the bitmap, and recompute dstRect if
334 // needed (if the src was clipped). No check needed if src==null.
335 if (src) {
336 if (!bitmapBounds.contains(*src)) {
337 if (!tmpSrc.intersect(bitmapBounds)) {
338 return; // nothing to draw
339 }
340 // recompute dst, based on the smaller tmpSrc
341 matrix.mapRect(&tmpDst, tmpSrc);
342 dstPtr = &tmpDst;
343 }
344
345 // since we may need to clamp to the borders of the src rect within
346 // the bitmap, we extract a subset.
347 SkIRect srcIR;
348 tmpSrc.roundOut(&srcIR);
349 if (!bitmap.extractSubset(&tmpBitmap, srcIR)) {
350 return;
351 }
352 bitmapPtr = &tmpBitmap;
353
354 // Since we did an extract, we need to adjust the matrix accordingly
355 SkScalar dx = 0, dy = 0;
356 if (srcIR.fLeft > 0) {
357 dx = SkIntToScalar(srcIR.fLeft);
358 }
359 if (srcIR.fTop > 0) {
360 dy = SkIntToScalar(srcIR.fTop);
361 }
362 if (dx || dy) {
363 matrix.preTranslate(dx, dy);
364 }
365
366 SkRect extractedBitmapBounds;
367 extractedBitmapBounds.isetWH(bitmapPtr->width(), bitmapPtr->height());
368 if (extractedBitmapBounds == tmpSrc) {
369 // no fractional part in src, we can just call drawBitmap
370 goto USE_DRAWBITMAP;
371 }
372 } else {
373 USE_DRAWBITMAP:
374 // We can go faster by just calling drawBitmap, which will concat the
375 // matrix with the CTM, and try to call drawSprite if it can. If not,
376 // it will make a shader and call drawRect, as we do below.
377 this->drawBitmap(draw, *bitmapPtr, matrix, paint);
378 return;
379 }
380
381 // construct a shader, so we can call drawRect with the dst
382 SkShader* s = SkShader::CreateBitmapShader(*bitmapPtr,
383 SkShader::kClamp_TileMode,
384 SkShader::kClamp_TileMode);
385 if (NULL == s) {
386 return;
387 }
388 s->setLocalMatrix(matrix);
389
390 SkPaint paintWithShader(paint);
391 paintWithShader.setStyle(SkPaint::kFill_Style);
392 paintWithShader.setShader(s)->unref();
393
394 // Call ourself, in case the subclass wanted to share this setup code
395 // but handle the drawRect code themselves.
396 this->drawRect(draw, *dstPtr, paintWithShader);
397}
398
399void SkBitmapDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap,
400 int x, int y, const SkPaint& paint) {
401 draw.drawSprite(bitmap, x, y, paint);
402}
403
404void SkBitmapDevice::drawText(const SkDraw& draw, const void* text, size_t len,
405 SkScalar x, SkScalar y, const SkPaint& paint) {
406 draw.drawText((const char*)text, len, x, y, paint);
407}
408
409void SkBitmapDevice::drawPosText(const SkDraw& draw, const void* text, size_t len,
410 const SkScalar xpos[], SkScalar y,
411 int scalarsPerPos, const SkPaint& paint) {
412 draw.drawPosText((const char*)text, len, xpos, y, scalarsPerPos, paint);
413}
414
415void SkBitmapDevice::drawTextOnPath(const SkDraw& draw, const void* text,
416 size_t len, const SkPath& path,
417 const SkMatrix* matrix,
418 const SkPaint& paint) {
419 draw.drawTextOnPath((const char*)text, len, path, matrix, paint);
420}
421
robertphillips@google.com53238bc2013-08-30 13:12:10 +0000422void SkBitmapDevice::drawVertices(const SkDraw& draw, SkCanvas::VertexMode vmode,
423 int vertexCount,
424 const SkPoint verts[], const SkPoint textures[],
425 const SkColor colors[], SkXfermode* xmode,
426 const uint16_t indices[], int indexCount,
427 const SkPaint& paint) {
428 draw.drawVertices(vmode, vertexCount, verts, textures, colors, xmode,
429 indices, indexCount, paint);
430}
431
432void SkBitmapDevice::drawDevice(const SkDraw& draw, SkBaseDevice* device,
433 int x, int y, const SkPaint& paint) {
434 const SkBitmap& src = device->accessBitmap(false);
435 draw.drawSprite(src, x, y, paint);
436}
437
reed@google.com76f10a32014-02-05 15:32:21 +0000438SkSurface* SkBitmapDevice::newSurface(const SkImageInfo& info) {
439 return SkSurface::NewRaster(info);
440}
441
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +0000442const void* SkBitmapDevice::peekPixels(SkImageInfo* info, size_t* rowBytes) {
443 if (fBitmap.getPixels() && fBitmap.asImageInfo(info)) {
444 if (rowBytes) {
445 *rowBytes = fBitmap.rowBytes();
446 }
447 return fBitmap.getPixels();
448 }
449 return NULL;
450}
451
robertphillips@google.com53238bc2013-08-30 13:12:10 +0000452///////////////////////////////////////////////////////////////////////////////
453
454bool SkBitmapDevice::filterTextFlags(const SkPaint& paint, TextFlags* flags) {
455 if (!paint.isLCDRenderText() || !paint.isAntiAlias()) {
456 // we're cool with the paint as is
457 return false;
458 }
459
460 if (SkBitmap::kARGB_8888_Config != fBitmap.config() ||
461 paint.getRasterizer() ||
462 paint.getPathEffect() ||
463 paint.isFakeBoldText() ||
464 paint.getStyle() != SkPaint::kFill_Style ||
465 !SkXfermode::IsMode(paint.getXfermode(), SkXfermode::kSrcOver_Mode)) {
466 // turn off lcd
467 flags->fFlags = paint.getFlags() & ~SkPaint::kLCDRenderText_Flag;
468 flags->fHinting = paint.getHinting();
469 return true;
470 }
471 // we're cool with the paint as is
472 return false;
473}