Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(173)

Side by Side Diff: skia/ext/vector_device.cc

Issue 125109: Refactor the PlatformContext layer to have only one class. (Closed)
Patch Set: Created 11 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « skia/ext/vector_device.h ('k') | skia/ext/vector_platform_device_win.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2006-2008 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 "skia/ext/vector_device.h"
6
7 #include "base/gfx/gdi_util.h"
8 #include "skia/ext/skia_utils_win.h"
9 #include "third_party/skia/include/core/SkUtils.h"
10
11 namespace skia {
12
13 VectorDevice* VectorDevice::create(HDC dc, int width, int height) {
14 InitializeDC(dc);
15
16 // Link the SkBitmap to the current selected bitmap in the device context.
17 SkBitmap bitmap;
18 HGDIOBJ selected_bitmap = GetCurrentObject(dc, OBJ_BITMAP);
19 bool succeeded = false;
20 if (selected_bitmap != NULL) {
21 BITMAP bitmap_data;
22 if (GetObject(selected_bitmap, sizeof(BITMAP), &bitmap_data) ==
23 sizeof(BITMAP)) {
24 // The context has a bitmap attached. Attach our SkBitmap to it.
25 // Warning: If the bitmap gets unselected from the HDC, VectorDevice has
26 // no way to detect this, so the HBITMAP could be released while SkBitmap
27 // still has a reference to it. Be cautious.
28 if (width == bitmap_data.bmWidth &&
29 height == bitmap_data.bmHeight) {
30 bitmap.setConfig(SkBitmap::kARGB_8888_Config,
31 bitmap_data.bmWidth,
32 bitmap_data.bmHeight,
33 bitmap_data.bmWidthBytes);
34 bitmap.setPixels(bitmap_data.bmBits);
35 succeeded = true;
36 }
37 }
38 }
39
40 if (!succeeded)
41 bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
42
43 return new VectorDevice(dc, bitmap);
44 }
45
46 VectorDevice::VectorDevice(HDC dc, const SkBitmap& bitmap)
47 : PlatformDeviceWin(bitmap),
48 hdc_(dc),
49 previous_brush_(NULL),
50 previous_pen_(NULL) {
51 transform_.reset();
52 }
53
54 VectorDevice::~VectorDevice() {
55 SkASSERT(previous_brush_ == NULL);
56 SkASSERT(previous_pen_ == NULL);
57 }
58
59
60 void VectorDevice::drawPaint(const SkDraw& draw, const SkPaint& paint) {
61 // TODO(maruel): Bypass the current transformation matrix.
62 SkRect rect;
63 rect.fLeft = 0;
64 rect.fTop = 0;
65 rect.fRight = SkIntToScalar(width() + 1);
66 rect.fBottom = SkIntToScalar(height() + 1);
67 drawRect(draw, rect, paint);
68 }
69
70 void VectorDevice::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode,
71 size_t count, const SkPoint pts[],
72 const SkPaint& paint) {
73 if (!count)
74 return;
75
76 if (mode == SkCanvas::kPoints_PointMode) {
77 SkASSERT(false);
78 return;
79 }
80
81 SkPaint tmp_paint(paint);
82 tmp_paint.setStyle(SkPaint::kStroke_Style);
83
84 // Draw a path instead.
85 SkPath path;
86 switch (mode) {
87 case SkCanvas::kLines_PointMode:
88 if (count % 2) {
89 SkASSERT(false);
90 return;
91 }
92 for (size_t i = 0; i < count / 2; ++i) {
93 path.moveTo(pts[2 * i]);
94 path.lineTo(pts[2 * i + 1]);
95 }
96 break;
97 case SkCanvas::kPolygon_PointMode:
98 path.moveTo(pts[0]);
99 for (size_t i = 1; i < count; ++i) {
100 path.lineTo(pts[i]);
101 }
102 break;
103 default:
104 SkASSERT(false);
105 return;
106 }
107 // Draw the calculated path.
108 drawPath(draw, path, tmp_paint);
109 }
110
111 void VectorDevice::drawRect(const SkDraw& draw, const SkRect& rect,
112 const SkPaint& paint) {
113 if (paint.getPathEffect()) {
114 // Draw a path instead.
115 SkPath path_orginal;
116 path_orginal.addRect(rect);
117
118 // Apply the path effect to the rect.
119 SkPath path_modified;
120 paint.getFillPath(path_orginal, &path_modified);
121
122 // Removes the path effect from the temporary SkPaint object.
123 SkPaint paint_no_effet(paint);
124 paint_no_effet.setPathEffect(NULL)->safeUnref();
125
126 // Draw the calculated path.
127 drawPath(draw, path_modified, paint_no_effet);
128 return;
129 }
130
131 if (!ApplyPaint(paint)) {
132 return;
133 }
134 HDC dc = getBitmapDC();
135 if (!Rectangle(dc, SkScalarRound(rect.fLeft),
136 SkScalarRound(rect.fTop),
137 SkScalarRound(rect.fRight),
138 SkScalarRound(rect.fBottom))) {
139 SkASSERT(false);
140 }
141 Cleanup();
142 }
143
144 void VectorDevice::drawPath(const SkDraw& draw, const SkPath& path,
145 const SkPaint& paint) {
146 if (paint.getPathEffect()) {
147 // Apply the path effect forehand.
148 SkPath path_modified;
149 paint.getFillPath(path, &path_modified);
150
151 // Removes the path effect from the temporary SkPaint object.
152 SkPaint paint_no_effet(paint);
153 paint_no_effet.setPathEffect(NULL)->safeUnref();
154
155 // Draw the calculated path.
156 drawPath(draw, path_modified, paint_no_effet);
157 return;
158 }
159
160 if (!ApplyPaint(paint)) {
161 return;
162 }
163 HDC dc = getBitmapDC();
164 PlatformDeviceWin::LoadPathToDC(dc, path);
165 switch (paint.getStyle()) {
166 case SkPaint::kFill_Style: {
167 BOOL res = StrokeAndFillPath(dc);
168 SkASSERT(res != 0);
169 break;
170 }
171 case SkPaint::kStroke_Style: {
172 BOOL res = StrokePath(dc);
173 SkASSERT(res != 0);
174 break;
175 }
176 case SkPaint::kStrokeAndFill_Style: {
177 BOOL res = StrokeAndFillPath(dc);
178 SkASSERT(res != 0);
179 break;
180 }
181 default:
182 SkASSERT(false);
183 break;
184 }
185 Cleanup();
186 }
187
188 void VectorDevice::drawBitmap(const SkDraw& draw, const SkBitmap& bitmap,
189 const SkMatrix& matrix, const SkPaint& paint) {
190 // Load the temporary matrix. This is what will translate, rotate and resize
191 // the bitmap.
192 SkMatrix actual_transform(transform_);
193 actual_transform.preConcat(matrix);
194 LoadTransformToDC(hdc_, actual_transform);
195
196 InternalDrawBitmap(bitmap, 0, 0, paint);
197
198 // Restore the original matrix.
199 LoadTransformToDC(hdc_, transform_);
200 }
201
202 void VectorDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap,
203 int x, int y, const SkPaint& paint) {
204 SkMatrix identity;
205 identity.reset();
206 LoadTransformToDC(hdc_, identity);
207
208 InternalDrawBitmap(bitmap, x, y, paint);
209
210 // Restore the original matrix.
211 LoadTransformToDC(hdc_, transform_);
212 }
213
214 void VectorDevice::drawText(const SkDraw& draw, const void* text, size_t byteLen gth,
215 SkScalar x, SkScalar y, const SkPaint& paint) {
216 // This function isn't used in the code. Verify this assumption.
217 SkASSERT(false);
218 }
219
220 void VectorDevice::drawPosText(const SkDraw& draw, const void* text, size_t len,
221 const SkScalar pos[], SkScalar constY,
222 int scalarsPerPos, const SkPaint& paint) {
223 // This function isn't used in the code. Verify this assumption.
224 SkASSERT(false);
225 }
226
227 void VectorDevice::drawTextOnPath(const SkDraw& draw, const void* text,
228 size_t len,
229 const SkPath& path, const SkMatrix* matrix,
230 const SkPaint& paint) {
231 // This function isn't used in the code. Verify this assumption.
232 SkASSERT(false);
233 }
234
235 void VectorDevice::drawVertices(const SkDraw& draw, SkCanvas::VertexMode vmode,
236 int vertexCount,
237 const SkPoint vertices[], const SkPoint texs[],
238 const SkColor colors[], SkXfermode* xmode,
239 const uint16_t indices[], int indexCount,
240 const SkPaint& paint) {
241 // This function isn't used in the code. Verify this assumption.
242 SkASSERT(false);
243 }
244
245 void VectorDevice::drawDevice(const SkDraw& draw, SkDevice* device, int x,
246 int y, const SkPaint& paint) {
247 // TODO(maruel): http://b/1183870 Playback the EMF buffer at printer's dpi if
248 // it is a vectorial device.
249 drawSprite(draw, device->accessBitmap(false), x, y, paint);
250 }
251
252 bool VectorDevice::ApplyPaint(const SkPaint& paint) {
253 // Note: The goal here is to transfert the SkPaint's state to the HDC's state.
254 // This function does not execute the SkPaint drawing commands. These should
255 // be executed in drawPaint().
256
257 SkPaint::Style style = paint.getStyle();
258 if (!paint.getAlpha())
259 style = SkPaint::kStyleCount;
260
261 switch (style) {
262 case SkPaint::kFill_Style:
263 if (!CreateBrush(true, paint) ||
264 !CreatePen(false, paint))
265 return false;
266 break;
267 case SkPaint::kStroke_Style:
268 if (!CreateBrush(false, paint) ||
269 !CreatePen(true, paint))
270 return false;
271 break;
272 case SkPaint::kStrokeAndFill_Style:
273 if (!CreateBrush(true, paint) ||
274 !CreatePen(true, paint))
275 return false;
276 break;
277 default:
278 if (!CreateBrush(false, paint) ||
279 !CreatePen(false, paint))
280 return false;
281 break;
282 }
283
284 /*
285 getFlags();
286 isAntiAlias();
287 isDither()
288 isLinearText()
289 isSubpixelText()
290 isUnderlineText()
291 isStrikeThruText()
292 isFakeBoldText()
293 isDevKernText()
294 isFilterBitmap()
295
296 // Skia's text is not used. This should be fixed.
297 getTextAlign()
298 getTextScaleX()
299 getTextSkewX()
300 getTextEncoding()
301 getFontMetrics()
302 getFontSpacing()
303 */
304
305 // BUG 1094907: Implement shaders. Shaders currently in use:
306 // SkShader::CreateBitmapShader
307 // SkGradientShader::CreateRadial
308 // SkGradientShader::CreateLinear
309 // SkASSERT(!paint.getShader());
310
311 // http://b/1106647 Implement loopers and mask filter. Looper currently in
312 // use:
313 // SkBlurDrawLooper is used for shadows.
314 // SkASSERT(!paint.getLooper());
315 // SkASSERT(!paint.getMaskFilter());
316
317 // http://b/1165900 Implement xfermode.
318 // SkASSERT(!paint.getXfermode());
319
320 // The path effect should be processed before arriving here.
321 SkASSERT(!paint.getPathEffect());
322
323 // These aren't used in the code. Verify this assumption.
324 SkASSERT(!paint.getColorFilter());
325 SkASSERT(!paint.getRasterizer());
326 // Reuse code to load Win32 Fonts.
327 SkASSERT(!paint.getTypeface());
328 return true;
329 }
330
331 void VectorDevice::setMatrixClip(const SkMatrix& transform,
332 const SkRegion& region) {
333 transform_ = transform;
334 LoadTransformToDC(hdc_, transform_);
335 clip_region_ = region;
336 if (!clip_region_.isEmpty())
337 LoadClipRegion();
338 }
339
340 void VectorDevice::drawToHDC(HDC dc, int x, int y, const RECT* src_rect) {
341 SkASSERT(false);
342 }
343
344 void VectorDevice::LoadClipRegion() {
345 SkMatrix t;
346 t.reset();
347 LoadClippingRegionToDC(hdc_, clip_region_, t);
348 }
349
350 bool VectorDevice::CreateBrush(bool use_brush, COLORREF color) {
351 SkASSERT(previous_brush_ == NULL);
352 // We can't use SetDCBrushColor() or DC_BRUSH when drawing to a EMF buffer.
353 // SetDCBrushColor() calls are not recorded at all and DC_BRUSH will use
354 // WHITE_BRUSH instead.
355
356 if (!use_brush) {
357 // Set the transparency.
358 if (0 == SetBkMode(hdc_, TRANSPARENT)) {
359 SkASSERT(false);
360 return false;
361 }
362
363 // Select the NULL brush.
364 previous_brush_ = SelectObject(GetStockObject(NULL_BRUSH));
365 return previous_brush_ != NULL;
366 }
367
368 // Set the opacity.
369 if (0 == SetBkMode(hdc_, OPAQUE)) {
370 SkASSERT(false);
371 return false;
372 }
373
374 // Create and select the brush.
375 previous_brush_ = SelectObject(CreateSolidBrush(color));
376 return previous_brush_ != NULL;
377 }
378
379 bool VectorDevice::CreatePen(bool use_pen, COLORREF color, int stroke_width,
380 float stroke_miter, DWORD pen_style) {
381 SkASSERT(previous_pen_ == NULL);
382 // We can't use SetDCPenColor() or DC_PEN when drawing to a EMF buffer.
383 // SetDCPenColor() calls are not recorded at all and DC_PEN will use BLACK_PEN
384 // instead.
385
386 // No pen case
387 if (!use_pen) {
388 previous_pen_ = SelectObject(GetStockObject(NULL_PEN));
389 return previous_pen_ != NULL;
390 }
391
392 // Use the stock pen if the stroke width is 0.
393 if (stroke_width == 0) {
394 // Create a pen with the right color.
395 previous_pen_ = SelectObject(::CreatePen(PS_SOLID, 0, color));
396 return previous_pen_ != NULL;
397 }
398
399 // Load a custom pen.
400 LOGBRUSH brush;
401 brush.lbStyle = BS_SOLID;
402 brush.lbColor = color;
403 brush.lbHatch = 0;
404 HPEN pen = ExtCreatePen(pen_style, stroke_width, &brush, 0, NULL);
405 SkASSERT(pen != NULL);
406 previous_pen_ = SelectObject(pen);
407 if (previous_pen_ == NULL)
408 return false;
409
410 if (!SetMiterLimit(hdc_, stroke_miter, NULL)) {
411 SkASSERT(false);
412 return false;
413 }
414 return true;
415 }
416
417 void VectorDevice::Cleanup() {
418 if (previous_brush_) {
419 HGDIOBJ result = SelectObject(previous_brush_);
420 previous_brush_ = NULL;
421 if (result) {
422 BOOL res = DeleteObject(result);
423 SkASSERT(res != 0);
424 }
425 }
426 if (previous_pen_) {
427 HGDIOBJ result = SelectObject(previous_pen_);
428 previous_pen_ = NULL;
429 if (result) {
430 BOOL res = DeleteObject(result);
431 SkASSERT(res != 0);
432 }
433 }
434 // Remove any loaded path from the context.
435 AbortPath(hdc_);
436 }
437
438 HGDIOBJ VectorDevice::SelectObject(HGDIOBJ object) {
439 HGDIOBJ result = ::SelectObject(hdc_, object);
440 SkASSERT(result != HGDI_ERROR);
441 if (result == HGDI_ERROR)
442 return NULL;
443 return result;
444 }
445
446 bool VectorDevice::CreateBrush(bool use_brush, const SkPaint& paint) {
447 // Make sure that for transparent color, no brush is used.
448 if (paint.getAlpha() == 0) {
449 // Test if it ever happen.
450 SkASSERT(false);
451 use_brush = false;
452 }
453
454 return CreateBrush(use_brush, SkColorToCOLORREF(paint.getColor()));
455 }
456
457 bool VectorDevice::CreatePen(bool use_pen, const SkPaint& paint) {
458 // Make sure that for transparent color, no pen is used.
459 if (paint.getAlpha() == 0) {
460 // Test if it ever happen.
461 SkASSERT(false);
462 use_pen = false;
463 }
464
465 DWORD pen_style = PS_GEOMETRIC | PS_SOLID;
466 switch (paint.getStrokeJoin()) {
467 case SkPaint::kMiter_Join:
468 // Connects path segments with a sharp join.
469 pen_style |= PS_JOIN_MITER;
470 break;
471 case SkPaint::kRound_Join:
472 // Connects path segments with a round join.
473 pen_style |= PS_JOIN_ROUND;
474 break;
475 case SkPaint::kBevel_Join:
476 // Connects path segments with a flat bevel join.
477 pen_style |= PS_JOIN_BEVEL;
478 break;
479 default:
480 SkASSERT(false);
481 break;
482 }
483 switch (paint.getStrokeCap()) {
484 case SkPaint::kButt_Cap:
485 // Begin/end contours with no extension.
486 pen_style |= PS_ENDCAP_FLAT;
487 break;
488 case SkPaint::kRound_Cap:
489 // Begin/end contours with a semi-circle extension.
490 pen_style |= PS_ENDCAP_ROUND;
491 break;
492 case SkPaint::kSquare_Cap:
493 // Begin/end contours with a half square extension.
494 pen_style |= PS_ENDCAP_SQUARE;
495 break;
496 default:
497 SkASSERT(false);
498 break;
499 }
500
501 return CreatePen(use_pen,
502 SkColorToCOLORREF(paint.getColor()),
503 SkScalarRound(paint.getStrokeWidth()),
504 paint.getStrokeMiter(),
505 pen_style);
506 }
507
508 void VectorDevice::InternalDrawBitmap(const SkBitmap& bitmap, int x, int y,
509 const SkPaint& paint) {
510 unsigned char alpha = paint.getAlpha();
511 if (alpha == 0)
512 return;
513
514 bool is_translucent;
515 if (alpha != 255) {
516 // ApplyPaint expect an opaque color.
517 SkPaint tmp_paint(paint);
518 tmp_paint.setAlpha(255);
519 if (!ApplyPaint(tmp_paint))
520 return;
521 is_translucent = true;
522 } else {
523 if (!ApplyPaint(paint))
524 return;
525 is_translucent = false;
526 }
527 int src_size_x = bitmap.width();
528 int src_size_y = bitmap.height();
529 if (!src_size_x || !src_size_y)
530 return;
531
532 // Create a BMP v4 header that we can serialize.
533 BITMAPV4HEADER bitmap_header;
534 gfx::CreateBitmapV4Header(src_size_x, src_size_y, &bitmap_header);
535 HDC dc = getBitmapDC();
536 SkAutoLockPixels lock(bitmap);
537 SkASSERT(bitmap.getConfig() == SkBitmap::kARGB_8888_Config);
538 const uint32_t* pixels = static_cast<const uint32_t*>(bitmap.getPixels());
539 if (pixels == NULL) {
540 SkASSERT(false);
541 return;
542 }
543
544 if (!is_translucent) {
545 int row_length = bitmap.rowBytesAsPixels();
546 // There is no quick way to determine if an image is opaque.
547 for (int y2 = 0; y2 < src_size_y; ++y2) {
548 for (int x2 = 0; x2 < src_size_x; ++x2) {
549 if (SkColorGetA(pixels[(y2 * row_length) + x2]) != 255) {
550 is_translucent = true;
551 y2 = src_size_y;
552 break;
553 }
554 }
555 }
556 }
557
558 BITMAPINFOHEADER hdr;
559 gfx::CreateBitmapHeader(src_size_x, src_size_y, &hdr);
560 if (is_translucent) {
561 // The image must be loaded as a bitmap inside a device context.
562 HDC bitmap_dc = ::CreateCompatibleDC(dc);
563 void* bits = NULL;
564 HBITMAP hbitmap = ::CreateDIBSection(
565 bitmap_dc, reinterpret_cast<const BITMAPINFO*>(&hdr),
566 DIB_RGB_COLORS, &bits, NULL, 0);
567 memcpy(bits, pixels, bitmap.getSize());
568 SkASSERT(hbitmap);
569 HGDIOBJ old_bitmap = ::SelectObject(bitmap_dc, hbitmap);
570
571 // After some analysis of IE7's behavior, this is the thing to do. I was
572 // sure IE7 was doing so kind of bitmasking due to the way translucent image
573 // where renderered but after some windbg tracing, it is being done by the
574 // printer driver after all (mostly HP printers). IE7 always use AlphaBlend
575 // for bitmasked images. The trick seems to switch the stretching mode in
576 // what the driver expects.
577 DWORD previous_mode = GetStretchBltMode(dc);
578 BOOL result = SetStretchBltMode(dc, COLORONCOLOR);
579 SkASSERT(result);
580 // Note that this function expect premultiplied colors (!)
581 BLENDFUNCTION blend_function = {AC_SRC_OVER, 0, alpha, AC_SRC_ALPHA};
582 result = GdiAlphaBlend(dc,
583 x, y, // Destination origin.
584 src_size_x, src_size_y, // Destination size.
585 bitmap_dc,
586 0, 0, // Source origin.
587 src_size_x, src_size_y, // Source size.
588 blend_function);
589 SkASSERT(result);
590 result = SetStretchBltMode(dc, previous_mode);
591 SkASSERT(result);
592
593 ::SelectObject(bitmap_dc, static_cast<HBITMAP>(old_bitmap));
594 DeleteObject(hbitmap);
595 DeleteDC(bitmap_dc);
596 } else {
597 BOOL result = StretchDIBits(dc,
598 x, y, // Destination origin.
599 src_size_x, src_size_y,
600 0, 0, // Source origin.
601 src_size_x, src_size_y, // Source size.
602 pixels,
603 reinterpret_cast<const BITMAPINFO*>(&hdr),
604 DIB_RGB_COLORS,
605 SRCCOPY);
606 SkASSERT(result);
607 }
608 Cleanup();
609 }
610
611 } // namespace skia
612
OLDNEW
« no previous file with comments | « skia/ext/vector_device.h ('k') | skia/ext/vector_platform_device_win.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698