odin-blend2d

Odin bindings to Blend2D
Log | Files | Refs | README | LICENSE

bl_bench_backend_coregraphics.cpp (22105B)


      1 // This file is part of Blend2D project <https://blend2d.com>
      2 //
      3 // See LICENSE.md for license and copyright information
      4 // SPDX-License-Identifier: Zlib
      5 
      6 #ifdef BL_BENCH_ENABLE_COREGRAPHICS
      7 
      8 #include "bl_bench_app.h"
      9 #include "bl_bench_backend.h"
     10 
     11 #include <ApplicationServices/ApplicationServices.h>
     12 #include <utility>
     13 
     14 namespace blbench {
     15 
     16 static uint32_t to_cg_bitmap_info(BLFormat format) noexcept {
     17   switch (format) {
     18     case BL_FORMAT_PRGB32: return kCGImageByteOrder32Little | kCGImageAlphaPremultipliedFirst;
     19     case BL_FORMAT_XRGB32: return kCGImageByteOrder32Little | kCGImageAlphaNoneSkipFirst;
     20 
     21     default: return 0;
     22   }
     23 }
     24 
     25 static CGBlendMode to_cg_blend_mode(BLCompOp comp_op) noexcept {
     26   switch (comp_op) {
     27     case BL_COMP_OP_SRC_OVER   : return kCGBlendModeNormal;
     28     case BL_COMP_OP_SRC_COPY   : return kCGBlendModeCopy;
     29     case BL_COMP_OP_SRC_IN     : return kCGBlendModeSourceIn;
     30     case BL_COMP_OP_SRC_OUT    : return kCGBlendModeSourceOut;
     31     case BL_COMP_OP_SRC_ATOP   : return kCGBlendModeSourceAtop;
     32     case BL_COMP_OP_DST_OVER   : return kCGBlendModeDestinationOver;
     33     case BL_COMP_OP_DST_IN     : return kCGBlendModeDestinationIn;
     34     case BL_COMP_OP_DST_OUT    : return kCGBlendModeDestinationOut;
     35     case BL_COMP_OP_DST_ATOP   : return kCGBlendModeDestinationAtop;
     36     case BL_COMP_OP_XOR        : return kCGBlendModeXOR;
     37     case BL_COMP_OP_CLEAR      : return kCGBlendModeClear;
     38     case BL_COMP_OP_PLUS       : return kCGBlendModePlusLighter;
     39     case BL_COMP_OP_MULTIPLY   : return kCGBlendModeMultiply;
     40     case BL_COMP_OP_SCREEN     : return kCGBlendModeScreen;
     41     case BL_COMP_OP_OVERLAY    : return kCGBlendModeOverlay;
     42     case BL_COMP_OP_DARKEN     : return kCGBlendModeDarken;
     43     case BL_COMP_OP_LIGHTEN    : return kCGBlendModeLighten;
     44     case BL_COMP_OP_COLOR_DODGE: return kCGBlendModeColorDodge;
     45     case BL_COMP_OP_COLOR_BURN : return kCGBlendModeColorBurn;
     46     case BL_COMP_OP_HARD_LIGHT : return kCGBlendModeHardLight;
     47     case BL_COMP_OP_SOFT_LIGHT : return kCGBlendModeSoftLight;
     48     case BL_COMP_OP_DIFFERENCE : return kCGBlendModeDifference;
     49     case BL_COMP_OP_EXCLUSION  : return kCGBlendModeExclusion;
     50 
     51     default:
     52       return kCGBlendModeNormal;
     53   }
     54 }
     55 
     56 template<typename RectT>
     57 static inline CGRect to_cg_rect(const RectT& rect) noexcept {
     58   return CGRectMake(CGFloat(rect.x), CGFloat(rect.y), CGFloat(rect.w), CGFloat(rect.h));
     59 }
     60 
     61 static inline void to_cg_color_components(CGFloat components[4], BLRgba32 color) noexcept {
     62   constexpr CGFloat scale = CGFloat(1.0f / 255.0f);
     63   components[0] = CGFloat(int(color.r())) * scale;
     64   components[1] = CGFloat(int(color.g())) * scale;
     65   components[2] = CGFloat(int(color.b())) * scale;
     66   components[3] = CGFloat(int(color.a())) * scale;
     67 }
     68 
     69 static void flipImage(BLImage& img) noexcept {
     70   BLImageData imgData;
     71   img.make_mutable(&imgData);
     72 
     73   uint32_t h = uint32_t(imgData.size.h);
     74   intptr_t stride = imgData.stride;
     75   uint8_t* pixel_data = static_cast<uint8_t*>(imgData.pixel_data);
     76 
     77   for (uint32_t y = 0; y < h / 2; y++) {
     78     uint8_t* a = pixel_data + intptr_t(y) * stride;
     79     uint8_t* b = pixel_data + intptr_t(h - y - 1) * stride;
     80 
     81     for (size_t x = 0; x < size_t(stride); x++) {
     82       std::swap(a[x], b[x]);
     83     }
     84 
     85   }
     86 }
     87 
     88 struct CoreGraphicsModule : public Backend {
     89   CGImageRef _cg_sprites[kBenchNumSprites] {};
     90 
     91   CGColorSpaceRef _cg_colorspace {};
     92   CGContextRef _cg_ctx {};
     93 
     94   CoreGraphicsModule();
     95   ~CoreGraphicsModule() override;
     96 
     97   void serialize_info(JSONBuilder& json) const override;
     98 
     99   bool supports_comp_op(BLCompOp comp_op) const override;
    100   bool supports_style(StyleKind style) const override;
    101 
    102   void before_run() override;
    103   void flush() override;
    104   void after_run() override;
    105 
    106   CGGradientRef create_gradient(StyleKind style) noexcept;
    107 
    108   BL_INLINE void render_solid_path(RenderOp op) noexcept;
    109 
    110   template<typename RectT>
    111   BL_INLINE void render_solid_rect(const RectT& rect, RenderOp op) noexcept;
    112 
    113   template<bool kSaveGState, typename RectT>
    114   BL_INLINE void render_styled_path(const RectT& rect, StyleKind style, RenderOp op) noexcept;
    115 
    116   template<bool kSaveGState, typename RectT>
    117   BL_INLINE void render_styled_rect(const RectT& rect, StyleKind style, RenderOp op) noexcept;
    118 
    119   void render_rect_a(RenderOp op) override;
    120   void render_rect_f(RenderOp op) override;
    121   void render_rect_rotated(RenderOp op) override;
    122   void render_round_f(RenderOp op) override;
    123   void render_round_rotated(RenderOp op) override;
    124   void render_polygon(RenderOp op, uint32_t complexity) override;
    125   void render_shape(RenderOp op, ShapeData shape) override;
    126 };
    127 
    128 CoreGraphicsModule::CoreGraphicsModule() {
    129   strcpy(_name, "CoreGraphics");
    130 }
    131 
    132 CoreGraphicsModule::~CoreGraphicsModule() {}
    133 
    134 void CoreGraphicsModule::serialize_info(JSONBuilder& json) const {
    135 }
    136 
    137 bool CoreGraphicsModule::supports_comp_op(BLCompOp comp_op) const {
    138   return comp_op == BL_COMP_OP_SRC_OVER    ||
    139          comp_op == BL_COMP_OP_SRC_COPY    ||
    140          comp_op == BL_COMP_OP_SRC_IN      ||
    141          comp_op == BL_COMP_OP_SRC_OUT     ||
    142          comp_op == BL_COMP_OP_SRC_ATOP    ||
    143          comp_op == BL_COMP_OP_DST_OVER    ||
    144          comp_op == BL_COMP_OP_DST_IN      ||
    145          comp_op == BL_COMP_OP_DST_OUT     ||
    146          comp_op == BL_COMP_OP_DST_ATOP    ||
    147          comp_op == BL_COMP_OP_XOR         ||
    148          comp_op == BL_COMP_OP_CLEAR       ||
    149          comp_op == BL_COMP_OP_PLUS        ||
    150          comp_op == BL_COMP_OP_MULTIPLY    ||
    151          comp_op == BL_COMP_OP_SCREEN      ||
    152          comp_op == BL_COMP_OP_OVERLAY     ||
    153          comp_op == BL_COMP_OP_DARKEN      ||
    154          comp_op == BL_COMP_OP_LIGHTEN     ||
    155          comp_op == BL_COMP_OP_COLOR_DODGE ||
    156          comp_op == BL_COMP_OP_COLOR_BURN  ||
    157          comp_op == BL_COMP_OP_HARD_LIGHT  ||
    158          comp_op == BL_COMP_OP_SOFT_LIGHT  ||
    159          comp_op == BL_COMP_OP_DIFFERENCE  ||
    160          comp_op == BL_COMP_OP_EXCLUSION   ;
    161 }
    162 
    163 bool CoreGraphicsModule::supports_style(StyleKind style) const {
    164   return style == StyleKind::kSolid     ||
    165          style == StyleKind::kLinearPad ||
    166          style == StyleKind::kRadialPad ||
    167          style == StyleKind::kConic     ||
    168          style == StyleKind::kPatternNN ||
    169          style == StyleKind::kPatternBI ;
    170 }
    171 
    172 void CoreGraphicsModule::before_run() {
    173   int w = int(_params.screen_w);
    174   int h = int(_params.screen_h);
    175   StyleKind style = _params.style;
    176 
    177   _cg_colorspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGBLinear);
    178 
    179   // Initialize the sprites.
    180   for (uint32_t i = 0; i < kBenchNumSprites; i++) {
    181     BLImage& sprite = _sprites[i];
    182     flipImage(sprite);
    183 
    184     BLImageData sprite_data;
    185     sprite.get_data(&sprite_data);
    186 
    187     BLFormat sprite_format = BLFormat(sprite_data.format);
    188 
    189     CGDataProviderRef dp = CGDataProviderCreateWithData(
    190       nullptr,
    191       sprite_data.pixel_data,
    192       size_t(sprite_data.size.h) * size_t(sprite_data.stride),
    193       nullptr);
    194 
    195     if (!dp) {
    196       printf("Failed to create CGDataProvider\n");
    197       continue;
    198     }
    199 
    200     CGImageRef cg_sprite = CGImageCreate(
    201       size_t(sprite_data.size.w),       // Width.
    202       size_t(sprite_data.size.h),       // Height.
    203       8,                                // Bits per component.
    204       32,                               // Bits per pixel.
    205       size_t(sprite_data.stride),       // Bytes per row.
    206       _cg_colorspace,                   // Colorspace.
    207       to_cg_bitmap_info(sprite_format), // Bitmap info.
    208       dp,                               // Data provider.
    209       nullptr,                          // Decode.
    210       style == StyleKind::kPatternBI,   // Should interpolate.
    211       kCGRenderingIntentDefault);       // Color rendering intent.
    212 
    213     if (!cg_sprite) {
    214       printf("Failed to create CGImage\n");
    215       continue;
    216     }
    217 
    218     _cg_sprites[i] = cg_sprite;
    219     CGDataProviderRelease(dp);
    220   }
    221 
    222   // Initialize the surface and the context.
    223   BLImageData surface_data;
    224   _surface.create(w, h, _params.format);
    225   _surface.make_mutable(&surface_data);
    226 
    227   _cg_ctx = CGBitmapContextCreate(
    228     surface_data.pixel_data,
    229     uint32_t(surface_data.size.w),
    230     uint32_t(surface_data.size.h),
    231     8,
    232     size_t(surface_data.stride),
    233     _cg_colorspace,
    234     to_cg_bitmap_info(BLFormat(surface_data.format)));
    235 
    236   const CGFloat transparent[4] {};
    237 
    238   // Setup the context.
    239   CGContextSetBlendMode(_cg_ctx, kCGBlendModeCopy);
    240   CGContextSetFillColorSpace(_cg_ctx, _cg_colorspace);
    241   CGContextSetStrokeColorSpace(_cg_ctx, _cg_colorspace);
    242 
    243   CGContextSetFillColor(_cg_ctx, transparent);
    244   CGContextFillRect(_cg_ctx, CGRectMake(0, 0, CGFloat(surface_data.size.w), CGFloat(surface_data.size.h)));
    245 
    246   CGContextSetBlendMode(_cg_ctx, to_cg_blend_mode(_params.comp_op));
    247   CGContextSetAllowsAntialiasing(_cg_ctx, true);
    248 
    249   CGContextSetLineJoin(_cg_ctx, kCGLineJoinMiter);
    250   CGContextSetLineWidth(_cg_ctx, CGFloat(_params.stroke_width));
    251 }
    252 
    253 void CoreGraphicsModule::flush() {
    254   CGContextSynchronize(_cg_ctx);
    255 }
    256 
    257 void CoreGraphicsModule::after_run() {
    258   CGContextRelease(_cg_ctx);
    259   CGColorSpaceRelease(_cg_colorspace);
    260 
    261   _cg_ctx = nullptr;
    262   _cg_colorspace = nullptr;
    263 
    264   // Free the sprites.
    265   for (uint32_t i = 0; i < kBenchNumSprites; i++) {
    266     if (_cg_sprites[i]) {
    267       CGImageRelease(_cg_sprites[i]);
    268       _cg_sprites[i] = nullptr;
    269     }
    270   }
    271 
    272   flipImage(_surface);
    273 }
    274 
    275 CGGradientRef CoreGraphicsModule::create_gradient(StyleKind style) noexcept {
    276   BLRgba32 c0 = _rnd_color.next_rgba32();
    277   BLRgba32 c1 = _rnd_color.next_rgba32();
    278   BLRgba32 c2 = _rnd_color.next_rgba32();
    279 
    280   switch (style) {
    281     case StyleKind::kLinearPad:
    282     case StyleKind::kLinearRepeat:
    283     case StyleKind::kRadialPad:
    284     case StyleKind::kRadialRepeat: {
    285       CGFloat components[12];
    286       to_cg_color_components(components + 0, c0);
    287       to_cg_color_components(components + 4, c1);
    288       to_cg_color_components(components + 8, c2);
    289 
    290       CGFloat locations[3] = {
    291         CGFloat(0.0),
    292         CGFloat(0.5),
    293         CGFloat(1.0)
    294       };
    295 
    296       return CGGradientCreateWithColorComponents(_cg_colorspace, components, locations, 3);
    297     }
    298 
    299     case StyleKind::kLinearReflect:
    300     case StyleKind::kRadialReflect: {
    301 
    302       CGFloat components[20];
    303       to_cg_color_components(components +  0, c0);
    304       to_cg_color_components(components +  4, c1);
    305       to_cg_color_components(components +  8, c2);
    306       to_cg_color_components(components + 12, c1);
    307       to_cg_color_components(components + 16, c0);
    308 
    309       CGFloat locations[5] = {
    310         CGFloat(0.0),
    311         CGFloat(0.25),
    312         CGFloat(0.5),
    313         CGFloat(0.75),
    314         CGFloat(1.0)
    315       };
    316 
    317       return CGGradientCreateWithColorComponents(_cg_colorspace, components, locations, 5);
    318     }
    319 
    320     case StyleKind::kConic: {
    321       BLRgba32 c0 = _rnd_color.next_rgba32();
    322       BLRgba32 c1 = _rnd_color.next_rgba32();
    323       BLRgba32 c2 = _rnd_color.next_rgba32();
    324 
    325       CGFloat components[16];
    326       to_cg_color_components(components +  0, c0);
    327       to_cg_color_components(components +  4, c1);
    328       to_cg_color_components(components +  8, c2);
    329       to_cg_color_components(components + 12, c0);
    330 
    331       CGFloat locations[4] = {
    332         CGFloat(0.0),
    333         CGFloat(0.33),
    334         CGFloat(0.66),
    335         CGFloat(1.0)
    336       };
    337 
    338       return CGGradientCreateWithColorComponents(_cg_colorspace, components, locations, 4);
    339     }
    340 
    341     default:
    342       return CGGradientRef{};
    343   }
    344 }
    345 
    346 BL_INLINE void CoreGraphicsModule::render_solid_path(RenderOp op) noexcept {
    347   CGFloat color[4];
    348   to_cg_color_components(color, _rnd_color.next_rgba32());
    349 
    350   if (op == RenderOp::kStroke) {
    351     CGContextSetStrokeColor(_cg_ctx, color);
    352     CGContextStrokePath(_cg_ctx);
    353   }
    354   else {
    355     CGContextSetFillColor(_cg_ctx, color);
    356     if (op == RenderOp::kFillNonZero)
    357       CGContextFillPath(_cg_ctx);
    358     else
    359       CGContextEOFillPath(_cg_ctx);
    360   }
    361 }
    362 
    363 template<typename RectT>
    364 BL_INLINE void CoreGraphicsModule::render_solid_rect(const RectT& rect, RenderOp op) noexcept {
    365   CGFloat color[4];
    366   to_cg_color_components(color, _rnd_color.next_rgba32());
    367 
    368   if (op == RenderOp::kStroke) {
    369     CGContextSetStrokeColor(_cg_ctx, color);
    370     CGContextStrokeRect(_cg_ctx, to_cg_rect(rect));
    371   }
    372   else {
    373     CGContextSetFillColor(_cg_ctx, color);
    374     CGContextFillRect(_cg_ctx, to_cg_rect(rect));
    375   }
    376 }
    377 
    378 template<bool kSaveGState, typename RectT>
    379 BL_INLINE void CoreGraphicsModule::render_styled_path(const RectT& rect, StyleKind style, RenderOp op) noexcept {
    380   if (kSaveGState)
    381     CGContextSaveGState(_cg_ctx);
    382 
    383   if (op == RenderOp::kStroke) {
    384     CGContextReplacePathWithStrokedPath(_cg_ctx);
    385   }
    386 
    387   if (op == RenderOp::kFillEvenOdd) {
    388     CGContextEOClip(_cg_ctx);
    389   }
    390   else {
    391     CGContextClip(_cg_ctx);
    392   }
    393 
    394   switch (style) {
    395     case StyleKind::kSolid:
    396       // Not reached (the caller must use render_solid_path() instead).
    397       break;
    398 
    399     case StyleKind::kLinearPad:
    400     case StyleKind::kLinearRepeat:
    401     case StyleKind::kLinearReflect: {
    402       CGFloat reflectScale = style == StyleKind::kLinearReflect ? 1.0 : 2.0;
    403       CGFloat w = rect.w * reflectScale;
    404       CGFloat h = rect.h * reflectScale;
    405 
    406       CGFloat x0 = CGFloat(rect.x) + w * CGFloat(0.2);
    407       CGFloat y0 = CGFloat(rect.y) + h * CGFloat(0.2);
    408       CGFloat x1 = CGFloat(rect.x) + w * CGFloat(0.8);
    409       CGFloat y1 = CGFloat(rect.y) + h * CGFloat(0.8);
    410 
    411       CGGradientRef gradient = create_gradient(style);
    412       CGGradientDrawingOptions options = kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation;
    413       CGContextDrawLinearGradient(_cg_ctx, gradient, CGPointMake(x0, y0), CGPointMake(x1, y1), options);
    414       CGGradientRelease(gradient);
    415       break;
    416     }
    417 
    418     case StyleKind::kRadialPad:
    419     case StyleKind::kRadialRepeat:
    420     case StyleKind::kRadialReflect: {
    421       CGFloat cx = CGFloat(rect.x + rect.w / 2);
    422       CGFloat cy = CGFloat(rect.y + rect.h / 2);
    423       CGFloat cr = CGFloat((rect.w + rect.h) / 4);
    424       CGFloat fx = CGFloat(cx - cr / 2);
    425       CGFloat fy = CGFloat(cy - cr / 2);
    426 
    427       CGGradientRef gradient = create_gradient(style);
    428       CGGradientDrawingOptions options = kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation;
    429       CGContextDrawRadialGradient(_cg_ctx, gradient, CGPointMake(cx, cy), cr, CGPointMake(fx, fy), CGFloat(0.0), options);
    430       CGGradientRelease(gradient);
    431       break;
    432     }
    433 
    434     case StyleKind::kConic: {
    435       double cx = rect.x + rect.w / 2;
    436       double cy = rect.y + rect.h / 2;
    437       double angle = 0.0;
    438 
    439       CGGradientRef gradient = create_gradient(style);
    440       CGContextDrawConicGradient(_cg_ctx, gradient, CGPointMake(cx, cy), CGFloat(angle));
    441       CGGradientRelease(gradient);
    442       break;
    443     }
    444 
    445     case StyleKind::kPatternNN:
    446     case StyleKind::kPatternBI: {
    447       uint32_t spriteId = nextSpriteId();
    448 
    449       CGContextDrawImage(_cg_ctx, to_cg_rect(rect), _cg_sprites[spriteId]);
    450       break;
    451     }
    452   }
    453 
    454   if (kSaveGState)
    455     CGContextRestoreGState(_cg_ctx);
    456 }
    457 
    458 template<bool kSaveGState, typename RectT>
    459 BL_INLINE void CoreGraphicsModule::render_styled_rect(const RectT& rect, StyleKind style, RenderOp op) noexcept {
    460   CGContextAddRect(_cg_ctx, to_cg_rect(rect));
    461   render_styled_path<kSaveGState>(rect, style, op);
    462 }
    463 
    464 void CoreGraphicsModule::render_rect_a(RenderOp op) {
    465   BLSizeI bounds(_params.screen_w, _params.screen_h);
    466   StyleKind style = _params.style;
    467   int wh = _params.shape_size;
    468 
    469   if (style == StyleKind::kSolid) {
    470     for (uint32_t i = 0, quantity = _params.quantity; i < quantity; i++) {
    471       render_solid_rect(_rnd_coord.next_rect_i(bounds, wh, wh), op);
    472     }
    473   }
    474   else if ((style == StyleKind::kPatternNN || style == StyleKind::kPatternBI) && op != RenderOp::kStroke) {
    475     CGFloat wh_f = CGFloat(wh);
    476     for (uint32_t i = 0, quantity = _params.quantity; i < quantity; i++) {
    477       BLRectI r = _rnd_coord.next_rect_i(bounds, wh, wh);
    478       uint32_t spriteId = nextSpriteId();
    479 
    480       CGContextDrawImage(_cg_ctx, CGRectMake(CGFloat(r.x), CGFloat(r.y), wh_f, wh_f), _cg_sprites[spriteId]);
    481     }
    482   }
    483   else {
    484     for (uint32_t i = 0, quantity = _params.quantity; i < quantity; i++) {
    485       render_styled_rect<true>(_rnd_coord.next_rect_i(bounds, wh, wh), style, op);
    486     }
    487   }
    488 }
    489 
    490 void CoreGraphicsModule::render_rect_f(RenderOp op) {
    491   BLSize bounds(_params.screen_w, _params.screen_h);
    492   StyleKind style = _params.style;
    493   double wh = _params.shape_size;
    494 
    495   if (style == StyleKind::kSolid) {
    496     for (uint32_t i = 0, quantity = _params.quantity; i < quantity; i++) {
    497       render_solid_rect(_rnd_coord.next_rect(bounds, wh, wh), op);
    498     }
    499   }
    500   else {
    501     for (uint32_t i = 0, quantity = _params.quantity; i < quantity; i++) {
    502       render_styled_rect<true>(_rnd_coord.next_rect(bounds, wh, wh), style, op);
    503     }
    504   }
    505 }
    506 
    507 void CoreGraphicsModule::render_rect_rotated(RenderOp op) {
    508   BLSize bounds(_params.screen_w, _params.screen_h);
    509   StyleKind style = _params.style;
    510 
    511   double cx = double(_params.screen_w) * 0.5;
    512   double cy = double(_params.screen_h) * 0.5;
    513   double wh = _params.shape_size;
    514   double angle = 0.0;
    515 
    516   for (uint32_t i = 0, quantity = _params.quantity; i < quantity; i++, angle += 0.01) {
    517     BLRect rect(_rnd_coord.next_rect(bounds, wh, wh));
    518 
    519     CGContextSaveGState(_cg_ctx);
    520     CGContextTranslateCTM(_cg_ctx, CGFloat(cx), CGFloat(cy));
    521     CGContextRotateCTM(_cg_ctx, CGFloat(angle));
    522     CGContextTranslateCTM(_cg_ctx, CGFloat(-cx), CGFloat(-cy));
    523 
    524     if (style == StyleKind::kSolid) {
    525       render_solid_rect(rect, op);
    526     }
    527     else {
    528       render_styled_rect<false>(rect, style, op);
    529     }
    530 
    531     CGContextRestoreGState(_cg_ctx);
    532   }
    533 }
    534 
    535 void CoreGraphicsModule::render_round_f(RenderOp op) {
    536   BLSize bounds(_params.screen_w, _params.screen_h);
    537   StyleKind style = _params.style;
    538   double wh = _params.shape_size;
    539 
    540   for (uint32_t i = 0, quantity = _params.quantity; i < quantity; i++) {
    541     BLRect rect(_rnd_coord.next_rect(bounds, wh, wh));
    542     double radius = _rnd_extra.next_double(4.0, 40.0);
    543 
    544     CGPathRef path = CGPathCreateWithRoundedRect(
    545       CGRectMake(rect.x, rect.y, rect.w, rect.h),
    546       std::min(rect.w * 0.5, radius),
    547       std::min(rect.h * 0.5, radius),
    548       nullptr);
    549 
    550     CGContextAddPath(_cg_ctx, path);
    551 
    552     if (style == StyleKind::kSolid) {
    553       render_solid_path(op);
    554     }
    555     else {
    556       render_styled_path<true>(rect, style, op);
    557     }
    558 
    559     CGPathRelease(path);
    560   }
    561 }
    562 
    563 void CoreGraphicsModule::render_round_rotated(RenderOp op) {
    564   BLSize bounds(_params.screen_w, _params.screen_h);
    565   StyleKind style = _params.style;
    566 
    567   double cx = double(_params.screen_w) * 0.5;
    568   double cy = double(_params.screen_h) * 0.5;
    569   double wh = _params.shape_size;
    570   double angle = 0.0;
    571 
    572   for (uint32_t i = 0, quantity = _params.quantity; i < quantity; i++, angle += 0.01) {
    573     BLRect rect(_rnd_coord.next_rect(bounds, wh, wh));
    574     double radius = _rnd_extra.next_double(4.0, 40.0);
    575 
    576     CGContextSaveGState(_cg_ctx);
    577     CGContextTranslateCTM(_cg_ctx, CGFloat(cx), CGFloat(cy));
    578     CGContextRotateCTM(_cg_ctx, CGFloat(angle));
    579     CGContextTranslateCTM(_cg_ctx, CGFloat(-cx), CGFloat(-cy));
    580 
    581     CGPathRef path = CGPathCreateWithRoundedRect(
    582       CGRectMake(rect.x, rect.y, rect.w, rect.h),
    583       std::min(rect.w * 0.5, radius),
    584       std::min(rect.h * 0.5, radius),
    585       nullptr);
    586 
    587     CGContextAddPath(_cg_ctx, path);
    588 
    589     if (style == StyleKind::kSolid) {
    590       render_solid_path(op);
    591     }
    592     else {
    593       render_styled_path<false>(rect, style, op);
    594     }
    595 
    596     CGPathRelease(path);
    597     CGContextRestoreGState(_cg_ctx);
    598   }
    599 }
    600 
    601 void CoreGraphicsModule::render_polygon(RenderOp op, uint32_t complexity) {
    602   BLSizeI bounds(_params.screen_w - _params.shape_size,
    603                  _params.screen_h - _params.shape_size);
    604   StyleKind style = _params.style;
    605   double wh = double(_params.shape_size);
    606 
    607   for (uint32_t i = 0, quantity = _params.quantity; i < quantity; i++) {
    608     BLPoint base(_rnd_coord.nextPoint(bounds));
    609 
    610     double x = _rnd_coord.next_double(base.x, base.x + wh);
    611     double y = _rnd_coord.next_double(base.y, base.y + wh);
    612 
    613     CGContextMoveToPoint(_cg_ctx, CGFloat(x), CGFloat(y));
    614     for (uint32_t p = 1; p < complexity; p++) {
    615       x = _rnd_coord.next_double(base.x, base.x + wh);
    616       y = _rnd_coord.next_double(base.y, base.y + wh);
    617       CGContextAddLineToPoint(_cg_ctx, CGFloat(x), CGFloat(y));
    618     }
    619     CGContextClosePath(_cg_ctx);
    620 
    621     if (style == StyleKind::kSolid) {
    622       render_solid_path(op);
    623     }
    624     else {
    625       render_styled_path<true>(BLRect(x, y, wh, wh), style, op);
    626     }
    627   }
    628 }
    629 
    630 void CoreGraphicsModule::render_shape(RenderOp op, ShapeData shape) {
    631   BLSizeI bounds(_params.screen_w - _params.shape_size,
    632                  _params.screen_h - _params.shape_size);
    633   StyleKind style = _params.style;
    634   double wh = double(_params.shape_size);
    635 
    636   CGMutablePathRef path = CGPathCreateMutable();
    637   ShapeIterator it(shape);
    638 
    639   while (it.has_command()) {
    640     if (it.is_move_to()) {
    641       CGPathMoveToPoint(
    642         path,
    643         nullptr,
    644         it.x(0) * wh, it.y(0) * wh);
    645     }
    646     else if (it.is_line_to()) {
    647       CGPathAddLineToPoint(
    648         path,
    649         nullptr,
    650         it.x(0) * wh, it.y(0) * wh);
    651     }
    652     else if (it.is_quad_to()) {
    653       CGPathAddQuadCurveToPoint(
    654         path,
    655         nullptr,
    656         it.x(0) * wh, it.y(0) * wh,
    657         it.x(1) * wh, it.y(1) * wh);
    658     }
    659     else if (it.is_cubic_to()) {
    660       CGPathAddCurveToPoint(
    661         path,
    662         nullptr,
    663         it.x(0) * wh, it.y(0) * wh,
    664         it.x(1) * wh, it.y(1) * wh,
    665         it.x(2) * wh, it.y(2) * wh);
    666     }
    667     else {
    668       CGPathCloseSubpath(path);
    669     }
    670     it.next();
    671   }
    672 
    673   for (uint32_t i = 0, quantity = _params.quantity; i < quantity; i++) {
    674     BLPoint base(_rnd_coord.nextPoint(bounds));
    675 
    676     CGContextSaveGState(_cg_ctx);
    677     CGContextTranslateCTM(_cg_ctx, CGFloat(base.x), CGFloat(base.y));
    678     CGContextAddPath(_cg_ctx, path);
    679 
    680     if (style == StyleKind::kSolid) {
    681       render_solid_path(op);
    682     }
    683     else {
    684       render_styled_path<false>(BLRect(base.x, base.y, wh, wh), style, op);
    685     }
    686 
    687     CGContextRestoreGState(_cg_ctx);
    688   }
    689 
    690   CGPathRelease(path);
    691 }
    692 
    693 Backend* create_cg_backend() {
    694   return new CoreGraphicsModule();
    695 }
    696 
    697 } // {blbench}
    698 
    699 #endif // BL_BENCH_ENABLE_COREGRAPHICS