vector_platform_device_emf_win.cc revision f2477e01787aa58f445919b809d89e252beef54f
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 "skia/ext/vector_platform_device_emf_win.h" 6 7#include <windows.h> 8 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" 18 19namespace skia { 20 21#define CHECK_FOR_NODRAW_ANNOTATION(paint) \ 22 do { if (paint.isNoDrawAnnotation()) { return; } } while (0) 23 24// static 25SkBaseDevice* VectorPlatformDeviceEmf::CreateDevice( 26 int width, int height, bool is_opaque, HANDLE shared_section) { 27 if (!is_opaque) { 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, 37 shared_section); 38 } 39 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); 51 return device; 52} 53 54static void FillBitmapInfoHeader(int width, int height, BITMAPINFOHEADER* hdr) { 55 hdr->biSize = sizeof(BITMAPINFOHEADER); 56 hdr->biWidth = width; 57 hdr->biHeight = -height; // Minus means top-down bitmap. 58 hdr->biPlanes = 1; 59 hdr->biBitCount = 32; 60 hdr->biCompression = BI_RGB; // no compression 61 hdr->biSizeImage = 0; 62 hdr->biXPelsPerMeter = 1; 63 hdr->biYPelsPerMeter = 1; 64 hdr->biClrUsed = 0; 65 hdr->biClrImportant = 0; 66} 67 68SkBaseDevice* VectorPlatformDeviceEmf::create(HDC dc, int width, int height) { 69 InitializeDC(dc); 70 71 // Link the SkBitmap to the current selected bitmap in the device context. 72 SkBitmap bitmap; 73 HGDIOBJ selected_bitmap = GetCurrentObject(dc, OBJ_BITMAP); 74 bool succeeded = false; 75 if (selected_bitmap != NULL) { 76 BITMAP bitmap_data; 77 if (GetObject(selected_bitmap, sizeof(BITMAP), &bitmap_data) == 78 sizeof(BITMAP)) { 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 83 // cautious. 84 if (width == bitmap_data.bmWidth && 85 height == bitmap_data.bmHeight) { 86 bitmap.setConfig(SkBitmap::kARGB_8888_Config, 87 bitmap_data.bmWidth, 88 bitmap_data.bmHeight, 89 bitmap_data.bmWidthBytes); 90 bitmap.setPixels(bitmap_data.bmBits); 91 succeeded = true; 92 } 93 } 94 } 95 96 if (!succeeded) 97 bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height); 98 99 return new VectorPlatformDeviceEmf(dc, bitmap); 100} 101 102VectorPlatformDeviceEmf::VectorPlatformDeviceEmf(HDC dc, const SkBitmap& bitmap) 103 : SkBitmapDevice(bitmap), 104 hdc_(dc), 105 previous_brush_(NULL), 106 previous_pen_(NULL) { 107 transform_.reset(); 108 SetPlatformDevice(this, this); 109} 110 111VectorPlatformDeviceEmf::~VectorPlatformDeviceEmf() { 112 SkASSERT(previous_brush_ == NULL); 113 SkASSERT(previous_pen_ == NULL); 114} 115 116HDC VectorPlatformDeviceEmf::BeginPlatformPaint() { 117 return hdc_; 118} 119 120uint32_t VectorPlatformDeviceEmf::getDeviceCapabilities() { 121 return SkBitmapDevice::getDeviceCapabilities() | kVector_Capability; 122} 123 124void VectorPlatformDeviceEmf::drawPaint(const SkDraw& draw, 125 const SkPaint& paint) { 126 // TODO(maruel): Bypass the current transformation matrix. 127 SkRect rect; 128 rect.fLeft = 0; 129 rect.fTop = 0; 130 rect.fRight = SkIntToScalar(width() + 1); 131 rect.fBottom = SkIntToScalar(height() + 1); 132 drawRect(draw, rect, paint); 133} 134 135void VectorPlatformDeviceEmf::drawPoints(const SkDraw& draw, 136 SkCanvas::PointMode mode, 137 size_t count, 138 const SkPoint pts[], 139 const SkPaint& paint) { 140 if (!count) 141 return; 142 143 if (mode == SkCanvas::kPoints_PointMode) { 144 SkASSERT(false); 145 return; 146 } 147 148 SkPaint tmp_paint(paint); 149 tmp_paint.setStyle(SkPaint::kStroke_Style); 150 151 // Draw a path instead. 152 SkPath path; 153 switch (mode) { 154 case SkCanvas::kLines_PointMode: 155 if (count % 2) { 156 SkASSERT(false); 157 return; 158 } 159 for (size_t i = 0; i < count / 2; ++i) { 160 path.moveTo(pts[2 * i]); 161 path.lineTo(pts[2 * i + 1]); 162 } 163 break; 164 case SkCanvas::kPolygon_PointMode: 165 path.moveTo(pts[0]); 166 for (size_t i = 1; i < count; ++i) { 167 path.lineTo(pts[i]); 168 } 169 break; 170 default: 171 SkASSERT(false); 172 return; 173 } 174 // Draw the calculated path. 175 drawPath(draw, path, tmp_paint); 176} 177 178void VectorPlatformDeviceEmf::drawRect(const SkDraw& draw, 179 const SkRect& rect, 180 const SkPaint& paint) { 181 CHECK_FOR_NODRAW_ANNOTATION(paint); 182 if (paint.getPathEffect()) { 183 // Draw a path instead. 184 SkPath path_orginal; 185 path_orginal.addRect(rect); 186 187 // Apply the path effect to the rect. 188 SkPath path_modified; 189 paint.getFillPath(path_orginal, &path_modified); 190 191 // Removes the path effect from the temporary SkPaint object. 192 SkPaint paint_no_effet(paint); 193 paint_no_effet.setPathEffect(NULL); 194 195 // Draw the calculated path. 196 drawPath(draw, path_modified, paint_no_effet); 197 return; 198 } 199 200 if (!ApplyPaint(paint)) { 201 return; 202 } 203 HDC dc = BeginPlatformPaint(); 204 if (!Rectangle(dc, SkScalarRound(rect.fLeft), 205 SkScalarRound(rect.fTop), 206 SkScalarRound(rect.fRight), 207 SkScalarRound(rect.fBottom))) { 208 SkASSERT(false); 209 } 210 EndPlatformPaint(); 211 Cleanup(); 212} 213 214void VectorPlatformDeviceEmf::drawRRect(const SkDraw& draw, const SkRRect& rr, 215 const SkPaint& paint) { 216 SkPath path; 217 path.addRRect(rr); 218 this->drawPath(draw, path, paint, NULL, true); 219} 220 221void VectorPlatformDeviceEmf::drawPath(const SkDraw& draw, 222 const SkPath& path, 223 const SkPaint& paint, 224 const SkMatrix* prePathMatrix, 225 bool pathIsMutable) { 226 CHECK_FOR_NODRAW_ANNOTATION(paint); 227 if (paint.getPathEffect()) { 228 // Apply the path effect forehand. 229 SkPath path_modified; 230 paint.getFillPath(path, &path_modified); 231 232 // Removes the path effect from the temporary SkPaint object. 233 SkPaint paint_no_effet(paint); 234 paint_no_effet.setPathEffect(NULL); 235 236 // Draw the calculated path. 237 drawPath(draw, path_modified, paint_no_effet); 238 return; 239 } 240 241 if (!ApplyPaint(paint)) { 242 return; 243 } 244 HDC dc = BeginPlatformPaint(); 245 if (PlatformDevice::LoadPathToDC(dc, path)) { 246 switch (paint.getStyle()) { 247 case SkPaint::kFill_Style: { 248 BOOL res = StrokeAndFillPath(dc); 249 SkASSERT(res != 0); 250 break; 251 } 252 case SkPaint::kStroke_Style: { 253 BOOL res = StrokePath(dc); 254 SkASSERT(res != 0); 255 break; 256 } 257 case SkPaint::kStrokeAndFill_Style: { 258 BOOL res = StrokeAndFillPath(dc); 259 SkASSERT(res != 0); 260 break; 261 } 262 default: 263 SkASSERT(false); 264 break; 265 } 266 } 267 EndPlatformPaint(); 268 Cleanup(); 269} 270 271void VectorPlatformDeviceEmf::drawBitmapRect(const SkDraw& draw, 272 const SkBitmap& bitmap, 273 const SkRect* src, 274 const SkRect& dst, 275 const SkPaint& paint, 276 SkCanvas::DrawBitmapRectFlags flags) { 277 SkMatrix matrix; 278 SkRect bitmapBounds, tmpSrc, tmpDst; 279 SkBitmap tmpBitmap; 280 281 bitmapBounds.isetWH(bitmap.width(), bitmap.height()); 282 283 // Compute matrix from the two rectangles 284 if (src) { 285 tmpSrc = *src; 286 } else { 287 tmpSrc = bitmapBounds; 288 } 289 matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit); 290 291 const SkBitmap* bitmapPtr = &bitmap; 292 293 // clip the tmpSrc to the bounds of the bitmap, and recompute dstRect if 294 // needed (if the src was clipped). No check needed if src==null. 295 if (src) { 296 if (!bitmapBounds.contains(*src)) { 297 if (!tmpSrc.intersect(bitmapBounds)) { 298 return; // nothing to draw 299 } 300 // recompute dst, based on the smaller tmpSrc 301 matrix.mapRect(&tmpDst, tmpSrc); 302 } 303 304 // since we may need to clamp to the borders of the src rect within 305 // the bitmap, we extract a subset. 306 // TODO: make sure this is handled in drawrect and remove it from here. 307 SkIRect srcIR; 308 tmpSrc.roundOut(&srcIR); 309 if (!bitmap.extractSubset(&tmpBitmap, srcIR)) { 310 return; 311 } 312 bitmapPtr = &tmpBitmap; 313 314 // Since we did an extract, we need to adjust the matrix accordingly 315 SkScalar dx = 0, dy = 0; 316 if (srcIR.fLeft > 0) { 317 dx = SkIntToScalar(srcIR.fLeft); 318 } 319 if (srcIR.fTop > 0) { 320 dy = SkIntToScalar(srcIR.fTop); 321 } 322 if (dx || dy) { 323 matrix.preTranslate(dx, dy); 324 } 325 } 326 this->drawBitmap(draw, *bitmapPtr, matrix, paint); 327} 328 329void VectorPlatformDeviceEmf::drawBitmap(const SkDraw& draw, 330 const SkBitmap& bitmap, 331 const SkMatrix& matrix, 332 const SkPaint& paint) { 333 // Load the temporary matrix. This is what will translate, rotate and resize 334 // the bitmap. 335 SkMatrix actual_transform(transform_); 336 actual_transform.preConcat(matrix); 337 LoadTransformToDC(hdc_, actual_transform); 338 339 InternalDrawBitmap(bitmap, 0, 0, paint); 340 341 // Restore the original matrix. 342 LoadTransformToDC(hdc_, transform_); 343} 344 345void VectorPlatformDeviceEmf::drawSprite(const SkDraw& draw, 346 const SkBitmap& bitmap, 347 int x, int y, 348 const SkPaint& paint) { 349 SkMatrix identity; 350 identity.reset(); 351 LoadTransformToDC(hdc_, identity); 352 353 InternalDrawBitmap(bitmap, x, y, paint); 354 355 // Restore the original matrix. 356 LoadTransformToDC(hdc_, transform_); 357} 358 359///////////////////////////////////////////////////////////////////////// 360 361static bool gdiCanHandleText(const SkPaint& paint) { 362 return !paint.getShader() && 363 !paint.getPathEffect() && 364 (SkPaint::kFill_Style == paint.getStyle()) && 365 (255 == paint.getAlpha()); 366} 367 368class SkGDIFontSetup { 369 public: 370 SkGDIFontSetup() : 371 fHDC(NULL), 372 fNewFont(NULL), 373 fSavedFont(NULL), 374 fSavedTextColor(0), 375 fUseGDI(false) { 376 SkDEBUGCODE(fUseGDIHasBeenCalled = false;) 377 } 378 ~SkGDIFontSetup(); 379 380 // can only be called once 381 bool useGDI(HDC hdc, const SkPaint&); 382 383 private: 384 HDC fHDC; 385 HFONT fNewFont; 386 HFONT fSavedFont; 387 COLORREF fSavedTextColor; 388 bool fUseGDI; 389 SkDEBUGCODE(bool fUseGDIHasBeenCalled;) 390}; 391 392bool SkGDIFontSetup::useGDI(HDC hdc, const SkPaint& paint) { 393 SkASSERT(!fUseGDIHasBeenCalled); 394 SkDEBUGCODE(fUseGDIHasBeenCalled = true;) 395 396 fUseGDI = gdiCanHandleText(paint); 397 if (fUseGDI) { 398 fSavedTextColor = GetTextColor(hdc); 399 SetTextColor(hdc, skia::SkColorToCOLORREF(paint.getColor())); 400 401 LOGFONT lf; 402 SkLOGFONTFromTypeface(paint.getTypeface(), &lf); 403 lf.lfHeight = -SkScalarRound(paint.getTextSize()); 404 fNewFont = CreateFontIndirect(&lf); 405 fSavedFont = (HFONT)::SelectObject(hdc, fNewFont); 406 fHDC = hdc; 407 } 408 return fUseGDI; 409} 410 411SkGDIFontSetup::~SkGDIFontSetup() { 412 if (fUseGDI) { 413 ::SelectObject(fHDC, fSavedFont); 414 ::DeleteObject(fNewFont); 415 SetTextColor(fHDC, fSavedTextColor); 416 } 417} 418 419static SkScalar getAscent(const SkPaint& paint) { 420 SkPaint::FontMetrics fm; 421 paint.getFontMetrics(&fm); 422 return fm.fAscent; 423} 424 425// return the options int for ExtTextOut. Only valid if the paint's text 426// encoding is not UTF8 (in which case ExtTextOut can't be used). 427static UINT getTextOutOptions(const SkPaint& paint) { 428 if (SkPaint::kGlyphID_TextEncoding == paint.getTextEncoding()) { 429 return ETO_GLYPH_INDEX; 430 } else { 431 SkASSERT(SkPaint::kUTF16_TextEncoding == paint.getTextEncoding()); 432 return 0; 433 } 434} 435 436static SkiaEnsureTypefaceCharactersAccessible 437 g_skia_ensure_typeface_characters_accessible = NULL; 438 439SK_API void SetSkiaEnsureTypefaceCharactersAccessible( 440 SkiaEnsureTypefaceCharactersAccessible func) { 441 // This function is supposed to be called once in process life time. 442 SkASSERT(g_skia_ensure_typeface_characters_accessible == NULL); 443 g_skia_ensure_typeface_characters_accessible = func; 444} 445 446void EnsureTypefaceCharactersAccessible( 447 const SkTypeface& typeface, const wchar_t* text, unsigned int text_length) { 448 LOGFONT lf; 449 SkLOGFONTFromTypeface(&typeface, &lf); 450 g_skia_ensure_typeface_characters_accessible(lf, text, text_length); 451} 452 453bool EnsureExtTextOut(HDC hdc, int x, int y, UINT options, const RECT * lprect, 454 LPCWSTR text, unsigned int characters, const int * lpDx, 455 SkTypeface* const typeface) { 456 bool success = ExtTextOut(hdc, x, y, options, lprect, text, characters, lpDx); 457 if (!success) { 458 if (typeface) { 459 EnsureTypefaceCharactersAccessible(*typeface, 460 text, 461 characters); 462 success = ExtTextOut(hdc, x, y, options, lprect, text, characters, lpDx); 463 if (!success) { 464 LOGFONT lf; 465 SkLOGFONTFromTypeface(typeface, &lf); 466 VLOG(1) << "SkFontHost::EnsureTypefaceCharactersAccessible FAILED for " 467 << " FaceName = " << lf.lfFaceName 468 << " and characters: " << string16(text, characters); 469 } 470 } else { 471 VLOG(1) << "ExtTextOut FAILED for default FaceName " 472 << " and characters: " << string16(text, characters); 473 } 474 } 475 return success; 476} 477 478void VectorPlatformDeviceEmf::drawText(const SkDraw& draw, 479 const void* text, 480 size_t byteLength, 481 SkScalar x, 482 SkScalar y, 483 const SkPaint& paint) { 484 SkGDIFontSetup setup; 485 bool useDrawPath = true; 486 487 if (SkPaint::kUTF8_TextEncoding != paint.getTextEncoding() 488 && setup.useGDI(hdc_, paint)) { 489 UINT options = getTextOutOptions(paint); 490 UINT count = byteLength >> 1; 491 useDrawPath = !EnsureExtTextOut(hdc_, SkScalarRound(x), 492 SkScalarRound(y + getAscent(paint)), options, 0, 493 reinterpret_cast<const wchar_t*>(text), count, NULL, 494 paint.getTypeface()); 495 } 496 497 if (useDrawPath) { 498 SkPath path; 499 paint.getTextPath(text, byteLength, x, y, &path); 500 drawPath(draw, path, paint); 501 } 502} 503 504static size_t size_utf8(const char* text) { 505 return SkUTF8_CountUTF8Bytes(text); 506} 507 508static size_t size_utf16(const char* text) { 509 uint16_t c = *reinterpret_cast<const uint16_t*>(text); 510 return SkUTF16_IsHighSurrogate(c) ? 4 : 2; 511} 512 513static size_t size_glyphid(const char* text) { 514 return 2; 515} 516 517void VectorPlatformDeviceEmf::drawPosText(const SkDraw& draw, 518 const void* text, 519 size_t len, 520 const SkScalar pos[], 521 SkScalar constY, 522 int scalarsPerPos, 523 const SkPaint& paint) { 524 SkGDIFontSetup setup; 525 bool useDrawText = true; 526 527 if (2 == scalarsPerPos 528 && SkPaint::kUTF8_TextEncoding != paint.getTextEncoding() 529 && setup.useGDI(hdc_, paint)) { 530 int startX = SkScalarRound(pos[0]); 531 int startY = SkScalarRound(pos[1] + getAscent(paint)); 532 const int count = len >> 1; 533 SkAutoSTMalloc<64, INT> storage(count); 534 INT* advances = storage.get(); 535 for (int i = 0; i < count - 1; ++i) { 536 advances[i] = SkScalarRound(pos[2] - pos[0]); 537 pos += 2; 538 } 539 useDrawText = !EnsureExtTextOut(hdc_, startX, startY, 540 getTextOutOptions(paint), 0, reinterpret_cast<const wchar_t*>(text), 541 count, advances, paint.getTypeface()); 542 } 543 544 if (useDrawText) { 545 size_t (*bytesPerCodePoint)(const char*); 546 switch (paint.getTextEncoding()) { 547 case SkPaint::kUTF8_TextEncoding: 548 bytesPerCodePoint = size_utf8; 549 break; 550 case SkPaint::kUTF16_TextEncoding: 551 bytesPerCodePoint = size_utf16; 552 break; 553 default: 554 SkASSERT(SkPaint::kGlyphID_TextEncoding == paint.getTextEncoding()); 555 bytesPerCodePoint = size_glyphid; 556 break; 557 } 558 559 const char* curr = reinterpret_cast<const char*>(text); 560 const char* stop = curr + len; 561 while (curr < stop) { 562 SkScalar y = (1 == scalarsPerPos) ? constY : pos[1]; 563 size_t bytes = bytesPerCodePoint(curr); 564 drawText(draw, curr, bytes, pos[0], y, paint); 565 curr += bytes; 566 pos += scalarsPerPos; 567 } 568 } 569} 570 571void VectorPlatformDeviceEmf::drawTextOnPath(const SkDraw& draw, 572 const void* text, 573 size_t len, 574 const SkPath& path, 575 const SkMatrix* matrix, 576 const SkPaint& paint) { 577 // This function isn't used in the code. Verify this assumption. 578 SkASSERT(false); 579} 580 581void VectorPlatformDeviceEmf::drawVertices(const SkDraw& draw, 582 SkCanvas::VertexMode vmode, 583 int vertexCount, 584 const SkPoint vertices[], 585 const SkPoint texs[], 586 const SkColor colors[], 587 SkXfermode* xmode, 588 const uint16_t indices[], 589 int indexCount, 590 const SkPaint& paint) { 591 // This function isn't used in the code. Verify this assumption. 592 SkASSERT(false); 593} 594 595void VectorPlatformDeviceEmf::drawDevice(const SkDraw& draw, 596 SkBaseDevice* device, 597 int x, 598 int y, 599 const SkPaint& paint) { 600 // TODO(maruel): http://b/1183870 Playback the EMF buffer at printer's dpi if 601 // it is a vectorial device. 602 drawSprite(draw, device->accessBitmap(false), x, y, paint); 603} 604 605bool VectorPlatformDeviceEmf::ApplyPaint(const SkPaint& paint) { 606 // Note: The goal here is to transfert the SkPaint's state to the HDC's state. 607 // This function does not execute the SkPaint drawing commands. These should 608 // be executed in drawPaint(). 609 610 SkPaint::Style style = paint.getStyle(); 611 if (!paint.getAlpha()) 612 style = (SkPaint::Style) SkPaint::kStyleCount; 613 614 switch (style) { 615 case SkPaint::kFill_Style: 616 if (!CreateBrush(true, paint) || 617 !CreatePen(false, paint)) 618 return false; 619 break; 620 case SkPaint::kStroke_Style: 621 if (!CreateBrush(false, paint) || 622 !CreatePen(true, paint)) 623 return false; 624 break; 625 case SkPaint::kStrokeAndFill_Style: 626 if (!CreateBrush(true, paint) || 627 !CreatePen(true, paint)) 628 return false; 629 break; 630 default: 631 if (!CreateBrush(false, paint) || 632 !CreatePen(false, paint)) 633 return false; 634 break; 635 } 636 637 /* 638 getFlags(); 639 isAntiAlias(); 640 isDither() 641 isLinearText() 642 isSubpixelText() 643 isUnderlineText() 644 isStrikeThruText() 645 isFakeBoldText() 646 isDevKernText() 647 isFilterBitmap() 648 649 // Skia's text is not used. This should be fixed. 650 getTextAlign() 651 getTextScaleX() 652 getTextSkewX() 653 getTextEncoding() 654 getFontMetrics() 655 getFontSpacing() 656 */ 657 658 // BUG 1094907: Implement shaders. Shaders currently in use: 659 // SkShader::CreateBitmapShader 660 // SkGradientShader::CreateRadial 661 // SkGradientShader::CreateLinear 662 // SkASSERT(!paint.getShader()); 663 664 // http://b/1106647 Implement loopers and mask filter. Looper currently in 665 // use: 666 // SkBlurDrawLooper is used for shadows. 667 // SkASSERT(!paint.getLooper()); 668 // SkASSERT(!paint.getMaskFilter()); 669 670 // http://b/1165900 Implement xfermode. 671 // SkASSERT(!paint.getXfermode()); 672 673 // The path effect should be processed before arriving here. 674 SkASSERT(!paint.getPathEffect()); 675 676 // This isn't used in the code. Verify this assumption. 677 SkASSERT(!paint.getRasterizer()); 678 // Reuse code to load Win32 Fonts. 679 return true; 680} 681 682void VectorPlatformDeviceEmf::setMatrixClip(const SkMatrix& transform, 683 const SkRegion& region, 684 const SkClipStack&) { 685 transform_ = transform; 686 LoadTransformToDC(hdc_, transform_); 687 clip_region_ = region; 688 if (!clip_region_.isEmpty()) 689 LoadClipRegion(); 690} 691 692void VectorPlatformDeviceEmf::DrawToNativeContext(HDC dc, int x, int y, 693 const RECT* src_rect) { 694 SkASSERT(false); 695} 696 697void VectorPlatformDeviceEmf::LoadClipRegion() { 698 SkMatrix t; 699 t.reset(); 700 LoadClippingRegionToDC(hdc_, clip_region_, t); 701} 702 703SkBaseDevice* VectorPlatformDeviceEmf::onCreateCompatibleDevice( 704 SkBitmap::Config config, int width, int height, bool isOpaque, 705 Usage /*usage*/) { 706 SkASSERT(config == SkBitmap::kARGB_8888_Config); 707 return VectorPlatformDeviceEmf::CreateDevice(width, height, isOpaque, NULL); 708} 709 710bool VectorPlatformDeviceEmf::CreateBrush(bool use_brush, COLORREF color) { 711 SkASSERT(previous_brush_ == NULL); 712 // We can't use SetDCBrushColor() or DC_BRUSH when drawing to a EMF buffer. 713 // SetDCBrushColor() calls are not recorded at all and DC_BRUSH will use 714 // WHITE_BRUSH instead. 715 716 if (!use_brush) { 717 // Set the transparency. 718 if (0 == SetBkMode(hdc_, TRANSPARENT)) { 719 SkASSERT(false); 720 return false; 721 } 722 723 // Select the NULL brush. 724 previous_brush_ = SelectObject(GetStockObject(NULL_BRUSH)); 725 return previous_brush_ != NULL; 726 } 727 728 // Set the opacity. 729 if (0 == SetBkMode(hdc_, OPAQUE)) { 730 SkASSERT(false); 731 return false; 732 } 733 734 // Create and select the brush. 735 previous_brush_ = SelectObject(CreateSolidBrush(color)); 736 return previous_brush_ != NULL; 737} 738 739bool VectorPlatformDeviceEmf::CreatePen(bool use_pen, 740 COLORREF color, 741 int stroke_width, 742 float stroke_miter, 743 DWORD pen_style) { 744 SkASSERT(previous_pen_ == NULL); 745 // We can't use SetDCPenColor() or DC_PEN when drawing to a EMF buffer. 746 // SetDCPenColor() calls are not recorded at all and DC_PEN will use BLACK_PEN 747 // instead. 748 749 // No pen case 750 if (!use_pen) { 751 previous_pen_ = SelectObject(GetStockObject(NULL_PEN)); 752 return previous_pen_ != NULL; 753 } 754 755 // Use the stock pen if the stroke width is 0. 756 if (stroke_width == 0) { 757 // Create a pen with the right color. 758 previous_pen_ = SelectObject(::CreatePen(PS_SOLID, 0, color)); 759 return previous_pen_ != NULL; 760 } 761 762 // Load a custom pen. 763 LOGBRUSH brush; 764 brush.lbStyle = BS_SOLID; 765 brush.lbColor = color; 766 brush.lbHatch = 0; 767 HPEN pen = ExtCreatePen(pen_style, stroke_width, &brush, 0, NULL); 768 SkASSERT(pen != NULL); 769 previous_pen_ = SelectObject(pen); 770 if (previous_pen_ == NULL) 771 return false; 772 773 if (!SetMiterLimit(hdc_, stroke_miter, NULL)) { 774 SkASSERT(false); 775 return false; 776 } 777 return true; 778} 779 780void VectorPlatformDeviceEmf::Cleanup() { 781 if (previous_brush_) { 782 HGDIOBJ result = SelectObject(previous_brush_); 783 previous_brush_ = NULL; 784 if (result) { 785 BOOL res = DeleteObject(result); 786 SkASSERT(res != 0); 787 } 788 } 789 if (previous_pen_) { 790 HGDIOBJ result = SelectObject(previous_pen_); 791 previous_pen_ = NULL; 792 if (result) { 793 BOOL res = DeleteObject(result); 794 SkASSERT(res != 0); 795 } 796 } 797 // Remove any loaded path from the context. 798 AbortPath(hdc_); 799} 800 801HGDIOBJ VectorPlatformDeviceEmf::SelectObject(HGDIOBJ object) { 802 HGDIOBJ result = ::SelectObject(hdc_, object); 803 SkASSERT(result != HGDI_ERROR); 804 if (result == HGDI_ERROR) 805 return NULL; 806 return result; 807} 808 809bool VectorPlatformDeviceEmf::CreateBrush(bool use_brush, 810 const SkPaint& paint) { 811 // Make sure that for transparent color, no brush is used. 812 if (paint.getAlpha() == 0) { 813 use_brush = false; 814 } 815 816 return CreateBrush(use_brush, SkColorToCOLORREF(paint.getColor())); 817} 818 819bool VectorPlatformDeviceEmf::CreatePen(bool use_pen, const SkPaint& paint) { 820 // Make sure that for transparent color, no pen is used. 821 if (paint.getAlpha() == 0) { 822 use_pen = false; 823 } 824 825 DWORD pen_style = PS_GEOMETRIC | PS_SOLID; 826 switch (paint.getStrokeJoin()) { 827 case SkPaint::kMiter_Join: 828 // Connects path segments with a sharp join. 829 pen_style |= PS_JOIN_MITER; 830 break; 831 case SkPaint::kRound_Join: 832 // Connects path segments with a round join. 833 pen_style |= PS_JOIN_ROUND; 834 break; 835 case SkPaint::kBevel_Join: 836 // Connects path segments with a flat bevel join. 837 pen_style |= PS_JOIN_BEVEL; 838 break; 839 default: 840 SkASSERT(false); 841 break; 842 } 843 switch (paint.getStrokeCap()) { 844 case SkPaint::kButt_Cap: 845 // Begin/end contours with no extension. 846 pen_style |= PS_ENDCAP_FLAT; 847 break; 848 case SkPaint::kRound_Cap: 849 // Begin/end contours with a semi-circle extension. 850 pen_style |= PS_ENDCAP_ROUND; 851 break; 852 case SkPaint::kSquare_Cap: 853 // Begin/end contours with a half square extension. 854 pen_style |= PS_ENDCAP_SQUARE; 855 break; 856 default: 857 SkASSERT(false); 858 break; 859 } 860 861 return CreatePen(use_pen, 862 SkColorToCOLORREF(paint.getColor()), 863 SkScalarRound(paint.getStrokeWidth()), 864 paint.getStrokeMiter(), 865 pen_style); 866} 867 868void VectorPlatformDeviceEmf::InternalDrawBitmap(const SkBitmap& bitmap, 869 int x, int y, 870 const SkPaint& paint) { 871 unsigned char alpha = paint.getAlpha(); 872 if (alpha == 0) 873 return; 874 875 bool is_translucent; 876 if (alpha != 255) { 877 // ApplyPaint expect an opaque color. 878 SkPaint tmp_paint(paint); 879 tmp_paint.setAlpha(255); 880 if (!ApplyPaint(tmp_paint)) 881 return; 882 is_translucent = true; 883 } else { 884 if (!ApplyPaint(paint)) 885 return; 886 is_translucent = false; 887 } 888 int src_size_x = bitmap.width(); 889 int src_size_y = bitmap.height(); 890 if (!src_size_x || !src_size_y) 891 return; 892 893 // Create a BMP v4 header that we can serialize. We use the shared "V3" 894 // fillter to fill the stardard items, then add in the "V4" stuff we want. 895 BITMAPV4HEADER bitmap_header; 896 memset(&bitmap_header, 0, sizeof(BITMAPV4HEADER)); 897 FillBitmapInfoHeader(src_size_x, src_size_y, 898 reinterpret_cast<BITMAPINFOHEADER*>(&bitmap_header)); 899 bitmap_header.bV4Size = sizeof(BITMAPV4HEADER); 900 bitmap_header.bV4RedMask = 0x00ff0000; 901 bitmap_header.bV4GreenMask = 0x0000ff00; 902 bitmap_header.bV4BlueMask = 0x000000ff; 903 bitmap_header.bV4AlphaMask = 0xff000000; 904 905 SkAutoLockPixels lock(bitmap); 906 SkASSERT(bitmap.config() == SkBitmap::kARGB_8888_Config); 907 const uint32_t* pixels = static_cast<const uint32_t*>(bitmap.getPixels()); 908 if (pixels == NULL) { 909 SkASSERT(false); 910 return; 911 } 912 913 if (!is_translucent) { 914 int row_length = bitmap.rowBytesAsPixels(); 915 // There is no quick way to determine if an image is opaque. 916 for (int y2 = 0; y2 < src_size_y; ++y2) { 917 for (int x2 = 0; x2 < src_size_x; ++x2) { 918 if (SkColorGetA(pixels[(y2 * row_length) + x2]) != 255) { 919 is_translucent = true; 920 y2 = src_size_y; 921 break; 922 } 923 } 924 } 925 } 926 927 HDC dc = BeginPlatformPaint(); 928 BITMAPINFOHEADER hdr; 929 FillBitmapInfoHeader(src_size_x, src_size_y, &hdr); 930 if (is_translucent) { 931 // The image must be loaded as a bitmap inside a device context. 932 HDC bitmap_dc = ::CreateCompatibleDC(dc); 933 void* bits = NULL; 934 HBITMAP hbitmap = ::CreateDIBSection( 935 bitmap_dc, reinterpret_cast<const BITMAPINFO*>(&hdr), 936 DIB_RGB_COLORS, &bits, NULL, 0); 937 938 // static cast to a char so we can do byte ptr arithmatic to 939 // get the offset. 940 unsigned char* dest_buffer = static_cast<unsigned char *>(bits); 941 942 // We will copy row by row to avoid having to worry about 943 // the row strides being different. 944 const int dest_row_size = hdr.biBitCount / 8 * hdr.biWidth; 945 for (int row = 0; row < bitmap.height(); ++row) { 946 int dest_offset = row * dest_row_size; 947 // pixels_offset in terms of pixel count. 948 int src_offset = row * bitmap.rowBytesAsPixels(); 949 memcpy(dest_buffer + dest_offset, pixels + src_offset, dest_row_size); 950 } 951 SkASSERT(hbitmap); 952 HGDIOBJ old_bitmap = ::SelectObject(bitmap_dc, hbitmap); 953 954 // After some analysis of IE7's behavior, this is the thing to do. I was 955 // sure IE7 was doing so kind of bitmasking due to the way translucent image 956 // where renderered but after some windbg tracing, it is being done by the 957 // printer driver after all (mostly HP printers). IE7 always use AlphaBlend 958 // for bitmasked images. The trick seems to switch the stretching mode in 959 // what the driver expects. 960 DWORD previous_mode = GetStretchBltMode(dc); 961 BOOL result = SetStretchBltMode(dc, COLORONCOLOR); 962 SkASSERT(result); 963 // Note that this function expect premultiplied colors (!) 964 BLENDFUNCTION blend_function = {AC_SRC_OVER, 0, alpha, AC_SRC_ALPHA}; 965 result = GdiAlphaBlend(dc, 966 x, y, // Destination origin. 967 src_size_x, src_size_y, // Destination size. 968 bitmap_dc, 969 0, 0, // Source origin. 970 src_size_x, src_size_y, // Source size. 971 blend_function); 972 SkASSERT(result); 973 result = SetStretchBltMode(dc, previous_mode); 974 SkASSERT(result); 975 976 ::SelectObject(bitmap_dc, static_cast<HBITMAP>(old_bitmap)); 977 DeleteObject(hbitmap); 978 DeleteDC(bitmap_dc); 979 } else { 980 int nCopied = StretchDIBits(dc, 981 x, y, // Destination origin. 982 src_size_x, src_size_y, 983 0, 0, // Source origin. 984 src_size_x, src_size_y, // Source size. 985 pixels, 986 reinterpret_cast<const BITMAPINFO*>(&hdr), 987 DIB_RGB_COLORS, 988 SRCCOPY); 989 } 990 EndPlatformPaint(); 991 Cleanup(); 992} 993 994} // namespace skia 995