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