odin-blend2d

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

rendercommandprocsync_p.h (9366B)


      1 // This file is part of Blend2D project <https://blend2d.com>
      2 //
      3 // See blend2d.h or LICENSE.md for license and copyright information
      4 // SPDX-License-Identifier: Zlib
      5 
      6 #ifndef BLEND2D_RASTER_RENDERCOMMANDPROCSYNC_P_H_INCLUDED
      7 #define BLEND2D_RASTER_RENDERCOMMANDPROCSYNC_P_H_INCLUDED
      8 
      9 #include "../context_p.h"
     10 #include "../geometry_p.h"
     11 #include "../pipeline/pipedefs_p.h"
     12 #include "../raster/analyticrasterizer_p.h"
     13 #include "../raster/edgebuilder_p.h"
     14 #include "../raster/rendercommand_p.h"
     15 #include "../raster/rasterdefs_p.h"
     16 #include "../raster/workdata_p.h"
     17 #include "../support/arenaallocator_p.h"
     18 #include "../support/intops_p.h"
     19 #include "../support/zeroallocator_p.h"
     20 
     21 //! \cond INTERNAL
     22 //! \addtogroup blend2d_raster_engine_impl
     23 //! \{
     24 
     25 namespace bl::RasterEngine {
     26 namespace CommandProcSync {
     27 
     28 static BL_INLINE BLResult fill_box_a(WorkData& work_data, const Pipeline::DispatchData& dispatch_data, uint32_t alpha, const BLBoxI& box_a, const void* fetch_data) noexcept {
     29   Pipeline::FillData fill_data;
     30   fill_data.init_box_a_8bpc(alpha, box_a.x0, box_a.y0, box_a.x1, box_a.y1);
     31 
     32   Pipeline::FillFunc fill_func = dispatch_data.fill_func;
     33   Pipeline::FetchFunc fetch_func = dispatch_data.fetch_func;
     34 
     35   if (fetch_func == nullptr) {
     36     fill_func(&work_data.ctx_data, &fill_data, fetch_data);
     37   }
     38   else {
     39     // TODO:
     40   }
     41 
     42   return BL_SUCCESS;
     43 }
     44 
     45 static BL_INLINE BLResult fill_box_u(WorkData& work_data, const Pipeline::DispatchData& dispatch_data, uint32_t alpha, const BLBoxI& box_u, const void* fetch_data) noexcept {
     46   Pipeline::FillData fill_data;
     47   Pipeline::BoxUToMaskData boxUToMaskData;
     48 
     49   if (!fill_data.init_box_u_8bpc_24x8(alpha, box_u.x0, box_u.y0, box_u.x1, box_u.y1, boxUToMaskData))
     50     return BL_SUCCESS;
     51 
     52   Pipeline::FillFunc fill_func = dispatch_data.fill_func;
     53   Pipeline::FetchFunc fetch_func = dispatch_data.fetch_func;
     54 
     55   if (fetch_func == nullptr) {
     56     fill_func(&work_data.ctx_data, &fill_data, fetch_data);
     57   }
     58   else {
     59     // TODO:
     60   }
     61 
     62   return BL_SUCCESS;
     63 }
     64 
     65 static BL_INLINE BLResult fill_box_masked_a(WorkData& work_data, const Pipeline::DispatchData& dispatch_data, uint32_t alpha, const RenderCommand::FillBoxMaskA& payload, const void* fetch_data) noexcept {
     66   const BLImageImpl* mask_impl = payload.mask_image_i.ptr;
     67   const BLPointI& mask_offset = payload.mask_offset_i;
     68   const uint8_t* mask_data = static_cast<const uint8_t*>(mask_impl->pixel_data) + mask_impl->stride * intptr_t(mask_offset.y) + uint32_t(mask_offset.x) * (mask_impl->depth / 8u);
     69 
     70   const BLBoxI& box_i = payload.box_i;
     71 
     72   Pipeline::MaskCommand mask_commands[2];
     73   Pipeline::MaskCommandType vMaskCmd = alpha >= 255 ? Pipeline::MaskCommandType::kVMaskA8WithGA : Pipeline::MaskCommandType::kVMaskA8WithoutGA;
     74 
     75   mask_commands[0].init_vmask(vMaskCmd, uint32_t(box_i.x0), uint32_t(box_i.x1), mask_data, mask_impl->stride);
     76   mask_commands[1].init_repeat();
     77 
     78   Pipeline::FillData fill_data;
     79   fill_data.init_mask_a(alpha, box_i.x0, box_i.y0, box_i.x1, box_i.y1, mask_commands);
     80 
     81   Pipeline::FillFunc fill_func = dispatch_data.fill_func;
     82   fill_func(&work_data.ctx_data, &fill_data, fetch_data);
     83 
     84   return BL_SUCCESS;
     85 }
     86 
     87 static BL_NOINLINE BLResult fill_analytic(WorkData& work_data, const Pipeline::DispatchData& dispatch_data, uint32_t alpha, const EdgeStorage<int>* edge_storage, BLFillRule fill_rule, const void* fetch_data) noexcept {
     88   // Rasterizer options to use - do not change unless you are improving the existing rasterizers.
     89   constexpr uint32_t kRasterizerOptions = AnalyticRasterizer::kOptionBandOffset | AnalyticRasterizer::kOptionRecordMinXMaxX;
     90 
     91   // Can only be called if there is something to fill.
     92   BL_ASSERT(edge_storage != nullptr);
     93   // Should have been verified by the caller.
     94   BL_ASSERT(edge_storage->bounding_box().y0 < edge_storage->bounding_box().y1);
     95 
     96   uint32_t band_height = edge_storage->band_height();
     97   uint32_t band_height_mask = band_height - 1;
     98 
     99   const uint32_t y_start = (uint32_t(edge_storage->bounding_box().y0)                          ) >> Pipeline::A8Info::kShift;
    100   const uint32_t y_end   = (uint32_t(edge_storage->bounding_box().y1) + Pipeline::A8Info::kMask) >> Pipeline::A8Info::kShift;
    101 
    102   size_t required_width = IntOps::align_up(uint32_t(work_data.dst_size().w) + 1u + BL_PIPE_PIXELS_PER_ONE_BIT, BL_PIPE_PIXELS_PER_ONE_BIT);
    103   size_t required_height = band_height;
    104   size_t cell_alignment = 16;
    105 
    106   size_t bit_stride = IntOps::word_count_from_bit_count<BLBitWord>(required_width / BL_PIPE_PIXELS_PER_ONE_BIT) * sizeof(BLBitWord);
    107   size_t cell_stride = required_width * sizeof(uint32_t);
    108 
    109   size_t bits_start = 0;
    110   size_t bits_size = required_height * bit_stride;
    111 
    112   size_t cells_start = IntOps::align_up(bits_start + bits_size, cell_alignment);
    113   BL_ASSERT(work_data.zero_buffer.size >= cells_start + required_height * cell_stride);
    114 
    115   AnalyticCellStorage cell_storage;
    116   cell_storage.init(
    117     reinterpret_cast<BLBitWord*>(work_data.zero_buffer.data + bits_start), bit_stride,
    118     IntOps::align_up(reinterpret_cast<uint32_t*>(work_data.zero_buffer.data + cells_start), cell_alignment), cell_stride);
    119 
    120   AnalyticActiveEdge<int>* active = nullptr;
    121   AnalyticActiveEdge<int>* pooled = nullptr;
    122 
    123   EdgeList<int>* band_edges = edge_storage->band_edges();
    124   uint32_t band_id = edge_storage->bandStartFromBBox();
    125   uint32_t band_end = edge_storage->bandEndFromBBox();
    126 
    127   uint32_t dst_width = uint32_t(work_data.dst_size().w);
    128 
    129   Pipeline::FillFunc fill_func = dispatch_data.fill_func;
    130   Pipeline::FillData fill_data;
    131 
    132   fill_data.init_analytic(alpha,
    133                         uint32_t(fill_rule),
    134                         cell_storage.bit_ptr_top, cell_storage.bit_stride,
    135                         cell_storage.cell_ptr_top, cell_storage.cell_stride);
    136 
    137   AnalyticRasterizer ras;
    138   ras.init(cell_storage.bit_ptr_top, cell_storage.bit_stride,
    139            cell_storage.cell_ptr_top, cell_storage.cell_stride,
    140            band_id * band_height, band_height);
    141   ras._band_offset = y_start;
    142 
    143   ArenaAllocator* work_zone = &work_data.work_zone;
    144   do {
    145     EdgeVector<int>* edges = band_edges[band_id].first();
    146     band_edges[band_id].reset();
    147 
    148     AnalyticActiveEdge<int>** pPrev = &active;
    149     AnalyticActiveEdge<int>* current = *pPrev;
    150 
    151     ras.reset_bounds();
    152     ras._band_end = bl_min((band_id + 1) * band_height, y_end) - 1;
    153 
    154     while (current) {
    155       ras.restore(current->state);
    156       ras.set_sign_mask_from_bit(current->sign_bit);
    157 
    158       for (;;) {
    159 Rasterize:
    160         if (ras.template rasterize<kRasterizerOptions | AnalyticRasterizer::kOptionBandingMode>()) {
    161           // The edge is fully rasterized.
    162           const EdgePoint<int>* pts = current->cur;
    163           while (pts != current->end) {
    164             pts++;
    165             if (!ras.prepare(pts[-2], pts[-1]))
    166               continue;
    167 
    168             current->cur = pts;
    169             if (uint32_t(ras._ey0) <= ras._band_end)
    170               goto Rasterize;
    171             else
    172               goto SaveState;
    173           }
    174 
    175           AnalyticActiveEdge<int>* old = current;
    176           current = current->next;
    177 
    178           old->next = pooled;
    179           pooled = old;
    180           break;
    181         }
    182 
    183 SaveState:
    184         // The edge is not fully rasterized and crosses the band.
    185         ras.save(current->state);
    186 
    187         *pPrev = current;
    188         pPrev = &current->next;
    189         current = *pPrev;
    190         break;
    191       }
    192     }
    193 
    194     if (edges) {
    195       if (!pooled) {
    196         pooled = static_cast<AnalyticActiveEdge<int>*>(work_zone->alloc(sizeof(AnalyticActiveEdge<int>)));
    197         if (BL_UNLIKELY(!pooled))
    198           return bl_make_error(BL_ERROR_OUT_OF_MEMORY);
    199         pooled->next = nullptr;
    200       }
    201 
    202       do {
    203         const EdgePoint<int>* pts = edges->pts + 1;
    204         const EdgePoint<int>* end = edges->pts + edges->count();
    205 
    206         uint32_t sign_bit = edges->sign_bit();
    207         ras.set_sign_mask_from_bit(sign_bit);
    208 
    209         edges = edges->next;
    210         do {
    211           pts++;
    212           if (!ras.prepare(pts[-2], pts[-1]))
    213             continue;
    214 
    215           if (uint32_t(ras._ey1) <= ras._band_end) {
    216             ras.template rasterize<kRasterizerOptions>();
    217           }
    218           else {
    219             current = pooled;
    220             pooled = current->next;
    221 
    222             current->sign_bit = sign_bit;
    223             current->cur = pts;
    224             current->end = end;
    225             current->next = nullptr;
    226 
    227             if (uint32_t(ras._ey0) <= ras._band_end)
    228               goto Rasterize;
    229             else
    230               goto SaveState;
    231           }
    232         } while (pts != end);
    233       } while (edges);
    234     }
    235 
    236     // Makes `active` or the last `AnalyticActiveEdge->next` null. It's important, because we don't unlink during
    237     // edge pooling as it's just faster to do it here.
    238     *pPrev = nullptr;
    239 
    240     if (ras.has_bounds()) {
    241       fill_data.analytic.box.x0 = int(ras._cellMinX);
    242       fill_data.analytic.box.x1 = int(bl_min(dst_width, IntOps::align_up(ras._cellMaxX + 1, BL_PIPE_PIXELS_PER_ONE_BIT)));
    243       fill_data.analytic.box.y0 = int(ras._band_offset);
    244       fill_data.analytic.box.y1 = int(ras._band_end) + 1;
    245 
    246       fill_func(&work_data.ctx_data, &fill_data, fetch_data);
    247     }
    248 
    249     ras._band_offset = (ras._band_offset + band_height) & ~band_height_mask;
    250   } while (++band_id < band_end);
    251 
    252   work_zone->clear();
    253   return BL_SUCCESS;
    254 }
    255 
    256 } // {CommandProcSync}
    257 } // {bl::RasterEngine}
    258 
    259 //! \}
    260 //! \endcond
    261 
    262 #endif // BLEND2D_RASTER_RENDERCOMMANDPROCSYNC_P_H_INCLUDED