summaryrefslogtreecommitdiffstats
path: root/WebCore/platform/graphics/chromium/TransparencyWin.cpp
diff options
context:
space:
mode:
authorFeng Qian <>2009-04-10 18:11:29 -0700
committerThe Android Open Source Project <initial-contribution@android.com>2009-04-10 18:11:29 -0700
commit8f72e70a9fd78eec56623b3a62e68f16b7b27e28 (patch)
tree181bf9a400c30a1bf34ea6d72560e8d00111d549 /WebCore/platform/graphics/chromium/TransparencyWin.cpp
parent7ed56f225e0ade046e1c2178977f72b2d896f196 (diff)
downloadexternal_webkit-8f72e70a9fd78eec56623b3a62e68f16b7b27e28.zip
external_webkit-8f72e70a9fd78eec56623b3a62e68f16b7b27e28.tar.gz
external_webkit-8f72e70a9fd78eec56623b3a62e68f16b7b27e28.tar.bz2
AI 145796: Land the WebKit merge @r42026.
Automated import of CL 145796
Diffstat (limited to 'WebCore/platform/graphics/chromium/TransparencyWin.cpp')
-rw-r--r--WebCore/platform/graphics/chromium/TransparencyWin.cpp480
1 files changed, 480 insertions, 0 deletions
diff --git a/WebCore/platform/graphics/chromium/TransparencyWin.cpp b/WebCore/platform/graphics/chromium/TransparencyWin.cpp
new file mode 100644
index 0000000..8c790af
--- /dev/null
+++ b/WebCore/platform/graphics/chromium/TransparencyWin.cpp
@@ -0,0 +1,480 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include <windows.h>
+
+#include "GraphicsContext.h"
+#include "ImageBuffer.h"
+#include "PlatformContextSkia.h"
+#include "SimpleFontData.h"
+#include "TransformationMatrix.h"
+#include "TransparencyWin.h"
+
+#include "SkColorPriv.h"
+#include "skia/ext/platform_canvas.h"
+
+namespace WebCore {
+
+namespace {
+
+// The maximum size in pixels of the buffer we'll keep around for drawing text
+// into. Buffers larger than this will be destroyed when we're done with them.
+const int maxCachedBufferPixelSize = 65536;
+
+inline skia::PlatformCanvas* canvasForContext(const GraphicsContext& context)
+{
+ return context.platformContext()->canvas();
+}
+
+inline const SkBitmap& bitmapForContext(const GraphicsContext& context)
+{
+ return canvasForContext(context)->getTopPlatformDevice().accessBitmap(false);
+}
+
+void compositeToCopy(const GraphicsContext& sourceLayers,
+ GraphicsContext& destContext,
+ const TransformationMatrix& matrix)
+{
+ // Make a list of all devices. The iterator goes top-down, and we want
+ // bottom-up. Note that each layer can also have an offset in canvas
+ // coordinates, which is the (x, y) position.
+ struct DeviceInfo {
+ DeviceInfo(SkDevice* d, int lx, int ly)
+ : device(d)
+ , x(lx)
+ , y(ly) {}
+ SkDevice* device;
+ int x;
+ int y;
+ };
+ Vector<DeviceInfo> devices;
+ SkCanvas* sourceCanvas = canvasForContext(sourceLayers);
+ SkCanvas::LayerIter iter(sourceCanvas, false);
+ while (!iter.done()) {
+ devices.append(DeviceInfo(iter.device(), iter.x(), iter.y()));
+ iter.next();
+ }
+
+ // Create a temporary canvas for the compositing into the destination.
+ SkBitmap* destBmp = const_cast<SkBitmap*>(&bitmapForContext(destContext));
+ SkCanvas destCanvas(*destBmp);
+ destCanvas.setMatrix(matrix);
+
+ for (int i = devices.size() - 1; i >= 0; i--) {
+ const SkBitmap& srcBmp = devices[i].device->accessBitmap(false);
+
+ SkRect destRect;
+ destRect.fLeft = devices[i].x;
+ destRect.fTop = devices[i].y;
+ destRect.fRight = destRect.fLeft + srcBmp.width();
+ destRect.fBottom = destRect.fTop + srcBmp.height();
+
+ destCanvas.drawBitmapRect(srcBmp, 0, destRect);
+ }
+}
+
+} // namespace
+
+// If either of these pointers is non-null, both must be valid and point to
+// bitmaps of the same size.
+class TransparencyWin::OwnedBuffers {
+public:
+ OwnedBuffers(const IntSize& size, bool needReferenceBuffer)
+ {
+ m_destBitmap.adopt(ImageBuffer::create(size, false));
+
+ if (needReferenceBuffer) {
+ m_referenceBitmap.setConfig(SkBitmap::kARGB_8888_Config, size.width(), size.height());
+ m_referenceBitmap.allocPixels();
+ m_referenceBitmap.eraseARGB(0, 0, 0, 0);
+ }
+ }
+
+ ImageBuffer* destBitmap() { return m_destBitmap.get(); }
+
+ // This bitmap will be empty if you don't specify needReferenceBuffer to the
+ // constructor.
+ SkBitmap* referenceBitmap() { return &m_referenceBitmap; }
+
+ // Returns whether the current layer will fix a buffer of the given size.
+ bool canHandleSize(const IntSize& size) const
+ {
+ return m_destBitmap->size().width() >= size.width() && m_destBitmap->size().height() >= size.height();
+ }
+
+private:
+ // The destination bitmap we're drawing into.
+ OwnPtr<ImageBuffer> m_destBitmap;
+
+ // This could be an ImageBuffer but this is an optimization. Since this is
+ // only ever used as a reference, we don't need to make a full
+ // PlatformCanvas using Skia on Windows. Just allocating a regular SkBitmap
+ // is much faster since it's just a Malloc rather than a GDI call.
+ SkBitmap m_referenceBitmap;
+};
+
+TransparencyWin::OwnedBuffers* TransparencyWin::m_cachedBuffers = 0;
+
+TransparencyWin::TransparencyWin()
+ : m_destContext(0)
+ , m_orgTransform()
+ , m_layerMode(NoLayer)
+ , m_transformMode(KeepTransform)
+ , m_drawContext(0)
+ , m_savedOnDrawContext(false)
+ , m_layerBuffer(0)
+ , m_referenceBitmap(0)
+{
+}
+
+TransparencyWin::~TransparencyWin()
+{
+ // This should be false, since calling composite() is mandatory.
+ ASSERT(!m_savedOnDrawContext);
+}
+
+void TransparencyWin::composite()
+{
+ // Matches the save() in initializeNewTextContext (or the constructor for
+ // SCALE) to put the context back into the same state we found it.
+ if (m_savedOnDrawContext) {
+ m_drawContext->restore();
+ m_savedOnDrawContext = false;
+ }
+
+ switch (m_layerMode) {
+ case NoLayer:
+ break;
+ case OpaqueCompositeLayer:
+ case WhiteLayer:
+ compositeOpaqueComposite();
+ break;
+ case TextComposite:
+ compositeTextComposite();
+ break;
+ }
+}
+
+void TransparencyWin::init(GraphicsContext* dest,
+ LayerMode layerMode,
+ TransformMode transformMode,
+ const IntRect& region)
+{
+ m_destContext = dest;
+ m_orgTransform = dest->getCTM();
+ m_layerMode = layerMode;
+ m_transformMode = transformMode;
+ m_sourceRect = region;
+
+ computeLayerSize();
+ setupLayer();
+ setupTransform(region);
+}
+
+void TransparencyWin::computeLayerSize()
+{
+ if (m_transformMode == Untransform) {
+ // The meaning of the "transformed" source rect is a little ambigous
+ // here. The rest of the code doesn't care about it in the Untransform
+ // case since we're using our own happy coordinate system. So we set it
+ // to be the source rect since that matches how the code below actually
+ // uses the variable: to determine how to translate things to account
+ // for the offset of the layer.
+ m_transformedSourceRect = m_sourceRect;
+ m_layerSize = IntSize(m_sourceRect.width(), m_sourceRect.height());
+ } else {
+ m_transformedSourceRect = m_orgTransform.mapRect(m_sourceRect);
+ m_layerSize = IntSize(m_transformedSourceRect.width(), m_transformedSourceRect.height());
+ }
+}
+
+void TransparencyWin::setupLayer()
+{
+ switch (m_layerMode) {
+ case NoLayer:
+ setupLayerForNoLayer();
+ break;
+ case OpaqueCompositeLayer:
+ setupLayerForOpaqueCompositeLayer();
+ break;
+ case TextComposite:
+ setupLayerForTextComposite();
+ break;
+ case WhiteLayer:
+ setupLayerForWhiteLayer();
+ break;
+ }
+}
+
+void TransparencyWin::setupLayerForNoLayer()
+{
+ m_drawContext = m_destContext; // Draw to the source context.
+}
+
+void TransparencyWin::setupLayerForOpaqueCompositeLayer()
+{
+ initializeNewContext();
+
+ TransformationMatrix mapping;
+ mapping.translate(-m_transformedSourceRect.x(), -m_transformedSourceRect.y());
+ if (m_transformMode == Untransform){
+ // Compute the inverse mapping from the canvas space to the
+ // coordinate space of our bitmap.
+ mapping = m_orgTransform.inverse() * mapping;
+ }
+ compositeToCopy(*m_destContext, *m_drawContext, mapping);
+
+ // Save the reference layer so we can tell what changed.
+ SkCanvas referenceCanvas(*m_referenceBitmap);
+ referenceCanvas.drawBitmap(bitmapForContext(*m_drawContext), 0, 0);
+ // Layer rect represents the part of the original layer.
+}
+
+void TransparencyWin::setupLayerForTextComposite()
+{
+ ASSERT(m_transformMode == KeepTransform);
+ // Fall through to filling with white.
+ setupLayerForWhiteLayer();
+}
+
+void TransparencyWin::setupLayerForWhiteLayer()
+{
+ initializeNewContext();
+ m_drawContext->fillRect(IntRect(IntPoint(0, 0), m_layerSize), Color::white);
+ // Layer rect represents the part of the original layer.
+}
+
+void TransparencyWin::setupTransform(const IntRect& region)
+{
+ switch (m_transformMode) {
+ case KeepTransform:
+ setupTransformForKeepTransform(region);
+ break;
+ case Untransform:
+ setupTransformForUntransform();
+ break;
+ case ScaleTransform:
+ setupTransformForScaleTransform();
+ break;
+ }
+}
+
+void TransparencyWin::setupTransformForKeepTransform(const IntRect& region)
+{
+ if (m_layerMode != NoLayer) {
+ // Need to save things since we're modifying the transform.
+ m_drawContext->save();
+ m_savedOnDrawContext = true;
+
+ // Account for the fact that the layer may be offset from the
+ // original. This only happens when we create a layer that has the
+ // same coordinate space as the parent.
+ TransformationMatrix xform;
+ xform.translate(-m_transformedSourceRect.x(), -m_transformedSourceRect.y());
+
+ // We're making a layer, so apply the old transform to the new one
+ // so it's maintained. We know the new layer has the identity
+ // transform now, we we can just multiply it.
+ xform = m_orgTransform * xform;
+ m_drawContext->concatCTM(xform);
+ }
+ m_drawRect = m_sourceRect;
+}
+
+void TransparencyWin::setupTransformForUntransform()
+{
+ ASSERT(m_layerMode != NoLayer);
+ // We now have a new layer with the identity transform, which is the
+ // Untransformed space we'll use for drawing.
+ m_drawRect = IntRect(IntPoint(0, 0), m_layerSize);
+}
+
+void TransparencyWin::setupTransformForScaleTransform()
+{
+ if (m_layerMode == NoLayer) {
+ // Need to save things since we're modifying the layer.
+ m_drawContext->save();
+ m_savedOnDrawContext = true;
+
+ // Undo the transform on the current layer when we're re-using the
+ // current one.
+ m_drawContext->concatCTM(m_drawContext->getCTM().inverse());
+
+ // We're drawing to the original layer with just a different size.
+ m_drawRect = m_transformedSourceRect;
+ } else {
+ // Just go ahead and use the layer's coordinate space to draw into.
+ // It will have the scaled size, and an identity transform loaded.
+ m_drawRect = IntRect(IntPoint(0, 0), m_layerSize);
+ }
+}
+
+void TransparencyWin::setTextCompositeColor(Color color)
+{
+ m_textCompositeColor = color;
+}
+
+void TransparencyWin::initializeNewContext()
+{
+ int pixelSize = m_layerSize.width() * m_layerSize.height();
+ if (pixelSize > maxCachedBufferPixelSize) {
+ // Create a 1-off buffer for drawing into. We only need the reference
+ // buffer if we're making an OpaqueCompositeLayer.
+ bool needReferenceBitmap = m_layerMode == OpaqueCompositeLayer;
+ m_ownedBuffers.set(new OwnedBuffers(m_layerSize, needReferenceBitmap));
+
+ m_layerBuffer = m_ownedBuffers->destBitmap();
+ m_drawContext = m_layerBuffer->context();
+ if (needReferenceBitmap)
+ m_referenceBitmap = m_ownedBuffers->referenceBitmap();
+ return;
+ }
+
+ if (m_cachedBuffers && m_cachedBuffers->canHandleSize(m_layerSize)) {
+ // We can re-use the existing buffer. We don't need to clear it since
+ // all layer modes will clear it in their initialization.
+ m_layerBuffer = m_cachedBuffers->destBitmap();
+ m_drawContext = m_cachedBuffers->destBitmap()->context();
+ bitmapForContext(*m_drawContext).eraseARGB(0, 0, 0, 0);
+ m_referenceBitmap = m_cachedBuffers->referenceBitmap();
+ m_referenceBitmap->eraseARGB(0, 0, 0, 0);
+ return;
+ }
+
+ // Create a new cached buffer.
+ if (m_cachedBuffers)
+ delete m_cachedBuffers;
+ m_cachedBuffers = new OwnedBuffers(m_layerSize, true);
+
+ m_layerBuffer = m_cachedBuffers->destBitmap();
+ m_drawContext = m_cachedBuffers->destBitmap()->context();
+ m_referenceBitmap = m_cachedBuffers->referenceBitmap();
+}
+
+void TransparencyWin::compositeOpaqueComposite()
+{
+ SkCanvas* destCanvas = canvasForContext(*m_destContext);
+ destCanvas->save();
+
+ SkBitmap* bitmap = const_cast<SkBitmap*>(
+ &bitmapForContext(*m_layerBuffer->context()));
+
+ // This function will be called for WhiteLayer as well, which we don't want
+ // to change.
+ if (m_layerMode == OpaqueCompositeLayer) {
+ // Fix up our bitmap, making it contain only the pixels which changed
+ // and transparent everywhere else.
+ SkAutoLockPixels sourceLock(*m_referenceBitmap);
+ SkAutoLockPixels lock(*bitmap);
+ for (int y = 0; y < bitmap->height(); y++) {
+ uint32_t* source = m_referenceBitmap->getAddr32(0, y);
+ uint32_t* dest = bitmap->getAddr32(0, y);
+ for (int x = 0; x < bitmap->width(); x++) {
+ // Clear out any pixels that were untouched.
+ if (dest[x] == source[x])
+ dest[x] = 0;
+ else
+ dest[x] |= (0xFF << SK_A32_SHIFT);
+ }
+ }
+ } else
+ makeLayerOpaque();
+
+ SkRect destRect;
+ if (m_transformMode != Untransform) {
+ // We want to use Untransformed space.
+ //
+ // Note that we DON'T call m_layerBuffer->image() here. This actually
+ // makes a copy of the image, which is unnecessary and slow. Instead, we
+ // just draw the image from inside the destination context.
+ SkMatrix identity;
+ identity.reset();
+ destCanvas->setMatrix(identity);
+
+ destRect.set(m_transformedSourceRect.x(), m_transformedSourceRect.y(), m_transformedSourceRect.right(), m_transformedSourceRect.bottom());
+ } else
+ destRect.set(m_sourceRect.x(), m_sourceRect.y(), m_sourceRect.right(), m_sourceRect.bottom());
+
+ SkPaint paint;
+ paint.setFilterBitmap(true);
+ paint.setAntiAlias(true);
+
+ // Note that we need to specify the source layer subset, since the bitmap
+ // may have been cached and it could be larger than what we're using.
+ SkIRect sourceRect = { 0, 0, m_layerSize.width(), m_layerSize.height() };
+ destCanvas->drawBitmapRect(*bitmap, &sourceRect, destRect, &paint);
+ destCanvas->restore();
+}
+
+void TransparencyWin::compositeTextComposite()
+{
+ const SkBitmap& bitmap = m_layerBuffer->context()->platformContext()->canvas()->getTopPlatformDevice().accessBitmap(true);
+ SkColor textColor = m_textCompositeColor.rgb();
+ for (int y = 0; y < m_layerSize.height(); y++) {
+ uint32_t* row = bitmap.getAddr32(0, y);
+ for (int x = 0; x < m_layerSize.width(); x++) {
+ // The alpha is the average of the R, G, and B channels.
+ int alpha = (SkGetPackedR32(row[x]) + SkGetPackedG32(row[x]) + SkGetPackedB32(row[x])) / 3;
+
+ // Apply that alpha to the text color and write the result.
+ row[x] = SkAlphaMulQ(textColor, SkAlpha255To256(255 - alpha));
+ }
+ }
+
+ // Now the layer has text with the proper color and opacity.
+ SkCanvas* destCanvas = canvasForContext(*m_destContext);
+
+ // We want to use Untransformed space (see above)
+ SkMatrix identity;
+ identity.reset();
+ destCanvas->setMatrix(identity);
+ SkRect destRect = { m_transformedSourceRect.x(), m_transformedSourceRect.y(), m_transformedSourceRect.right(), m_transformedSourceRect.bottom() };
+
+ // Note that we need to specify the source layer subset, since the bitmap
+ // may have been cached and it could be larger than what we're using.
+ SkIRect sourceRect = { 0, 0, m_layerSize.width(), m_layerSize.height() };
+ destCanvas->drawBitmapRect(bitmap, &sourceRect, destRect, 0);
+ destCanvas->restore();
+}
+
+void TransparencyWin::makeLayerOpaque()
+{
+ SkBitmap& bitmap = const_cast<SkBitmap&>(m_drawContext->platformContext()->
+ canvas()->getTopPlatformDevice().accessBitmap(true));
+ for (int y = 0; y < m_layerSize.height(); y++) {
+ uint32_t* row = bitmap.getAddr32(0, y);
+ for (int x = 0; x < m_layerSize.width(); x++)
+ row[x] |= 0xFF000000;
+ }
+}
+
+} // namespace WebCore
+