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 "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