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