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