OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "ui/gfx/canvas.h" | |
6 | |
7 #include <cmath> | |
8 #include <limits> | |
9 | |
10 #include "base/i18n/rtl.h" | |
11 #include "base/logging.h" | |
12 #include "third_party/skia/include/core/SkBitmap.h" | |
13 #include "third_party/skia/include/effects/SkGradientShader.h" | |
14 #include "ui/gfx/font_list.h" | |
15 #include "ui/gfx/geometry/rect_conversions.h" | |
16 #include "ui/gfx/geometry/safe_integer_conversions.h" | |
17 #include "ui/gfx/rect.h" | |
18 #include "ui/gfx/scoped_canvas.h" | |
19 #include "ui/gfx/size_conversions.h" | |
20 #include "ui/gfx/skia_util.h" | |
21 #include "ui/gfx/transform.h" | |
22 | |
23 #if defined(OS_WIN) | |
24 #include "ui/gfx/canvas_skia_paint.h" | |
25 #endif | |
26 | |
27 namespace gfx { | |
28 | |
29 Canvas::Canvas(const Size& size, float image_scale, bool is_opaque) | |
30 : image_scale_(image_scale), | |
31 canvas_(NULL) { | |
32 Size pixel_size = ToCeiledSize(ScaleSize(size, image_scale)); | |
33 owned_canvas_ = skia::AdoptRef(skia::CreatePlatformCanvas(pixel_size.width(), | |
34 pixel_size.height(), | |
35 is_opaque)); | |
36 canvas_ = owned_canvas_.get(); | |
37 #if defined(OS_WIN) || defined(OS_MACOSX) | |
38 // skia::PlatformCanvas instances are initialized to 0 by Cairo on Linux, but | |
39 // uninitialized on Win and Mac. | |
40 if (!is_opaque) | |
41 owned_canvas_->clear(SkColorSetARGB(0, 0, 0, 0)); | |
42 #endif | |
43 | |
44 SkScalar scale_scalar = SkFloatToScalar(image_scale); | |
45 canvas_->scale(scale_scalar, scale_scalar); | |
46 } | |
47 | |
48 Canvas::Canvas(const ImageSkiaRep& image_rep, bool is_opaque) | |
49 : image_scale_(image_rep.scale()), | |
50 owned_canvas_(skia::AdoptRef( | |
51 skia::CreatePlatformCanvas(image_rep.pixel_width(), | |
52 image_rep.pixel_height(), | |
53 is_opaque))), | |
54 canvas_(owned_canvas_.get()) { | |
55 SkScalar scale_scalar = SkFloatToScalar(image_scale_); | |
56 canvas_->scale(scale_scalar, scale_scalar); | |
57 DrawImageInt(ImageSkia(image_rep), 0, 0); | |
58 } | |
59 | |
60 Canvas::Canvas() | |
61 : image_scale_(1.0), | |
62 owned_canvas_(skia::AdoptRef(skia::CreatePlatformCanvas(0, 0, false))), | |
63 canvas_(owned_canvas_.get()) { | |
64 } | |
65 | |
66 Canvas::~Canvas() { | |
67 } | |
68 | |
69 // static | |
70 Canvas* Canvas::CreateCanvasWithoutScaling(SkCanvas* canvas, | |
71 float image_scale) { | |
72 return new Canvas(canvas, image_scale); | |
73 } | |
74 | |
75 void Canvas::RecreateBackingCanvas(const Size& size, | |
76 float image_scale, | |
77 bool is_opaque) { | |
78 image_scale_ = image_scale; | |
79 Size pixel_size = ToFlooredSize(ScaleSize(size, image_scale)); | |
80 owned_canvas_ = skia::AdoptRef(skia::CreatePlatformCanvas(pixel_size.width(), | |
81 pixel_size.height(), | |
82 is_opaque)); | |
83 canvas_ = owned_canvas_.get(); | |
84 SkScalar scale_scalar = SkFloatToScalar(image_scale); | |
85 canvas_->scale(scale_scalar, scale_scalar); | |
86 } | |
87 | |
88 // static | |
89 void Canvas::SizeStringInt(const base::string16& text, | |
90 const FontList& font_list, | |
91 int* width, | |
92 int* height, | |
93 int line_height, | |
94 int flags) { | |
95 float fractional_width = *width; | |
96 float factional_height = *height; | |
97 SizeStringFloat(text, font_list, &fractional_width, | |
98 &factional_height, line_height, flags); | |
99 *width = std::ceil(fractional_width); | |
100 *height = std::ceil(factional_height); | |
101 } | |
102 | |
103 // static | |
104 int Canvas::GetStringWidth(const base::string16& text, | |
105 const FontList& font_list) { | |
106 int width = 0, height = 0; | |
107 SizeStringInt(text, font_list, &width, &height, 0, NO_ELLIPSIS); | |
108 return width; | |
109 } | |
110 | |
111 // static | |
112 float Canvas::GetStringWidthF(const base::string16& text, | |
113 const FontList& font_list) { | |
114 float width = 0, height = 0; | |
115 SizeStringFloat(text, font_list, &width, &height, 0, NO_ELLIPSIS); | |
116 return width; | |
117 } | |
118 | |
119 // static | |
120 int Canvas::DefaultCanvasTextAlignment() { | |
121 return base::i18n::IsRTL() ? TEXT_ALIGN_RIGHT : TEXT_ALIGN_LEFT; | |
122 } | |
123 | |
124 ImageSkiaRep Canvas::ExtractImageRep() const { | |
125 // Make a bitmap to return, and a canvas to draw into it. We don't just want | |
126 // to call extractSubset or the copy constructor, since we want an actual copy | |
127 // of the bitmap. | |
128 const SkISize size = canvas_->getDeviceSize(); | |
129 SkBitmap result; | |
130 result.allocN32Pixels(size.width(), size.height()); | |
131 | |
132 canvas_->readPixels(&result, 0, 0); | |
133 return ImageSkiaRep(result, image_scale_); | |
134 } | |
135 | |
136 void Canvas::DrawDashedRect(const Rect& rect, SkColor color) { | |
137 if (rect.IsEmpty()) | |
138 return; | |
139 // Create a 2D bitmap containing alternating on/off pixels - we do this | |
140 // so that you never get two pixels of the same color around the edges | |
141 // of the focus rect (this may mean that opposing edges of the rect may | |
142 // have a dot pattern out of phase to each other). | |
143 static SkColor last_color; | |
144 static SkBitmap* dots = NULL; | |
145 if (!dots || last_color != color) { | |
146 int col_pixels = 32; | |
147 int row_pixels = 32; | |
148 | |
149 delete dots; | |
150 last_color = color; | |
151 dots = new SkBitmap; | |
152 dots->allocN32Pixels(col_pixels, row_pixels); | |
153 dots->eraseARGB(0, 0, 0, 0); | |
154 | |
155 uint32_t* dot = dots->getAddr32(0, 0); | |
156 for (int i = 0; i < row_pixels; i++) { | |
157 for (int u = 0; u < col_pixels; u++) { | |
158 if ((u % 2 + i % 2) % 2 != 0) { | |
159 dot[i * row_pixels + u] = color; | |
160 } | |
161 } | |
162 } | |
163 } | |
164 | |
165 // Make a shader for the bitmap with an origin of the box we'll draw. This | |
166 // shader is refcounted and will have an initial refcount of 1. | |
167 skia::RefPtr<SkShader> shader = skia::AdoptRef( | |
168 SkShader::CreateBitmapShader( | |
169 *dots, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode)); | |
170 // Assign the shader to the paint & release our reference. The paint will | |
171 // now own the shader and the shader will be destroyed when the paint goes | |
172 // out of scope. | |
173 SkPaint paint; | |
174 paint.setShader(shader.get()); | |
175 | |
176 DrawRect(Rect(rect.x(), rect.y(), rect.width(), 1), paint); | |
177 DrawRect(Rect(rect.x(), rect.y() + rect.height() - 1, rect.width(), 1), | |
178 paint); | |
179 DrawRect(Rect(rect.x(), rect.y(), 1, rect.height()), paint); | |
180 DrawRect(Rect(rect.x() + rect.width() - 1, rect.y(), 1, rect.height()), | |
181 paint); | |
182 } | |
183 | |
184 void Canvas::Save() { | |
185 canvas_->save(); | |
186 } | |
187 | |
188 void Canvas::SaveLayerAlpha(uint8 alpha) { | |
189 canvas_->saveLayerAlpha(NULL, alpha); | |
190 } | |
191 | |
192 void Canvas::SaveLayerAlpha(uint8 alpha, const Rect& layer_bounds) { | |
193 SkRect bounds(RectToSkRect(layer_bounds)); | |
194 canvas_->saveLayerAlpha(&bounds, alpha); | |
195 } | |
196 | |
197 void Canvas::Restore() { | |
198 canvas_->restore(); | |
199 } | |
200 | |
201 void Canvas::ClipRect(const Rect& rect) { | |
202 canvas_->clipRect(RectToSkRect(rect)); | |
203 } | |
204 | |
205 void Canvas::ClipPath(const SkPath& path, bool do_anti_alias) { | |
206 canvas_->clipPath(path, SkRegion::kIntersect_Op, do_anti_alias); | |
207 } | |
208 | |
209 bool Canvas::IsClipEmpty() const { | |
210 return canvas_->isClipEmpty(); | |
211 } | |
212 | |
213 bool Canvas::GetClipBounds(Rect* bounds) { | |
214 SkRect out; | |
215 if (canvas_->getClipBounds(&out)) { | |
216 *bounds = ToEnclosingRect(SkRectToRectF(out)); | |
217 return true; | |
218 } | |
219 *bounds = gfx::Rect(); | |
220 return false; | |
221 } | |
222 | |
223 void Canvas::Translate(const Vector2d& offset) { | |
224 canvas_->translate(SkIntToScalar(offset.x()), SkIntToScalar(offset.y())); | |
225 } | |
226 | |
227 void Canvas::Scale(int x_scale, int y_scale) { | |
228 canvas_->scale(SkIntToScalar(x_scale), SkIntToScalar(y_scale)); | |
229 } | |
230 | |
231 void Canvas::DrawColor(SkColor color) { | |
232 DrawColor(color, SkXfermode::kSrcOver_Mode); | |
233 } | |
234 | |
235 void Canvas::DrawColor(SkColor color, SkXfermode::Mode mode) { | |
236 canvas_->drawColor(color, mode); | |
237 } | |
238 | |
239 void Canvas::FillRect(const Rect& rect, SkColor color) { | |
240 FillRect(rect, color, SkXfermode::kSrcOver_Mode); | |
241 } | |
242 | |
243 void Canvas::FillRect(const Rect& rect, | |
244 SkColor color, | |
245 SkXfermode::Mode mode) { | |
246 SkPaint paint; | |
247 paint.setColor(color); | |
248 paint.setStyle(SkPaint::kFill_Style); | |
249 paint.setXfermodeMode(mode); | |
250 DrawRect(rect, paint); | |
251 } | |
252 | |
253 void Canvas::DrawRect(const Rect& rect, SkColor color) { | |
254 DrawRect(rect, color, SkXfermode::kSrcOver_Mode); | |
255 } | |
256 | |
257 void Canvas::DrawRect(const Rect& rect, | |
258 SkColor color, | |
259 SkXfermode::Mode mode) { | |
260 SkPaint paint; | |
261 paint.setColor(color); | |
262 paint.setStyle(SkPaint::kStroke_Style); | |
263 // Set a stroke width of 0, which will put us down the stroke rect path. If | |
264 // we set a stroke width of 1, for example, this will internally create a | |
265 // path and fill it, which causes problems near the edge of the canvas. | |
266 paint.setStrokeWidth(SkIntToScalar(0)); | |
267 paint.setXfermodeMode(mode); | |
268 | |
269 DrawRect(rect, paint); | |
270 } | |
271 | |
272 void Canvas::DrawRect(const Rect& rect, const SkPaint& paint) { | |
273 canvas_->drawIRect(RectToSkIRect(rect), paint); | |
274 } | |
275 | |
276 void Canvas::DrawPoint(const Point& p1, const SkPaint& paint) { | |
277 canvas_->drawPoint(SkIntToScalar(p1.x()), SkIntToScalar(p1.y()), paint); | |
278 } | |
279 | |
280 void Canvas::DrawLine(const Point& p1, const Point& p2, SkColor color) { | |
281 SkPaint paint; | |
282 paint.setColor(color); | |
283 paint.setStrokeWidth(SkIntToScalar(1)); | |
284 DrawLine(p1, p2, paint); | |
285 } | |
286 | |
287 void Canvas::DrawLine(const Point& p1, const Point& p2, const SkPaint& paint) { | |
288 canvas_->drawLine(SkIntToScalar(p1.x()), SkIntToScalar(p1.y()), | |
289 SkIntToScalar(p2.x()), SkIntToScalar(p2.y()), paint); | |
290 } | |
291 | |
292 void Canvas::DrawCircle(const Point& center_point, | |
293 int radius, | |
294 const SkPaint& paint) { | |
295 canvas_->drawCircle(SkIntToScalar(center_point.x()), | |
296 SkIntToScalar(center_point.y()), SkIntToScalar(radius), paint); | |
297 } | |
298 | |
299 void Canvas::DrawRoundRect(const Rect& rect, | |
300 int radius, | |
301 const SkPaint& paint) { | |
302 canvas_->drawRoundRect(RectToSkRect(rect), SkIntToScalar(radius), | |
303 SkIntToScalar(radius), paint); | |
304 } | |
305 | |
306 void Canvas::DrawPath(const SkPath& path, const SkPaint& paint) { | |
307 canvas_->drawPath(path, paint); | |
308 } | |
309 | |
310 void Canvas::DrawFocusRect(const Rect& rect) { | |
311 DrawDashedRect(rect, SK_ColorGRAY); | |
312 } | |
313 | |
314 void Canvas::DrawSolidFocusRect(const Rect& rect, SkColor color) { | |
315 SkPaint paint; | |
316 paint.setColor(color); | |
317 paint.setStrokeWidth(SkIntToScalar(1)); | |
318 // Note: We cannot use DrawRect since it would create a path and fill it which | |
319 // would cause problems near the edge of the canvas. | |
320 int x1 = std::min(rect.x(), rect.right()); | |
321 int x2 = std::max(rect.x(), rect.right()); | |
322 int y1 = std::min(rect.y(), rect.bottom()); | |
323 int y2 = std::max(rect.y(), rect.bottom()); | |
324 DrawLine(Point(x1, y1), Point(x2, y1), paint); | |
325 DrawLine(Point(x1, y2), Point(x2, y2), paint); | |
326 DrawLine(Point(x1, y1), Point(x1, y2), paint); | |
327 DrawLine(Point(x2, y1), Point(x2, y2 + 1), paint); | |
328 } | |
329 | |
330 void Canvas::DrawImageInt(const ImageSkia& image, int x, int y) { | |
331 SkPaint paint; | |
332 DrawImageInt(image, x, y, paint); | |
333 } | |
334 | |
335 void Canvas::DrawImageInt(const ImageSkia& image, int x, int y, uint8 a) { | |
336 SkPaint paint; | |
337 paint.setAlpha(a); | |
338 DrawImageInt(image, x, y, paint); | |
339 } | |
340 | |
341 void Canvas::DrawImageInt(const ImageSkia& image, | |
342 int x, | |
343 int y, | |
344 const SkPaint& paint) { | |
345 const ImageSkiaRep& image_rep = image.GetRepresentation(image_scale_); | |
346 if (image_rep.is_null()) | |
347 return; | |
348 const SkBitmap& bitmap = image_rep.sk_bitmap(); | |
349 float bitmap_scale = image_rep.scale(); | |
350 | |
351 ScopedCanvas scoper(this); | |
352 canvas_->scale(SkFloatToScalar(1.0f / bitmap_scale), | |
353 SkFloatToScalar(1.0f / bitmap_scale)); | |
354 canvas_->drawBitmap(bitmap, | |
355 SkFloatToScalar(x * bitmap_scale), | |
356 SkFloatToScalar(y * bitmap_scale), | |
357 &paint); | |
358 } | |
359 | |
360 void Canvas::DrawImageInt(const ImageSkia& image, | |
361 int src_x, | |
362 int src_y, | |
363 int src_w, | |
364 int src_h, | |
365 int dest_x, | |
366 int dest_y, | |
367 int dest_w, | |
368 int dest_h, | |
369 bool filter) { | |
370 SkPaint p; | |
371 DrawImageInt(image, src_x, src_y, src_w, src_h, dest_x, dest_y, | |
372 dest_w, dest_h, filter, p); | |
373 } | |
374 | |
375 void Canvas::DrawImageInt(const ImageSkia& image, | |
376 int src_x, | |
377 int src_y, | |
378 int src_w, | |
379 int src_h, | |
380 int dest_x, | |
381 int dest_y, | |
382 int dest_w, | |
383 int dest_h, | |
384 bool filter, | |
385 const SkPaint& paint) { | |
386 DrawImageIntHelper(image, src_x, src_y, src_w, src_h, dest_x, dest_y, dest_w, | |
387 dest_h, filter, paint, image_scale_, false); | |
388 } | |
389 | |
390 void Canvas::DrawImageIntInPixel(const ImageSkia& image, | |
391 int src_x, | |
392 int src_y, | |
393 int src_w, | |
394 int src_h, | |
395 int dest_x, | |
396 int dest_y, | |
397 int dest_w, | |
398 int dest_h, | |
399 bool filter, | |
400 const SkPaint& paint) { | |
401 // All values passed into this function are in pixels, i.e. no scaling needs | |
402 // be done. | |
403 // Logic as below:- | |
404 // 1. Get the matrix transform from the canvas. | |
405 // 2. Set the scale in the matrix to 1.0 while honoring the direction of the | |
406 // the scale (x/y). Example RTL layouts. | |
407 // 3. Round off the X and Y translation components in the matrix. This is to | |
408 // reduce floating point errors during rect transformation. This is needed | |
409 // for fractional scale factors like 1.25/1.5, etc. | |
410 // 4. Save the current state of the canvas. | |
411 // 5. Set the modified matrix in the canvas. This ensures that no scaling | |
412 // will be done for draw operations on the canvas. | |
413 // 6. Draw the image. | |
414 // 7. Restore the state of the canvas and the SkCanvas matrix stack. | |
415 SkMatrix matrix = canvas_->getTotalMatrix(); | |
416 | |
417 // Ensure that the direction of the x and y scales is preserved. This is | |
418 // important for RTL layouts. | |
419 matrix.setScaleX(matrix.getScaleX() > 0 ? 1.0f : -1.0f); | |
420 matrix.setScaleY(matrix.getScaleY() > 0 ? 1.0f : -1.0f); | |
421 | |
422 // Floor so that we get consistent rounding. | |
423 matrix.setTranslateX(SkScalarFloorToScalar(matrix.getTranslateX())); | |
424 matrix.setTranslateY(SkScalarFloorToScalar(matrix.getTranslateY())); | |
425 | |
426 ScopedCanvas scoper(this); | |
427 | |
428 canvas_->setMatrix(matrix); | |
429 | |
430 DrawImageIntHelper(image, | |
431 src_x, | |
432 src_y, | |
433 src_w, | |
434 src_h, | |
435 dest_x, | |
436 dest_y, | |
437 dest_w, | |
438 dest_h, | |
439 filter, | |
440 paint, | |
441 image_scale_, | |
442 true); | |
443 } | |
444 | |
445 void Canvas::DrawImageInPath(const ImageSkia& image, | |
446 int x, | |
447 int y, | |
448 const SkPath& path, | |
449 const SkPaint& paint) { | |
450 const ImageSkiaRep& image_rep = image.GetRepresentation(image_scale_); | |
451 if (image_rep.is_null()) | |
452 return; | |
453 | |
454 SkMatrix matrix; | |
455 matrix.setTranslate(SkIntToScalar(x), SkIntToScalar(y)); | |
456 skia::RefPtr<SkShader> shader = CreateImageRepShader( | |
457 image_rep, | |
458 SkShader::kRepeat_TileMode, | |
459 matrix); | |
460 | |
461 SkPaint p(paint); | |
462 p.setShader(shader.get()); | |
463 canvas_->drawPath(path, p); | |
464 } | |
465 | |
466 void Canvas::DrawStringRect(const base::string16& text, | |
467 const FontList& font_list, | |
468 SkColor color, | |
469 const Rect& display_rect) { | |
470 DrawStringRectWithFlags(text, font_list, color, display_rect, | |
471 DefaultCanvasTextAlignment()); | |
472 } | |
473 | |
474 void Canvas::DrawStringRectWithFlags(const base::string16& text, | |
475 const FontList& font_list, | |
476 SkColor color, | |
477 const Rect& display_rect, | |
478 int flags) { | |
479 DrawStringRectWithShadows(text, font_list, color, display_rect, 0, flags, | |
480 ShadowValues()); | |
481 } | |
482 | |
483 void Canvas::TileImageInt(const ImageSkia& image, | |
484 int x, | |
485 int y, | |
486 int w, | |
487 int h) { | |
488 TileImageInt(image, 0, 0, x, y, w, h); | |
489 } | |
490 | |
491 void Canvas::TileImageInt(const ImageSkia& image, | |
492 int src_x, | |
493 int src_y, | |
494 int dest_x, | |
495 int dest_y, | |
496 int w, | |
497 int h) { | |
498 TileImageInt(image, src_x, src_y, 1.0f, 1.0f, dest_x, dest_y, w, h); | |
499 } | |
500 | |
501 void Canvas::TileImageInt(const ImageSkia& image, | |
502 int src_x, | |
503 int src_y, | |
504 float tile_scale_x, | |
505 float tile_scale_y, | |
506 int dest_x, | |
507 int dest_y, | |
508 int w, | |
509 int h) { | |
510 if (!IntersectsClipRectInt(dest_x, dest_y, w, h)) | |
511 return; | |
512 | |
513 const ImageSkiaRep& image_rep = image.GetRepresentation(image_scale_); | |
514 if (image_rep.is_null()) | |
515 return; | |
516 | |
517 SkMatrix shader_scale; | |
518 shader_scale.setScale(SkFloatToScalar(tile_scale_x), | |
519 SkFloatToScalar(tile_scale_y)); | |
520 shader_scale.preTranslate(SkIntToScalar(-src_x), SkIntToScalar(-src_y)); | |
521 shader_scale.postTranslate(SkIntToScalar(dest_x), SkIntToScalar(dest_y)); | |
522 | |
523 skia::RefPtr<SkShader> shader = CreateImageRepShader( | |
524 image_rep, | |
525 SkShader::kRepeat_TileMode, | |
526 shader_scale); | |
527 | |
528 SkPaint paint; | |
529 paint.setShader(shader.get()); | |
530 paint.setXfermodeMode(SkXfermode::kSrcOver_Mode); | |
531 | |
532 SkRect dest_rect = { SkIntToScalar(dest_x), | |
533 SkIntToScalar(dest_y), | |
534 SkIntToScalar(dest_x + w), | |
535 SkIntToScalar(dest_y + h) }; | |
536 canvas_->drawRect(dest_rect, paint); | |
537 } | |
538 | |
539 NativeDrawingContext Canvas::BeginPlatformPaint() { | |
540 return skia::BeginPlatformPaint(canvas_); | |
541 } | |
542 | |
543 void Canvas::EndPlatformPaint() { | |
544 skia::EndPlatformPaint(canvas_); | |
545 } | |
546 | |
547 void Canvas::Transform(const gfx::Transform& transform) { | |
548 canvas_->concat(transform.matrix()); | |
549 } | |
550 | |
551 Canvas::Canvas(SkCanvas* canvas, float image_scale) | |
552 : image_scale_(image_scale), | |
553 owned_canvas_(), | |
554 canvas_(canvas) { | |
555 DCHECK(canvas); | |
556 } | |
557 | |
558 bool Canvas::IntersectsClipRectInt(int x, int y, int w, int h) { | |
559 SkRect clip; | |
560 return canvas_->getClipBounds(&clip) && | |
561 clip.intersect(SkIntToScalar(x), SkIntToScalar(y), SkIntToScalar(x + w), | |
562 SkIntToScalar(y + h)); | |
563 } | |
564 | |
565 bool Canvas::IntersectsClipRect(const Rect& rect) { | |
566 return IntersectsClipRectInt(rect.x(), rect.y(), | |
567 rect.width(), rect.height()); | |
568 } | |
569 | |
570 void Canvas::DrawImageIntHelper(const ImageSkia& image, | |
571 int src_x, | |
572 int src_y, | |
573 int src_w, | |
574 int src_h, | |
575 int dest_x, | |
576 int dest_y, | |
577 int dest_w, | |
578 int dest_h, | |
579 bool filter, | |
580 const SkPaint& paint, | |
581 float image_scale, | |
582 bool pixel) { | |
583 DLOG_ASSERT(src_x + src_w < std::numeric_limits<int16_t>::max() && | |
584 src_y + src_h < std::numeric_limits<int16_t>::max()); | |
585 if (src_w <= 0 || src_h <= 0) { | |
586 NOTREACHED() << "Attempting to draw bitmap from an empty rect!"; | |
587 return; | |
588 } | |
589 | |
590 if (!IntersectsClipRectInt(dest_x, dest_y, dest_w, dest_h)) | |
591 return; | |
592 | |
593 float user_scale_x = static_cast<float>(dest_w) / src_w; | |
594 float user_scale_y = static_cast<float>(dest_h) / src_h; | |
595 | |
596 const ImageSkiaRep& image_rep = image.GetRepresentation(image_scale); | |
597 if (image_rep.is_null()) | |
598 return; | |
599 | |
600 SkRect dest_rect = { SkIntToScalar(dest_x), | |
601 SkIntToScalar(dest_y), | |
602 SkIntToScalar(dest_x + dest_w), | |
603 SkIntToScalar(dest_y + dest_h) }; | |
604 | |
605 if (src_w == dest_w && src_h == dest_h && | |
606 user_scale_x == 1.0f && user_scale_y == 1.0f && | |
607 image_rep.scale() == 1.0f && !pixel) { | |
608 // Workaround for apparent bug in Skia that causes image to occasionally | |
609 // shift. | |
610 SkIRect src_rect = { src_x, src_y, src_x + src_w, src_y + src_h }; | |
611 const SkBitmap& bitmap = image_rep.sk_bitmap(); | |
612 canvas_->drawBitmapRect(bitmap, &src_rect, dest_rect, &paint); | |
613 return; | |
614 } | |
615 | |
616 // Make a bitmap shader that contains the bitmap we want to draw. This is | |
617 // basically what SkCanvas.drawBitmap does internally, but it gives us | |
618 // more control over quality and will use the mipmap in the source image if | |
619 // it has one, whereas drawBitmap won't. | |
620 SkMatrix shader_scale; | |
621 shader_scale.setScale(SkFloatToScalar(user_scale_x), | |
622 SkFloatToScalar(user_scale_y)); | |
623 shader_scale.preTranslate(SkIntToScalar(-src_x), SkIntToScalar(-src_y)); | |
624 shader_scale.postTranslate(SkIntToScalar(dest_x), SkIntToScalar(dest_y)); | |
625 | |
626 skia::RefPtr<SkShader> shader = CreateImageRepShaderForScale( | |
627 image_rep, | |
628 SkShader::kRepeat_TileMode, | |
629 shader_scale, | |
630 pixel ? 1.0f : image_rep.scale()); | |
631 | |
632 // Set up our paint to use the shader & release our reference (now just owned | |
633 // by the paint). | |
634 SkPaint p(paint); | |
635 p.setFilterLevel(filter ? SkPaint::kLow_FilterLevel | |
636 : SkPaint::kNone_FilterLevel); | |
637 p.setShader(shader.get()); | |
638 | |
639 // The rect will be filled by the bitmap. | |
640 canvas_->drawRect(dest_rect, p); | |
641 } | |
642 | |
643 } // namespace gfx | |
OLD | NEW |