chromeos: bluetooth: add BluetoothInputClient
[chromium-blink-merge.git] / skia / ext / vector_platform_device_emf_win.cc
blob99f7dc8d86f4d51b9d667b3f8c0cfe7a986ef428
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.
5 #include <windows.h>
7 #include "skia/ext/vector_platform_device_emf_win.h"
9 #include "skia/ext/bitmap_platform_device.h"
10 #include "skia/ext/skia_utils_win.h"
11 #include "third_party/skia/include/core/SkTemplates.h"
12 #include "third_party/skia/include/core/SkUtils.h"
13 #include "third_party/skia/include/ports/SkTypeface_win.h"
15 namespace skia {
17 // static
18 SkDevice* VectorPlatformDeviceEmf::CreateDevice(
19 int width, int height, bool is_opaque, HANDLE shared_section) {
20 if (!is_opaque) {
21 // TODO(maruel): http://crbug.com/18382 When restoring a semi-transparent
22 // layer, i.e. merging it, we need to rasterize it because GDI doesn't
23 // support transparency except for AlphaBlend(). Right now, a
24 // BitmapPlatformDevice is created when VectorCanvas think a saveLayers()
25 // call is being done. The way to save a layer would be to create an
26 // EMF-based VectorDevice and have this device registers the drawing. When
27 // playing back the device into a bitmap, do it at the printer's dpi instead
28 // of the layout's dpi (which is much lower).
29 return BitmapPlatformDevice::create(width, height, is_opaque,
30 shared_section);
33 // TODO(maruel): http://crbug.com/18383 Look if it would be worth to
34 // increase the resolution by ~10x (any worthy factor) to increase the
35 // rendering precision (think about printing) while using a relatively
36 // low dpi. This happens because we receive float as input but the GDI
37 // functions works with integers. The idea is to premultiply the matrix
38 // with this factor and multiply each SkScalar that are passed to
39 // SkScalarRound(value) as SkScalarRound(value * 10). Safari is already
40 // doing the same for text rendering.
41 SkASSERT(shared_section);
42 SkDevice* device = VectorPlatformDeviceEmf::create(
43 reinterpret_cast<HDC>(shared_section), width, height);
44 return device;
47 static void FillBitmapInfoHeader(int width, int height, BITMAPINFOHEADER* hdr) {
48 hdr->biSize = sizeof(BITMAPINFOHEADER);
49 hdr->biWidth = width;
50 hdr->biHeight = -height; // Minus means top-down bitmap.
51 hdr->biPlanes = 1;
52 hdr->biBitCount = 32;
53 hdr->biCompression = BI_RGB; // no compression
54 hdr->biSizeImage = 0;
55 hdr->biXPelsPerMeter = 1;
56 hdr->biYPelsPerMeter = 1;
57 hdr->biClrUsed = 0;
58 hdr->biClrImportant = 0;
61 SkDevice* VectorPlatformDeviceEmf::create(HDC dc, int width, int height) {
62 InitializeDC(dc);
64 // Link the SkBitmap to the current selected bitmap in the device context.
65 SkBitmap bitmap;
66 HGDIOBJ selected_bitmap = GetCurrentObject(dc, OBJ_BITMAP);
67 bool succeeded = false;
68 if (selected_bitmap != NULL) {
69 BITMAP bitmap_data;
70 if (GetObject(selected_bitmap, sizeof(BITMAP), &bitmap_data) ==
71 sizeof(BITMAP)) {
72 // The context has a bitmap attached. Attach our SkBitmap to it.
73 // Warning: If the bitmap gets unselected from the HDC,
74 // VectorPlatformDeviceEmf has no way to detect this, so the HBITMAP
75 // could be released while SkBitmap still has a reference to it. Be
76 // cautious.
77 if (width == bitmap_data.bmWidth &&
78 height == bitmap_data.bmHeight) {
79 bitmap.setConfig(SkBitmap::kARGB_8888_Config,
80 bitmap_data.bmWidth,
81 bitmap_data.bmHeight,
82 bitmap_data.bmWidthBytes);
83 bitmap.setPixels(bitmap_data.bmBits);
84 succeeded = true;
89 if (!succeeded)
90 bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
92 return new VectorPlatformDeviceEmf(dc, bitmap);
95 VectorPlatformDeviceEmf::VectorPlatformDeviceEmf(HDC dc, const SkBitmap& bitmap)
96 : SkDevice(bitmap),
97 hdc_(dc),
98 previous_brush_(NULL),
99 previous_pen_(NULL),
100 alpha_blend_used_(false) {
101 transform_.reset();
102 SetPlatformDevice(this, this);
105 VectorPlatformDeviceEmf::~VectorPlatformDeviceEmf() {
106 SkASSERT(previous_brush_ == NULL);
107 SkASSERT(previous_pen_ == NULL);
110 HDC VectorPlatformDeviceEmf::BeginPlatformPaint() {
111 return hdc_;
114 uint32_t VectorPlatformDeviceEmf::getDeviceCapabilities() {
115 return SkDevice::getDeviceCapabilities() | kVector_Capability;
118 void VectorPlatformDeviceEmf::drawPaint(const SkDraw& draw,
119 const SkPaint& paint) {
120 // TODO(maruel): Bypass the current transformation matrix.
121 SkRect rect;
122 rect.fLeft = 0;
123 rect.fTop = 0;
124 rect.fRight = SkIntToScalar(width() + 1);
125 rect.fBottom = SkIntToScalar(height() + 1);
126 drawRect(draw, rect, paint);
129 void VectorPlatformDeviceEmf::drawPoints(const SkDraw& draw,
130 SkCanvas::PointMode mode,
131 size_t count,
132 const SkPoint pts[],
133 const SkPaint& paint) {
134 if (!count)
135 return;
137 if (mode == SkCanvas::kPoints_PointMode) {
138 SkASSERT(false);
139 return;
142 SkPaint tmp_paint(paint);
143 tmp_paint.setStyle(SkPaint::kStroke_Style);
145 // Draw a path instead.
146 SkPath path;
147 switch (mode) {
148 case SkCanvas::kLines_PointMode:
149 if (count % 2) {
150 SkASSERT(false);
151 return;
153 for (size_t i = 0; i < count / 2; ++i) {
154 path.moveTo(pts[2 * i]);
155 path.lineTo(pts[2 * i + 1]);
157 break;
158 case SkCanvas::kPolygon_PointMode:
159 path.moveTo(pts[0]);
160 for (size_t i = 1; i < count; ++i) {
161 path.lineTo(pts[i]);
163 break;
164 default:
165 SkASSERT(false);
166 return;
168 // Draw the calculated path.
169 drawPath(draw, path, tmp_paint);
172 void VectorPlatformDeviceEmf::drawRect(const SkDraw& draw,
173 const SkRect& rect,
174 const SkPaint& paint) {
175 if (paint.getPathEffect()) {
176 // Draw a path instead.
177 SkPath path_orginal;
178 path_orginal.addRect(rect);
180 // Apply the path effect to the rect.
181 SkPath path_modified;
182 paint.getFillPath(path_orginal, &path_modified);
184 // Removes the path effect from the temporary SkPaint object.
185 SkPaint paint_no_effet(paint);
186 SkSafeUnref(paint_no_effet.setPathEffect(NULL));
188 // Draw the calculated path.
189 drawPath(draw, path_modified, paint_no_effet);
190 return;
193 if (!ApplyPaint(paint)) {
194 return;
196 HDC dc = BeginPlatformPaint();
197 if (!Rectangle(dc, SkScalarRound(rect.fLeft),
198 SkScalarRound(rect.fTop),
199 SkScalarRound(rect.fRight),
200 SkScalarRound(rect.fBottom))) {
201 SkASSERT(false);
203 EndPlatformPaint();
204 Cleanup();
207 void VectorPlatformDeviceEmf::drawPath(const SkDraw& draw,
208 const SkPath& path,
209 const SkPaint& paint,
210 const SkMatrix* prePathMatrix,
211 bool pathIsMutable) {
212 if (paint.getPathEffect()) {
213 // Apply the path effect forehand.
214 SkPath path_modified;
215 paint.getFillPath(path, &path_modified);
217 // Removes the path effect from the temporary SkPaint object.
218 SkPaint paint_no_effet(paint);
219 SkSafeUnref(paint_no_effet.setPathEffect(NULL));
221 // Draw the calculated path.
222 drawPath(draw, path_modified, paint_no_effet);
223 return;
226 if (!ApplyPaint(paint)) {
227 return;
229 HDC dc = BeginPlatformPaint();
230 if (PlatformDevice::LoadPathToDC(dc, path)) {
231 switch (paint.getStyle()) {
232 case SkPaint::kFill_Style: {
233 BOOL res = StrokeAndFillPath(dc);
234 SkASSERT(res != 0);
235 break;
237 case SkPaint::kStroke_Style: {
238 BOOL res = StrokePath(dc);
239 SkASSERT(res != 0);
240 break;
242 case SkPaint::kStrokeAndFill_Style: {
243 BOOL res = StrokeAndFillPath(dc);
244 SkASSERT(res != 0);
245 break;
247 default:
248 SkASSERT(false);
249 break;
252 EndPlatformPaint();
253 Cleanup();
256 void VectorPlatformDeviceEmf::drawBitmap(const SkDraw& draw,
257 const SkBitmap& bitmap,
258 const SkIRect* srcRectOrNull,
259 const SkMatrix& matrix,
260 const SkPaint& paint) {
261 // Load the temporary matrix. This is what will translate, rotate and resize
262 // the bitmap.
263 SkMatrix actual_transform(transform_);
264 actual_transform.preConcat(matrix);
265 LoadTransformToDC(hdc_, actual_transform);
267 InternalDrawBitmap(bitmap, 0, 0, paint);
269 // Restore the original matrix.
270 LoadTransformToDC(hdc_, transform_);
273 void VectorPlatformDeviceEmf::drawSprite(const SkDraw& draw,
274 const SkBitmap& bitmap,
275 int x, int y,
276 const SkPaint& paint) {
277 SkMatrix identity;
278 identity.reset();
279 LoadTransformToDC(hdc_, identity);
281 InternalDrawBitmap(bitmap, x, y, paint);
283 // Restore the original matrix.
284 LoadTransformToDC(hdc_, transform_);
287 /////////////////////////////////////////////////////////////////////////
289 static bool gdiCanHandleText(const SkPaint& paint) {
290 return !paint.getShader() &&
291 !paint.getPathEffect() &&
292 (SkPaint::kFill_Style == paint.getStyle()) &&
293 (255 == paint.getAlpha());
296 class SkGDIFontSetup {
297 public:
298 SkGDIFontSetup() :
299 fHDC(NULL),
300 fNewFont(NULL),
301 fSavedFont(NULL),
302 fSavedTextColor(0),
303 fUseGDI(false) {
304 SkDEBUGCODE(fUseGDIHasBeenCalled = false;)
306 ~SkGDIFontSetup();
308 // can only be called once
309 bool useGDI(HDC hdc, const SkPaint&);
311 private:
312 HDC fHDC;
313 HFONT fNewFont;
314 HFONT fSavedFont;
315 COLORREF fSavedTextColor;
316 bool fUseGDI;
317 SkDEBUGCODE(bool fUseGDIHasBeenCalled;)
320 bool SkGDIFontSetup::useGDI(HDC hdc, const SkPaint& paint) {
321 SkASSERT(!fUseGDIHasBeenCalled);
322 SkDEBUGCODE(fUseGDIHasBeenCalled = true;)
324 fUseGDI = gdiCanHandleText(paint);
325 if (fUseGDI) {
326 fSavedTextColor = GetTextColor(hdc);
327 SetTextColor(hdc, skia::SkColorToCOLORREF(paint.getColor()));
329 LOGFONT lf;
330 SkLOGFONTFromTypeface(paint.getTypeface(), &lf);
331 lf.lfHeight = -SkScalarRound(paint.getTextSize());
332 fNewFont = CreateFontIndirect(&lf);
333 fSavedFont = (HFONT)::SelectObject(hdc, fNewFont);
334 fHDC = hdc;
336 return fUseGDI;
339 SkGDIFontSetup::~SkGDIFontSetup() {
340 if (fUseGDI) {
341 ::SelectObject(fHDC, fSavedFont);
342 ::DeleteObject(fNewFont);
343 SetTextColor(fHDC, fSavedTextColor);
347 static SkScalar getAscent(const SkPaint& paint) {
348 SkPaint::FontMetrics fm;
349 paint.getFontMetrics(&fm);
350 return fm.fAscent;
353 // return the options int for ExtTextOut. Only valid if the paint's text
354 // encoding is not UTF8 (in which case ExtTextOut can't be used).
355 static UINT getTextOutOptions(const SkPaint& paint) {
356 if (SkPaint::kGlyphID_TextEncoding == paint.getTextEncoding()) {
357 return ETO_GLYPH_INDEX;
358 } else {
359 SkASSERT(SkPaint::kUTF16_TextEncoding == paint.getTextEncoding());
360 return 0;
364 void VectorPlatformDeviceEmf::drawText(const SkDraw& draw,
365 const void* text,
366 size_t byteLength,
367 SkScalar x,
368 SkScalar y,
369 const SkPaint& paint) {
370 SkGDIFontSetup setup;
371 if (SkPaint::kUTF8_TextEncoding != paint.getTextEncoding()
372 && setup.useGDI(hdc_, paint)) {
373 UINT options = getTextOutOptions(paint);
374 UINT count = byteLength >> 1;
375 ExtTextOut(hdc_, SkScalarRound(x), SkScalarRound(y + getAscent(paint)),
376 options, 0, reinterpret_cast<const wchar_t*>(text), count, NULL);
377 } else {
378 SkPath path;
379 paint.getTextPath(text, byteLength, x, y, &path);
380 drawPath(draw, path, paint);
384 static size_t size_utf8(const char* text) {
385 return SkUTF8_CountUTF8Bytes(text);
388 static size_t size_utf16(const char* text) {
389 uint16_t c = *reinterpret_cast<const uint16_t*>(text);
390 return SkUTF16_IsHighSurrogate(c) ? 4 : 2;
393 static size_t size_glyphid(const char* text) {
394 return 2;
397 void VectorPlatformDeviceEmf::drawPosText(const SkDraw& draw,
398 const void* text,
399 size_t len,
400 const SkScalar pos[],
401 SkScalar constY,
402 int scalarsPerPos,
403 const SkPaint& paint) {
404 SkGDIFontSetup setup;
405 if (2 == scalarsPerPos
406 && SkPaint::kUTF8_TextEncoding != paint.getTextEncoding()
407 && setup.useGDI(hdc_, paint)) {
408 int startX = SkScalarRound(pos[0]);
409 int startY = SkScalarRound(pos[1] + getAscent(paint));
410 const int count = len >> 1;
411 SkAutoSTMalloc<64, INT> storage(count);
412 INT* advances = storage.get();
413 for (int i = 0; i < count - 1; ++i) {
414 advances[i] = SkScalarRound(pos[2] - pos[0]);
415 pos += 2;
417 ExtTextOut(hdc_, startX, startY, getTextOutOptions(paint), 0,
418 reinterpret_cast<const wchar_t*>(text), count, advances);
419 } else {
420 size_t (*bytesPerCodePoint)(const char*);
421 switch (paint.getTextEncoding()) {
422 case SkPaint::kUTF8_TextEncoding:
423 bytesPerCodePoint = size_utf8;
424 break;
425 case SkPaint::kUTF16_TextEncoding:
426 bytesPerCodePoint = size_utf16;
427 break;
428 default:
429 SkASSERT(SkPaint::kGlyphID_TextEncoding == paint.getTextEncoding());
430 bytesPerCodePoint = size_glyphid;
431 break;
434 const char* curr = reinterpret_cast<const char*>(text);
435 const char* stop = curr + len;
436 while (curr < stop) {
437 SkScalar y = (1 == scalarsPerPos) ? constY : pos[1];
438 size_t bytes = bytesPerCodePoint(curr);
439 drawText(draw, curr, bytes, pos[0], y, paint);
440 curr += bytes;
441 pos += scalarsPerPos;
446 void VectorPlatformDeviceEmf::drawTextOnPath(const SkDraw& draw,
447 const void* text,
448 size_t len,
449 const SkPath& path,
450 const SkMatrix* matrix,
451 const SkPaint& paint) {
452 // This function isn't used in the code. Verify this assumption.
453 SkASSERT(false);
456 void VectorPlatformDeviceEmf::drawVertices(const SkDraw& draw,
457 SkCanvas::VertexMode vmode,
458 int vertexCount,
459 const SkPoint vertices[],
460 const SkPoint texs[],
461 const SkColor colors[],
462 SkXfermode* xmode,
463 const uint16_t indices[],
464 int indexCount,
465 const SkPaint& paint) {
466 // This function isn't used in the code. Verify this assumption.
467 SkASSERT(false);
470 void VectorPlatformDeviceEmf::drawDevice(const SkDraw& draw,
471 SkDevice* device,
472 int x,
473 int y,
474 const SkPaint& paint) {
475 // TODO(maruel): http://b/1183870 Playback the EMF buffer at printer's dpi if
476 // it is a vectorial device.
477 drawSprite(draw, device->accessBitmap(false), x, y, paint);
480 bool VectorPlatformDeviceEmf::ApplyPaint(const SkPaint& paint) {
481 // Note: The goal here is to transfert the SkPaint's state to the HDC's state.
482 // This function does not execute the SkPaint drawing commands. These should
483 // be executed in drawPaint().
485 SkPaint::Style style = paint.getStyle();
486 if (!paint.getAlpha())
487 style = SkPaint::kStyleCount;
489 switch (style) {
490 case SkPaint::kFill_Style:
491 if (!CreateBrush(true, paint) ||
492 !CreatePen(false, paint))
493 return false;
494 break;
495 case SkPaint::kStroke_Style:
496 if (!CreateBrush(false, paint) ||
497 !CreatePen(true, paint))
498 return false;
499 break;
500 case SkPaint::kStrokeAndFill_Style:
501 if (!CreateBrush(true, paint) ||
502 !CreatePen(true, paint))
503 return false;
504 break;
505 default:
506 if (!CreateBrush(false, paint) ||
507 !CreatePen(false, paint))
508 return false;
509 break;
513 getFlags();
514 isAntiAlias();
515 isDither()
516 isLinearText()
517 isSubpixelText()
518 isUnderlineText()
519 isStrikeThruText()
520 isFakeBoldText()
521 isDevKernText()
522 isFilterBitmap()
524 // Skia's text is not used. This should be fixed.
525 getTextAlign()
526 getTextScaleX()
527 getTextSkewX()
528 getTextEncoding()
529 getFontMetrics()
530 getFontSpacing()
533 // BUG 1094907: Implement shaders. Shaders currently in use:
534 // SkShader::CreateBitmapShader
535 // SkGradientShader::CreateRadial
536 // SkGradientShader::CreateLinear
537 // SkASSERT(!paint.getShader());
539 // http://b/1106647 Implement loopers and mask filter. Looper currently in
540 // use:
541 // SkBlurDrawLooper is used for shadows.
542 // SkASSERT(!paint.getLooper());
543 // SkASSERT(!paint.getMaskFilter());
545 // http://b/1165900 Implement xfermode.
546 // SkASSERT(!paint.getXfermode());
548 // The path effect should be processed before arriving here.
549 SkASSERT(!paint.getPathEffect());
551 // This isn't used in the code. Verify this assumption.
552 SkASSERT(!paint.getRasterizer());
553 // Reuse code to load Win32 Fonts.
554 return true;
557 void VectorPlatformDeviceEmf::setMatrixClip(const SkMatrix& transform,
558 const SkRegion& region,
559 const SkClipStack&) {
560 transform_ = transform;
561 LoadTransformToDC(hdc_, transform_);
562 clip_region_ = region;
563 if (!clip_region_.isEmpty())
564 LoadClipRegion();
567 void VectorPlatformDeviceEmf::DrawToNativeContext(HDC dc, int x, int y,
568 const RECT* src_rect) {
569 SkASSERT(false);
572 void VectorPlatformDeviceEmf::LoadClipRegion() {
573 SkMatrix t;
574 t.reset();
575 LoadClippingRegionToDC(hdc_, clip_region_, t);
578 SkDevice* VectorPlatformDeviceEmf::onCreateCompatibleDevice(
579 SkBitmap::Config config, int width, int height, bool isOpaque,
580 Usage /*usage*/) {
581 SkASSERT(config == SkBitmap::kARGB_8888_Config);
582 return VectorPlatformDeviceEmf::CreateDevice(width, height, isOpaque, NULL);
585 bool VectorPlatformDeviceEmf::CreateBrush(bool use_brush, COLORREF color) {
586 SkASSERT(previous_brush_ == NULL);
587 // We can't use SetDCBrushColor() or DC_BRUSH when drawing to a EMF buffer.
588 // SetDCBrushColor() calls are not recorded at all and DC_BRUSH will use
589 // WHITE_BRUSH instead.
591 if (!use_brush) {
592 // Set the transparency.
593 if (0 == SetBkMode(hdc_, TRANSPARENT)) {
594 SkASSERT(false);
595 return false;
598 // Select the NULL brush.
599 previous_brush_ = SelectObject(GetStockObject(NULL_BRUSH));
600 return previous_brush_ != NULL;
603 // Set the opacity.
604 if (0 == SetBkMode(hdc_, OPAQUE)) {
605 SkASSERT(false);
606 return false;
609 // Create and select the brush.
610 previous_brush_ = SelectObject(CreateSolidBrush(color));
611 return previous_brush_ != NULL;
614 bool VectorPlatformDeviceEmf::CreatePen(bool use_pen,
615 COLORREF color,
616 int stroke_width,
617 float stroke_miter,
618 DWORD pen_style) {
619 SkASSERT(previous_pen_ == NULL);
620 // We can't use SetDCPenColor() or DC_PEN when drawing to a EMF buffer.
621 // SetDCPenColor() calls are not recorded at all and DC_PEN will use BLACK_PEN
622 // instead.
624 // No pen case
625 if (!use_pen) {
626 previous_pen_ = SelectObject(GetStockObject(NULL_PEN));
627 return previous_pen_ != NULL;
630 // Use the stock pen if the stroke width is 0.
631 if (stroke_width == 0) {
632 // Create a pen with the right color.
633 previous_pen_ = SelectObject(::CreatePen(PS_SOLID, 0, color));
634 return previous_pen_ != NULL;
637 // Load a custom pen.
638 LOGBRUSH brush;
639 brush.lbStyle = BS_SOLID;
640 brush.lbColor = color;
641 brush.lbHatch = 0;
642 HPEN pen = ExtCreatePen(pen_style, stroke_width, &brush, 0, NULL);
643 SkASSERT(pen != NULL);
644 previous_pen_ = SelectObject(pen);
645 if (previous_pen_ == NULL)
646 return false;
648 if (!SetMiterLimit(hdc_, stroke_miter, NULL)) {
649 SkASSERT(false);
650 return false;
652 return true;
655 void VectorPlatformDeviceEmf::Cleanup() {
656 if (previous_brush_) {
657 HGDIOBJ result = SelectObject(previous_brush_);
658 previous_brush_ = NULL;
659 if (result) {
660 BOOL res = DeleteObject(result);
661 SkASSERT(res != 0);
664 if (previous_pen_) {
665 HGDIOBJ result = SelectObject(previous_pen_);
666 previous_pen_ = NULL;
667 if (result) {
668 BOOL res = DeleteObject(result);
669 SkASSERT(res != 0);
672 // Remove any loaded path from the context.
673 AbortPath(hdc_);
676 HGDIOBJ VectorPlatformDeviceEmf::SelectObject(HGDIOBJ object) {
677 HGDIOBJ result = ::SelectObject(hdc_, object);
678 SkASSERT(result != HGDI_ERROR);
679 if (result == HGDI_ERROR)
680 return NULL;
681 return result;
684 bool VectorPlatformDeviceEmf::CreateBrush(bool use_brush,
685 const SkPaint& paint) {
686 // Make sure that for transparent color, no brush is used.
687 if (paint.getAlpha() == 0) {
688 use_brush = false;
691 return CreateBrush(use_brush, SkColorToCOLORREF(paint.getColor()));
694 bool VectorPlatformDeviceEmf::CreatePen(bool use_pen, const SkPaint& paint) {
695 // Make sure that for transparent color, no pen is used.
696 if (paint.getAlpha() == 0) {
697 use_pen = false;
700 DWORD pen_style = PS_GEOMETRIC | PS_SOLID;
701 switch (paint.getStrokeJoin()) {
702 case SkPaint::kMiter_Join:
703 // Connects path segments with a sharp join.
704 pen_style |= PS_JOIN_MITER;
705 break;
706 case SkPaint::kRound_Join:
707 // Connects path segments with a round join.
708 pen_style |= PS_JOIN_ROUND;
709 break;
710 case SkPaint::kBevel_Join:
711 // Connects path segments with a flat bevel join.
712 pen_style |= PS_JOIN_BEVEL;
713 break;
714 default:
715 SkASSERT(false);
716 break;
718 switch (paint.getStrokeCap()) {
719 case SkPaint::kButt_Cap:
720 // Begin/end contours with no extension.
721 pen_style |= PS_ENDCAP_FLAT;
722 break;
723 case SkPaint::kRound_Cap:
724 // Begin/end contours with a semi-circle extension.
725 pen_style |= PS_ENDCAP_ROUND;
726 break;
727 case SkPaint::kSquare_Cap:
728 // Begin/end contours with a half square extension.
729 pen_style |= PS_ENDCAP_SQUARE;
730 break;
731 default:
732 SkASSERT(false);
733 break;
736 return CreatePen(use_pen,
737 SkColorToCOLORREF(paint.getColor()),
738 SkScalarRound(paint.getStrokeWidth()),
739 paint.getStrokeMiter(),
740 pen_style);
743 void VectorPlatformDeviceEmf::InternalDrawBitmap(const SkBitmap& bitmap,
744 int x, int y,
745 const SkPaint& paint) {
746 unsigned char alpha = paint.getAlpha();
747 if (alpha == 0)
748 return;
750 bool is_translucent;
751 if (alpha != 255) {
752 // ApplyPaint expect an opaque color.
753 SkPaint tmp_paint(paint);
754 tmp_paint.setAlpha(255);
755 if (!ApplyPaint(tmp_paint))
756 return;
757 is_translucent = true;
758 } else {
759 if (!ApplyPaint(paint))
760 return;
761 is_translucent = false;
763 int src_size_x = bitmap.width();
764 int src_size_y = bitmap.height();
765 if (!src_size_x || !src_size_y)
766 return;
768 // Create a BMP v4 header that we can serialize. We use the shared "V3"
769 // fillter to fill the stardard items, then add in the "V4" stuff we want.
770 BITMAPV4HEADER bitmap_header;
771 memset(&bitmap_header, 0, sizeof(BITMAPV4HEADER));
772 FillBitmapInfoHeader(src_size_x, src_size_y,
773 reinterpret_cast<BITMAPINFOHEADER*>(&bitmap_header));
774 bitmap_header.bV4Size = sizeof(BITMAPV4HEADER);
775 bitmap_header.bV4RedMask = 0x00ff0000;
776 bitmap_header.bV4GreenMask = 0x0000ff00;
777 bitmap_header.bV4BlueMask = 0x000000ff;
778 bitmap_header.bV4AlphaMask = 0xff000000;
780 SkAutoLockPixels lock(bitmap);
781 SkASSERT(bitmap.getConfig() == SkBitmap::kARGB_8888_Config);
782 const uint32_t* pixels = static_cast<const uint32_t*>(bitmap.getPixels());
783 if (pixels == NULL) {
784 SkASSERT(false);
785 return;
788 if (!is_translucent) {
789 int row_length = bitmap.rowBytesAsPixels();
790 // There is no quick way to determine if an image is opaque.
791 for (int y2 = 0; y2 < src_size_y; ++y2) {
792 for (int x2 = 0; x2 < src_size_x; ++x2) {
793 if (SkColorGetA(pixels[(y2 * row_length) + x2]) != 255) {
794 is_translucent = true;
795 y2 = src_size_y;
796 break;
802 HDC dc = BeginPlatformPaint();
803 BITMAPINFOHEADER hdr;
804 FillBitmapInfoHeader(src_size_x, src_size_y, &hdr);
805 if (is_translucent) {
806 // The image must be loaded as a bitmap inside a device context.
807 HDC bitmap_dc = ::CreateCompatibleDC(dc);
808 void* bits = NULL;
809 HBITMAP hbitmap = ::CreateDIBSection(
810 bitmap_dc, reinterpret_cast<const BITMAPINFO*>(&hdr),
811 DIB_RGB_COLORS, &bits, NULL, 0);
813 // static cast to a char so we can do byte ptr arithmatic to
814 // get the offset.
815 unsigned char* dest_buffer = static_cast<unsigned char *>(bits);
817 // We will copy row by row to avoid having to worry about
818 // the row strides being different.
819 const int dest_row_size = hdr.biBitCount / 8 * hdr.biWidth;
820 for (int row = 0; row < bitmap.height(); ++row) {
821 int dest_offset = row * dest_row_size;
822 // pixels_offset in terms of pixel count.
823 int src_offset = row * bitmap.rowBytesAsPixels();
824 memcpy(dest_buffer + dest_offset, pixels + src_offset, dest_row_size);
826 SkASSERT(hbitmap);
827 HGDIOBJ old_bitmap = ::SelectObject(bitmap_dc, hbitmap);
829 // After some analysis of IE7's behavior, this is the thing to do. I was
830 // sure IE7 was doing so kind of bitmasking due to the way translucent image
831 // where renderered but after some windbg tracing, it is being done by the
832 // printer driver after all (mostly HP printers). IE7 always use AlphaBlend
833 // for bitmasked images. The trick seems to switch the stretching mode in
834 // what the driver expects.
835 DWORD previous_mode = GetStretchBltMode(dc);
836 BOOL result = SetStretchBltMode(dc, COLORONCOLOR);
837 SkASSERT(result);
838 // Note that this function expect premultiplied colors (!)
839 BLENDFUNCTION blend_function = {AC_SRC_OVER, 0, alpha, AC_SRC_ALPHA};
840 result = GdiAlphaBlend(dc,
841 x, y, // Destination origin.
842 src_size_x, src_size_y, // Destination size.
843 bitmap_dc,
844 0, 0, // Source origin.
845 src_size_x, src_size_y, // Source size.
846 blend_function);
847 SkASSERT(result);
848 result = SetStretchBltMode(dc, previous_mode);
849 SkASSERT(result);
851 alpha_blend_used_ = true;
853 ::SelectObject(bitmap_dc, static_cast<HBITMAP>(old_bitmap));
854 DeleteObject(hbitmap);
855 DeleteDC(bitmap_dc);
856 } else {
857 int nCopied = StretchDIBits(dc,
858 x, y, // Destination origin.
859 src_size_x, src_size_y,
860 0, 0, // Source origin.
861 src_size_x, src_size_y, // Source size.
862 pixels,
863 reinterpret_cast<const BITMAPINFO*>(&hdr),
864 DIB_RGB_COLORS,
865 SRCCOPY);
867 EndPlatformPaint();
868 Cleanup();
871 } // namespace skia