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 "skia/ext/vector_platform_device_emf_win.h"
9 #include "base/logging.h"
10 #include "base/strings/string16.h"
11 #include "skia/ext/bitmap_platform_device.h"
12 #include "skia/ext/skia_utils_win.h"
13 #include "third_party/skia/include/core/SkFontHost.h"
14 #include "third_party/skia/include/core/SkPathEffect.h"
15 #include "third_party/skia/include/core/SkTemplates.h"
16 #include "third_party/skia/include/core/SkUtils.h"
17 #include "third_party/skia/include/ports/SkTypeface_win.h"
21 #define CHECK_FOR_NODRAW_ANNOTATION(paint) \
22 do { if (paint.isNoDrawAnnotation()) { return; } } while (0)
25 SkBaseDevice* VectorPlatformDeviceEmf::CreateDevice(
26 int width, int height, bool is_opaque, HANDLE shared_section) {
28 // TODO(maruel): http://crbug.com/18382 When restoring a semi-transparent
29 // layer, i.e. merging it, we need to rasterize it because GDI doesn't
30 // support transparency except for AlphaBlend(). Right now, a
31 // BitmapPlatformDevice is created when VectorCanvas think a saveLayers()
32 // call is being done. The way to save a layer would be to create an
33 // EMF-based VectorDevice and have this device registers the drawing. When
34 // playing back the device into a bitmap, do it at the printer's dpi instead
35 // of the layout's dpi (which is much lower).
36 return BitmapPlatformDevice::Create(width, height, is_opaque,
40 // TODO(maruel): http://crbug.com/18383 Look if it would be worth to
41 // increase the resolution by ~10x (any worthy factor) to increase the
42 // rendering precision (think about printing) while using a relatively
43 // low dpi. This happens because we receive float as input but the GDI
44 // functions works with integers. The idea is to premultiply the matrix
45 // with this factor and multiply each SkScalar that are passed to
46 // SkScalarRound(value) as SkScalarRound(value * 10). Safari is already
47 // doing the same for text rendering.
48 SkASSERT(shared_section);
49 SkBaseDevice* device = VectorPlatformDeviceEmf::create(
50 reinterpret_cast<HDC>(shared_section), width, height);
54 static void FillBitmapInfoHeader(int width, int height, BITMAPINFOHEADER* hdr) {
55 hdr->biSize = sizeof(BITMAPINFOHEADER);
57 hdr->biHeight = -height; // Minus means top-down bitmap.
60 hdr->biCompression = BI_RGB; // no compression
62 hdr->biXPelsPerMeter = 1;
63 hdr->biYPelsPerMeter = 1;
65 hdr->biClrImportant = 0;
68 SkBaseDevice* VectorPlatformDeviceEmf::create(HDC dc, int width, int height) {
71 // Link the SkBitmap to the current selected bitmap in the device context.
73 HGDIOBJ selected_bitmap = GetCurrentObject(dc, OBJ_BITMAP);
74 bool succeeded = false;
75 if (selected_bitmap != NULL) {
77 if (GetObject(selected_bitmap, sizeof(BITMAP), &bitmap_data) ==
79 // The context has a bitmap attached. Attach our SkBitmap to it.
80 // Warning: If the bitmap gets unselected from the HDC,
81 // VectorPlatformDeviceEmf has no way to detect this, so the HBITMAP
82 // could be released while SkBitmap still has a reference to it. Be
84 if (width == bitmap_data.bmWidth &&
85 height == bitmap_data.bmHeight) {
86 bitmap.setConfig(SkBitmap::kARGB_8888_Config,
89 bitmap_data.bmWidthBytes);
90 bitmap.setPixels(bitmap_data.bmBits);
97 bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
99 return new VectorPlatformDeviceEmf(dc, bitmap);
102 VectorPlatformDeviceEmf::VectorPlatformDeviceEmf(HDC dc, const SkBitmap& bitmap)
103 : SkBitmapDevice(bitmap),
105 previous_brush_(NULL),
106 previous_pen_(NULL) {
108 SetPlatformDevice(this, this);
111 VectorPlatformDeviceEmf::~VectorPlatformDeviceEmf() {
112 SkASSERT(previous_brush_ == NULL);
113 SkASSERT(previous_pen_ == NULL);
116 HDC VectorPlatformDeviceEmf::BeginPlatformPaint() {
120 void VectorPlatformDeviceEmf::drawPaint(const SkDraw& draw,
121 const SkPaint& paint) {
122 // TODO(maruel): Bypass the current transformation matrix.
126 rect.fRight = SkIntToScalar(width() + 1);
127 rect.fBottom = SkIntToScalar(height() + 1);
128 drawRect(draw, rect, paint);
131 void VectorPlatformDeviceEmf::drawPoints(const SkDraw& draw,
132 SkCanvas::PointMode mode,
135 const SkPaint& paint) {
139 if (mode == SkCanvas::kPoints_PointMode) {
144 SkPaint tmp_paint(paint);
145 tmp_paint.setStyle(SkPaint::kStroke_Style);
147 // Draw a path instead.
150 case SkCanvas::kLines_PointMode:
155 for (size_t i = 0; i < count / 2; ++i) {
156 path.moveTo(pts[2 * i]);
157 path.lineTo(pts[2 * i + 1]);
160 case SkCanvas::kPolygon_PointMode:
162 for (size_t i = 1; i < count; ++i) {
170 // Draw the calculated path.
171 drawPath(draw, path, tmp_paint);
174 void VectorPlatformDeviceEmf::drawRect(const SkDraw& draw,
176 const SkPaint& paint) {
177 CHECK_FOR_NODRAW_ANNOTATION(paint);
178 if (paint.getPathEffect()) {
179 // Draw a path instead.
181 path_orginal.addRect(rect);
183 // Apply the path effect to the rect.
184 SkPath path_modified;
185 paint.getFillPath(path_orginal, &path_modified);
187 // Removes the path effect from the temporary SkPaint object.
188 SkPaint paint_no_effet(paint);
189 paint_no_effet.setPathEffect(NULL);
191 // Draw the calculated path.
192 drawPath(draw, path_modified, paint_no_effet);
196 if (!ApplyPaint(paint)) {
199 HDC dc = BeginPlatformPaint();
200 if (!Rectangle(dc, SkScalarRoundToInt(rect.fLeft),
201 SkScalarRoundToInt(rect.fTop),
202 SkScalarRoundToInt(rect.fRight),
203 SkScalarRoundToInt(rect.fBottom))) {
210 void VectorPlatformDeviceEmf::drawRRect(const SkDraw& draw, const SkRRect& rr,
211 const SkPaint& paint) {
214 this->drawPath(draw, path, paint, NULL, true);
217 void VectorPlatformDeviceEmf::drawPath(const SkDraw& draw,
219 const SkPaint& paint,
220 const SkMatrix* prePathMatrix,
221 bool pathIsMutable) {
222 CHECK_FOR_NODRAW_ANNOTATION(paint);
223 if (paint.getPathEffect()) {
224 // Apply the path effect forehand.
225 SkPath path_modified;
226 paint.getFillPath(path, &path_modified);
228 // Removes the path effect from the temporary SkPaint object.
229 SkPaint paint_no_effet(paint);
230 paint_no_effet.setPathEffect(NULL);
232 // Draw the calculated path.
233 drawPath(draw, path_modified, paint_no_effet);
237 if (!ApplyPaint(paint)) {
240 HDC dc = BeginPlatformPaint();
241 if (PlatformDevice::LoadPathToDC(dc, path)) {
242 switch (paint.getStyle()) {
243 case SkPaint::kFill_Style: {
244 BOOL res = StrokeAndFillPath(dc);
248 case SkPaint::kStroke_Style: {
249 BOOL res = StrokePath(dc);
253 case SkPaint::kStrokeAndFill_Style: {
254 BOOL res = StrokeAndFillPath(dc);
267 void VectorPlatformDeviceEmf::drawBitmapRect(const SkDraw& draw,
268 const SkBitmap& bitmap,
271 const SkPaint& paint,
272 SkCanvas::DrawBitmapRectFlags flags) {
274 SkRect bitmapBounds, tmpSrc, tmpDst;
277 bitmapBounds.isetWH(bitmap.width(), bitmap.height());
279 // Compute matrix from the two rectangles
283 tmpSrc = bitmapBounds;
285 matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
287 const SkBitmap* bitmapPtr = &bitmap;
289 // clip the tmpSrc to the bounds of the bitmap, and recompute dstRect if
290 // needed (if the src was clipped). No check needed if src==null.
292 if (!bitmapBounds.contains(*src)) {
293 if (!tmpSrc.intersect(bitmapBounds)) {
294 return; // nothing to draw
296 // recompute dst, based on the smaller tmpSrc
297 matrix.mapRect(&tmpDst, tmpSrc);
300 // since we may need to clamp to the borders of the src rect within
301 // the bitmap, we extract a subset.
302 // TODO: make sure this is handled in drawrect and remove it from here.
304 tmpSrc.roundOut(&srcIR);
305 if (!bitmap.extractSubset(&tmpBitmap, srcIR)) {
308 bitmapPtr = &tmpBitmap;
310 // Since we did an extract, we need to adjust the matrix accordingly
311 SkScalar dx = 0, dy = 0;
312 if (srcIR.fLeft > 0) {
313 dx = SkIntToScalar(srcIR.fLeft);
315 if (srcIR.fTop > 0) {
316 dy = SkIntToScalar(srcIR.fTop);
319 matrix.preTranslate(dx, dy);
322 this->drawBitmap(draw, *bitmapPtr, matrix, paint);
325 void VectorPlatformDeviceEmf::drawBitmap(const SkDraw& draw,
326 const SkBitmap& bitmap,
327 const SkMatrix& matrix,
328 const SkPaint& paint) {
329 // Load the temporary matrix. This is what will translate, rotate and resize
331 SkMatrix actual_transform(transform_);
332 actual_transform.preConcat(matrix);
333 LoadTransformToDC(hdc_, actual_transform);
335 InternalDrawBitmap(bitmap, 0, 0, paint);
337 // Restore the original matrix.
338 LoadTransformToDC(hdc_, transform_);
341 void VectorPlatformDeviceEmf::drawSprite(const SkDraw& draw,
342 const SkBitmap& bitmap,
344 const SkPaint& paint) {
347 LoadTransformToDC(hdc_, identity);
349 InternalDrawBitmap(bitmap, x, y, paint);
351 // Restore the original matrix.
352 LoadTransformToDC(hdc_, transform_);
355 /////////////////////////////////////////////////////////////////////////
357 static bool gdiCanHandleText(const SkPaint& paint) {
358 return !paint.getShader() &&
359 !paint.getPathEffect() &&
360 (SkPaint::kFill_Style == paint.getStyle()) &&
361 (255 == paint.getAlpha());
364 class SkGDIFontSetup {
372 SkDEBUGCODE(fUseGDIHasBeenCalled = false;)
376 // can only be called once
377 bool useGDI(HDC hdc, const SkPaint&);
383 COLORREF fSavedTextColor;
385 SkDEBUGCODE(bool fUseGDIHasBeenCalled;)
388 bool SkGDIFontSetup::useGDI(HDC hdc, const SkPaint& paint) {
389 SkASSERT(!fUseGDIHasBeenCalled);
390 SkDEBUGCODE(fUseGDIHasBeenCalled = true;)
392 fUseGDI = gdiCanHandleText(paint);
394 fSavedTextColor = GetTextColor(hdc);
395 SetTextColor(hdc, skia::SkColorToCOLORREF(paint.getColor()));
398 SkLOGFONTFromTypeface(paint.getTypeface(), &lf);
399 lf.lfHeight = -SkScalarRoundToInt(paint.getTextSize());
400 fNewFont = CreateFontIndirect(&lf);
401 fSavedFont = (HFONT)::SelectObject(hdc, fNewFont);
407 SkGDIFontSetup::~SkGDIFontSetup() {
409 ::SelectObject(fHDC, fSavedFont);
410 ::DeleteObject(fNewFont);
411 SetTextColor(fHDC, fSavedTextColor);
415 static SkScalar getAscent(const SkPaint& paint) {
416 SkPaint::FontMetrics fm;
417 paint.getFontMetrics(&fm);
421 // return the options int for ExtTextOut. Only valid if the paint's text
422 // encoding is not UTF8 (in which case ExtTextOut can't be used).
423 static UINT getTextOutOptions(const SkPaint& paint) {
424 if (SkPaint::kGlyphID_TextEncoding == paint.getTextEncoding()) {
425 return ETO_GLYPH_INDEX;
427 SkASSERT(SkPaint::kUTF16_TextEncoding == paint.getTextEncoding());
432 static SkiaEnsureTypefaceCharactersAccessible
433 g_skia_ensure_typeface_characters_accessible = NULL;
435 SK_API void SetSkiaEnsureTypefaceCharactersAccessible(
436 SkiaEnsureTypefaceCharactersAccessible func) {
437 // This function is supposed to be called once in process life time.
438 SkASSERT(g_skia_ensure_typeface_characters_accessible == NULL);
439 g_skia_ensure_typeface_characters_accessible = func;
442 void EnsureTypefaceCharactersAccessible(
443 const SkTypeface& typeface, const wchar_t* text, unsigned int text_length) {
445 SkLOGFONTFromTypeface(&typeface, &lf);
446 g_skia_ensure_typeface_characters_accessible(lf, text, text_length);
449 bool EnsureExtTextOut(HDC hdc, int x, int y, UINT options, const RECT * lprect,
450 LPCWSTR text, unsigned int characters, const int * lpDx,
451 SkTypeface* const typeface) {
452 bool success = ExtTextOut(hdc, x, y, options, lprect, text, characters, lpDx);
455 EnsureTypefaceCharactersAccessible(*typeface,
458 success = ExtTextOut(hdc, x, y, options, lprect, text, characters, lpDx);
461 SkLOGFONTFromTypeface(typeface, &lf);
462 VLOG(1) << "SkFontHost::EnsureTypefaceCharactersAccessible FAILED for "
463 << " FaceName = " << lf.lfFaceName
464 << " and characters: " << base::string16(text, characters);
467 VLOG(1) << "ExtTextOut FAILED for default FaceName "
468 << " and characters: " << base::string16(text, characters);
474 void VectorPlatformDeviceEmf::drawText(const SkDraw& draw,
479 const SkPaint& paint) {
480 SkGDIFontSetup setup;
481 bool useDrawPath = true;
483 if (SkPaint::kUTF8_TextEncoding != paint.getTextEncoding()
484 && setup.useGDI(hdc_, paint)) {
485 UINT options = getTextOutOptions(paint);
486 UINT count = byteLength >> 1;
487 useDrawPath = !EnsureExtTextOut(hdc_, SkScalarRoundToInt(x),
488 SkScalarRoundToInt(y + getAscent(paint)), options, 0,
489 reinterpret_cast<const wchar_t*>(text), count, NULL,
490 paint.getTypeface());
495 paint.getTextPath(text, byteLength, x, y, &path);
496 drawPath(draw, path, paint);
500 static size_t size_utf8(const char* text) {
501 return SkUTF8_CountUTF8Bytes(text);
504 static size_t size_utf16(const char* text) {
505 uint16_t c = *reinterpret_cast<const uint16_t*>(text);
506 return SkUTF16_IsHighSurrogate(c) ? 4 : 2;
509 static size_t size_glyphid(const char* text) {
513 void VectorPlatformDeviceEmf::drawPosText(const SkDraw& draw,
516 const SkScalar pos[],
519 const SkPaint& paint) {
520 SkGDIFontSetup setup;
521 bool useDrawText = true;
523 if (2 == scalarsPerPos
524 && SkPaint::kUTF8_TextEncoding != paint.getTextEncoding()
525 && setup.useGDI(hdc_, paint)) {
526 int startX = SkScalarRoundToInt(pos[0]);
527 int startY = SkScalarRoundToInt(pos[1] + getAscent(paint));
528 const int count = len >> 1;
529 SkAutoSTMalloc<64, INT> storage(count);
530 INT* advances = storage.get();
531 for (int i = 0; i < count - 1; ++i) {
532 advances[i] = SkScalarRoundToInt(pos[2] - pos[0]);
535 useDrawText = !EnsureExtTextOut(hdc_, startX, startY,
536 getTextOutOptions(paint), 0, reinterpret_cast<const wchar_t*>(text),
537 count, advances, paint.getTypeface());
541 size_t (*bytesPerCodePoint)(const char*);
542 switch (paint.getTextEncoding()) {
543 case SkPaint::kUTF8_TextEncoding:
544 bytesPerCodePoint = size_utf8;
546 case SkPaint::kUTF16_TextEncoding:
547 bytesPerCodePoint = size_utf16;
550 SkASSERT(SkPaint::kGlyphID_TextEncoding == paint.getTextEncoding());
551 bytesPerCodePoint = size_glyphid;
555 const char* curr = reinterpret_cast<const char*>(text);
556 const char* stop = curr + len;
557 while (curr < stop) {
558 SkScalar y = (1 == scalarsPerPos) ? constY : pos[1];
559 size_t bytes = bytesPerCodePoint(curr);
560 drawText(draw, curr, bytes, pos[0], y, paint);
562 pos += scalarsPerPos;
567 void VectorPlatformDeviceEmf::drawTextOnPath(const SkDraw& draw,
571 const SkMatrix* matrix,
572 const SkPaint& paint) {
573 // This function isn't used in the code. Verify this assumption.
577 void VectorPlatformDeviceEmf::drawVertices(const SkDraw& draw,
578 SkCanvas::VertexMode vmode,
580 const SkPoint vertices[],
581 const SkPoint texs[],
582 const SkColor colors[],
584 const uint16_t indices[],
586 const SkPaint& paint) {
587 // This function isn't used in the code. Verify this assumption.
591 void VectorPlatformDeviceEmf::drawDevice(const SkDraw& draw,
592 SkBaseDevice* device,
595 const SkPaint& paint) {
596 // TODO(maruel): http://b/1183870 Playback the EMF buffer at printer's dpi if
597 // it is a vectorial device.
598 drawSprite(draw, device->accessBitmap(false), x, y, paint);
601 bool VectorPlatformDeviceEmf::ApplyPaint(const SkPaint& paint) {
602 // Note: The goal here is to transfert the SkPaint's state to the HDC's state.
603 // This function does not execute the SkPaint drawing commands. These should
604 // be executed in drawPaint().
606 SkPaint::Style style = paint.getStyle();
607 if (!paint.getAlpha())
608 style = (SkPaint::Style) SkPaint::kStyleCount;
611 case SkPaint::kFill_Style:
612 if (!CreateBrush(true, paint) ||
613 !CreatePen(false, paint))
616 case SkPaint::kStroke_Style:
617 if (!CreateBrush(false, paint) ||
618 !CreatePen(true, paint))
621 case SkPaint::kStrokeAndFill_Style:
622 if (!CreateBrush(true, paint) ||
623 !CreatePen(true, paint))
627 if (!CreateBrush(false, paint) ||
628 !CreatePen(false, paint))
645 // Skia's text is not used. This should be fixed.
654 // BUG 1094907: Implement shaders. Shaders currently in use:
655 // SkShader::CreateBitmapShader
656 // SkGradientShader::CreateRadial
657 // SkGradientShader::CreateLinear
658 // SkASSERT(!paint.getShader());
660 // http://b/1106647 Implement loopers and mask filter. Looper currently in
662 // SkBlurDrawLooper is used for shadows.
663 // SkASSERT(!paint.getLooper());
664 // SkASSERT(!paint.getMaskFilter());
666 // http://b/1165900 Implement xfermode.
667 // SkASSERT(!paint.getXfermode());
669 // The path effect should be processed before arriving here.
670 SkASSERT(!paint.getPathEffect());
672 // This isn't used in the code. Verify this assumption.
673 SkASSERT(!paint.getRasterizer());
674 // Reuse code to load Win32 Fonts.
678 void VectorPlatformDeviceEmf::setMatrixClip(const SkMatrix& transform,
679 const SkRegion& region,
680 const SkClipStack&) {
681 transform_ = transform;
682 LoadTransformToDC(hdc_, transform_);
683 clip_region_ = region;
684 if (!clip_region_.isEmpty())
688 void VectorPlatformDeviceEmf::DrawToNativeContext(HDC dc, int x, int y,
689 const RECT* src_rect) {
693 void VectorPlatformDeviceEmf::LoadClipRegion() {
696 LoadClippingRegionToDC(hdc_, clip_region_, t);
699 #ifdef SK_SUPPORT_LEGACY_COMPATIBLEDEVICE_CONFIG
700 SkBaseDevice* VectorPlatformDeviceEmf::onCreateCompatibleDevice(
701 SkBitmap::Config config, int width, int height, bool isOpaque,
703 SkASSERT(config == SkBitmap::kARGB_8888_Config);
704 return VectorPlatformDeviceEmf::CreateDevice(width, height, isOpaque, NULL);
708 SkBaseDevice* VectorPlatformDeviceEmf::onCreateDevice(const SkImageInfo& info,
710 SkASSERT(info.colorType() == kPMColor_SkColorType);
711 return VectorPlatformDeviceEmf::CreateDevice(
712 info.width(), info.height(), info.isOpaque(), NULL);
715 bool VectorPlatformDeviceEmf::CreateBrush(bool use_brush, COLORREF color) {
716 SkASSERT(previous_brush_ == NULL);
717 // We can't use SetDCBrushColor() or DC_BRUSH when drawing to a EMF buffer.
718 // SetDCBrushColor() calls are not recorded at all and DC_BRUSH will use
719 // WHITE_BRUSH instead.
722 // Set the transparency.
723 if (0 == SetBkMode(hdc_, TRANSPARENT)) {
728 // Select the NULL brush.
729 previous_brush_ = SelectObject(GetStockObject(NULL_BRUSH));
730 return previous_brush_ != NULL;
734 if (0 == SetBkMode(hdc_, OPAQUE)) {
739 // Create and select the brush.
740 previous_brush_ = SelectObject(CreateSolidBrush(color));
741 return previous_brush_ != NULL;
744 bool VectorPlatformDeviceEmf::CreatePen(bool use_pen,
749 SkASSERT(previous_pen_ == NULL);
750 // We can't use SetDCPenColor() or DC_PEN when drawing to a EMF buffer.
751 // SetDCPenColor() calls are not recorded at all and DC_PEN will use BLACK_PEN
756 previous_pen_ = SelectObject(GetStockObject(NULL_PEN));
757 return previous_pen_ != NULL;
760 // Use the stock pen if the stroke width is 0.
761 if (stroke_width == 0) {
762 // Create a pen with the right color.
763 previous_pen_ = SelectObject(::CreatePen(PS_SOLID, 0, color));
764 return previous_pen_ != NULL;
767 // Load a custom pen.
769 brush.lbStyle = BS_SOLID;
770 brush.lbColor = color;
772 HPEN pen = ExtCreatePen(pen_style, stroke_width, &brush, 0, NULL);
773 SkASSERT(pen != NULL);
774 previous_pen_ = SelectObject(pen);
775 if (previous_pen_ == NULL)
778 if (!SetMiterLimit(hdc_, stroke_miter, NULL)) {
785 void VectorPlatformDeviceEmf::Cleanup() {
786 if (previous_brush_) {
787 HGDIOBJ result = SelectObject(previous_brush_);
788 previous_brush_ = NULL;
790 BOOL res = DeleteObject(result);
795 HGDIOBJ result = SelectObject(previous_pen_);
796 previous_pen_ = NULL;
798 BOOL res = DeleteObject(result);
802 // Remove any loaded path from the context.
806 HGDIOBJ VectorPlatformDeviceEmf::SelectObject(HGDIOBJ object) {
807 HGDIOBJ result = ::SelectObject(hdc_, object);
808 SkASSERT(result != HGDI_ERROR);
809 if (result == HGDI_ERROR)
814 bool VectorPlatformDeviceEmf::CreateBrush(bool use_brush,
815 const SkPaint& paint) {
816 // Make sure that for transparent color, no brush is used.
817 if (paint.getAlpha() == 0) {
821 return CreateBrush(use_brush, SkColorToCOLORREF(paint.getColor()));
824 bool VectorPlatformDeviceEmf::CreatePen(bool use_pen, const SkPaint& paint) {
825 // Make sure that for transparent color, no pen is used.
826 if (paint.getAlpha() == 0) {
830 DWORD pen_style = PS_GEOMETRIC | PS_SOLID;
831 switch (paint.getStrokeJoin()) {
832 case SkPaint::kMiter_Join:
833 // Connects path segments with a sharp join.
834 pen_style |= PS_JOIN_MITER;
836 case SkPaint::kRound_Join:
837 // Connects path segments with a round join.
838 pen_style |= PS_JOIN_ROUND;
840 case SkPaint::kBevel_Join:
841 // Connects path segments with a flat bevel join.
842 pen_style |= PS_JOIN_BEVEL;
848 switch (paint.getStrokeCap()) {
849 case SkPaint::kButt_Cap:
850 // Begin/end contours with no extension.
851 pen_style |= PS_ENDCAP_FLAT;
853 case SkPaint::kRound_Cap:
854 // Begin/end contours with a semi-circle extension.
855 pen_style |= PS_ENDCAP_ROUND;
857 case SkPaint::kSquare_Cap:
858 // Begin/end contours with a half square extension.
859 pen_style |= PS_ENDCAP_SQUARE;
866 return CreatePen(use_pen,
867 SkColorToCOLORREF(paint.getColor()),
868 SkScalarRoundToInt(paint.getStrokeWidth()),
869 paint.getStrokeMiter(),
873 void VectorPlatformDeviceEmf::InternalDrawBitmap(const SkBitmap& bitmap,
875 const SkPaint& paint) {
876 unsigned char alpha = paint.getAlpha();
882 // ApplyPaint expect an opaque color.
883 SkPaint tmp_paint(paint);
884 tmp_paint.setAlpha(255);
885 if (!ApplyPaint(tmp_paint))
887 is_translucent = true;
889 if (!ApplyPaint(paint))
891 is_translucent = false;
893 int src_size_x = bitmap.width();
894 int src_size_y = bitmap.height();
895 if (!src_size_x || !src_size_y)
898 // Create a BMP v4 header that we can serialize. We use the shared "V3"
899 // fillter to fill the stardard items, then add in the "V4" stuff we want.
900 BITMAPV4HEADER bitmap_header;
901 memset(&bitmap_header, 0, sizeof(BITMAPV4HEADER));
902 FillBitmapInfoHeader(src_size_x, src_size_y,
903 reinterpret_cast<BITMAPINFOHEADER*>(&bitmap_header));
904 bitmap_header.bV4Size = sizeof(BITMAPV4HEADER);
905 bitmap_header.bV4RedMask = 0x00ff0000;
906 bitmap_header.bV4GreenMask = 0x0000ff00;
907 bitmap_header.bV4BlueMask = 0x000000ff;
908 bitmap_header.bV4AlphaMask = 0xff000000;
910 SkAutoLockPixels lock(bitmap);
911 SkASSERT(bitmap.config() == SkBitmap::kARGB_8888_Config);
912 const uint32_t* pixels = static_cast<const uint32_t*>(bitmap.getPixels());
913 if (pixels == NULL) {
918 if (!is_translucent) {
919 int row_length = bitmap.rowBytesAsPixels();
920 // There is no quick way to determine if an image is opaque.
921 for (int y2 = 0; y2 < src_size_y; ++y2) {
922 for (int x2 = 0; x2 < src_size_x; ++x2) {
923 if (SkColorGetA(pixels[(y2 * row_length) + x2]) != 255) {
924 is_translucent = true;
932 HDC dc = BeginPlatformPaint();
933 BITMAPINFOHEADER hdr;
934 FillBitmapInfoHeader(src_size_x, src_size_y, &hdr);
935 if (is_translucent) {
936 // The image must be loaded as a bitmap inside a device context.
937 HDC bitmap_dc = ::CreateCompatibleDC(dc);
939 HBITMAP hbitmap = ::CreateDIBSection(
940 bitmap_dc, reinterpret_cast<const BITMAPINFO*>(&hdr),
941 DIB_RGB_COLORS, &bits, NULL, 0);
943 // static cast to a char so we can do byte ptr arithmatic to
945 unsigned char* dest_buffer = static_cast<unsigned char *>(bits);
947 // We will copy row by row to avoid having to worry about
948 // the row strides being different.
949 const int dest_row_size = hdr.biBitCount / 8 * hdr.biWidth;
950 for (int row = 0; row < bitmap.height(); ++row) {
951 int dest_offset = row * dest_row_size;
952 // pixels_offset in terms of pixel count.
953 int src_offset = row * bitmap.rowBytesAsPixels();
954 memcpy(dest_buffer + dest_offset, pixels + src_offset, dest_row_size);
957 HGDIOBJ old_bitmap = ::SelectObject(bitmap_dc, hbitmap);
959 // After some analysis of IE7's behavior, this is the thing to do. I was
960 // sure IE7 was doing so kind of bitmasking due to the way translucent image
961 // where renderered but after some windbg tracing, it is being done by the
962 // printer driver after all (mostly HP printers). IE7 always use AlphaBlend
963 // for bitmasked images. The trick seems to switch the stretching mode in
964 // what the driver expects.
965 DWORD previous_mode = GetStretchBltMode(dc);
966 BOOL result = SetStretchBltMode(dc, COLORONCOLOR);
968 // Note that this function expect premultiplied colors (!)
969 BLENDFUNCTION blend_function = {AC_SRC_OVER, 0, alpha, AC_SRC_ALPHA};
970 result = GdiAlphaBlend(dc,
971 x, y, // Destination origin.
972 src_size_x, src_size_y, // Destination size.
974 0, 0, // Source origin.
975 src_size_x, src_size_y, // Source size.
978 result = SetStretchBltMode(dc, previous_mode);
981 ::SelectObject(bitmap_dc, static_cast<HBITMAP>(old_bitmap));
982 DeleteObject(hbitmap);
985 int nCopied = StretchDIBits(dc,
986 x, y, // Destination origin.
987 src_size_x, src_size_y,
988 0, 0, // Source origin.
989 src_size_x, src_size_y, // Source size.
991 reinterpret_cast<const BITMAPINFO*>(&hdr),