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