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 = ¤t->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