8 Chunk(uint16_t width, uint16_t input_mask)
17 Block(uint16_t height) : height_(height), all_inputs_(0) {}
19 void AddChunk(uint16_t width, uint16_t input_mask) {
20 chunks_.emplace_back(width, input_mask);
21 all_inputs_ |= input_mask;
24 void Merge(uint16_t x_offset, uint16_t width, uint16_t input_mask);
27 friend class Composition;
31 std::vector<Chunk> chunks_;
34inline void Block::Merge(uint16_t x_offset, uint16_t width,
35 uint16_t input_mask) {
36 std::vector<Chunk> newchunks;
37 newchunks.reserve(chunks_.size());
39 auto i = chunks_.begin();
41 uint16_t remaining_old_width;
42 all_inputs_ |= input_mask;
44 while (cursor + i->width_ <= x_offset) {
45 newchunks.push_back(std::move(*i));
49 remaining_old_width = i->width_;
50 if (cursor < x_offset) {
51 uint16_t w = x_offset - cursor;
52 newchunks.emplace_back(w, i->input_mask_);
54 remaining_old_width -= w;
57 remaining_old_width = i->width_;
61 newchunks.emplace_back(remaining_old_width, i->input_mask_);
62 cursor += remaining_old_width;
64 if (i == chunks_.end()) {
65 chunks_.swap(newchunks);
68 remaining_old_width = i->width_;
71 uint16_t w = std::min(remaining_old_width, width);
72 newchunks.emplace_back(w, i->input_mask_ | input_mask);
73 if (w == remaining_old_width) {
75 if (i == chunks_.end()) {
76 chunks_.swap(newchunks);
79 remaining_old_width = i->width_;
82 remaining_old_width -= w;
92 uint16_t get(uint16_t idx)
const {
return prg_[idx]; }
93 std::size_t size()
const {
return prg_.size(); }
96 friend class Composition;
98 std::vector<uint16_t> prg_;
107 WRITE_SINGLE = 10003,
113 Composition(
const Box& bounds) : bounds_(bounds), input_count_(0) {
114 data_.emplace_back(bounds.height());
115 data_.back().AddChunk(bounds.width(), 0);
121 void Compile(Program* prg);
125 std::vector<Box> input_extents_;
126 std::vector<BlendingMode> blending_modes_;
127 std::vector<Block> data_;
167inline void Composition::Compile(Program* prg) {
168 std::vector<uint16_t>*
code = &prg->prg_;
171 for (
const auto& input : input_extents_) {
172 if (input.yMin() < bounds_.
yMin()) {
173 code->push_back(SKIP);
175 code->push_back((bounds_.
yMin() - input.yMin()) * input.width());
180 for (
const auto& block : data_) {
228 code->push_back(LOOP);
229 code->push_back(block.height_);
232 for (
const auto& input : input_extents_) {
233 if (input.xMin() < bounds_.
xMin() && block.all_inputs_ & (1 << i)) {
234 code->push_back(SKIP);
236 code->push_back(bounds_.
xMin() - input.xMin());
241 for (
const auto& chunk : block.chunks_) {
242 uint16_t mask = chunk.input_mask_;
246 uint16_t skip_mask = mask;
248 int max_skipped_index = 0;
249 for (
int index = 0; index < this->input_count_; ++index) {
250 if ((skip_mask & 1) == 0) {
253 max_skipped_index = index;
258 int skip_mask_mask = ((1 << max_skipped_index) - 1);
259 skip_mask = mask & skip_mask_mask;
260 mask &= ~skip_mask_mask;
262 while (skip_mask > 0) {
264 code->push_back(SKIP);
265 code->push_back(index);
266 code->push_back(chunk.width_);
276 bool dst_clear =
true;
280 IsBlendingModeDestinationClearing(blending_modes_[index])) {
281 code->push_back(SKIP);
282 code->push_back(index);
283 code->push_back(chunk.width_);
284 mask &= ~(1 << index);
294 code->push_back(BLANK);
295 code->push_back(chunk.width_);
297 uint16_t mask_copy = mask;
299 while (!(mask_copy & 1)) {
303 if (mask_copy == 1) {
304 code->push_back(WRITE_SINGLE);
305 code->push_back(index);
306 code->push_back(chunk.width_);
308 code->push_back(WRITE);
309 code->push_back(mask);
310 code->push_back(chunk.width_);
316 for (
const auto& input : input_extents_) {
317 if (input.xMax() > bounds_.
xMax() && block.all_inputs_ & (1 << i)) {
318 code->push_back(SKIP);
320 code->push_back(input.xMax() - bounds_.
xMax());
324 code->push_back(RET);
326 code->push_back(EXIT);
331 Engine(
const Program* program) : program_(program), pc_(0) {}
333 Instruction fetch() {
335 Instruction i = (Instruction)program_->get(pc_++);
338 loop_counter_ = program_->get(pc_++);
344 if (loop_counter_ > 0) {
355 uint16_t read_word() {
return program_->get(pc_++); }
358 const Program* program_;
361 uint16_t loop_counter_;
365 input_extents_.push_back(extents);
367 int input_idx = input_count_;
368 uint16_t input_mask = 1 << input_idx;
371 if (extents.empty())
return false;
372 std::vector<Block> newdata;
373 newdata.reserve(data_.capacity());
374 auto i = data_.begin();
376 uint16_t remaining_old_height;
377 uint16_t remaining_new_height = extents.height();
378 cursor = bounds_.
yMin();
379 while (cursor + i->height_ <= extents.yMin()) {
380 newdata.push_back(std::move(*i));
381 cursor += i->height_;
384 remaining_old_height = i->height_;
385 if (cursor < extents.yMin()) {
386 uint16_t height = extents.yMin() - cursor;
387 newdata.push_back(*i);
388 newdata.back().height_ = height;
390 remaining_old_height -= height;
393 if (remaining_new_height == 0) {
394 newdata.push_back(std::move(*i));
395 newdata.back().height_ = remaining_old_height;
396 cursor += remaining_old_height;
398 if (i == data_.end()) {
402 remaining_old_height = i->height_;
404 uint16_t height = std::min(remaining_old_height, remaining_new_height);
405 if (height == remaining_old_height) {
406 newdata.push_back(std::move(*i));
408 remaining_old_height = (i == data_.end()) ? 0 : i->height_;
410 newdata.push_back(*i);
411 remaining_old_height -= height;
413 remaining_new_height -= height;
414 newdata.back().height_ = height;
415 newdata.back().Merge(extents.xMin() - bounds_.
xMin(), extents.width(),
417 if (remaining_old_height == 0) {
426void WriteRect(Engine* engine,
const Box& bounds,
427 internal::BufferingStream* streams,
429 s.out().setAddress(bounds, s.blending_mode());
430 BufferedColorWriter
writer(s.out());
432 switch (engine->fetch()) {
437 uint16_t count = engine->read_word();
438 writer.writeColorN(s.bgcolor(), count);
442 uint16_t input = engine->read_word();
443 uint16_t count = engine->read_word();
444 streams[input].skip(count);
448 uint16_t input = engine->read_word();
449 uint16_t count = engine->read_word();
451 Color* buf =
writer.buffer_ptr();
452 uint16_t batch =
writer.remaining_buffer_space();
453 if (batch > count) batch = count;
454 if (s.bgcolor() == color::Transparent) {
455 streams[input].read(buf, batch);
458 streams[input].blend(buf, batch, blending_modes[input]);
460 writer.advance_buffer_ptr(batch);
466 uint16_t inputs = engine->read_word();
467 uint16_t count = engine->read_word();
470 uint16_t input_mask = inputs;
471 Color* buf =
writer.buffer_ptr();
472 uint16_t batch =
writer.remaining_buffer_space();
473 if (batch > count) batch = count;
475 if (input_mask & 1) {
476 streams[input].read(buf, batch);
485 if (input_mask == 0)
break;
486 if (input_mask & 1) {
487 streams[input].blend(buf, batch, blending_modes[input]);
493 if (s.bgcolor() != color::Transparent) {
494 for (
int i = 0; i < batch; ++i) {
498 writer.advance_buffer_ptr(batch);
511void WriteVisible(Engine* engine,
const Box& bounds,
512 internal::BufferingStream* streams,
514 BufferedPixelWriter
writer(s.out(), s.blending_mode());
515 uint16_t x = bounds.xMin();
516 uint16_t y = bounds.yMin();
518 switch (engine->fetch()) {
523 uint16_t count = engine->read_word();
525 if (x > bounds.xMax()) {
526 y += (x - bounds.xMin()) / bounds.width();
527 x = (x - bounds.xMin()) % bounds.width() + bounds.xMin();
532 uint16_t input = engine->read_word();
533 uint16_t count = engine->read_word();
534 streams[input].skip(count);
538 uint16_t input = engine->read_word();
539 uint16_t count = engine->read_word();
540 if (s.bgcolor() == color::Transparent) {
541 while (count-- > 0) {
542 Color c = streams[input].next();
547 if (x > bounds.xMax()) {
553 while (count-- > 0) {
554 Color c = streams[input].next();
559 if (x > bounds.xMax()) {
568 uint16_t inputs = engine->read_word();
569 uint16_t count = engine->read_word();
573 uint16_t input_mask = inputs;
575 if (batch > count) batch = count;
577 if (input_mask & 1) {
578 streams[input].read(buf, batch);
587 if (input_mask == 0)
break;
588 if (input_mask & 1) {
589 streams[input].blend(buf, batch, blending_modes[input]);
595 if (s.bgcolor() == color::Transparent) {
596 for (
int i = 0; i < batch; ++i) {
597 if (buf[i].a() != 0) {
601 if (x > bounds.xMax()) {
607 for (
int i = 0; i < batch; ++i) {
608 if (buf[i].a() != 0) {
612 if (x > bounds.xMax()) {
630class StreamableComboStream :
public PixelStream {
632 StreamableComboStream(Program prg,
633 std::vector<internal::BufferingStream> streams,
634 std::vector<BlendingMode> blending_modes)
635 : prg_(std::move(prg)),
637 streams_(std::move(streams)),
638 blending_modes_(std::move(blending_modes)),
639 remaining_count_(0) {}
641 void Read(Color* buf, uint16_t size)
override {
644 while (remaining_count_ == 0) {
645 last_instruction_ = engine_.fetch();
646 switch (last_instruction_) {
651 remaining_count_ = engine_.read_word();
655 uint16_t input = engine_.read_word();
656 uint16_t count = engine_.read_word();
657 streams_[input].skip(count);
661 input_ = engine_.read_word();
662 remaining_count_ = engine_.read_word();
666 input_ = engine_.read_word();
667 remaining_count_ = engine_.read_word();
676 uint16_t batch = std::min(size, remaining_count_);
677 switch (last_instruction_) {
679 FillColor(result, batch, color::Transparent);
683 streams_[input_].read(result, batch);
688 uint16_t input_mask = input_;
690 if (input_mask & 1) {
691 streams_[input].read(result, batch);
700 if (input_mask == 0)
break;
701 if (input_mask & 1) {
702 streams_[input].blend(result, batch, blending_modes_[input]);
714 remaining_count_ -= batch;
721 std::vector<internal::BufferingStream> streams_;
722 std::vector<BlendingMode> blending_modes_;
723 Instruction last_instruction_;
725 uint16_t remaining_count_;
730void StreamableStack::drawTo(
const Surface& s)
const {
732 if (bounds.empty())
return;
733 std::vector<internal::BufferingStream> streams;
734 std::vector<BlendingMode> blending_modes;
735 Composition composition(bounds);
736 for (
const auto& input : inputs_) {
738 Box::Intersect(input.extents(), bounds.translate(-s.dx(), -s.dy()));
740 input.blending_mode())) {
743 streams.emplace_back(
nullptr, 0);
745 blending_modes.push_back(input.blending_mode());
749 composition.Compile(&prg);
752 WriteRect(&engine, bounds, &*streams.begin(), &*blending_modes.begin(), s);
754 WriteVisible(&engine, bounds, &*streams.begin(), &*blending_modes.begin(),
761 std::vector<internal::BufferingStream>
streams;
764 for (
const auto&
input : inputs_) {
769 streams.emplace_back(
nullptr, 0);
776 return std::unique_ptr<PixelStream>(
new StreamableComboStream(
781 const Box& clip_box)
const {
783 std::vector<internal::BufferingStream>
streams;
786 for (
const auto&
input : inputs_) {
791 streams.emplace_back(
nullptr, 0);
798 return std::unique_ptr<PixelStream>(
new StreamableComboStream(
BufferedRectWriter & writer
Axis-aligned integer rectangle.
int16_t xMin() const
Minimum x (inclusive).
int16_t xMax() const
Maximum x (inclusive).
Box translate(int16_t x_offset, int16_t y_offset) const
Return a translated copy of this box.
int32_t area() const
Area in pixels.
static Box Intersect(const Box &a, const Box &b)
Return the intersection of two boxes (may be empty).
int16_t yMin() const
Minimum y (inclusive).
void writePixel(int16_t x, int16_t y, Color color)
Box extents() const override
Return the overall extents of the stack.
std::unique_ptr< PixelStream > createStream() const override
Create a stream for the full stack.
Defines 140 opaque HTML named colors.
BlendingMode
Porter-Duff style blending modes.
@ kDestinationIn
Destination which overlaps the source, replaces the source.
@ kDestinationOut
Destination is placed, where it falls outside of the source.
@ kDestination
Only the destination will be present.
@ kSource
The new ARGB8888 value completely replaces the old one.
@ kDestinationAtop
Destination which overlaps the source replaces the source. Source is placed elsewhere.
@ kSourceIn
The source that overlaps the destination, replaces the destination.
@ kSourceAtop
Source which overlaps the destination, replaces the destination. Destination is placed elsewhere.
@ kClear
No regions are enabled.
@ kSourceOut
Source is placed, where it falls outside of the destination.
Color AlphaBlend(Color bgc, Color fgc)
static const uint8_t kPixelWritingBufferSize
void FillColor(Color *buf, uint32_t count, Color color)
Fill an array with a single color.
@ kExtents
Fill the entire extents box (possibly with fully transparent pixels).
BlendingMode blending_mode