1 /* 2 * Copyright (C) 2009 Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 #include "config.h" 32 33 #include "platform/graphics/win/TransparencyWin.h" 34 35 #include "SkColorPriv.h" 36 #include "platform/fonts/SimpleFontData.h" 37 #include "platform/graphics/GraphicsContext.h" 38 #include "platform/graphics/skia/SkiaUtils.h" 39 #include "skia/ext/platform_canvas.h" 40 41 namespace WebCore { 42 43 namespace { 44 45 // The maximum size in pixels of the buffer we'll keep around for drawing text 46 // into. Buffers larger than this will be destroyed when we're done with them. 47 const int maxCachedBufferPixelSize = 65536; 48 49 inline const SkBitmap& bitmapForContext(const GraphicsContext& context) 50 { 51 return context.layerBitmap(); 52 } 53 54 void compositeToCopy(GraphicsContext& sourceLayers, GraphicsContext& destContext, const AffineTransform& matrix) 55 { 56 // Make a list of all devices. The iterator goes top-down, and we want 57 // bottom-up. Note that each layer can also have an offset in canvas 58 // coordinates, which is the (x, y) position. 59 struct DeviceInfo { 60 DeviceInfo(SkBaseDevice* d, int lx, int ly) 61 : device(d) 62 , x(lx) 63 , y(ly) { } 64 SkBaseDevice* device; 65 int x; 66 int y; 67 }; 68 Vector<DeviceInfo> devices; 69 SkCanvas* sourceCanvas = sourceLayers.canvas(); 70 SkCanvas::LayerIter iter(sourceCanvas, false); 71 while (!iter.done()) { 72 devices.append(DeviceInfo(iter.device(), iter.x(), iter.y())); 73 iter.next(); 74 } 75 76 // Create a temporary canvas for the compositing into the destination. 77 SkBitmap* destBmp = const_cast<SkBitmap*>(&bitmapForContext(destContext)); 78 SkCanvas destCanvas(*destBmp); 79 destCanvas.setMatrix(affineTransformToSkMatrix(matrix)); 80 81 for (int i = devices.size() - 1; i >= 0; i--) { 82 const SkBitmap& srcBmp = devices[i].device->accessBitmap(false); 83 84 SkRect destRect; 85 destRect.fLeft = devices[i].x; 86 destRect.fTop = devices[i].y; 87 destRect.fRight = destRect.fLeft + srcBmp.width(); 88 destRect.fBottom = destRect.fTop + srcBmp.height(); 89 90 destCanvas.drawBitmapRect(srcBmp, 0, destRect); 91 } 92 } 93 94 } // namespace 95 96 // If either of these pointers is non-null, both must be valid and point to 97 // bitmaps of the same size. 98 class TransparencyWin::OwnedBuffers { 99 public: 100 OwnedBuffers(const IntSize& size, bool needReferenceBuffer) 101 { 102 m_destBitmap = ImageBuffer::create(size); 103 104 if (needReferenceBuffer) { 105 m_referenceBitmap.setConfig(SkBitmap::kARGB_8888_Config, size.width(), size.height()); 106 m_referenceBitmap.allocPixels(); 107 m_referenceBitmap.eraseARGB(0, 0, 0, 0); 108 } 109 } 110 111 ImageBuffer* destBitmap() { return m_destBitmap.get(); } 112 113 // This bitmap will be empty if you don't specify needReferenceBuffer to the 114 // constructor. 115 SkBitmap* referenceBitmap() { return &m_referenceBitmap; } 116 117 // Returns whether the current layer will fix a buffer of the given size. 118 bool canHandleSize(const IntSize& size) const 119 { 120 return m_destBitmap->size().width() >= size.width() && m_destBitmap->size().height() >= size.height(); 121 } 122 123 private: 124 // The destination bitmap we're drawing into. 125 OwnPtr<ImageBuffer> m_destBitmap; 126 127 // This could be an ImageBuffer but this is an optimization. Since this is 128 // only ever used as a reference, we don't need to make a full 129 // PlatformCanvas using Skia on Windows. Just allocating a regular SkBitmap 130 // is much faster since it's just a Malloc rather than a GDI call. 131 SkBitmap m_referenceBitmap; 132 }; 133 134 TransparencyWin::OwnedBuffers* TransparencyWin::m_cachedBuffers = 0; 135 136 TransparencyWin::TransparencyWin() 137 : m_destContext(0) 138 , m_orgTransform() 139 , m_layerMode(NoLayer) 140 , m_transformMode(KeepTransform) 141 , m_drawContext(0) 142 , m_savedOnDrawContext(false) 143 , m_layerBuffer(0) 144 , m_referenceBitmap(0) 145 , m_validLayer(false) 146 { 147 } 148 149 TransparencyWin::~TransparencyWin() 150 { 151 // This should be false, since calling composite() is mandatory. 152 ASSERT(!m_savedOnDrawContext); 153 } 154 155 void TransparencyWin::composite() 156 { 157 // Matches the save() in initializeNewTextContext (or the constructor for 158 // SCALE) to put the context back into the same state we found it. 159 if (m_savedOnDrawContext) { 160 m_drawContext->restore(); 161 m_savedOnDrawContext = false; 162 } 163 164 switch (m_layerMode) { 165 case NoLayer: 166 break; 167 case OpaqueCompositeLayer: 168 case WhiteLayer: 169 compositeOpaqueComposite(); 170 break; 171 case TextComposite: 172 compositeTextComposite(); 173 break; 174 } 175 } 176 177 void TransparencyWin::init(GraphicsContext* dest, LayerMode layerMode, TransformMode transformMode, const IntRect& region) 178 { 179 m_destContext = dest; 180 m_orgTransform = dest->getCTM(); 181 m_layerMode = layerMode; 182 m_transformMode = transformMode; 183 m_sourceRect = region; 184 185 computeLayerSize(); 186 setupLayer(); 187 setupTransform(region); 188 } 189 190 void TransparencyWin::computeLayerSize() 191 { 192 if (m_transformMode == Untransform) { 193 // The meaning of the "transformed" source rect is a little ambigous 194 // here. The rest of the code doesn't care about it in the Untransform 195 // case since we're using our own happy coordinate system. So we set it 196 // to be the source rect since that matches how the code below actually 197 // uses the variable: to determine how to translate things to account 198 // for the offset of the layer. 199 m_transformedSourceRect = m_sourceRect; 200 } else if (m_transformMode == KeepTransform && m_layerMode != TextComposite) { 201 // FIXME: support clipping for other modes 202 IntRect clippedSourceRect = m_sourceRect; 203 SkRect clipBounds; 204 if (m_destContext->getClipBounds(&clipBounds)) { 205 FloatRect clipRect(clipBounds.left(), clipBounds.top(), clipBounds.width(), clipBounds.height()); 206 clippedSourceRect.intersect(enclosingIntRect(clipRect)); 207 } 208 m_transformedSourceRect = m_orgTransform.mapRect(clippedSourceRect); 209 } else { 210 m_transformedSourceRect = m_orgTransform.mapRect(m_sourceRect); 211 } 212 213 m_layerSize = IntSize(m_transformedSourceRect.width(), m_transformedSourceRect.height()); 214 } 215 216 void TransparencyWin::setupLayer() 217 { 218 switch (m_layerMode) { 219 case NoLayer: 220 setupLayerForNoLayer(); 221 break; 222 case OpaqueCompositeLayer: 223 setupLayerForOpaqueCompositeLayer(); 224 break; 225 case TextComposite: 226 setupLayerForTextComposite(); 227 break; 228 case WhiteLayer: 229 setupLayerForWhiteLayer(); 230 break; 231 } 232 } 233 234 void TransparencyWin::setupLayerForNoLayer() 235 { 236 m_drawContext = m_destContext; // Draw to the source context. 237 m_validLayer = true; 238 } 239 240 void TransparencyWin::setupLayerForOpaqueCompositeLayer() 241 { 242 initializeNewContext(); 243 if (!m_validLayer) 244 return; 245 246 AffineTransform mapping; 247 mapping.translate(-m_transformedSourceRect.x(), -m_transformedSourceRect.y()); 248 if (m_transformMode == Untransform) { 249 // Compute the inverse mapping from the canvas space to the 250 // coordinate space of our bitmap. 251 mapping *= m_orgTransform.inverse(); 252 } 253 compositeToCopy(*m_destContext, *m_drawContext, mapping); 254 255 // Save the reference layer so we can tell what changed. 256 SkCanvas referenceCanvas(*m_referenceBitmap); 257 referenceCanvas.drawBitmap(bitmapForContext(*m_drawContext), 0, 0); 258 // Layer rect represents the part of the original layer. 259 } 260 261 void TransparencyWin::setupLayerForTextComposite() 262 { 263 ASSERT(m_transformMode == KeepTransform); 264 // Fall through to filling with white. 265 setupLayerForWhiteLayer(); 266 } 267 268 void TransparencyWin::setupLayerForWhiteLayer() 269 { 270 initializeNewContext(); 271 if (!m_validLayer) 272 return; 273 274 m_drawContext->fillRect(IntRect(IntPoint(0, 0), m_layerSize), Color::white); 275 // Layer rect represents the part of the original layer. 276 } 277 278 void TransparencyWin::setupTransform(const IntRect& region) 279 { 280 switch (m_transformMode) { 281 case KeepTransform: 282 setupTransformForKeepTransform(region); 283 break; 284 case Untransform: 285 setupTransformForUntransform(); 286 break; 287 case ScaleTransform: 288 setupTransformForScaleTransform(); 289 break; 290 } 291 } 292 293 void TransparencyWin::setupTransformForKeepTransform(const IntRect& region) 294 { 295 if (!m_validLayer) 296 return; 297 298 if (m_layerMode != NoLayer) { 299 // Need to save things since we're modifying the transform. 300 m_drawContext->save(); 301 m_savedOnDrawContext = true; 302 303 // Account for the fact that the layer may be offset from the 304 // original. This only happens when we create a layer that has the 305 // same coordinate space as the parent. 306 AffineTransform xform; 307 xform.translate(-m_transformedSourceRect.x(), -m_transformedSourceRect.y()); 308 309 // We're making a layer, so apply the old transform to the new one 310 // so it's maintained. We know the new layer has the identity 311 // transform now, we we can just multiply it. 312 xform *= m_orgTransform; 313 m_drawContext->concatCTM(xform); 314 } 315 m_drawRect = m_sourceRect; 316 } 317 318 void TransparencyWin::setupTransformForUntransform() 319 { 320 ASSERT(m_layerMode != NoLayer); 321 // We now have a new layer with the identity transform, which is the 322 // Untransformed space we'll use for drawing. 323 m_drawRect = IntRect(IntPoint(0, 0), m_layerSize); 324 } 325 326 void TransparencyWin::setupTransformForScaleTransform() 327 { 328 if (!m_validLayer) 329 return; 330 331 if (m_layerMode == NoLayer) { 332 // Need to save things since we're modifying the layer. 333 m_drawContext->save(); 334 m_savedOnDrawContext = true; 335 336 // Undo the transform on the current layer when we're re-using the 337 // current one. 338 m_drawContext->concatCTM(m_drawContext->getCTM().inverse()); 339 340 // We're drawing to the original layer with just a different size. 341 m_drawRect = m_transformedSourceRect; 342 } else { 343 // Just go ahead and use the layer's coordinate space to draw into. 344 // It will have the scaled size, and an identity transform loaded. 345 m_drawRect = IntRect(IntPoint(0, 0), m_layerSize); 346 } 347 } 348 349 void TransparencyWin::setTextCompositeColor(Color color) 350 { 351 m_textCompositeColor = color; 352 } 353 354 void TransparencyWin::initializeNewContext() 355 { 356 int pixelSize = m_layerSize.width() * m_layerSize.height(); 357 if (pixelSize <= 0) 358 return; 359 360 if (pixelSize > maxCachedBufferPixelSize) { 361 // Create a 1-off buffer for drawing into. We only need the reference 362 // buffer if we're making an OpaqueCompositeLayer. 363 bool needReferenceBitmap = m_layerMode == OpaqueCompositeLayer; 364 m_ownedBuffers = adoptPtr(new OwnedBuffers(m_layerSize, needReferenceBitmap)); 365 m_layerBuffer = m_ownedBuffers->destBitmap(); 366 if (!m_layerBuffer) 367 return; 368 369 m_drawContext = m_layerBuffer->context(); 370 if (needReferenceBitmap) { 371 m_referenceBitmap = m_ownedBuffers->referenceBitmap(); 372 if (!m_referenceBitmap || !m_referenceBitmap->getPixels()) 373 return; 374 } 375 m_validLayer = true; 376 return; 377 } 378 379 if (m_cachedBuffers && m_cachedBuffers->canHandleSize(m_layerSize)) { 380 // We can re-use the existing buffer. We don't need to clear it since 381 // all layer modes will clear it in their initialization. 382 m_layerBuffer = m_cachedBuffers->destBitmap(); 383 m_drawContext = m_cachedBuffers->destBitmap()->context(); 384 bitmapForContext(*m_drawContext).eraseARGB(0, 0, 0, 0); 385 m_referenceBitmap = m_cachedBuffers->referenceBitmap(); 386 m_referenceBitmap->eraseARGB(0, 0, 0, 0); 387 m_validLayer = true; 388 return; 389 } 390 391 // Create a new cached buffer. 392 if (m_cachedBuffers) 393 delete m_cachedBuffers; 394 m_cachedBuffers = new OwnedBuffers(m_layerSize, true); 395 396 m_layerBuffer = m_cachedBuffers->destBitmap(); 397 m_drawContext = m_cachedBuffers->destBitmap()->context(); 398 m_referenceBitmap = m_cachedBuffers->referenceBitmap(); 399 m_validLayer = true; 400 } 401 402 void TransparencyWin::compositeOpaqueComposite() 403 { 404 if (!m_validLayer) 405 return; 406 407 m_destContext->save(); 408 409 SkBitmap* bitmap = const_cast<SkBitmap*>( 410 &bitmapForContext(*m_layerBuffer->context())); 411 412 // This function will be called for WhiteLayer as well, which we don't want 413 // to change. 414 if (m_layerMode == OpaqueCompositeLayer) { 415 // Fix up our bitmap, making it contain only the pixels which changed 416 // and transparent everywhere else. 417 SkAutoLockPixels sourceLock(*m_referenceBitmap); 418 SkAutoLockPixels lock(*bitmap); 419 for (int y = 0; y < bitmap->height(); y++) { 420 uint32_t* source = m_referenceBitmap->getAddr32(0, y); 421 uint32_t* dest = bitmap->getAddr32(0, y); 422 for (int x = 0; x < bitmap->width(); x++) { 423 // Clear out any pixels that were untouched. 424 if (dest[x] == source[x]) 425 dest[x] = 0; 426 else 427 dest[x] |= (0xFF << SK_A32_SHIFT); 428 } 429 } 430 } else { 431 makeLayerOpaque(); 432 } 433 434 SkRect destRect; 435 if (m_transformMode != Untransform) { 436 // We want to use Untransformed space. 437 // 438 // Note that we DON'T call m_layerBuffer->image() here. This actually 439 // makes a copy of the image, which is unnecessary and slow. Instead, we 440 // just draw the image from inside the destination context. 441 SkMatrix identity; 442 identity.reset(); 443 m_destContext->setMatrix(identity); 444 445 destRect.set(m_transformedSourceRect.x(), m_transformedSourceRect.y(), m_transformedSourceRect.maxX(), m_transformedSourceRect.maxY()); 446 } else { 447 destRect.set(m_sourceRect.x(), m_sourceRect.y(), m_sourceRect.maxX(), m_sourceRect.maxY()); 448 } 449 450 SkPaint paint; 451 paint.setFilterBitmap(true); 452 paint.setAntiAlias(true); 453 454 // Note that we need to specify the source layer subset, since the bitmap 455 // may have been cached and it could be larger than what we're using. 456 SkRect sourceRect = SkRect::MakeWH( 457 SkIntToScalar(m_layerSize.width()), 458 SkIntToScalar(m_layerSize.height())); 459 m_destContext->drawBitmapRect(*bitmap, &sourceRect, destRect, &paint); 460 m_destContext->restore(); 461 } 462 463 void TransparencyWin::compositeTextComposite() 464 { 465 if (!m_validLayer) 466 return; 467 468 const SkBitmap& bitmap = m_layerBuffer->context()->layerBitmap(GraphicsContext::ReadWrite); 469 SkColor textColor = m_textCompositeColor.rgb(); 470 for (int y = 0; y < m_layerSize.height(); y++) { 471 uint32_t* row = bitmap.getAddr32(0, y); 472 for (int x = 0; x < m_layerSize.width(); x++) { 473 // The alpha is the average of the R, G, and B channels. 474 int alpha = (SkGetPackedR32(row[x]) + SkGetPackedG32(row[x]) + SkGetPackedB32(row[x])) / 3; 475 476 // Apply that alpha to the text color and write the result. 477 row[x] = SkAlphaMulQ(textColor, SkAlpha255To256(255 - alpha)); 478 } 479 } 480 481 // Now the layer has text with the proper color and opacity. 482 m_destContext->save(); 483 484 // We want to use Untransformed space (see above) 485 SkMatrix identity; 486 identity.reset(); 487 m_destContext->setMatrix(identity); 488 SkRect destRect = { m_transformedSourceRect.x(), m_transformedSourceRect.y(), m_transformedSourceRect.maxX(), m_transformedSourceRect.maxY() }; 489 490 // Note that we need to specify the source layer subset, since the bitmap 491 // may have been cached and it could be larger than what we're using. 492 SkRect sourceRect = SkRect::MakeWH( 493 SkIntToScalar(m_layerSize.width()), 494 SkIntToScalar(m_layerSize.height())); 495 m_destContext->drawBitmapRect(bitmap, &sourceRect, destRect, 0); 496 m_destContext->restore(); 497 } 498 499 void TransparencyWin::makeLayerOpaque() 500 { 501 if (!m_validLayer) 502 return; 503 504 SkBitmap& bitmap = const_cast<SkBitmap&>(m_drawContext->layerBitmap(GraphicsContext::ReadWrite)); 505 for (int y = 0; y < m_layerSize.height(); y++) { 506 uint32_t* row = bitmap.getAddr32(0, y); 507 for (int x = 0; x < m_layerSize.width(); x++) 508 row[x] |= 0xFF000000; 509 } 510 } 511 512 } // namespace WebCore 513