1 #include "config.h"
2 #include "LayerAndroid.h"
3
4 #if USE(ACCELERATED_COMPOSITING)
5
6 #include "AndroidAnimation.h"
7 #include "ClassTracker.h"
8 #include "DrawExtra.h"
9 #include "GLUtils.h"
10 #include "ImagesManager.h"
11 #include "MediaLayer.h"
12 #include "PaintedSurface.h"
13 #include "ParseCanvas.h"
14 #include "SkBitmapRef.h"
15 #include "SkBounder.h"
16 #include "SkDrawFilter.h"
17 #include "SkPaint.h"
18 #include "SkPicture.h"
19 #include "TilesManager.h"
20
21 #include <wtf/CurrentTime.h>
22 #include <math.h>
23
24 #define LAYER_DEBUG // Add diagonals for debugging
25 #undef LAYER_DEBUG
26
27 #include <cutils/log.h>
28 #include <wtf/text/CString.h>
29 #define XLOGC(...) android_printLog(ANDROID_LOG_DEBUG, "LayerAndroid", __VA_ARGS__)
30
31 #ifdef DEBUG
32
33 #undef XLOG
34 #define XLOG(...) android_printLog(ANDROID_LOG_DEBUG, "LayerAndroid", __VA_ARGS__)
35
36 #else
37
38 #undef XLOG
39 #define XLOG(...)
40
41 #endif // DEBUG
42
43 namespace WebCore {
44
45 static int gUniqueId;
46
47 class OpacityDrawFilter : public SkDrawFilter {
48 public:
OpacityDrawFilter(int opacity)49 OpacityDrawFilter(int opacity) : m_opacity(opacity) { }
filter(SkPaint * paint,Type)50 virtual void filter(SkPaint* paint, Type)
51 {
52 paint->setAlpha(m_opacity);
53 }
54 private:
55 int m_opacity;
56 };
57
58 ///////////////////////////////////////////////////////////////////////////////
59
LayerAndroid(RenderLayer * owner)60 LayerAndroid::LayerAndroid(RenderLayer* owner) : Layer(),
61 m_haveClip(false),
62 m_isFixed(false),
63 m_isIframe(false),
64 m_backfaceVisibility(true),
65 m_visible(true),
66 m_preserves3D(false),
67 m_anchorPointZ(0),
68 m_recordingPicture(0),
69 m_extra(0),
70 m_uniqueId(++gUniqueId),
71 m_texture(0),
72 m_imageRef(0),
73 m_imageTexture(0),
74 m_pictureUsed(0),
75 m_requestSent(false),
76 m_scale(1),
77 m_lastComputeTextureSize(0),
78 m_owningLayer(owner),
79 m_type(LayerAndroid::WebCoreLayer)
80 {
81 m_backgroundColor = 0;
82
83 m_preserves3D = false;
84 m_dirty = false;
85 m_iframeOffset.set(0,0);
86 m_dirtyRegion.setEmpty();
87 #ifdef DEBUG_COUNT
88 ClassTracker::instance()->increment("LayerAndroid");
89 ClassTracker::instance()->add(this);
90 #endif
91 }
92
LayerAndroid(const LayerAndroid & layer)93 LayerAndroid::LayerAndroid(const LayerAndroid& layer) : Layer(layer),
94 m_haveClip(layer.m_haveClip),
95 m_isIframe(layer.m_isIframe),
96 m_extra(0), // deliberately not copied
97 m_uniqueId(layer.m_uniqueId),
98 m_texture(0),
99 m_imageTexture(0),
100 m_requestSent(false),
101 m_owningLayer(layer.m_owningLayer),
102 m_type(LayerAndroid::UILayer)
103 {
104 m_isFixed = layer.m_isFixed;
105 m_imageRef = layer.m_imageRef;
106 if (m_imageRef)
107 ImagesManager::instance()->addImage(m_imageRef);
108 m_renderLayerPos = layer.m_renderLayerPos;
109 m_transform = layer.m_transform;
110 m_backfaceVisibility = layer.m_backfaceVisibility;
111 m_visible = layer.m_visible;
112 m_backgroundColor = layer.m_backgroundColor;
113
114 m_fixedLeft = layer.m_fixedLeft;
115 m_fixedTop = layer.m_fixedTop;
116 m_fixedRight = layer.m_fixedRight;
117 m_fixedBottom = layer.m_fixedBottom;
118 m_fixedMarginLeft = layer.m_fixedMarginLeft;
119 m_fixedMarginTop = layer.m_fixedMarginTop;
120 m_fixedMarginRight = layer.m_fixedMarginRight;
121 m_fixedMarginBottom = layer.m_fixedMarginBottom;
122 m_fixedRect = layer.m_fixedRect;
123 m_iframeOffset = layer.m_iframeOffset;
124 m_recordingPicture = layer.m_recordingPicture;
125 SkSafeRef(m_recordingPicture);
126
127 m_preserves3D = layer.m_preserves3D;
128 m_anchorPointZ = layer.m_anchorPointZ;
129 m_drawTransform = layer.m_drawTransform;
130 m_childrenTransform = layer.m_childrenTransform;
131 m_dirty = layer.m_dirty;
132 m_pictureUsed = layer.m_pictureUsed;
133 m_dirtyRegion = layer.m_dirtyRegion;
134 m_scale = layer.m_scale;
135 m_lastComputeTextureSize = 0;
136
137 for (int i = 0; i < layer.countChildren(); i++)
138 addChild(layer.getChild(i)->copy())->unref();
139
140 KeyframesMap::const_iterator end = layer.m_animations.end();
141 for (KeyframesMap::const_iterator it = layer.m_animations.begin(); it != end; ++it) {
142 pair<String, int> key((it->second)->name(), (it->second)->type());
143 m_animations.add(key, (it->second)->copy());
144 }
145
146 #ifdef DEBUG_COUNT
147 ClassTracker::instance()->increment("LayerAndroid - recopy (UI?)");
148 ClassTracker::instance()->add(this);
149 #endif
150 }
151
LayerAndroid(SkPicture * picture)152 LayerAndroid::LayerAndroid(SkPicture* picture) : Layer(),
153 m_haveClip(false),
154 m_isFixed(false),
155 m_isIframe(false),
156 m_recordingPicture(picture),
157 m_extra(0),
158 m_uniqueId(-1),
159 m_texture(0),
160 m_imageRef(0),
161 m_imageTexture(0),
162 m_requestSent(false),
163 m_scale(1),
164 m_lastComputeTextureSize(0),
165 m_owningLayer(0),
166 m_type(LayerAndroid::NavCacheLayer)
167 {
168 m_backgroundColor = 0;
169 m_dirty = false;
170 SkSafeRef(m_recordingPicture);
171 m_iframeOffset.set(0,0);
172 m_dirtyRegion.setEmpty();
173 #ifdef DEBUG_COUNT
174 ClassTracker::instance()->increment("LayerAndroid - from picture");
175 ClassTracker::instance()->add(this);
176 #endif
177 }
178
~LayerAndroid()179 LayerAndroid::~LayerAndroid()
180 {
181 if (m_imageTexture)
182 ImagesManager::instance()->removeImage(m_imageTexture->imageRef());
183 delete m_extra;
184 SkSafeUnref(m_recordingPicture);
185 m_animations.clear();
186 #ifdef DEBUG_COUNT
187 ClassTracker::instance()->remove(this);
188 if (m_type == LayerAndroid::WebCoreLayer)
189 ClassTracker::instance()->decrement("LayerAndroid");
190 else if (m_type == LayerAndroid::UILayer)
191 ClassTracker::instance()->decrement("LayerAndroid - recopy (UI)");
192 else if (m_type == LayerAndroid::NavCacheLayer)
193 ClassTracker::instance()->decrement("LayerAndroid - from picture");
194 #endif
195 }
196
197 static int gDebugNbAnims = 0;
198
evaluateAnimations()199 bool LayerAndroid::evaluateAnimations()
200 {
201 double time = WTF::currentTime();
202 gDebugNbAnims = 0;
203 return evaluateAnimations(time);
204 }
205
hasAnimations() const206 bool LayerAndroid::hasAnimations() const
207 {
208 for (int i = 0; i < countChildren(); i++) {
209 if (getChild(i)->hasAnimations())
210 return true;
211 }
212 return !!m_animations.size();
213 }
214
evaluateAnimations(double time)215 bool LayerAndroid::evaluateAnimations(double time)
216 {
217 bool hasRunningAnimations = false;
218 for (int i = 0; i < countChildren(); i++) {
219 if (getChild(i)->evaluateAnimations(time))
220 hasRunningAnimations = true;
221 }
222
223 m_hasRunningAnimations = false;
224 int nbAnims = 0;
225 KeyframesMap::const_iterator end = m_animations.end();
226 for (KeyframesMap::const_iterator it = m_animations.begin(); it != end; ++it) {
227 gDebugNbAnims++;
228 nbAnims++;
229 LayerAndroid* currentLayer = const_cast<LayerAndroid*>(this);
230 m_hasRunningAnimations |= (it->second)->evaluate(currentLayer, time);
231 }
232
233 return hasRunningAnimations || m_hasRunningAnimations;
234 }
235
addDirtyArea(GLWebViewState * glWebViewState)236 void LayerAndroid::addDirtyArea(GLWebViewState* glWebViewState)
237 {
238 IntSize layerSize(getSize().width(), getSize().height());
239
240 FloatRect area = TilesManager::instance()->shader()->rectInInvScreenCoord(m_drawTransform, layerSize);
241 FloatRect clip = TilesManager::instance()->shader()->convertScreenCoordToInvScreenCoord(m_clippingRect);
242
243 area.intersect(clip);
244 IntRect dirtyArea(area.x(), area.y(), area.width(), area.height());
245 glWebViewState->addDirtyArea(dirtyArea);
246 }
247
addAnimation(PassRefPtr<AndroidAnimation> prpAnim)248 void LayerAndroid::addAnimation(PassRefPtr<AndroidAnimation> prpAnim)
249 {
250 RefPtr<AndroidAnimation> anim = prpAnim;
251 pair<String, int> key(anim->name(), anim->type());
252 removeAnimationsForProperty(anim->type());
253 m_animations.add(key, anim);
254 }
255
removeAnimationsForProperty(AnimatedPropertyID property)256 void LayerAndroid::removeAnimationsForProperty(AnimatedPropertyID property)
257 {
258 KeyframesMap::const_iterator end = m_animations.end();
259 Vector<pair<String, int> > toDelete;
260 for (KeyframesMap::const_iterator it = m_animations.begin(); it != end; ++it) {
261 if ((it->second)->type() == property)
262 toDelete.append(it->first);
263 }
264
265 for (unsigned int i = 0; i < toDelete.size(); i++)
266 m_animations.remove(toDelete[i]);
267 }
268
removeAnimationsForKeyframes(const String & name)269 void LayerAndroid::removeAnimationsForKeyframes(const String& name)
270 {
271 KeyframesMap::const_iterator end = m_animations.end();
272 Vector<pair<String, int> > toDelete;
273 for (KeyframesMap::const_iterator it = m_animations.begin(); it != end; ++it) {
274 if ((it->second)->name() == name)
275 toDelete.append(it->first);
276 }
277
278 for (unsigned int i = 0; i < toDelete.size(); i++)
279 m_animations.remove(toDelete[i]);
280 }
281
282 // We only use the bounding rect of the layer as mask...
283 // FIXME: use a real mask?
setMaskLayer(LayerAndroid * layer)284 void LayerAndroid::setMaskLayer(LayerAndroid* layer)
285 {
286 if (layer)
287 m_haveClip = true;
288 }
289
setBackgroundColor(SkColor color)290 void LayerAndroid::setBackgroundColor(SkColor color)
291 {
292 m_backgroundColor = color;
293 }
294
295 static int gDebugChildLevel;
296
translation() const297 FloatPoint LayerAndroid::translation() const
298 {
299 TransformationMatrix::DecomposedType tDecomp;
300 m_transform.decompose(tDecomp);
301 FloatPoint p(tDecomp.translateX, tDecomp.translateY);
302 return p;
303 }
304
bounds() const305 SkRect LayerAndroid::bounds() const
306 {
307 SkRect rect;
308 bounds(&rect);
309 return rect;
310 }
311
bounds(SkRect * rect) const312 void LayerAndroid::bounds(SkRect* rect) const
313 {
314 const SkPoint& pos = this->getPosition();
315 const SkSize& size = this->getSize();
316
317 // The returned rect has the translation applied
318 // FIXME: apply the full transform to the rect,
319 // and fix the text selection accordingly
320 FloatPoint p(pos.fX, pos.fY);
321 p = m_transform.mapPoint(p);
322 rect->fLeft = p.x();
323 rect->fTop = p.y();
324 rect->fRight = p.x() + size.width();
325 rect->fBottom = p.y() + size.height();
326 }
327
boundsIsUnique(const SkTDArray<SkRect> & region,const SkRect & local)328 static bool boundsIsUnique(const SkTDArray<SkRect>& region,
329 const SkRect& local)
330 {
331 for (int i = 0; i < region.count(); i++) {
332 if (region[i].contains(local))
333 return false;
334 }
335 return true;
336 }
337
clipArea(SkTDArray<SkRect> * region) const338 void LayerAndroid::clipArea(SkTDArray<SkRect>* region) const
339 {
340 SkRect local;
341 local.set(0, 0, std::numeric_limits<float>::max(),
342 std::numeric_limits<float>::max());
343 clipInner(region, local);
344 }
345
clipInner(SkTDArray<SkRect> * region,const SkRect & local) const346 void LayerAndroid::clipInner(SkTDArray<SkRect>* region,
347 const SkRect& local) const
348 {
349 SkRect localBounds;
350 bounds(&localBounds);
351 localBounds.intersect(local);
352 if (localBounds.isEmpty())
353 return;
354 if (m_recordingPicture && boundsIsUnique(*region, localBounds))
355 *region->append() = localBounds;
356 for (int i = 0; i < countChildren(); i++)
357 getChild(i)->clipInner(region, m_haveClip ? localBounds : local);
358 }
359
360 class FindCheck : public SkBounder {
361 public:
FindCheck()362 FindCheck()
363 : m_drew(false)
364 , m_drewText(false)
365 {
366 }
367
drew() const368 bool drew() const { return m_drew; }
drewText() const369 bool drewText() const { return m_drewText; }
reset()370 void reset() { m_drew = m_drewText = false; }
371
372 protected:
onIRect(const SkIRect &)373 virtual bool onIRect(const SkIRect& )
374 {
375 m_drew = true;
376 return false;
377 }
378
onIRectGlyph(const SkIRect &,const SkBounder::GlyphRec &)379 virtual bool onIRectGlyph(const SkIRect& , const SkBounder::GlyphRec& )
380 {
381 m_drew = m_drewText = true;
382 return false;
383 }
384
385 bool m_drew;
386 bool m_drewText;
387 };
388
389 class FindCanvas : public ParseCanvas {
390 public:
draw(SkPicture * picture,SkScalar offsetX,SkScalar offsetY)391 void draw(SkPicture* picture, SkScalar offsetX, SkScalar offsetY)
392 {
393 save();
394 translate(-offsetX, -offsetY);
395 picture->draw(this);
396 restore();
397 }
398 };
399
400 class LayerAndroid::FindState {
401 public:
402 static const int TOUCH_SLOP = 10;
403
FindState(int x,int y)404 FindState(int x, int y)
405 : m_x(x)
406 , m_y(y)
407 , m_bestX(x)
408 , m_bestY(y)
409 , m_best(0)
410 {
411 m_bitmap.setConfig(SkBitmap::kARGB_8888_Config, TOUCH_SLOP * 2,
412 TOUCH_SLOP * 2);
413 m_checker.setBounder(&m_findCheck);
414 m_checker.setBitmapDevice(m_bitmap);
415 }
416
best() const417 const LayerAndroid* best() const { return m_best; }
bestX() const418 int bestX() const { return m_bestX; }
bestY() const419 int bestY() const { return m_bestY; }
420
drew(SkPicture * picture,const SkRect & localBounds)421 bool drew(SkPicture* picture, const SkRect& localBounds)
422 {
423 m_findCheck.reset();
424 SkScalar localX = SkIntToScalar(m_x - TOUCH_SLOP) - localBounds.fLeft;
425 SkScalar localY = SkIntToScalar(m_y - TOUCH_SLOP) - localBounds.fTop;
426 m_checker.draw(picture, localX, localY);
427 return m_findCheck.drew();
428 }
429
drewText()430 bool drewText() { return m_findCheck.drewText(); }
431
setBest(const LayerAndroid * best,int x,int y)432 void setBest(const LayerAndroid* best, int x, int y)
433 {
434 m_best = best;
435 m_bestX = x;
436 m_bestY = y;
437 }
x() const438 int x() const { return m_x; }
y() const439 int y() const { return m_y; }
440
setLocation(int x,int y)441 void setLocation(int x, int y)
442 {
443 m_x = x;
444 m_y = y;
445 }
446
447 protected:
448 int m_x;
449 int m_y;
450 int m_bestX;
451 int m_bestY;
452 const LayerAndroid* m_best;
453 FindCheck m_findCheck;
454 SkBitmap m_bitmap;
455 FindCanvas m_checker;
456 };
457
findInner(LayerAndroid::FindState & state) const458 void LayerAndroid::findInner(LayerAndroid::FindState& state) const
459 {
460 int x = state.x();
461 int y = state.y();
462 SkRect localBounds;
463 bounds(&localBounds);
464 if (!localBounds.contains(x, y))
465 return;
466 // Move into local coordinates.
467 state.setLocation(x - localBounds.fLeft, y - localBounds.fTop);
468 for (int i = 0; i < countChildren(); i++)
469 getChild(i)->findInner(state);
470 // Move back into the parent coordinates.
471 int testX = state.x();
472 int testY = state.y();
473 state.setLocation(x + localBounds.fLeft, y + localBounds.fTop);
474 if (!m_recordingPicture)
475 return;
476 if (!contentIsScrollable() && !state.drew(m_recordingPicture, localBounds))
477 return;
478 state.setBest(this, testX, testY); // set last match (presumably on top)
479 }
480
find(int * xPtr,int * yPtr,SkPicture * root) const481 const LayerAndroid* LayerAndroid::find(int* xPtr, int* yPtr, SkPicture* root) const
482 {
483 FindState state(*xPtr, *yPtr);
484 SkRect rootBounds;
485 rootBounds.setEmpty();
486 if (root && state.drew(root, rootBounds) && state.drewText())
487 return 0; // use the root picture only if it contains the text
488 findInner(state);
489 *xPtr = state.bestX();
490 *yPtr = state.bestY();
491 return state.best();
492 }
493
494 ///////////////////////////////////////////////////////////////////////////////
495
updateFixedLayersPositions(SkRect viewport,LayerAndroid * parentIframeLayer)496 void LayerAndroid::updateFixedLayersPositions(SkRect viewport, LayerAndroid* parentIframeLayer)
497 {
498 XLOG("updating fixed positions, using viewport %fx%f - %fx%f",
499 viewport.fLeft, viewport.fTop,
500 viewport.width(), viewport.height());
501 // If this is an iframe, accumulate the offset from the parent with
502 // current position, and change the parent pointer.
503 if (m_isIframe) {
504 // If this is the top level, take the current position
505 SkPoint parentOffset;
506 parentOffset.set(0,0);
507 if (parentIframeLayer)
508 parentOffset = parentIframeLayer->getPosition();
509
510 m_iframeOffset = parentOffset + getPosition();
511
512 parentIframeLayer = this;
513 }
514
515 if (m_isFixed) {
516 // So if this is a fixed layer inside a iframe, use the iframe offset
517 // and the iframe's size as the viewport and pass to the children
518 if (parentIframeLayer) {
519 viewport = SkRect::MakeXYWH(parentIframeLayer->m_iframeOffset.fX,
520 parentIframeLayer->m_iframeOffset.fY,
521 parentIframeLayer->getSize().width(),
522 parentIframeLayer->getSize().height());
523 }
524 float w = viewport.width();
525 float h = viewport.height();
526 float dx = viewport.fLeft;
527 float dy = viewport.fTop;
528 float x = dx;
529 float y = dy;
530
531 // It turns out that when it is 'auto', we should use the webkit value
532 // from the original render layer's X,Y, that will take care of alignment
533 // with the parent's layer and fix Margin etc.
534 if (!(m_fixedLeft.defined() || m_fixedRight.defined()))
535 x += m_renderLayerPos.x();
536 else if (m_fixedLeft.defined() || !m_fixedRight.defined())
537 x += m_fixedMarginLeft.calcFloatValue(w) + m_fixedLeft.calcFloatValue(w) - m_fixedRect.fLeft;
538 else
539 x += w - m_fixedMarginRight.calcFloatValue(w) - m_fixedRight.calcFloatValue(w) - m_fixedRect.fRight;
540
541 if (!(m_fixedTop.defined() || m_fixedBottom.defined()))
542 y += m_renderLayerPos.y();
543 else if (m_fixedTop.defined() || !m_fixedBottom.defined())
544 y += m_fixedMarginTop.calcFloatValue(h) + m_fixedTop.calcFloatValue(h) - m_fixedRect.fTop;
545 else
546 y += h - m_fixedMarginBottom.calcFloatValue(h) - m_fixedBottom.calcFloatValue(h) - m_fixedRect.fBottom;
547
548 this->setPosition(x, y);
549 }
550
551 int count = this->countChildren();
552 for (int i = 0; i < count; i++)
553 this->getChild(i)->updateFixedLayersPositions(viewport, parentIframeLayer);
554 }
555
updatePositions()556 void LayerAndroid::updatePositions()
557 {
558 // apply the viewport to us
559 if (!m_isFixed) {
560 // turn our fields into a matrix.
561 //
562 // FIXME: this should happen in the caller, and we should remove these
563 // fields from our subclass
564 SkMatrix matrix;
565 GLUtils::toSkMatrix(matrix, m_transform);
566 this->setMatrix(matrix);
567 }
568
569 // now apply it to our children
570 int count = this->countChildren();
571 for (int i = 0; i < count; i++)
572 this->getChild(i)->updatePositions();
573 }
574
updateGLPositionsAndScale(const TransformationMatrix & parentMatrix,const FloatRect & clipping,float opacity,float scale)575 void LayerAndroid::updateGLPositionsAndScale(const TransformationMatrix& parentMatrix,
576 const FloatRect& clipping, float opacity, float scale)
577 {
578 m_atomicSync.lock();
579 IntSize layerSize(getSize().width(), getSize().height());
580 FloatPoint anchorPoint(getAnchorPoint().fX, getAnchorPoint().fY);
581 FloatPoint position(getPosition().fX, getPosition().fY);
582 float centerOffsetX = (0.5f - anchorPoint.x()) * layerSize.width();
583 float centerOffsetY = (0.5f - anchorPoint.y()) * layerSize.height();
584 float originX = anchorPoint.x() * layerSize.width();
585 float originY = anchorPoint.y() * layerSize.height();
586 TransformationMatrix localMatrix;
587 if (!m_isFixed)
588 localMatrix = parentMatrix;
589 localMatrix.translate3d(originX + position.x(),
590 originY + position.y(),
591 anchorPointZ());
592 localMatrix.multiply(m_transform);
593 localMatrix.translate3d(-originX,
594 -originY,
595 -anchorPointZ());
596
597 m_atomicSync.unlock();
598 setDrawTransform(localMatrix);
599 if (m_drawTransform.isIdentityOrTranslation()) {
600 // adjust the translation coordinates of the draw transform matrix so
601 // that layers (defined in content coordinates) will align to display/view pixels
602 float desiredContentX = round(m_drawTransform.m41() * scale) / scale;
603 float desiredContentY = round(m_drawTransform.m42() * scale) / scale;
604 XLOG("fudging translation from %f, %f to %f, %f",
605 m_drawTransform.m41(), m_drawTransform.m42(),
606 desiredContentX, desiredContentY);
607 m_drawTransform.setM41(desiredContentX);
608 m_drawTransform.setM42(desiredContentY);
609 }
610
611 m_zValue = TilesManager::instance()->shader()->zValue(m_drawTransform, getSize().width(), getSize().height());
612
613 m_atomicSync.lock();
614 m_scale = scale;
615 m_atomicSync.unlock();
616
617 opacity *= getOpacity();
618 setDrawOpacity(opacity);
619
620 if (m_haveClip) {
621 // The clipping rect calculation and intersetion will be done in Screen Coord now.
622 FloatRect clip =
623 TilesManager::instance()->shader()->rectInScreenCoord(m_drawTransform, layerSize);
624 clip.intersect(clipping);
625 setDrawClip(clip);
626 } else {
627 setDrawClip(clipping);
628 }
629
630 if (!m_backfaceVisibility
631 && m_drawTransform.inverse().m33() < 0) {
632 setVisible(false);
633 return;
634 } else {
635 setVisible(true);
636 }
637
638 int count = this->countChildren();
639 if (!count)
640 return;
641
642 // Flatten to 2D if the layer doesn't preserve 3D.
643 if (!preserves3D()) {
644 localMatrix.setM13(0);
645 localMatrix.setM23(0);
646 localMatrix.setM31(0);
647 localMatrix.setM32(0);
648 localMatrix.setM33(1);
649 localMatrix.setM34(0);
650 localMatrix.setM43(0);
651 }
652
653 // now apply it to our children
654
655 if (!m_childrenTransform.isIdentity()) {
656 localMatrix.translate(getSize().width() * 0.5f, getSize().height() * 0.5f);
657 localMatrix.multiply(m_childrenTransform);
658 localMatrix.translate(-getSize().width() * 0.5f, -getSize().height() * 0.5f);
659 }
660 for (int i = 0; i < count; i++)
661 this->getChild(i)->updateGLPositionsAndScale(localMatrix, drawClip(), opacity, scale);
662 }
663
setContentsImage(SkBitmapRef * img)664 void LayerAndroid::setContentsImage(SkBitmapRef* img)
665 {
666 m_imageRef = img;
667 if (!img)
668 return;
669
670 ImagesManager::instance()->addImage(img);
671 }
672
needsTexture()673 bool LayerAndroid::needsTexture()
674 {
675 return m_imageRef || (prepareContext()
676 && m_recordingPicture->width() && m_recordingPicture->height());
677 }
678
removeTexture(PaintedSurface * texture)679 void LayerAndroid::removeTexture(PaintedSurface* texture)
680 {
681 if (texture == m_texture)
682 m_texture = 0;
683 }
684
clippedRect() const685 IntRect LayerAndroid::clippedRect() const
686 {
687 IntRect r(0, 0, getWidth(), getHeight());
688 IntRect tr = m_drawTransform.mapRect(r);
689 IntRect cr = TilesManager::instance()->shader()->clippedRectWithViewport(tr);
690 IntRect rect = m_drawTransform.inverse().mapRect(cr);
691 return rect;
692 }
693
nbLayers()694 int LayerAndroid::nbLayers()
695 {
696 int nb = 0;
697 int count = this->countChildren();
698 for (int i = 0; i < count; i++)
699 nb += this->getChild(i)->nbLayers();
700 return nb+1;
701 }
702
nbTexturedLayers()703 int LayerAndroid::nbTexturedLayers()
704 {
705 int nb = 0;
706 int count = this->countChildren();
707 for (int i = 0; i < count; i++)
708 nb += this->getChild(i)->nbTexturedLayers();
709 if (needsTexture())
710 nb++;
711 return nb;
712 }
713
showLayer(int indent)714 void LayerAndroid::showLayer(int indent)
715 {
716 char spaces[256];
717 memset(spaces, 0, 256);
718 for (int i = 0; i < indent; i++)
719 spaces[i] = ' ';
720
721 if (!indent)
722 XLOGC("\n\n--- LAYERS TREE ---");
723
724 IntRect r(0, 0, getWidth(), getHeight());
725 IntRect tr = m_drawTransform.mapRect(r);
726 XLOGC("%s [%d:0x%x] - %s - (%d, %d, %d, %d) %s prepareContext(%d), pic w: %d h: %d",
727 spaces, uniqueId(), m_owningLayer,
728 needsTexture() ? "needs a texture" : "no texture",
729 tr.x(), tr.y(), tr.width(), tr.height(),
730 contentIsScrollable() ? "SCROLLABLE" : "",
731 prepareContext(),
732 m_recordingPicture ? m_recordingPicture->width() : -1,
733 m_recordingPicture ? m_recordingPicture->height() : -1);
734
735 int count = this->countChildren();
736 for (int i = 0; i < count; i++)
737 this->getChild(i)->showLayer(indent + 1);
738 }
739
740 // We go through our tree, and if we have layer in the new
741 // tree that is similar, we transfer our texture to it.
742 // Otherwise, we remove ourselves from the texture so
743 // that TilesManager::swapLayersTextures() have a chance
744 // at deallocating the textures (PaintedSurfaces)
assignTextureTo(LayerAndroid * newTree)745 void LayerAndroid::assignTextureTo(LayerAndroid* newTree)
746 {
747 int count = this->countChildren();
748 for (int i = 0; i < count; i++)
749 this->getChild(i)->assignTextureTo(newTree);
750
751 if (newTree) {
752 LayerAndroid* newLayer = newTree->findById(uniqueId());
753 if (newLayer == this)
754 return;
755 if (newLayer && m_texture) {
756 m_texture->replaceLayer(newLayer);
757 newLayer->m_texture = m_texture;
758 m_texture = 0;
759 }
760 if (!newLayer && m_texture) {
761 m_texture->removeLayer(this);
762 m_texture = 0;
763 }
764 }
765 }
766
updateWithTree(LayerAndroid * newTree)767 bool LayerAndroid::updateWithTree(LayerAndroid* newTree)
768 {
769 // Disable fast update for now
770 #if (0)
771 bool needsRepaint = false;
772 int count = this->countChildren();
773 for (int i = 0; i < count; i++)
774 needsRepaint |= this->getChild(i)->updateWithTree(newTree);
775
776 if (newTree) {
777 LayerAndroid* newLayer = newTree->findById(uniqueId());
778 needsRepaint |= updateWithLayer(newLayer);
779 }
780 return needsRepaint;
781 #else
782 return true;
783 #endif
784 }
785
786 // Return true to indicate to WebViewCore that the updates
787 // are too complicated to be fully handled and we need a full
788 // call to webkit (e.g. handle repaints)
updateWithLayer(LayerAndroid * layer)789 bool LayerAndroid::updateWithLayer(LayerAndroid* layer)
790 {
791 if (!layer)
792 return true;
793
794 android::AutoMutex lock(m_atomicSync);
795 m_position = layer->m_position;
796 m_anchorPoint = layer->m_anchorPoint;
797 m_size = layer->m_size;
798 m_opacity = layer->m_opacity;
799 m_transform = layer->m_transform;
800
801 if (m_imageRef != layer->m_imageRef)
802 m_visible = false;
803
804 if ((m_recordingPicture != layer->m_recordingPicture)
805 || (m_imageRef != layer->m_imageRef))
806 return true;
807
808 return false;
809 }
810
createTexture()811 void LayerAndroid::createTexture()
812 {
813 int count = this->countChildren();
814 for (int i = 0; i < count; i++)
815 this->getChild(i)->createTexture();
816
817 if (!needsTexture())
818 return;
819
820 if (m_imageRef) {
821 if (!m_imageTexture) {
822 m_imageTexture = ImagesManager::instance()->getTextureForImage(m_imageRef);
823 m_dirtyRegion.setEmpty();
824 }
825 if (m_texture) {
826 m_texture->removeLayer(this);
827 m_texture = 0;
828 }
829 } else {
830 if (!m_texture)
831 m_texture = new PaintedSurface(this);
832
833 // pass the invalidated regions to the PaintedSurface
834 m_texture->markAsDirty(m_dirtyRegion);
835 m_dirtyRegion.setEmpty();
836 }
837 }
838
compareLayerZ(const LayerAndroid * a,const LayerAndroid * b)839 static inline bool compareLayerZ(const LayerAndroid* a, const LayerAndroid* b)
840 {
841 return a->zValue() > b->zValue();
842 }
843
markAsDirty(const SkRegion & dirtyArea)844 void LayerAndroid::markAsDirty(const SkRegion& dirtyArea)
845 {
846 m_dirtyRegion.op(dirtyArea, SkRegion::kUnion_Op);
847 }
848
849 // We call this in WebViewCore, when copying the tree of layers.
850 // As we construct a new tree that will be passed on the UI,
851 // we mark the webkit-side tree as having no more dirty region
852 // (otherwise we would continuously have those dirty region UI-side)
clearDirtyRegion()853 void LayerAndroid::clearDirtyRegion()
854 {
855 int count = this->countChildren();
856 for (int i = 0; i < count; i++)
857 this->getChild(i)->clearDirtyRegion();
858
859 m_dirtyRegion.setEmpty();
860 }
861
prepare(GLWebViewState * glWebViewState)862 void LayerAndroid::prepare(GLWebViewState* glWebViewState)
863 {
864 m_state = glWebViewState;
865
866 int count = this->countChildren();
867 if (count > 0) {
868 Vector <LayerAndroid*> sublayers;
869 for (int i = 0; i < count; i++)
870 sublayers.append(this->getChild(i));
871
872 // now we sort for the transparency
873 std::stable_sort(sublayers.begin(), sublayers.end(), compareLayerZ);
874
875 // iterate in reverse so top layers get textures first
876 for (int i = count-1; i >= 0; i--)
877 sublayers[i]->prepare(glWebViewState);
878 }
879
880 if (m_texture)
881 m_texture->prepare(glWebViewState);
882
883 if (m_imageTexture)
884 m_imageTexture->prepareGL();
885 }
886
drawGL(GLWebViewState * glWebViewState,SkMatrix & matrix)887 bool LayerAndroid::drawGL(GLWebViewState* glWebViewState, SkMatrix& matrix)
888 {
889 TilesManager::instance()->shader()->clip(m_clippingRect);
890 if (!m_visible)
891 return false;
892
893 bool askScreenUpdate = false;
894
895 if (m_texture)
896 askScreenUpdate |= m_texture->draw();
897
898 if (m_imageTexture)
899 m_imageTexture->drawGL(this);
900
901 // When the layer is dirty, the UI thread should be notified to redraw.
902 askScreenUpdate |= drawChildrenGL(glWebViewState, matrix);
903 m_atomicSync.lock();
904 askScreenUpdate |= m_dirty;
905 if (askScreenUpdate || m_hasRunningAnimations || m_drawTransform.hasPerspective())
906 addDirtyArea(glWebViewState);
907
908 m_atomicSync.unlock();
909 return askScreenUpdate;
910 }
911
drawChildrenGL(GLWebViewState * glWebViewState,SkMatrix & matrix)912 bool LayerAndroid::drawChildrenGL(GLWebViewState* glWebViewState, SkMatrix& matrix)
913 {
914 bool askScreenUpdate = false;
915 int count = this->countChildren();
916 if (count > 0) {
917 Vector <LayerAndroid*> sublayers;
918 for (int i = 0; i < count; i++)
919 sublayers.append(this->getChild(i));
920
921 // now we sort for the transparency
922 std::stable_sort(sublayers.begin(), sublayers.end(), compareLayerZ);
923 for (int i = 0; i < count; i++) {
924 LayerAndroid* layer = sublayers[i];
925 askScreenUpdate |= layer->drawGL(glWebViewState, matrix);
926 }
927 }
928
929 return askScreenUpdate;
930 }
931
extraDraw(SkCanvas * canvas)932 void LayerAndroid::extraDraw(SkCanvas* canvas)
933 {
934 m_atomicSync.lock();
935 if (m_extra)
936 canvas->drawPicture(*m_extra);
937 m_atomicSync.unlock();
938 }
939
contentDraw(SkCanvas * canvas)940 void LayerAndroid::contentDraw(SkCanvas* canvas)
941 {
942 if (m_recordingPicture)
943 canvas->drawPicture(*m_recordingPicture);
944
945 if (TilesManager::instance()->getShowVisualIndicator()) {
946 float w = getSize().width();
947 float h = getSize().height();
948 SkPaint paint;
949 paint.setARGB(128, 255, 0, 0);
950 canvas->drawLine(0, 0, w, h, paint);
951 canvas->drawLine(0, h, w, 0, paint);
952 paint.setARGB(128, 0, 255, 0);
953 canvas->drawLine(0, 0, 0, h, paint);
954 canvas->drawLine(0, h, w, h, paint);
955 canvas->drawLine(w, h, w, 0, paint);
956 canvas->drawLine(w, 0, 0, 0, paint);
957
958 if (m_isFixed) {
959 SkPaint paint;
960 paint.setARGB(80, 255, 0, 0);
961 canvas->drawRect(m_fixedRect, paint);
962 }
963 }
964 }
965
onDraw(SkCanvas * canvas,SkScalar opacity)966 void LayerAndroid::onDraw(SkCanvas* canvas, SkScalar opacity)
967 {
968 if (m_haveClip) {
969 SkRect r;
970 r.set(0, 0, getSize().width(), getSize().height());
971 canvas->clipRect(r);
972 return;
973 }
974
975 if (!prepareContext())
976 return;
977
978 // we just have this save/restore for opacity...
979 SkAutoCanvasRestore restore(canvas, true);
980
981 int canvasOpacity = SkScalarRound(opacity * 255);
982 if (canvasOpacity < 255)
983 canvas->setDrawFilter(new OpacityDrawFilter(canvasOpacity));
984
985 if (m_imageRef) {
986 if (!m_imageTexture) {
987 m_imageTexture = ImagesManager::instance()->getTextureForImage(m_imageRef);
988 m_dirtyRegion.setEmpty();
989 }
990 if (m_imageTexture) {
991 SkRect dest;
992 dest.set(0, 0, getSize().width(), getSize().height());
993 m_imageTexture->drawCanvas(canvas, dest);
994 }
995 }
996 contentDraw(canvas);
997 }
998
recordContext()999 SkPicture* LayerAndroid::recordContext()
1000 {
1001 if (prepareContext(true))
1002 return m_recordingPicture;
1003 return 0;
1004 }
1005
prepareContext(bool force)1006 bool LayerAndroid::prepareContext(bool force)
1007 {
1008 if (masksToBounds())
1009 return false;
1010
1011 if (force || !m_recordingPicture ||
1012 (m_recordingPicture &&
1013 ((m_recordingPicture->width() != (int) getSize().width()) ||
1014 (m_recordingPicture->height() != (int) getSize().height())))) {
1015 SkSafeUnref(m_recordingPicture);
1016 m_recordingPicture = new SkPicture();
1017 }
1018
1019 return m_recordingPicture;
1020 }
1021
subtractLayers(const SkRect & visibleRect) const1022 SkRect LayerAndroid::subtractLayers(const SkRect& visibleRect) const
1023 {
1024 SkRect result;
1025 if (m_recordingPicture) {
1026 // FIXME: This seems wrong. localToGlobal() applies the full local transform,
1027 // se surely we should operate globalMatrix on size(), not bounds() with
1028 // the position removed? Perhaps we never noticed the bug because most
1029 // layers don't use a local transform?
1030 // See http://b/5338388
1031 SkRect globalRect = bounds();
1032 globalRect.offset(-getPosition()); // localToGlobal adds in position
1033 SkMatrix globalMatrix;
1034 localToGlobal(&globalMatrix);
1035 globalMatrix.mapRect(&globalRect);
1036 SkIRect roundedGlobal;
1037 globalRect.round(&roundedGlobal);
1038 SkIRect iVisibleRect;
1039 visibleRect.round(&iVisibleRect);
1040 SkRegion visRegion(iVisibleRect);
1041 visRegion.op(roundedGlobal, SkRegion::kDifference_Op);
1042 result.set(visRegion.getBounds());
1043 #if DEBUG_NAV_UI
1044 SkDebugf("%s visibleRect=(%g,%g,r=%g,b=%g) globalRect=(%g,%g,r=%g,b=%g)"
1045 "result=(%g,%g,r=%g,b=%g)", __FUNCTION__,
1046 visibleRect.fLeft, visibleRect.fTop,
1047 visibleRect.fRight, visibleRect.fBottom,
1048 globalRect.fLeft, globalRect.fTop,
1049 globalRect.fRight, globalRect.fBottom,
1050 result.fLeft, result.fTop, result.fRight, result.fBottom);
1051 #endif
1052 } else
1053 result = visibleRect;
1054 for (int i = 0; i < countChildren(); i++)
1055 result = getChild(i)->subtractLayers(result);
1056 return result;
1057 }
1058
1059 // Debug tools : dump the layers tree in a file.
1060 // The format is simple:
1061 // properties have the form: key = value;
1062 // all statements are finished with a semi-colon.
1063 // value can be:
1064 // - int
1065 // - float
1066 // - array of elements
1067 // - composed type
1068 // a composed type enclose properties in { and }
1069 // an array enclose composed types in { }, separated with a comma.
1070 // exemple:
1071 // {
1072 // x = 3;
1073 // y = 4;
1074 // value = {
1075 // x = 3;
1076 // y = 4;
1077 // };
1078 // anarray = [
1079 // { x = 3; },
1080 // { y = 4; }
1081 // ];
1082 // }
1083
lwrite(FILE * file,const char * str)1084 void lwrite(FILE* file, const char* str)
1085 {
1086 fwrite(str, sizeof(char), strlen(str), file);
1087 }
1088
writeIndent(FILE * file,int indentLevel)1089 void writeIndent(FILE* file, int indentLevel)
1090 {
1091 if (indentLevel)
1092 fprintf(file, "%*s", indentLevel*2, " ");
1093 }
1094
writeln(FILE * file,int indentLevel,const char * str)1095 void writeln(FILE* file, int indentLevel, const char* str)
1096 {
1097 writeIndent(file, indentLevel);
1098 lwrite(file, str);
1099 lwrite(file, "\n");
1100 }
1101
writeIntVal(FILE * file,int indentLevel,const char * str,int value)1102 void writeIntVal(FILE* file, int indentLevel, const char* str, int value)
1103 {
1104 writeIndent(file, indentLevel);
1105 fprintf(file, "%s = %d;\n", str, value);
1106 }
1107
writeHexVal(FILE * file,int indentLevel,const char * str,int value)1108 void writeHexVal(FILE* file, int indentLevel, const char* str, int value)
1109 {
1110 writeIndent(file, indentLevel);
1111 fprintf(file, "%s = %x;\n", str, value);
1112 }
1113
writeFloatVal(FILE * file,int indentLevel,const char * str,float value)1114 void writeFloatVal(FILE* file, int indentLevel, const char* str, float value)
1115 {
1116 writeIndent(file, indentLevel);
1117 fprintf(file, "%s = %.3f;\n", str, value);
1118 }
1119
writePoint(FILE * file,int indentLevel,const char * str,SkPoint point)1120 void writePoint(FILE* file, int indentLevel, const char* str, SkPoint point)
1121 {
1122 writeIndent(file, indentLevel);
1123 fprintf(file, "%s = { x = %.3f; y = %.3f; };\n", str, point.fX, point.fY);
1124 }
1125
writeSize(FILE * file,int indentLevel,const char * str,SkSize size)1126 void writeSize(FILE* file, int indentLevel, const char* str, SkSize size)
1127 {
1128 writeIndent(file, indentLevel);
1129 fprintf(file, "%s = { w = %.3f; h = %.3f; };\n", str, size.width(), size.height());
1130 }
1131
writeRect(FILE * file,int indentLevel,const char * str,SkRect rect)1132 void writeRect(FILE* file, int indentLevel, const char* str, SkRect rect)
1133 {
1134 writeIndent(file, indentLevel);
1135 fprintf(file, "%s = { x = %.3f; y = %.3f; w = %.3f; h = %.3f; };\n",
1136 str, rect.fLeft, rect.fTop, rect.width(), rect.height());
1137 }
1138
writeLength(FILE * file,int indentLevel,const char * str,SkLength length)1139 void writeLength(FILE* file, int indentLevel, const char* str, SkLength length)
1140 {
1141 if (!length.defined())
1142 return;
1143 writeIndent(file, indentLevel);
1144 fprintf(file, "%s = { type = %d; value = %.2f; };\n", str, length.type, length.value);
1145 }
1146
writeMatrix(FILE * file,int indentLevel,const char * str,const TransformationMatrix & matrix)1147 void writeMatrix(FILE* file, int indentLevel, const char* str, const TransformationMatrix& matrix)
1148 {
1149 writeIndent(file, indentLevel);
1150 fprintf(file, "%s = { (%.2f,%.2f,%.2f,%.2f),(%.2f,%.2f,%.2f,%.2f),"
1151 "(%.2f,%.2f,%.2f,%.2f),(%.2f,%.2f,%.2f,%.2f) };\n",
1152 str,
1153 matrix.m11(), matrix.m12(), matrix.m13(), matrix.m14(),
1154 matrix.m21(), matrix.m22(), matrix.m23(), matrix.m24(),
1155 matrix.m31(), matrix.m32(), matrix.m33(), matrix.m34(),
1156 matrix.m41(), matrix.m42(), matrix.m43(), matrix.m44());
1157 }
1158
dumpLayers(FILE * file,int indentLevel) const1159 void LayerAndroid::dumpLayers(FILE* file, int indentLevel) const
1160 {
1161 writeln(file, indentLevel, "{");
1162
1163 writeHexVal(file, indentLevel + 1, "layer", (int)this);
1164 writeIntVal(file, indentLevel + 1, "layerId", m_uniqueId);
1165 writeIntVal(file, indentLevel + 1, "haveClip", m_haveClip);
1166 writeIntVal(file, indentLevel + 1, "isFixed", m_isFixed);
1167 writeIntVal(file, indentLevel + 1, "m_isIframe", m_isIframe);
1168 writePoint(file, indentLevel + 1, "m_iframeOffset", m_iframeOffset);
1169
1170 writeFloatVal(file, indentLevel + 1, "opacity", getOpacity());
1171 writeSize(file, indentLevel + 1, "size", getSize());
1172 writePoint(file, indentLevel + 1, "position", getPosition());
1173 writePoint(file, indentLevel + 1, "anchor", getAnchorPoint());
1174
1175 writeMatrix(file, indentLevel + 1, "drawMatrix", m_drawTransform);
1176 writeMatrix(file, indentLevel + 1, "transformMatrix", m_transform);
1177 writeRect(file, indentLevel + 1, "clippingRect", SkRect(m_clippingRect));
1178
1179 if (m_isFixed) {
1180 writeLength(file, indentLevel + 1, "fixedLeft", m_fixedLeft);
1181 writeLength(file, indentLevel + 1, "fixedTop", m_fixedTop);
1182 writeLength(file, indentLevel + 1, "fixedRight", m_fixedRight);
1183 writeLength(file, indentLevel + 1, "fixedBottom", m_fixedBottom);
1184 writeLength(file, indentLevel + 1, "fixedMarginLeft", m_fixedMarginLeft);
1185 writeLength(file, indentLevel + 1, "fixedMarginTop", m_fixedMarginTop);
1186 writeLength(file, indentLevel + 1, "fixedMarginRight", m_fixedMarginRight);
1187 writeLength(file, indentLevel + 1, "fixedMarginBottom", m_fixedMarginBottom);
1188 writeRect(file, indentLevel + 1, "fixedRect", m_fixedRect);
1189 }
1190
1191 if (m_recordingPicture) {
1192 writeIntVal(file, indentLevel + 1, "m_recordingPicture.width", m_recordingPicture->width());
1193 writeIntVal(file, indentLevel + 1, "m_recordingPicture.height", m_recordingPicture->height());
1194 }
1195
1196 if (countChildren()) {
1197 writeln(file, indentLevel + 1, "children = [");
1198 for (int i = 0; i < countChildren(); i++) {
1199 if (i > 0)
1200 writeln(file, indentLevel + 1, ", ");
1201 getChild(i)->dumpLayers(file, indentLevel + 1);
1202 }
1203 writeln(file, indentLevel + 1, "];");
1204 }
1205 writeln(file, indentLevel, "}");
1206 }
1207
dumpToLog() const1208 void LayerAndroid::dumpToLog() const
1209 {
1210 FILE* file = fopen("/data/data/com.android.browser/layertmp", "w");
1211 dumpLayers(file, 0);
1212 fclose(file);
1213 file = fopen("/data/data/com.android.browser/layertmp", "r");
1214 char buffer[512];
1215 bzero(buffer, sizeof(buffer));
1216 while (fgets(buffer, sizeof(buffer), file))
1217 SkDebugf("%s", buffer);
1218 fclose(file);
1219 }
1220
findById(int match)1221 LayerAndroid* LayerAndroid::findById(int match)
1222 {
1223 if (m_uniqueId == match)
1224 return this;
1225 for (int i = 0; i < countChildren(); i++) {
1226 LayerAndroid* result = getChild(i)->findById(match);
1227 if (result)
1228 return result;
1229 }
1230 return 0;
1231 }
1232
setExtra(DrawExtra * extra)1233 void LayerAndroid::setExtra(DrawExtra* extra)
1234 {
1235 for (int i = 0; i < countChildren(); i++)
1236 getChild(i)->setExtra(extra);
1237
1238 android::AutoMutex lock(m_atomicSync);
1239 if (extra || (m_extra && !extra))
1240 m_dirty = true;
1241
1242 delete m_extra;
1243 m_extra = 0;
1244
1245 if (!extra)
1246 return;
1247
1248 if (m_recordingPicture) {
1249 IntRect dummy; // inval area, unused for now
1250 m_extra = new SkPicture();
1251 SkCanvas* canvas = m_extra->beginRecording(m_recordingPicture->width(),
1252 m_recordingPicture->height());
1253 extra->draw(canvas, this, &dummy);
1254 m_extra->endRecording();
1255 needsRepaint();
1256 }
1257 }
1258
1259 } // namespace WebCore
1260
1261 #endif // USE(ACCELERATED_COMPOSITING)
1262