| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2006-2008 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_device.h" | |
| 6 | |
| 7 #include "base/gfx/gdi_util.h" | |
| 8 #include "skia/ext/skia_utils_win.h" | |
| 9 #include "third_party/skia/include/core/SkUtils.h" | |
| 10 | |
| 11 namespace skia { | |
| 12 | |
| 13 VectorDevice* VectorDevice::create(HDC dc, int width, int height) { | |
| 14 InitializeDC(dc); | |
| 15 | |
| 16 // Link the SkBitmap to the current selected bitmap in the device context. | |
| 17 SkBitmap bitmap; | |
| 18 HGDIOBJ selected_bitmap = GetCurrentObject(dc, OBJ_BITMAP); | |
| 19 bool succeeded = false; | |
| 20 if (selected_bitmap != NULL) { | |
| 21 BITMAP bitmap_data; | |
| 22 if (GetObject(selected_bitmap, sizeof(BITMAP), &bitmap_data) == | |
| 23 sizeof(BITMAP)) { | |
| 24 // The context has a bitmap attached. Attach our SkBitmap to it. | |
| 25 // Warning: If the bitmap gets unselected from the HDC, VectorDevice has | |
| 26 // no way to detect this, so the HBITMAP could be released while SkBitmap | |
| 27 // still has a reference to it. Be cautious. | |
| 28 if (width == bitmap_data.bmWidth && | |
| 29 height == bitmap_data.bmHeight) { | |
| 30 bitmap.setConfig(SkBitmap::kARGB_8888_Config, | |
| 31 bitmap_data.bmWidth, | |
| 32 bitmap_data.bmHeight, | |
| 33 bitmap_data.bmWidthBytes); | |
| 34 bitmap.setPixels(bitmap_data.bmBits); | |
| 35 succeeded = true; | |
| 36 } | |
| 37 } | |
| 38 } | |
| 39 | |
| 40 if (!succeeded) | |
| 41 bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height); | |
| 42 | |
| 43 return new VectorDevice(dc, bitmap); | |
| 44 } | |
| 45 | |
| 46 VectorDevice::VectorDevice(HDC dc, const SkBitmap& bitmap) | |
| 47 : PlatformDeviceWin(bitmap), | |
| 48 hdc_(dc), | |
| 49 previous_brush_(NULL), | |
| 50 previous_pen_(NULL) { | |
| 51 transform_.reset(); | |
| 52 } | |
| 53 | |
| 54 VectorDevice::~VectorDevice() { | |
| 55 SkASSERT(previous_brush_ == NULL); | |
| 56 SkASSERT(previous_pen_ == NULL); | |
| 57 } | |
| 58 | |
| 59 | |
| 60 void VectorDevice::drawPaint(const SkDraw& draw, const SkPaint& paint) { | |
| 61 // TODO(maruel): Bypass the current transformation matrix. | |
| 62 SkRect rect; | |
| 63 rect.fLeft = 0; | |
| 64 rect.fTop = 0; | |
| 65 rect.fRight = SkIntToScalar(width() + 1); | |
| 66 rect.fBottom = SkIntToScalar(height() + 1); | |
| 67 drawRect(draw, rect, paint); | |
| 68 } | |
| 69 | |
| 70 void VectorDevice::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode, | |
| 71 size_t count, const SkPoint pts[], | |
| 72 const SkPaint& paint) { | |
| 73 if (!count) | |
| 74 return; | |
| 75 | |
| 76 if (mode == SkCanvas::kPoints_PointMode) { | |
| 77 SkASSERT(false); | |
| 78 return; | |
| 79 } | |
| 80 | |
| 81 SkPaint tmp_paint(paint); | |
| 82 tmp_paint.setStyle(SkPaint::kStroke_Style); | |
| 83 | |
| 84 // Draw a path instead. | |
| 85 SkPath path; | |
| 86 switch (mode) { | |
| 87 case SkCanvas::kLines_PointMode: | |
| 88 if (count % 2) { | |
| 89 SkASSERT(false); | |
| 90 return; | |
| 91 } | |
| 92 for (size_t i = 0; i < count / 2; ++i) { | |
| 93 path.moveTo(pts[2 * i]); | |
| 94 path.lineTo(pts[2 * i + 1]); | |
| 95 } | |
| 96 break; | |
| 97 case SkCanvas::kPolygon_PointMode: | |
| 98 path.moveTo(pts[0]); | |
| 99 for (size_t i = 1; i < count; ++i) { | |
| 100 path.lineTo(pts[i]); | |
| 101 } | |
| 102 break; | |
| 103 default: | |
| 104 SkASSERT(false); | |
| 105 return; | |
| 106 } | |
| 107 // Draw the calculated path. | |
| 108 drawPath(draw, path, tmp_paint); | |
| 109 } | |
| 110 | |
| 111 void VectorDevice::drawRect(const SkDraw& draw, const SkRect& rect, | |
| 112 const SkPaint& paint) { | |
| 113 if (paint.getPathEffect()) { | |
| 114 // Draw a path instead. | |
| 115 SkPath path_orginal; | |
| 116 path_orginal.addRect(rect); | |
| 117 | |
| 118 // Apply the path effect to the rect. | |
| 119 SkPath path_modified; | |
| 120 paint.getFillPath(path_orginal, &path_modified); | |
| 121 | |
| 122 // Removes the path effect from the temporary SkPaint object. | |
| 123 SkPaint paint_no_effet(paint); | |
| 124 paint_no_effet.setPathEffect(NULL)->safeUnref(); | |
| 125 | |
| 126 // Draw the calculated path. | |
| 127 drawPath(draw, path_modified, paint_no_effet); | |
| 128 return; | |
| 129 } | |
| 130 | |
| 131 if (!ApplyPaint(paint)) { | |
| 132 return; | |
| 133 } | |
| 134 HDC dc = getBitmapDC(); | |
| 135 if (!Rectangle(dc, SkScalarRound(rect.fLeft), | |
| 136 SkScalarRound(rect.fTop), | |
| 137 SkScalarRound(rect.fRight), | |
| 138 SkScalarRound(rect.fBottom))) { | |
| 139 SkASSERT(false); | |
| 140 } | |
| 141 Cleanup(); | |
| 142 } | |
| 143 | |
| 144 void VectorDevice::drawPath(const SkDraw& draw, const SkPath& path, | |
| 145 const SkPaint& paint) { | |
| 146 if (paint.getPathEffect()) { | |
| 147 // Apply the path effect forehand. | |
| 148 SkPath path_modified; | |
| 149 paint.getFillPath(path, &path_modified); | |
| 150 | |
| 151 // Removes the path effect from the temporary SkPaint object. | |
| 152 SkPaint paint_no_effet(paint); | |
| 153 paint_no_effet.setPathEffect(NULL)->safeUnref(); | |
| 154 | |
| 155 // Draw the calculated path. | |
| 156 drawPath(draw, path_modified, paint_no_effet); | |
| 157 return; | |
| 158 } | |
| 159 | |
| 160 if (!ApplyPaint(paint)) { | |
| 161 return; | |
| 162 } | |
| 163 HDC dc = getBitmapDC(); | |
| 164 PlatformDeviceWin::LoadPathToDC(dc, path); | |
| 165 switch (paint.getStyle()) { | |
| 166 case SkPaint::kFill_Style: { | |
| 167 BOOL res = StrokeAndFillPath(dc); | |
| 168 SkASSERT(res != 0); | |
| 169 break; | |
| 170 } | |
| 171 case SkPaint::kStroke_Style: { | |
| 172 BOOL res = StrokePath(dc); | |
| 173 SkASSERT(res != 0); | |
| 174 break; | |
| 175 } | |
| 176 case SkPaint::kStrokeAndFill_Style: { | |
| 177 BOOL res = StrokeAndFillPath(dc); | |
| 178 SkASSERT(res != 0); | |
| 179 break; | |
| 180 } | |
| 181 default: | |
| 182 SkASSERT(false); | |
| 183 break; | |
| 184 } | |
| 185 Cleanup(); | |
| 186 } | |
| 187 | |
| 188 void VectorDevice::drawBitmap(const SkDraw& draw, const SkBitmap& bitmap, | |
| 189 const SkMatrix& matrix, const SkPaint& paint) { | |
| 190 // Load the temporary matrix. This is what will translate, rotate and resize | |
| 191 // the bitmap. | |
| 192 SkMatrix actual_transform(transform_); | |
| 193 actual_transform.preConcat(matrix); | |
| 194 LoadTransformToDC(hdc_, actual_transform); | |
| 195 | |
| 196 InternalDrawBitmap(bitmap, 0, 0, paint); | |
| 197 | |
| 198 // Restore the original matrix. | |
| 199 LoadTransformToDC(hdc_, transform_); | |
| 200 } | |
| 201 | |
| 202 void VectorDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap, | |
| 203 int x, int y, const SkPaint& paint) { | |
| 204 SkMatrix identity; | |
| 205 identity.reset(); | |
| 206 LoadTransformToDC(hdc_, identity); | |
| 207 | |
| 208 InternalDrawBitmap(bitmap, x, y, paint); | |
| 209 | |
| 210 // Restore the original matrix. | |
| 211 LoadTransformToDC(hdc_, transform_); | |
| 212 } | |
| 213 | |
| 214 void VectorDevice::drawText(const SkDraw& draw, const void* text, size_t byteLen
gth, | |
| 215 SkScalar x, SkScalar y, const SkPaint& paint) { | |
| 216 // This function isn't used in the code. Verify this assumption. | |
| 217 SkASSERT(false); | |
| 218 } | |
| 219 | |
| 220 void VectorDevice::drawPosText(const SkDraw& draw, const void* text, size_t len, | |
| 221 const SkScalar pos[], SkScalar constY, | |
| 222 int scalarsPerPos, const SkPaint& paint) { | |
| 223 // This function isn't used in the code. Verify this assumption. | |
| 224 SkASSERT(false); | |
| 225 } | |
| 226 | |
| 227 void VectorDevice::drawTextOnPath(const SkDraw& draw, const void* text, | |
| 228 size_t len, | |
| 229 const SkPath& path, const SkMatrix* matrix, | |
| 230 const SkPaint& paint) { | |
| 231 // This function isn't used in the code. Verify this assumption. | |
| 232 SkASSERT(false); | |
| 233 } | |
| 234 | |
| 235 void VectorDevice::drawVertices(const SkDraw& draw, SkCanvas::VertexMode vmode, | |
| 236 int vertexCount, | |
| 237 const SkPoint vertices[], const SkPoint texs[], | |
| 238 const SkColor colors[], SkXfermode* xmode, | |
| 239 const uint16_t indices[], int indexCount, | |
| 240 const SkPaint& paint) { | |
| 241 // This function isn't used in the code. Verify this assumption. | |
| 242 SkASSERT(false); | |
| 243 } | |
| 244 | |
| 245 void VectorDevice::drawDevice(const SkDraw& draw, SkDevice* device, int x, | |
| 246 int y, const SkPaint& paint) { | |
| 247 // TODO(maruel): http://b/1183870 Playback the EMF buffer at printer's dpi if | |
| 248 // it is a vectorial device. | |
| 249 drawSprite(draw, device->accessBitmap(false), x, y, paint); | |
| 250 } | |
| 251 | |
| 252 bool VectorDevice::ApplyPaint(const SkPaint& paint) { | |
| 253 // Note: The goal here is to transfert the SkPaint's state to the HDC's state. | |
| 254 // This function does not execute the SkPaint drawing commands. These should | |
| 255 // be executed in drawPaint(). | |
| 256 | |
| 257 SkPaint::Style style = paint.getStyle(); | |
| 258 if (!paint.getAlpha()) | |
| 259 style = SkPaint::kStyleCount; | |
| 260 | |
| 261 switch (style) { | |
| 262 case SkPaint::kFill_Style: | |
| 263 if (!CreateBrush(true, paint) || | |
| 264 !CreatePen(false, paint)) | |
| 265 return false; | |
| 266 break; | |
| 267 case SkPaint::kStroke_Style: | |
| 268 if (!CreateBrush(false, paint) || | |
| 269 !CreatePen(true, paint)) | |
| 270 return false; | |
| 271 break; | |
| 272 case SkPaint::kStrokeAndFill_Style: | |
| 273 if (!CreateBrush(true, paint) || | |
| 274 !CreatePen(true, paint)) | |
| 275 return false; | |
| 276 break; | |
| 277 default: | |
| 278 if (!CreateBrush(false, paint) || | |
| 279 !CreatePen(false, paint)) | |
| 280 return false; | |
| 281 break; | |
| 282 } | |
| 283 | |
| 284 /* | |
| 285 getFlags(); | |
| 286 isAntiAlias(); | |
| 287 isDither() | |
| 288 isLinearText() | |
| 289 isSubpixelText() | |
| 290 isUnderlineText() | |
| 291 isStrikeThruText() | |
| 292 isFakeBoldText() | |
| 293 isDevKernText() | |
| 294 isFilterBitmap() | |
| 295 | |
| 296 // Skia's text is not used. This should be fixed. | |
| 297 getTextAlign() | |
| 298 getTextScaleX() | |
| 299 getTextSkewX() | |
| 300 getTextEncoding() | |
| 301 getFontMetrics() | |
| 302 getFontSpacing() | |
| 303 */ | |
| 304 | |
| 305 // BUG 1094907: Implement shaders. Shaders currently in use: | |
| 306 // SkShader::CreateBitmapShader | |
| 307 // SkGradientShader::CreateRadial | |
| 308 // SkGradientShader::CreateLinear | |
| 309 // SkASSERT(!paint.getShader()); | |
| 310 | |
| 311 // http://b/1106647 Implement loopers and mask filter. Looper currently in | |
| 312 // use: | |
| 313 // SkBlurDrawLooper is used for shadows. | |
| 314 // SkASSERT(!paint.getLooper()); | |
| 315 // SkASSERT(!paint.getMaskFilter()); | |
| 316 | |
| 317 // http://b/1165900 Implement xfermode. | |
| 318 // SkASSERT(!paint.getXfermode()); | |
| 319 | |
| 320 // The path effect should be processed before arriving here. | |
| 321 SkASSERT(!paint.getPathEffect()); | |
| 322 | |
| 323 // These aren't used in the code. Verify this assumption. | |
| 324 SkASSERT(!paint.getColorFilter()); | |
| 325 SkASSERT(!paint.getRasterizer()); | |
| 326 // Reuse code to load Win32 Fonts. | |
| 327 SkASSERT(!paint.getTypeface()); | |
| 328 return true; | |
| 329 } | |
| 330 | |
| 331 void VectorDevice::setMatrixClip(const SkMatrix& transform, | |
| 332 const SkRegion& region) { | |
| 333 transform_ = transform; | |
| 334 LoadTransformToDC(hdc_, transform_); | |
| 335 clip_region_ = region; | |
| 336 if (!clip_region_.isEmpty()) | |
| 337 LoadClipRegion(); | |
| 338 } | |
| 339 | |
| 340 void VectorDevice::drawToHDC(HDC dc, int x, int y, const RECT* src_rect) { | |
| 341 SkASSERT(false); | |
| 342 } | |
| 343 | |
| 344 void VectorDevice::LoadClipRegion() { | |
| 345 SkMatrix t; | |
| 346 t.reset(); | |
| 347 LoadClippingRegionToDC(hdc_, clip_region_, t); | |
| 348 } | |
| 349 | |
| 350 bool VectorDevice::CreateBrush(bool use_brush, COLORREF color) { | |
| 351 SkASSERT(previous_brush_ == NULL); | |
| 352 // We can't use SetDCBrushColor() or DC_BRUSH when drawing to a EMF buffer. | |
| 353 // SetDCBrushColor() calls are not recorded at all and DC_BRUSH will use | |
| 354 // WHITE_BRUSH instead. | |
| 355 | |
| 356 if (!use_brush) { | |
| 357 // Set the transparency. | |
| 358 if (0 == SetBkMode(hdc_, TRANSPARENT)) { | |
| 359 SkASSERT(false); | |
| 360 return false; | |
| 361 } | |
| 362 | |
| 363 // Select the NULL brush. | |
| 364 previous_brush_ = SelectObject(GetStockObject(NULL_BRUSH)); | |
| 365 return previous_brush_ != NULL; | |
| 366 } | |
| 367 | |
| 368 // Set the opacity. | |
| 369 if (0 == SetBkMode(hdc_, OPAQUE)) { | |
| 370 SkASSERT(false); | |
| 371 return false; | |
| 372 } | |
| 373 | |
| 374 // Create and select the brush. | |
| 375 previous_brush_ = SelectObject(CreateSolidBrush(color)); | |
| 376 return previous_brush_ != NULL; | |
| 377 } | |
| 378 | |
| 379 bool VectorDevice::CreatePen(bool use_pen, COLORREF color, int stroke_width, | |
| 380 float stroke_miter, DWORD pen_style) { | |
| 381 SkASSERT(previous_pen_ == NULL); | |
| 382 // We can't use SetDCPenColor() or DC_PEN when drawing to a EMF buffer. | |
| 383 // SetDCPenColor() calls are not recorded at all and DC_PEN will use BLACK_PEN | |
| 384 // instead. | |
| 385 | |
| 386 // No pen case | |
| 387 if (!use_pen) { | |
| 388 previous_pen_ = SelectObject(GetStockObject(NULL_PEN)); | |
| 389 return previous_pen_ != NULL; | |
| 390 } | |
| 391 | |
| 392 // Use the stock pen if the stroke width is 0. | |
| 393 if (stroke_width == 0) { | |
| 394 // Create a pen with the right color. | |
| 395 previous_pen_ = SelectObject(::CreatePen(PS_SOLID, 0, color)); | |
| 396 return previous_pen_ != NULL; | |
| 397 } | |
| 398 | |
| 399 // Load a custom pen. | |
| 400 LOGBRUSH brush; | |
| 401 brush.lbStyle = BS_SOLID; | |
| 402 brush.lbColor = color; | |
| 403 brush.lbHatch = 0; | |
| 404 HPEN pen = ExtCreatePen(pen_style, stroke_width, &brush, 0, NULL); | |
| 405 SkASSERT(pen != NULL); | |
| 406 previous_pen_ = SelectObject(pen); | |
| 407 if (previous_pen_ == NULL) | |
| 408 return false; | |
| 409 | |
| 410 if (!SetMiterLimit(hdc_, stroke_miter, NULL)) { | |
| 411 SkASSERT(false); | |
| 412 return false; | |
| 413 } | |
| 414 return true; | |
| 415 } | |
| 416 | |
| 417 void VectorDevice::Cleanup() { | |
| 418 if (previous_brush_) { | |
| 419 HGDIOBJ result = SelectObject(previous_brush_); | |
| 420 previous_brush_ = NULL; | |
| 421 if (result) { | |
| 422 BOOL res = DeleteObject(result); | |
| 423 SkASSERT(res != 0); | |
| 424 } | |
| 425 } | |
| 426 if (previous_pen_) { | |
| 427 HGDIOBJ result = SelectObject(previous_pen_); | |
| 428 previous_pen_ = NULL; | |
| 429 if (result) { | |
| 430 BOOL res = DeleteObject(result); | |
| 431 SkASSERT(res != 0); | |
| 432 } | |
| 433 } | |
| 434 // Remove any loaded path from the context. | |
| 435 AbortPath(hdc_); | |
| 436 } | |
| 437 | |
| 438 HGDIOBJ VectorDevice::SelectObject(HGDIOBJ object) { | |
| 439 HGDIOBJ result = ::SelectObject(hdc_, object); | |
| 440 SkASSERT(result != HGDI_ERROR); | |
| 441 if (result == HGDI_ERROR) | |
| 442 return NULL; | |
| 443 return result; | |
| 444 } | |
| 445 | |
| 446 bool VectorDevice::CreateBrush(bool use_brush, const SkPaint& paint) { | |
| 447 // Make sure that for transparent color, no brush is used. | |
| 448 if (paint.getAlpha() == 0) { | |
| 449 // Test if it ever happen. | |
| 450 SkASSERT(false); | |
| 451 use_brush = false; | |
| 452 } | |
| 453 | |
| 454 return CreateBrush(use_brush, SkColorToCOLORREF(paint.getColor())); | |
| 455 } | |
| 456 | |
| 457 bool VectorDevice::CreatePen(bool use_pen, const SkPaint& paint) { | |
| 458 // Make sure that for transparent color, no pen is used. | |
| 459 if (paint.getAlpha() == 0) { | |
| 460 // Test if it ever happen. | |
| 461 SkASSERT(false); | |
| 462 use_pen = false; | |
| 463 } | |
| 464 | |
| 465 DWORD pen_style = PS_GEOMETRIC | PS_SOLID; | |
| 466 switch (paint.getStrokeJoin()) { | |
| 467 case SkPaint::kMiter_Join: | |
| 468 // Connects path segments with a sharp join. | |
| 469 pen_style |= PS_JOIN_MITER; | |
| 470 break; | |
| 471 case SkPaint::kRound_Join: | |
| 472 // Connects path segments with a round join. | |
| 473 pen_style |= PS_JOIN_ROUND; | |
| 474 break; | |
| 475 case SkPaint::kBevel_Join: | |
| 476 // Connects path segments with a flat bevel join. | |
| 477 pen_style |= PS_JOIN_BEVEL; | |
| 478 break; | |
| 479 default: | |
| 480 SkASSERT(false); | |
| 481 break; | |
| 482 } | |
| 483 switch (paint.getStrokeCap()) { | |
| 484 case SkPaint::kButt_Cap: | |
| 485 // Begin/end contours with no extension. | |
| 486 pen_style |= PS_ENDCAP_FLAT; | |
| 487 break; | |
| 488 case SkPaint::kRound_Cap: | |
| 489 // Begin/end contours with a semi-circle extension. | |
| 490 pen_style |= PS_ENDCAP_ROUND; | |
| 491 break; | |
| 492 case SkPaint::kSquare_Cap: | |
| 493 // Begin/end contours with a half square extension. | |
| 494 pen_style |= PS_ENDCAP_SQUARE; | |
| 495 break; | |
| 496 default: | |
| 497 SkASSERT(false); | |
| 498 break; | |
| 499 } | |
| 500 | |
| 501 return CreatePen(use_pen, | |
| 502 SkColorToCOLORREF(paint.getColor()), | |
| 503 SkScalarRound(paint.getStrokeWidth()), | |
| 504 paint.getStrokeMiter(), | |
| 505 pen_style); | |
| 506 } | |
| 507 | |
| 508 void VectorDevice::InternalDrawBitmap(const SkBitmap& bitmap, int x, int y, | |
| 509 const SkPaint& paint) { | |
| 510 unsigned char alpha = paint.getAlpha(); | |
| 511 if (alpha == 0) | |
| 512 return; | |
| 513 | |
| 514 bool is_translucent; | |
| 515 if (alpha != 255) { | |
| 516 // ApplyPaint expect an opaque color. | |
| 517 SkPaint tmp_paint(paint); | |
| 518 tmp_paint.setAlpha(255); | |
| 519 if (!ApplyPaint(tmp_paint)) | |
| 520 return; | |
| 521 is_translucent = true; | |
| 522 } else { | |
| 523 if (!ApplyPaint(paint)) | |
| 524 return; | |
| 525 is_translucent = false; | |
| 526 } | |
| 527 int src_size_x = bitmap.width(); | |
| 528 int src_size_y = bitmap.height(); | |
| 529 if (!src_size_x || !src_size_y) | |
| 530 return; | |
| 531 | |
| 532 // Create a BMP v4 header that we can serialize. | |
| 533 BITMAPV4HEADER bitmap_header; | |
| 534 gfx::CreateBitmapV4Header(src_size_x, src_size_y, &bitmap_header); | |
| 535 HDC dc = getBitmapDC(); | |
| 536 SkAutoLockPixels lock(bitmap); | |
| 537 SkASSERT(bitmap.getConfig() == SkBitmap::kARGB_8888_Config); | |
| 538 const uint32_t* pixels = static_cast<const uint32_t*>(bitmap.getPixels()); | |
| 539 if (pixels == NULL) { | |
| 540 SkASSERT(false); | |
| 541 return; | |
| 542 } | |
| 543 | |
| 544 if (!is_translucent) { | |
| 545 int row_length = bitmap.rowBytesAsPixels(); | |
| 546 // There is no quick way to determine if an image is opaque. | |
| 547 for (int y2 = 0; y2 < src_size_y; ++y2) { | |
| 548 for (int x2 = 0; x2 < src_size_x; ++x2) { | |
| 549 if (SkColorGetA(pixels[(y2 * row_length) + x2]) != 255) { | |
| 550 is_translucent = true; | |
| 551 y2 = src_size_y; | |
| 552 break; | |
| 553 } | |
| 554 } | |
| 555 } | |
| 556 } | |
| 557 | |
| 558 BITMAPINFOHEADER hdr; | |
| 559 gfx::CreateBitmapHeader(src_size_x, src_size_y, &hdr); | |
| 560 if (is_translucent) { | |
| 561 // The image must be loaded as a bitmap inside a device context. | |
| 562 HDC bitmap_dc = ::CreateCompatibleDC(dc); | |
| 563 void* bits = NULL; | |
| 564 HBITMAP hbitmap = ::CreateDIBSection( | |
| 565 bitmap_dc, reinterpret_cast<const BITMAPINFO*>(&hdr), | |
| 566 DIB_RGB_COLORS, &bits, NULL, 0); | |
| 567 memcpy(bits, pixels, bitmap.getSize()); | |
| 568 SkASSERT(hbitmap); | |
| 569 HGDIOBJ old_bitmap = ::SelectObject(bitmap_dc, hbitmap); | |
| 570 | |
| 571 // After some analysis of IE7's behavior, this is the thing to do. I was | |
| 572 // sure IE7 was doing so kind of bitmasking due to the way translucent image | |
| 573 // where renderered but after some windbg tracing, it is being done by the | |
| 574 // printer driver after all (mostly HP printers). IE7 always use AlphaBlend | |
| 575 // for bitmasked images. The trick seems to switch the stretching mode in | |
| 576 // what the driver expects. | |
| 577 DWORD previous_mode = GetStretchBltMode(dc); | |
| 578 BOOL result = SetStretchBltMode(dc, COLORONCOLOR); | |
| 579 SkASSERT(result); | |
| 580 // Note that this function expect premultiplied colors (!) | |
| 581 BLENDFUNCTION blend_function = {AC_SRC_OVER, 0, alpha, AC_SRC_ALPHA}; | |
| 582 result = GdiAlphaBlend(dc, | |
| 583 x, y, // Destination origin. | |
| 584 src_size_x, src_size_y, // Destination size. | |
| 585 bitmap_dc, | |
| 586 0, 0, // Source origin. | |
| 587 src_size_x, src_size_y, // Source size. | |
| 588 blend_function); | |
| 589 SkASSERT(result); | |
| 590 result = SetStretchBltMode(dc, previous_mode); | |
| 591 SkASSERT(result); | |
| 592 | |
| 593 ::SelectObject(bitmap_dc, static_cast<HBITMAP>(old_bitmap)); | |
| 594 DeleteObject(hbitmap); | |
| 595 DeleteDC(bitmap_dc); | |
| 596 } else { | |
| 597 BOOL result = StretchDIBits(dc, | |
| 598 x, y, // Destination origin. | |
| 599 src_size_x, src_size_y, | |
| 600 0, 0, // Source origin. | |
| 601 src_size_x, src_size_y, // Source size. | |
| 602 pixels, | |
| 603 reinterpret_cast<const BITMAPINFO*>(&hdr), | |
| 604 DIB_RGB_COLORS, | |
| 605 SRCCOPY); | |
| 606 SkASSERT(result); | |
| 607 } | |
| 608 Cleanup(); | |
| 609 } | |
| 610 | |
| 611 } // namespace skia | |
| 612 | |
| OLD | NEW |