blob: 8c92f71dceee7aacee32d684293abab260b937ba [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.org28fcae22014-04-11 17:15:40 +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()) {
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +000081 if (!bitmap.setInfo(info)) {
commit-bot@chromium.org15a14052014-02-16 00:59:25 +000082 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
reed@google.com9c135db2014-03-12 18:28:35 +0000135void* SkBitmapDevice::onAccessPixels(SkImageInfo* info, size_t* rowBytes) {
136 if (fBitmap.getPixels()) {
137 *info = fBitmap.info();
138 *rowBytes = fBitmap.rowBytes();
139 return fBitmap.getPixels();
140 }
141 return NULL;
142}
143
commit-bot@chromium.org231f6b82014-03-25 13:38:44 +0000144#include "SkConfig8888.h"
reed@google.com7111d462014-03-25 16:20:24 +0000145
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000146bool SkBitmapDevice::onWritePixels(const SkImageInfo& srcInfo, const void* srcPixels,
147 size_t srcRowBytes, int x, int y) {
reed@google.com0d30c512014-03-14 14:02:58 +0000148 // since we don't stop creating un-pixeled devices yet, check for no pixels here
149 if (NULL == fBitmap.getPixels()) {
150 return false;
151 }
152
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000153 SkImageInfo dstInfo = fBitmap.info();
154 dstInfo.fWidth = srcInfo.width();
155 dstInfo.fHeight = srcInfo.height();
156
157 void* dstPixels = fBitmap.getAddr(x, y);
158 size_t dstRowBytes = fBitmap.rowBytes();
159
reedb184f7f2014-07-13 04:32:32 -0700160 if (SkPixelInfo::CopyPixels(dstInfo, dstPixels, dstRowBytes, srcInfo, srcPixels, srcRowBytes)) {
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000161 fBitmap.notifyPixelsChanged();
162 return true;
163 }
164 return false;
165}
robertphillips@google.com53238bc2013-08-30 13:12:10 +0000166
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000167bool SkBitmapDevice::onReadPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRowBytes,
168 int x, int y) {
reedb184f7f2014-07-13 04:32:32 -0700169 return fBitmap.readPixels(dstInfo, dstPixels, dstRowBytes, x, y);
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000170}
171
robertphillips@google.com53238bc2013-08-30 13:12:10 +0000172///////////////////////////////////////////////////////////////////////////////
173
174void SkBitmapDevice::drawPaint(const SkDraw& draw, const SkPaint& paint) {
175 draw.drawPaint(paint);
176}
177
178void SkBitmapDevice::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode, size_t count,
179 const SkPoint pts[], const SkPaint& paint) {
reed@google.com44699382013-10-31 17:28:30 +0000180 CHECK_FOR_ANNOTATION(paint);
robertphillips@google.com53238bc2013-08-30 13:12:10 +0000181 draw.drawPoints(mode, count, pts, paint);
182}
183
184void SkBitmapDevice::drawRect(const SkDraw& draw, const SkRect& r, const SkPaint& paint) {
reed@google.com44699382013-10-31 17:28:30 +0000185 CHECK_FOR_ANNOTATION(paint);
robertphillips@google.com53238bc2013-08-30 13:12:10 +0000186 draw.drawRect(r, paint);
187}
188
189void SkBitmapDevice::drawOval(const SkDraw& draw, const SkRect& oval, const SkPaint& paint) {
reed@google.com44699382013-10-31 17:28:30 +0000190 CHECK_FOR_ANNOTATION(paint);
robertphillips@google.com53238bc2013-08-30 13:12:10 +0000191
192 SkPath path;
193 path.addOval(oval);
194 // call the VIRTUAL version, so any subclasses who do handle drawPath aren't
195 // required to override drawOval.
196 this->drawPath(draw, path, paint, NULL, true);
197}
198
199void SkBitmapDevice::drawRRect(const SkDraw& draw, const SkRRect& rrect, const SkPaint& paint) {
reed@google.com44699382013-10-31 17:28:30 +0000200 CHECK_FOR_ANNOTATION(paint);
robertphillips@google.com53238bc2013-08-30 13:12:10 +0000201
robertphillips@google.com50a76002013-11-12 01:16:56 +0000202#ifdef SK_IGNORE_BLURRED_RRECT_OPT
203 SkPath path;
204
205 path.addRRect(rrect);
206 // call the VIRTUAL version, so any subclasses who do handle drawPath aren't
207 // required to override drawRRect.
208 this->drawPath(draw, path, paint, NULL, true);
209#else
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000210 draw.drawRRect(rrect, paint);
robertphillips@google.com50a76002013-11-12 01:16:56 +0000211#endif
robertphillips@google.com53238bc2013-08-30 13:12:10 +0000212}
213
214void SkBitmapDevice::drawPath(const SkDraw& draw, const SkPath& path,
215 const SkPaint& paint, const SkMatrix* prePathMatrix,
216 bool pathIsMutable) {
reed@google.com44699382013-10-31 17:28:30 +0000217 CHECK_FOR_ANNOTATION(paint);
robertphillips@google.com53238bc2013-08-30 13:12:10 +0000218 draw.drawPath(path, paint, prePathMatrix, pathIsMutable);
219}
220
221void SkBitmapDevice::drawBitmap(const SkDraw& draw, const SkBitmap& bitmap,
222 const SkMatrix& matrix, const SkPaint& paint) {
223 draw.drawBitmap(bitmap, matrix, paint);
224}
225
226void SkBitmapDevice::drawBitmapRect(const SkDraw& draw, const SkBitmap& bitmap,
227 const SkRect* src, const SkRect& dst,
228 const SkPaint& paint,
229 SkCanvas::DrawBitmapRectFlags flags) {
230 SkMatrix matrix;
231 SkRect bitmapBounds, tmpSrc, tmpDst;
232 SkBitmap tmpBitmap;
233
234 bitmapBounds.isetWH(bitmap.width(), bitmap.height());
235
236 // Compute matrix from the two rectangles
237 if (src) {
238 tmpSrc = *src;
239 } else {
240 tmpSrc = bitmapBounds;
241 }
242 matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
243
244 const SkRect* dstPtr = &dst;
245 const SkBitmap* bitmapPtr = &bitmap;
246
247 // clip the tmpSrc to the bounds of the bitmap, and recompute dstRect if
248 // needed (if the src was clipped). No check needed if src==null.
249 if (src) {
250 if (!bitmapBounds.contains(*src)) {
251 if (!tmpSrc.intersect(bitmapBounds)) {
252 return; // nothing to draw
253 }
254 // recompute dst, based on the smaller tmpSrc
255 matrix.mapRect(&tmpDst, tmpSrc);
256 dstPtr = &tmpDst;
257 }
258
259 // since we may need to clamp to the borders of the src rect within
260 // the bitmap, we extract a subset.
261 SkIRect srcIR;
262 tmpSrc.roundOut(&srcIR);
263 if (!bitmap.extractSubset(&tmpBitmap, srcIR)) {
264 return;
265 }
266 bitmapPtr = &tmpBitmap;
267
268 // Since we did an extract, we need to adjust the matrix accordingly
269 SkScalar dx = 0, dy = 0;
270 if (srcIR.fLeft > 0) {
271 dx = SkIntToScalar(srcIR.fLeft);
272 }
273 if (srcIR.fTop > 0) {
274 dy = SkIntToScalar(srcIR.fTop);
275 }
276 if (dx || dy) {
277 matrix.preTranslate(dx, dy);
278 }
279
280 SkRect extractedBitmapBounds;
281 extractedBitmapBounds.isetWH(bitmapPtr->width(), bitmapPtr->height());
282 if (extractedBitmapBounds == tmpSrc) {
283 // no fractional part in src, we can just call drawBitmap
284 goto USE_DRAWBITMAP;
285 }
286 } else {
287 USE_DRAWBITMAP:
288 // We can go faster by just calling drawBitmap, which will concat the
289 // matrix with the CTM, and try to call drawSprite if it can. If not,
290 // it will make a shader and call drawRect, as we do below.
291 this->drawBitmap(draw, *bitmapPtr, matrix, paint);
292 return;
293 }
294
295 // construct a shader, so we can call drawRect with the dst
296 SkShader* s = SkShader::CreateBitmapShader(*bitmapPtr,
297 SkShader::kClamp_TileMode,
commit-bot@chromium.org9c9005a2014-04-28 14:55:39 +0000298 SkShader::kClamp_TileMode,
299 &matrix);
robertphillips@google.com53238bc2013-08-30 13:12:10 +0000300 if (NULL == s) {
301 return;
302 }
robertphillips@google.com53238bc2013-08-30 13:12:10 +0000303
304 SkPaint paintWithShader(paint);
305 paintWithShader.setStyle(SkPaint::kFill_Style);
306 paintWithShader.setShader(s)->unref();
307
308 // Call ourself, in case the subclass wanted to share this setup code
309 // but handle the drawRect code themselves.
310 this->drawRect(draw, *dstPtr, paintWithShader);
311}
312
313void SkBitmapDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap,
314 int x, int y, const SkPaint& paint) {
315 draw.drawSprite(bitmap, x, y, paint);
316}
317
318void SkBitmapDevice::drawText(const SkDraw& draw, const void* text, size_t len,
319 SkScalar x, SkScalar y, const SkPaint& paint) {
320 draw.drawText((const char*)text, len, x, y, paint);
321}
322
323void SkBitmapDevice::drawPosText(const SkDraw& draw, const void* text, size_t len,
324 const SkScalar xpos[], SkScalar y,
325 int scalarsPerPos, const SkPaint& paint) {
326 draw.drawPosText((const char*)text, len, xpos, y, scalarsPerPos, paint);
327}
328
329void SkBitmapDevice::drawTextOnPath(const SkDraw& draw, const void* text,
330 size_t len, const SkPath& path,
331 const SkMatrix* matrix,
332 const SkPaint& paint) {
333 draw.drawTextOnPath((const char*)text, len, path, matrix, paint);
334}
335
robertphillips@google.com53238bc2013-08-30 13:12:10 +0000336void SkBitmapDevice::drawVertices(const SkDraw& draw, SkCanvas::VertexMode vmode,
337 int vertexCount,
338 const SkPoint verts[], const SkPoint textures[],
339 const SkColor colors[], SkXfermode* xmode,
340 const uint16_t indices[], int indexCount,
341 const SkPaint& paint) {
342 draw.drawVertices(vmode, vertexCount, verts, textures, colors, xmode,
343 indices, indexCount, paint);
344}
345
346void SkBitmapDevice::drawDevice(const SkDraw& draw, SkBaseDevice* device,
347 int x, int y, const SkPaint& paint) {
348 const SkBitmap& src = device->accessBitmap(false);
349 draw.drawSprite(src, x, y, paint);
350}
351
reed@google.com76f10a32014-02-05 15:32:21 +0000352SkSurface* SkBitmapDevice::newSurface(const SkImageInfo& info) {
353 return SkSurface::NewRaster(info);
354}
355
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +0000356const void* SkBitmapDevice::peekPixels(SkImageInfo* info, size_t* rowBytes) {
commit-bot@chromium.org466f5f32014-05-27 21:30:37 +0000357 const SkImageInfo bmInfo = fBitmap.info();
358 if (fBitmap.getPixels() && (kUnknown_SkColorType != bmInfo.colorType())) {
359 if (info) {
360 *info = bmInfo;
361 }
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +0000362 if (rowBytes) {
363 *rowBytes = fBitmap.rowBytes();
364 }
365 return fBitmap.getPixels();
366 }
367 return NULL;
368}
369
senorblanco55b6d8b2014-07-30 11:26:46 -0700370SkImageFilter::UniqueIDCache* SkBitmapDevice::getImageFilterCache() {
371 SkImageFilter::UniqueIDCache* cache = SkImageFilter::UniqueIDCache::Get();
372 cache->ref();
373 return cache;
374}
375
robertphillips@google.com53238bc2013-08-30 13:12:10 +0000376///////////////////////////////////////////////////////////////////////////////
377
378bool SkBitmapDevice::filterTextFlags(const SkPaint& paint, TextFlags* flags) {
379 if (!paint.isLCDRenderText() || !paint.isAntiAlias()) {
380 // we're cool with the paint as is
381 return false;
382 }
383
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +0000384 if (kN32_SkColorType != fBitmap.colorType() ||
robertphillips@google.com53238bc2013-08-30 13:12:10 +0000385 paint.getRasterizer() ||
386 paint.getPathEffect() ||
387 paint.isFakeBoldText() ||
388 paint.getStyle() != SkPaint::kFill_Style ||
389 !SkXfermode::IsMode(paint.getXfermode(), SkXfermode::kSrcOver_Mode)) {
390 // turn off lcd
391 flags->fFlags = paint.getFlags() & ~SkPaint::kLCDRenderText_Flag;
392 flags->fHinting = paint.getHinting();
393 return true;
394 }
395 // we're cool with the paint as is
396 return false;
397}