1 /* 2 * Copyright 2015 Julian Harnath <julian.harnath@rwth-aachen.de> 3 * All rights reserved. Distributed under the terms of the MIT license. 4 */ 5 #include "Layer.h" 6 7 #include "AlphaMask.h" 8 #include "BitmapHWInterface.h" 9 #include "DrawingEngine.h" 10 #include "DrawState.h" 11 #include "IntRect.h" 12 #include "PictureBoundingBoxPlayer.h" 13 #include "ServerBitmap.h" 14 #include "View.h" 15 16 17 class LayerCanvas : public Canvas { 18 public: 19 LayerCanvas(DrawingEngine* drawingEngine, DrawState* drawState, 20 BRect bitmapBounds) 21 : 22 Canvas(), 23 fDrawingEngine(drawingEngine), 24 fBitmapBounds(bitmapBounds) 25 { 26 fDrawState.SetTo(drawState); 27 } 28 29 virtual DrawingEngine* GetDrawingEngine() const 30 { 31 return fDrawingEngine; 32 } 33 34 virtual ServerPicture* GetPicture(int32 token) const 35 { 36 return NULL; 37 } 38 39 virtual void RebuildClipping(bool) 40 { 41 } 42 43 virtual void ResyncDrawState() 44 { 45 fDrawingEngine->SetDrawState(fDrawState.Get()); 46 } 47 48 virtual void UpdateCurrentDrawingRegion() 49 { 50 bool hasDrawStateClipping = fDrawState->GetCombinedClippingRegion( 51 &fCurrentDrawingRegion); 52 53 BRegion bitmapRegion(fBitmapBounds); 54 if (hasDrawStateClipping) 55 fCurrentDrawingRegion.IntersectWith(&bitmapRegion); 56 else 57 fCurrentDrawingRegion = bitmapRegion; 58 59 fDrawingEngine->ConstrainClippingRegion(&fCurrentDrawingRegion); 60 } 61 62 virtual IntRect Bounds() const 63 { 64 return fBitmapBounds; 65 } 66 67 protected: 68 virtual void _LocalToScreenTransform(SimpleTransform&) const 69 { 70 } 71 72 virtual void _ScreenToLocalTransform(SimpleTransform&) const 73 { 74 } 75 76 private: 77 DrawingEngine* fDrawingEngine; 78 BRegion fCurrentDrawingRegion; 79 BRect fBitmapBounds; 80 }; 81 82 83 Layer::Layer(uint8 opacity) 84 : 85 fOpacity(opacity), 86 fLeftTopOffset(0, 0) 87 { 88 } 89 90 91 Layer::~Layer() 92 { 93 } 94 95 96 void 97 Layer::PushLayer(Layer* layer) 98 { 99 PushPicture(layer); 100 } 101 102 103 Layer* 104 Layer::PopLayer() 105 { 106 return static_cast<Layer*>(PopPicture()); 107 } 108 109 110 UtilityBitmap* 111 Layer::RenderToBitmap(Canvas* canvas) 112 { 113 BRect boundingBox = _DetermineBoundingBox(canvas); 114 if (!boundingBox.IsValid()) 115 return NULL; 116 117 fLeftTopOffset = boundingBox.LeftTop(); 118 119 BReference<UtilityBitmap> layerBitmap(_AllocateBitmap(boundingBox), true); 120 if (layerBitmap == NULL) 121 return NULL; 122 123 BitmapHWInterface layerInterface(layerBitmap); 124 ObjectDeleter<DrawingEngine> const layerEngine(layerInterface.CreateDrawingEngine()); 125 if (!layerEngine.IsSet()) 126 return NULL; 127 128 layerEngine->SetRendererOffset(boundingBox.left, boundingBox.top); 129 // Drawing commands of the layer's picture use coordinates in the 130 // coordinate space of the underlying canvas. The coordinate origin 131 // of the layer bitmap is at boundingBox.LeftTop(). So all the drawing 132 // from the picture needs to be offset to be moved into the bitmap. 133 // We use a low-level offsetting via the AGG renderer here because the 134 // offset needs to be processed independently, after all other 135 // transforms, even after the BAffineTransforms (which are processed in 136 // Painter), to prevent this origin from being further transformed by 137 // e.g. scaling. 138 139 LayerCanvas layerCanvas(layerEngine.Get(), canvas->DetachDrawState(), boundingBox); 140 141 AlphaMask* const mask = layerCanvas.GetAlphaMask(); 142 IntPoint oldOffset; 143 if (mask != NULL) { 144 // Move alpha mask to bitmap origin 145 oldOffset = mask->SetCanvasGeometry(IntPoint(0, 0), boundingBox); 146 } 147 148 layerCanvas.CurrentState()->SetDrawingMode(B_OP_ALPHA); 149 layerCanvas.CurrentState()->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_COMPOSITE); 150 151 layerCanvas.ResyncDrawState(); 152 // Apply state to the new drawing engine of the layer canvas 153 154 if (layerEngine->LockParallelAccess()) { 155 layerCanvas.UpdateCurrentDrawingRegion(); 156 157 // Draw recorded picture into bitmap 158 Play(&layerCanvas); 159 layerEngine->UnlockParallelAccess(); 160 } 161 162 if (mask != NULL) { 163 // Move alpha mask back to its old position 164 // Note: this needs to be adapted if setting alpha masks is 165 // implemented as BPicture command (the mask now might be a different 166 // one than before). 167 layerCanvas.CurrentState()->CombinedTransform().Apply(oldOffset); 168 mask->SetCanvasGeometry(oldOffset, boundingBox); 169 layerCanvas.ResyncDrawState(); 170 } 171 172 canvas->SetDrawState(layerCanvas.DetachDrawState()); 173 // Update state in canvas (the top-of-stack state could be a different 174 // state instance now, if the picture commands contained push/pop 175 // commands) 176 177 return layerBitmap.Detach(); 178 } 179 180 181 IntPoint 182 Layer::LeftTopOffset() const 183 { 184 return fLeftTopOffset; 185 } 186 187 188 uint8 189 Layer::Opacity() const 190 { 191 return fOpacity; 192 } 193 194 195 BRect 196 Layer::_DetermineBoundingBox(Canvas* canvas) 197 { 198 BRect boundingBox; 199 PictureBoundingBoxPlayer::Play(this, canvas->CurrentState(), &boundingBox); 200 201 if (!boundingBox.IsValid()) 202 return boundingBox; 203 204 // Round up and add an additional 2 pixels on the bottom/right to 205 // compensate for the various types of rounding used in Painter. 206 boundingBox.left = floorf(boundingBox.left); 207 boundingBox.right = ceilf(boundingBox.right) + 2; 208 boundingBox.top = floorf(boundingBox.top); 209 boundingBox.bottom = ceilf(boundingBox.bottom) + 2; 210 211 // TODO: for optimization, crop the bounding box to the underlying 212 // view bounds here 213 214 return boundingBox; 215 } 216 217 218 UtilityBitmap* 219 Layer::_AllocateBitmap(const BRect& bounds) 220 { 221 BReference<UtilityBitmap> layerBitmap(new(std::nothrow) UtilityBitmap(bounds, 222 B_RGBA32, 0), true); 223 if (layerBitmap == NULL) 224 return NULL; 225 if (!layerBitmap->IsValid()) 226 return NULL; 227 228 memset(layerBitmap->Bits(), 0, layerBitmap->BitsLength()); 229 230 return layerBitmap.Detach(); 231 } 232