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