<commit_before>/* Copyright (c) 2014-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 */

#import "_ASCoreAnimationExtras.h"
#import "_ASAsyncTransaction.h"
#import "ASAssert.h"
#import "ASDisplayNodeInternal.h"

@implementation ASDisplayNode (AsyncDisplay)

/**
 * Support for limiting the number of concurrent displays.
 * Set __ASDisplayLayerMaxConcurrentDisplayCount to change the maximum allowed number of concurrent displays.
 */

#define ASDISPLAYNODE_DELAY_DISPLAY 0

#if ASDISPLAYNODE_DELAY_DISPLAY
static long __ASDisplayLayerMaxConcurrentDisplayCount = 1;
#define ASDN_DELAY_FOR_DISPLAY() usleep( (long)(0.1 * USEC_PER_SEC) )
#else
// Basing this off of CPU core count would make sense, but first some experimentation should be done to understand
// if having more ready-to-run work keeps the CPU clock up (or other interesting scheduler effects).
static long __ASDisplayLayerMaxConcurrentDisplayCount = 8;
#define ASDN_DELAY_FOR_DISPLAY()
#endif

static dispatch_semaphore_t __ASDisplayLayerConcurrentDisplaySemaphore;

/*
 * Call __ASDisplayLayerIncrementConcurrentDisplayCount() upon entry into a display block (either drawRect: or display).
 * This will block if the number of currently executing displays is equal or greater to the limit.
 */
static void __ASDisplayLayerIncrementConcurrentDisplayCount(BOOL displayIsAsync, BOOL isRasterizing)
{
  // Displays while rasterizing are not counted as concurrent displays, because they draw in serial when their rasterizing container displays.
  if (isRasterizing) {
    return;
  }

  static dispatch_once_t onceToken;
  if (displayIsAsync) {
    dispatch_once(&onceToken, ^{
      __ASDisplayLayerConcurrentDisplaySemaphore = dispatch_semaphore_create(__ASDisplayLayerMaxConcurrentDisplayCount);
    });

    dispatch_semaphore_wait(__ASDisplayLayerConcurrentDisplaySemaphore, DISPATCH_TIME_FOREVER);
  }
}

/*
 * Call __ASDisplayLayerDecrementConcurrentDisplayCount() upon exit from a display block, matching calls to __ASDisplayLayerIncrementConcurrentDisplayCount().
 */
static void __ASDisplayLayerDecrementConcurrentDisplayCount(BOOL displayIsAsync, BOOL isRasterizing)
{
  // Displays while rasterizing are not counted as concurrent displays, becuase they draw in serial when their rasterizing container displays.
  if (isRasterizing) {
    return;
  }

  if (displayIsAsync) {
    dispatch_semaphore_signal(__ASDisplayLayerConcurrentDisplaySemaphore);
  }
}

- (NSObject *)drawParameters
{
  if (_flags.implementsDrawParameters) {
    return [self drawParametersForAsyncLayer:self.asyncLayer];
  }

  return nil;
}

- (void)_recursivelyRasterizeSelfAndSublayersWithIsCancelledBlock:(asdisplaynode_iscancelled_block_t)isCancelledBlock displayBlocks:(NSMutableArray *)displayBlocks
{
  // Skip subtrees that are hidden or zero alpha.
  if (self.isHidden || self.alpha <= 0.0) {
    return;
  }
    
  BOOL rasterizingFromAscendent = [self __rasterizedContainerNode] != nil;

  // if super node is rasterizing descendents, subnodes will not have had layout calls becase they don't have layers
  if (rasterizingFromAscendent) {
    [self __layout];
  }

  // Capture these outside the display block so they are retained.
  UIColor *backgroundColor = self.backgroundColor;
  CGRect bounds = self.bounds;
  CGFloat cornerRadius = self.cornerRadius;
  BOOL clipsToBounds = self.clipsToBounds;

  CGRect frame;
  
  // If this is the root container node, use a frame with a zero origin to draw into. If not, calculate the correct frame using the node's position, transform and anchorPoint.
  if (self.shouldRasterizeDescendants) {
    frame = CGRectMake(0.0f, 0.0f, bounds.size.width, bounds.size.height);
  } else {
    CGPoint position = self.position;
    CGPoint anchorPoint = self.anchorPoint;
    
    // Pretty hacky since full 3D transforms aren't actually supported, but attempt to compute the transformed frame of this node so that we can composite it into approximately the right spot.
    CGAffineTransform transform = CATransform3DGetAffineTransform(self.transform);
    CGSize scaledBoundsSize = CGSizeApplyAffineTransform(bounds.size, transform);
    CGPoint origin = CGPointMake(position.x - scaledBoundsSize.width * anchorPoint.x,
                                 position.y - scaledBoundsSize.height * anchorPoint.y);
    frame = CGRectMake(origin.x, origin.y, bounds.size.width, bounds.size.height);
  }

  // Get the display block for this node.
  asyncdisplaykit_async_transaction_operation_block_t displayBlock = [self _displayBlockWithAsynchronous:NO isCancelledBlock:isCancelledBlock rasterizing:YES];

  // We'll display something if there is a display block and/or a background color.
  BOOL shouldDisplay = displayBlock || backgroundColor;

  // If we should display, then push a transform, draw the background color, and draw the contents.
  // The transform is popped in a block added after the recursion into subnodes.
  if (shouldDisplay) {
    dispatch_block_t pushAndDisplayBlock = ^{
      // Push transform relative to parent.
      CGContextRef context = UIGraphicsGetCurrentContext();
      CGContextSaveGState(context);

      CGContextTranslateCTM(context, frame.origin.x, frame.origin.y);

      //support cornerRadius
      if (rasterizingFromAscendent && cornerRadius && clipsToBounds) {
        [[UIBezierPath bezierPathWithRoundedRect:bounds cornerRadius:self.cornerRadius] addClip];
      }

      // Fill background if any.
      CGColorRef backgroundCGColor = backgroundColor.CGColor;
      if (backgroundColor && CGColorGetAlpha(backgroundCGColor) > 0.0) {
        CGContextSetFillColorWithColor(context, backgroundCGColor);
        CGContextFillRect(context, bounds);
      }

      // If there is a display block, call it to get the image, then copy the image into the current context (which is the rasterized container's backing store).<commit_msg>Fix shouldRasterizeSubnodes skipping setting offset and clipping.<commit_after>/* Copyright (c) 2014-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 */

#import "_ASCoreAnimationExtras.h"
#import "_ASAsyncTransaction.h"
#import "ASAssert.h"
#import "ASDisplayNodeInternal.h"

@implementation ASDisplayNode (AsyncDisplay)

/**
 * Support for limiting the number of concurrent displays.
 * Set __ASDisplayLayerMaxConcurrentDisplayCount to change the maximum allowed number of concurrent displays.
 */

#define ASDISPLAYNODE_DELAY_DISPLAY 0

#if ASDISPLAYNODE_DELAY_DISPLAY
static long __ASDisplayLayerMaxConcurrentDisplayCount = 1;
#define ASDN_DELAY_FOR_DISPLAY() usleep( (long)(0.1 * USEC_PER_SEC) )
#else
// Basing this off of CPU core count would make sense, but first some experimentation should be done to understand
// if having more ready-to-run work keeps the CPU clock up (or other interesting scheduler effects).
static long __ASDisplayLayerMaxConcurrentDisplayCount = 8;
#define ASDN_DELAY_FOR_DISPLAY()
#endif

static dispatch_semaphore_t __ASDisplayLayerConcurrentDisplaySemaphore;

/*
 * Call __ASDisplayLayerIncrementConcurrentDisplayCount() upon entry into a display block (either drawRect: or display).
 * This will block if the number of currently executing displays is equal or greater to the limit.
 */
static void __ASDisplayLayerIncrementConcurrentDisplayCount(BOOL displayIsAsync, BOOL isRasterizing)
{
  // Displays while rasterizing are not counted as concurrent displays, because they draw in serial when their rasterizing container displays.
  if (isRasterizing) {
    return;
  }

  static dispatch_once_t onceToken;
  if (displayIsAsync) {
    dispatch_once(&onceToken, ^{
      __ASDisplayLayerConcurrentDisplaySemaphore = dispatch_semaphore_create(__ASDisplayLayerMaxConcurrentDisplayCount);
    });

    dispatch_semaphore_wait(__ASDisplayLayerConcurrentDisplaySemaphore, DISPATCH_TIME_FOREVER);
  }
}

/*
 * Call __ASDisplayLayerDecrementConcurrentDisplayCount() upon exit from a display block, matching calls to __ASDisplayLayerIncrementConcurrentDisplayCount().
 */
static void __ASDisplayLayerDecrementConcurrentDisplayCount(BOOL displayIsAsync, BOOL isRasterizing)
{
  // Displays while rasterizing are not counted as concurrent displays, becuase they draw in serial when their rasterizing container displays.
  if (isRasterizing) {
    return;
  }

  if (displayIsAsync) {
    dispatch_semaphore_signal(__ASDisplayLayerConcurrentDisplaySemaphore);
  }
}

- (NSObject *)drawParameters
{
  if (_flags.implementsDrawParameters) {
    return [self drawParametersForAsyncLayer:self.asyncLayer];
  }

  return nil;
}

- (void)_recursivelyRasterizeSelfAndSublayersWithIsCancelledBlock:(asdisplaynode_iscancelled_block_t)isCancelledBlock displayBlocks:(NSMutableArray *)displayBlocks
{
  // Skip subtrees that are hidden or zero alpha.
  if (self.isHidden || self.alpha <= 0.0) {
    return;
  }
    
  BOOL rasterizingFromAscendent = [self __rasterizedContainerNode] != nil;

  // if super node is rasterizing descendents, subnodes will not have had layout calls becase they don't have layers
  if (rasterizingFromAscendent) {
    [self __layout];
  }

  // Capture these outside the display block so they are retained.
  UIColor *backgroundColor = self.backgroundColor;
  CGRect bounds = self.bounds;
  CGFloat cornerRadius = self.cornerRadius;
  BOOL clipsToBounds = self.clipsToBounds;

  CGRect frame;
  
  // If this is the root container node, use a frame with a zero origin to draw into. If not, calculate the correct frame using the node's position, transform and anchorPoint.
  if (self.shouldRasterizeDescendants) {
    frame = CGRectMake(0.0f, 0.0f, bounds.size.width, bounds.size.height);
  } else {
    CGPoint position = self.position;
    CGPoint anchorPoint = self.anchorPoint;
    
    // Pretty hacky since full 3D transforms aren't actually supported, but attempt to compute the transformed frame of this node so that we can composite it into approximately the right spot.
    CGAffineTransform transform = CATransform3DGetAffineTransform(self.transform);
    CGSize scaledBoundsSize = CGSizeApplyAffineTransform(bounds.size, transform);
    CGPoint origin = CGPointMake(position.x - scaledBoundsSize.width * anchorPoint.x,
                                 position.y - scaledBoundsSize.height * anchorPoint.y);
    frame = CGRectMake(origin.x, origin.y, bounds.size.width, bounds.size.height);
  }

  // Get the display block for this node.
  asyncdisplaykit_async_transaction_operation_block_t displayBlock = [self _displayBlockWithAsynchronous:NO isCancelledBlock:isCancelledBlock rasterizing:YES];

  // We'll display something if there is a display block, clipping, translation and/or a background color.
  BOOL shouldDisplay = displayBlock || backgroundColor || CGPointEqualToPoint(CGPointZero, frame.origin) == NO || clipsToBounds;

  // If we should display, then push a transform, draw the background color, and draw the contents.
  // The transform is popped in a block added after the recursion into subnodes.
  if (shouldDisplay) {
    dispatch_block_t pushAndDisplayBlock = ^{
      // Push transform relative to parent.
      CGContextRef context = UIGraphicsGetCurrentContext();
      CGContextSaveGState(context);

      CGContextTranslateCTM(context, frame.origin.x, frame.origin.y);

      //support cornerRadius
      if (clipsToBounds) {
        if (cornerRadius) {
          [[UIBezierPath bezierPathWithRoundedRect:bounds cornerRadius:self.cornerRadius] addClip];
        } else {
          [[UIBezierPath bezierPathWithRect:bounds] addClip];
        }
      }

      // Fill background if any.
      CGColorRef backgroundCGColor = backgroundColor.CGColor;
      if (backgroundColor && CGColorGetAlpha(backgroundCGColor) > 0.0) {
        CGContextSetFillColorWithColor(context, backgroundCGColor);
        CGContextFillRect(context, bounds);
      }

      // If there is a display block, call it to get the image, then copy the image into the current context (which is the rasterized container's backing store).
{'fraction_non_alphanumeric': 0.069557507858467, 'fraction_numerical': 0.0030627871362940277, 'mean_word_length': 4.5, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4225056', 'n_tokens_mistral': 3564, 'n_tokens_neox': 3389, 'n_words': 1103}