34#include "freertos/task.h"
37#include "images/cursors.h"
40#pragma GCC optimize ("O2")
49const RGB888 COLOR2RGB888[16] = {
74bool RGB222::lowBitOnly =
false;
81RGB222::RGB222(RGB888
const & value)
101RGB888::RGB888(
Color color)
103 *
this = COLOR2RGB888[(int)color];
112uint8_t RGB888toPackedRGB222(RGB888
const & rgb)
115 static const int CONVR64[4] = { 0 << 0,
119 static const int CONVG64[4] = { 0 << 2,
123 static const int CONVB64[4] = { 0 << 4,
128 static const int CONVR8[4] = { 0 << 0,
132 static const int CONVG8[4] = { 0 << 2,
136 static const int CONVB8[4] = { 0 << 4,
141 if (RGB222::lowBitOnly)
142 return (CONVR8[rgb.R >> 6]) | (CONVG8[rgb.G >> 6]) | (CONVB8[rgb.B >> 6]);
144 return (CONVR64[rgb.R >> 6]) | (CONVG64[rgb.G >> 6]) | (CONVB64[rgb.B >> 6]);
161 savedBackgroundWidth = 0;
162 savedBackgroundHeight = 0;
163 savedBackground =
nullptr;
166 collisionDetectorObject =
nullptr;
176 free(savedBackground);
180void Sprite::clearBitmaps()
188Sprite * Sprite::addBitmap(Bitmap * bitmap)
191 frames = (Bitmap**) realloc(frames,
sizeof(Bitmap*) * framesCount);
192 frames[framesCount - 1] = bitmap;
197Sprite * Sprite::addBitmap(Bitmap * bitmap[],
int count)
199 frames = (Bitmap**) realloc(frames,
sizeof(Bitmap*) * (framesCount + count));
200 for (
int i = 0; i < count; ++i)
201 frames[framesCount + i] = bitmap[i];
202 framesCount += count;
207Sprite * Sprite::moveBy(
int offsetX,
int offsetY)
215Sprite * Sprite::moveBy(
int offsetX,
int offsetY,
int wrapAroundWidth,
int wrapAroundHeight)
219 if (x > wrapAroundWidth)
220 x = - (int) getWidth();
221 if (x < - (
int) getWidth())
223 if (y > wrapAroundHeight)
224 y = - (int) getHeight();
225 if (y < - (
int) getHeight())
226 y = wrapAroundHeight;
231Sprite * Sprite::moveTo(
int x,
int y)
246Bitmap::Bitmap(
int width_,
int height_,
void const * data_,
PixelFormat format_, RGB888 foregroundColor_,
bool copy)
250 foregroundColor(foregroundColor_),
251 data((uint8_t*)data_),
261Bitmap::Bitmap(
int width_,
int height_,
void const * data_,
PixelFormat format_,
bool copy)
262 : Bitmap(width_, height_, data_, format_, RGB888(255, 255, 255), copy)
267void Bitmap::allocate()
273 dataAllocated =
true;
275 case PixelFormat::Undefined:
276 case PixelFormat::Native:
278 case PixelFormat::Mask:
279 data = (uint8_t*) heap_caps_malloc((
width + 7) / 8 *
height, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
281 case PixelFormat::RGBA2222:
282 data = (uint8_t*) heap_caps_malloc(
width *
height, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
284 case PixelFormat::RGBA8888:
285 data = (uint8_t*) heap_caps_malloc(
width *
height * 4, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
292void Bitmap::copyFrom(
void const * srcData)
295 case PixelFormat::Undefined:
296 case PixelFormat::Native:
298 case PixelFormat::Mask:
301 case PixelFormat::RGBA2222:
304 case PixelFormat::RGBA8888:
311void Bitmap::setPixel(
int x,
int y,
int value)
313 int rowlen = (
width + 7) / 8;
314 uint8_t * rowptr =
data + y * rowlen;
316 rowptr[x >> 3] |= 0x80 >> (x & 7);
318 rowptr[x >> 3] &= ~(0x80 >> (x & 7));
322void Bitmap::setPixel(
int x,
int y, RGBA2222 value)
328void Bitmap::setPixel(
int x,
int y, RGBA8888 value)
334int Bitmap::getAlpha(
int x,
int y)
338 case PixelFormat::Undefined:
340 case PixelFormat::Native:
343 case PixelFormat::Mask:
345 int rowlen = (
width + 7) / 8;
346 uint8_t * rowptr =
data + y * rowlen;
347 r = (rowptr[x >> 3] >> (7 - (x & 7))) & 1;
350 case PixelFormat::RGBA2222:
353 case PixelFormat::RGBA8888:
364 heap_caps_free((
void*)
data);
377BitmappedDisplayController::BitmappedDisplayController()
380 m_execQueue =
nullptr;
381 m_backgroundPrimitiveExecutionEnabled =
true;
384 m_doubleBuffered =
false;
385 m_mouseCursor.visible =
false;
386 m_backgroundPrimitiveTimeoutEnabled =
true;
387 m_spritesHidden =
true;
391BitmappedDisplayController::~BitmappedDisplayController()
393 vQueueDelete(m_execQueue);
397void BitmappedDisplayController::setDoubleBuffered(
bool value)
399 m_doubleBuffered = value;
401 vQueueDelete(m_execQueue);
403 m_execQueue = xQueueCreate(value ? 1 : BitmappedDisplayController::queueSize,
sizeof(Primitive));
407void IRAM_ATTR BitmappedDisplayController::resetPaintState()
409 m_paintState.penColor = RGB888(255, 255, 255);
410 m_paintState.brushColor = RGB888(0, 0, 0);
411 m_paintState.position = Point(0, 0);
412 m_paintState.glyphOptions.value = 0;
413 m_paintState.paintOptions = PaintOptions();
414 m_paintState.scrollingRegion = Rect(0, 0, getViewPortWidth() - 1, getViewPortHeight() - 1);
415 m_paintState.origin = Point(0, 0);
416 m_paintState.clippingRect = Rect(0, 0, getViewPortWidth() - 1, getViewPortHeight() - 1);
417 m_paintState.absClippingRect = m_paintState.clippingRect;
418 m_paintState.penWidth = 1;
419 m_paintState.lineEnds = LineEnds::None;
423void BitmappedDisplayController::addPrimitive(Primitive & primitive)
425 if ((m_backgroundPrimitiveExecutionEnabled && m_doubleBuffered ==
false) || primitive.cmd == PrimitiveCmd::SwapBuffers) {
426 primitiveReplaceDynamicBuffers(primitive);
427 xQueueSendToBack(m_execQueue, &primitive, portMAX_DELAY);
429 if (m_doubleBuffered) {
431 ulTaskNotifyTake(
true, portMAX_DELAY);
435 Rect updateRect = Rect(SHRT_MAX, SHRT_MAX, SHRT_MIN, SHRT_MIN);
436 execPrimitive(primitive, updateRect,
false);
437 showSprites(updateRect);
445void BitmappedDisplayController::primitiveReplaceDynamicBuffers(Primitive & primitive)
447 switch (primitive.cmd) {
448 case PrimitiveCmd::DrawPath:
449 case PrimitiveCmd::FillPath:
451 int sz = primitive.path.pointsCount *
sizeof(Point);
453 void * newbuf =
nullptr;
455 while ((newbuf = m_primDynMemPool.alloc(sz)) ==
nullptr)
457 memcpy(newbuf, primitive.path.points, sz);
458 primitive.path.points = (Point*)newbuf;
459 primitive.path.freePoints =
true;
471bool IRAM_ATTR BitmappedDisplayController::getPrimitiveISR(Primitive * primitive)
473 return xQueueReceiveFromISR(m_execQueue, primitive,
nullptr);
477bool BitmappedDisplayController::getPrimitive(Primitive * primitive,
int timeOutMS)
479 return xQueueReceive(m_execQueue, primitive, msToTicks(timeOutMS));
484void BitmappedDisplayController::waitForPrimitives()
487 xQueuePeek(m_execQueue, &p, portMAX_DELAY);
491void BitmappedDisplayController::primitivesExecutionWait()
493 if (m_backgroundPrimitiveExecutionEnabled) {
494 while (uxQueueMessagesWaiting(m_execQueue) > 0)
503void BitmappedDisplayController::enableBackgroundPrimitiveExecution(
bool value)
505 if (value != m_backgroundPrimitiveExecutionEnabled) {
507 resumeBackgroundPrimitiveExecution();
509 suspendBackgroundPrimitiveExecution();
512 m_backgroundPrimitiveExecutionEnabled = value;
519void IRAM_ATTR BitmappedDisplayController::processPrimitives()
521 suspendBackgroundPrimitiveExecution();
522 Rect updateRect =
Rect(SHRT_MAX, SHRT_MAX, SHRT_MIN, SHRT_MIN);
524 while (xQueueReceive(m_execQueue, &prim, 0) == pdTRUE)
525 execPrimitive(prim, updateRect,
false);
526 showSprites(updateRect);
527 resumeBackgroundPrimitiveExecution();
528 Primitive p(PrimitiveCmd::Refresh, updateRect);
533void BitmappedDisplayController::setSprites(
Sprite * sprites,
int count,
int spriteSize)
536 primitivesExecutionWait();
538 m_spriteSize = spriteSize;
539 m_spritesCount = count;
542 if (!isDoubleBuffered()) {
543 uint8_t * spritePtr = (uint8_t*)m_sprites;
544 for (
int i = 0; i < m_spritesCount; ++i, spritePtr += m_spriteSize) {
546 int reqBackBufferSize = 0;
547 for (
int i = 0; i < sprite->framesCount; ++i)
548 reqBackBufferSize = tmax(reqBackBufferSize, sprite->frames[i]->
width * getBitmapSavePixelSize() * sprite->frames[i]->
height);
549 if (reqBackBufferSize > 0)
550 sprite->savedBackground = (uint8_t*) realloc(sprite->savedBackground, reqBackBufferSize);
556Sprite * IRAM_ATTR BitmappedDisplayController::getSprite(
int index)
558 return (Sprite*) ((uint8_t*)m_sprites + index * m_spriteSize);
562void BitmappedDisplayController::refreshSprites()
564 Primitive p(PrimitiveCmd::RefreshSprites);
569void IRAM_ATTR BitmappedDisplayController::hideSprites(
Rect & updateRect)
571 if (!m_spritesHidden) {
572 m_spritesHidden =
true;
575 if (spritesCount() > 0 && !isDoubleBuffered()) {
577 for (
int i = spritesCount() - 1; i >= 0; --i) {
578 Sprite * sprite = getSprite(i);
579 if (sprite->allowDraw && sprite->savedBackgroundWidth > 0) {
580 int savedX = sprite->savedX;
581 int savedY = sprite->savedY;
582 int savedWidth = sprite->savedBackgroundWidth;
583 int savedHeight = sprite->savedBackgroundHeight;
584 Bitmap bitmap(savedWidth, savedHeight, sprite->savedBackground, PixelFormat::Native);
585 absDrawBitmap(savedX, savedY, &bitmap,
nullptr,
true);
586 updateRect = updateRect.merge(
Rect(savedX, savedY, savedX + savedWidth - 1, savedY + savedHeight - 1));
587 sprite->savedBackgroundWidth = sprite->savedBackgroundHeight = 0;
593 Sprite * mouseSprite = mouseCursor();
594 if (mouseSprite->savedBackgroundWidth > 0) {
595 int savedX = mouseSprite->savedX;
596 int savedY = mouseSprite->savedY;
597 int savedWidth = mouseSprite->savedBackgroundWidth;
598 int savedHeight = mouseSprite->savedBackgroundHeight;
599 Bitmap bitmap(savedWidth, savedHeight, mouseSprite->savedBackground, PixelFormat::Native);
600 absDrawBitmap(savedX, savedY, &bitmap,
nullptr,
true);
601 updateRect = updateRect.merge(Rect(savedX, savedY, savedX + savedWidth - 1, savedY + savedHeight - 1));
602 mouseSprite->savedBackgroundWidth = mouseSprite->savedBackgroundHeight = 0;
609void IRAM_ATTR BitmappedDisplayController::showSprites(Rect & updateRect)
611 if (m_spritesHidden) {
612 m_spritesHidden =
false;
616 for (
int i = 0; i < spritesCount(); ++i) {
617 Sprite * sprite = getSprite(i);
618 if (sprite->visible && sprite->allowDraw && sprite->getFrame()) {
620 int spriteX = sprite->x;
621 int spriteY = sprite->y;
622 Bitmap
const * bitmap = sprite->getFrame();
623 int bitmapWidth = bitmap->width;
624 int bitmapHeight = bitmap->height;
625 absDrawBitmap(spriteX, spriteY, bitmap, sprite->savedBackground,
true);
626 sprite->savedX = spriteX;
627 sprite->savedY = spriteY;
628 sprite->savedBackgroundWidth = bitmapWidth;
629 sprite->savedBackgroundHeight = bitmapHeight;
630 if (sprite->isStatic)
631 sprite->allowDraw =
false;
632 updateRect = updateRect.merge(Rect(spriteX, spriteY, spriteX + bitmapWidth - 1, spriteY + bitmapHeight - 1));
638 Sprite * mouseSprite = mouseCursor();
639 if (mouseSprite->visible && mouseSprite->getFrame()) {
641 int spriteX = mouseSprite->x;
642 int spriteY = mouseSprite->y;
643 Bitmap
const * bitmap = mouseSprite->getFrame();
644 int bitmapWidth = bitmap->width;
645 int bitmapHeight = bitmap->height;
646 absDrawBitmap(spriteX, spriteY, bitmap, mouseSprite->savedBackground,
true);
647 mouseSprite->savedX = spriteX;
648 mouseSprite->savedY = spriteY;
649 mouseSprite->savedBackgroundWidth = bitmapWidth;
650 mouseSprite->savedBackgroundHeight = bitmapHeight;
651 updateRect = updateRect.merge(Rect(spriteX, spriteY, spriteX + bitmapWidth - 1, spriteY + bitmapHeight - 1));
659void BitmappedDisplayController::setMouseCursor(
Cursor * cursor)
661 if (cursor ==
nullptr || &cursor->
bitmap != m_mouseCursor.getFrame()) {
662 m_mouseCursor.visible =
false;
663 m_mouseCursor.clearBitmaps();
667 primitivesExecutionWait();
670 m_mouseCursor.moveBy(+m_mouseHotspotX, +m_mouseHotspotY);
673 m_mouseCursor.addBitmap(&cursor->
bitmap);
674 m_mouseCursor.visible =
true;
675 m_mouseCursor.moveBy(-m_mouseHotspotX, -m_mouseHotspotY);
676 if (!isDoubleBuffered())
677 m_mouseCursor.savedBackground = (uint8_t*) realloc(m_mouseCursor.savedBackground, cursor->
bitmap.
width * getBitmapSavePixelSize() * cursor->
bitmap.
height);
684void BitmappedDisplayController::setMouseCursor(
CursorName cursorName)
686 setMouseCursor(&CURSORS[(
int)cursorName]);
690void BitmappedDisplayController::setMouseCursorPos(
int X,
int Y)
692 m_mouseCursor.moveTo(
X - m_mouseHotspotX,
Y - m_mouseHotspotY);
697void IRAM_ATTR BitmappedDisplayController::execPrimitive(Primitive
const & prim,
Rect & updateRect,
bool insideISR)
700 case PrimitiveCmd::Flush:
702 case PrimitiveCmd::Refresh:
703 updateRect = updateRect.merge(prim.rect);
705 case PrimitiveCmd::Reset:
708 case PrimitiveCmd::SetPenColor:
709 paintState().penColor = prim.color;
711 case PrimitiveCmd::SetBrushColor:
712 paintState().brushColor = prim.color;
714 case PrimitiveCmd::SetPixel:
715 setPixelAt( (PixelDesc) { prim.position, getActualPenColor() }, updateRect );
717 case PrimitiveCmd::SetPixelAt:
718 setPixelAt(prim.pixelDesc, updateRect);
720 case PrimitiveCmd::MoveTo:
721 paintState().position = Point(prim.position.X + paintState().origin.X, prim.position.Y + paintState().origin.Y);
723 case PrimitiveCmd::LineTo:
724 lineTo(prim.position, updateRect);
726 case PrimitiveCmd::FillRect:
727 fillRect(prim.rect, getActualBrushColor(), updateRect);
729 case PrimitiveCmd::DrawRect:
730 drawRect(prim.rect, updateRect);
732 case PrimitiveCmd::FillEllipse:
733 fillEllipse(paintState().position.X, paintState().position.Y, prim.size, getActualBrushColor(), updateRect);
735 case PrimitiveCmd::DrawEllipse:
736 drawEllipse(prim.size, updateRect);
738 case PrimitiveCmd::Clear:
739 updateRect = updateRect.merge(Rect(0, 0, getViewPortWidth() - 1, getViewPortHeight() - 1));
742 case PrimitiveCmd::VScroll:
743 updateRect = updateRect.merge(Rect(paintState().scrollingRegion.X1, paintState().scrollingRegion.Y1, paintState().scrollingRegion.X2, paintState().scrollingRegion.Y2));
744 VScroll(prim.ivalue, updateRect);
746 case PrimitiveCmd::HScroll:
747 updateRect = updateRect.merge(Rect(paintState().scrollingRegion.X1, paintState().scrollingRegion.Y1, paintState().scrollingRegion.X2, paintState().scrollingRegion.Y2));
748 HScroll(prim.ivalue, updateRect);
750 case PrimitiveCmd::DrawGlyph:
751 drawGlyph(prim.glyph, paintState().glyphOptions, paintState().penColor, paintState().brushColor, updateRect);
753 case PrimitiveCmd::SetGlyphOptions:
754 paintState().glyphOptions = prim.glyphOptions;
756 case PrimitiveCmd::SetPaintOptions:
757 paintState().paintOptions = prim.paintOptions;
759 case PrimitiveCmd::InvertRect:
760 invertRect(prim.rect, updateRect);
762 case PrimitiveCmd::CopyRect:
763 copyRect(prim.rect, updateRect);
765 case PrimitiveCmd::SetScrollingRegion:
766 paintState().scrollingRegion = prim.rect;
768 case PrimitiveCmd::SwapFGBG:
771 case PrimitiveCmd::RenderGlyphsBuffer:
772 renderGlyphsBuffer(prim.glyphsBufferRenderInfo, updateRect);
774 case PrimitiveCmd::DrawBitmap:
775 drawBitmap(prim.bitmapDrawingInfo, updateRect);
777 case PrimitiveCmd::RefreshSprites:
778 hideSprites(updateRect);
779 showSprites(updateRect);
781 case PrimitiveCmd::SwapBuffers:
783 updateRect = updateRect.merge(Rect(0, 0, getViewPortWidth() - 1, getViewPortHeight() - 1));
785 vTaskNotifyGiveFromISR(prim.notifyTask,
nullptr);
787 xTaskNotifyGive(prim.notifyTask);
790 case PrimitiveCmd::DrawPath:
791 drawPath(prim.path, updateRect);
793 case PrimitiveCmd::FillPath:
794 fillPath(prim.path, getActualBrushColor(), updateRect);
796 case PrimitiveCmd::SetOrigin:
797 paintState().origin = prim.position;
798 updateAbsoluteClippingRect();
800 case PrimitiveCmd::SetClippingRect:
801 paintState().clippingRect = prim.rect;
802 updateAbsoluteClippingRect();
804 case PrimitiveCmd::SetPenWidth:
805 paintState().penWidth = imax(1, prim.ivalue);
807 case PrimitiveCmd::SetLineEnds:
808 paintState().lineEnds = prim.lineEnds;
814RGB888 IRAM_ATTR BitmappedDisplayController::getActualBrushColor()
816 return paintState().paintOptions.swapFGBG ? paintState().penColor : paintState().brushColor;
820RGB888 IRAM_ATTR BitmappedDisplayController::getActualPenColor()
822 return paintState().paintOptions.swapFGBG ? paintState().brushColor : paintState().penColor;
826void IRAM_ATTR BitmappedDisplayController::lineTo(Point
const & position, Rect & updateRect)
828 RGB888 color = getActualPenColor();
830 int origX = paintState().origin.X;
831 int origY = paintState().origin.Y;
832 int x1 = paintState().position.X;
833 int y1 = paintState().position.Y;
834 int x2 = position.X + origX;
835 int y2 = position.Y + origY;
837 int hw = paintState().penWidth / 2;
838 updateRect = updateRect.merge(Rect(imin(x1, x2) - hw, imin(y1, y2) - hw, imax(x1, x2) + hw, imax(y1, y2) + hw));
839 hideSprites(updateRect);
840 absDrawLine(x1, y1, x2, y2, color);
842 paintState().position = Point(x2, y2);
846void IRAM_ATTR BitmappedDisplayController::updateAbsoluteClippingRect()
848 int X1 = iclamp(paintState().origin.X + paintState().clippingRect.X1, 0, getViewPortWidth() - 1);
849 int Y1 = iclamp(paintState().origin.Y + paintState().clippingRect.Y1, 0, getViewPortHeight() - 1);
850 int X2 = iclamp(paintState().origin.X + paintState().clippingRect.X2, 0, getViewPortWidth() - 1);
851 int Y2 = iclamp(paintState().origin.Y + paintState().clippingRect.Y2, 0, getViewPortHeight() - 1);
852 paintState().absClippingRect = Rect(
X1,
Y1,
X2,
Y2);
856void IRAM_ATTR BitmappedDisplayController::drawRect(Rect
const & rect, Rect & updateRect)
858 int x1 = (rect.X1 < rect.X2 ? rect.X1 : rect.X2) + paintState().origin.X;
859 int y1 = (rect.Y1 < rect.Y2 ? rect.Y1 : rect.Y2) + paintState().origin.Y;
860 int x2 = (rect.X1 < rect.X2 ? rect.X2 : rect.X1) + paintState().origin.X;
861 int y2 = (rect.Y1 < rect.Y2 ? rect.Y2 : rect.Y1) + paintState().origin.Y;
863 int hw = paintState().penWidth / 2;
864 updateRect = updateRect.merge(Rect(x1 - hw, y1 - hw, x2 + hw, y2 + hw));
865 hideSprites(updateRect);
866 RGB888 color = getActualPenColor();
868 absDrawLine(x1 + 1, y1, x2, y1, color);
869 absDrawLine(x2, y1 + 1, x2, y2, color);
870 absDrawLine(x2 - 1, y2, x1, y2, color);
871 absDrawLine(x1, y2 - 1, x1, y1, color);
875void IRAM_ATTR BitmappedDisplayController::fillRect(Rect
const & rect, RGB888
const & color, Rect & updateRect)
877 int x1 = (rect.X1 < rect.X2 ? rect.X1 : rect.X2) + paintState().origin.X;
878 int y1 = (rect.Y1 < rect.Y2 ? rect.Y1 : rect.Y2) + paintState().origin.Y;
879 int x2 = (rect.X1 < rect.X2 ? rect.X2 : rect.X1) + paintState().origin.X;
880 int y2 = (rect.Y1 < rect.Y2 ? rect.Y2 : rect.Y1) + paintState().origin.Y;
882 const int clipX1 = paintState().absClippingRect.X1;
883 const int clipY1 = paintState().absClippingRect.Y1;
884 const int clipX2 = paintState().absClippingRect.X2;
885 const int clipY2 = paintState().absClippingRect.Y2;
887 if (x1 > clipX2 || x2 < clipX1 || y1 > clipY2 || y2 < clipY1)
890 x1 = iclamp(x1, clipX1, clipX2);
891 y1 = iclamp(y1, clipY1, clipY2);
892 x2 = iclamp(x2, clipX1, clipX2);
893 y2 = iclamp(y2, clipY1, clipY2);
895 updateRect = updateRect.merge(Rect(x1, y1, x2, y2));
896 hideSprites(updateRect);
898 for (
int y = y1; y <= y2; ++y)
899 rawFillRow(y, x1, x2, color);
904void IRAM_ATTR BitmappedDisplayController::fillEllipse(
int centerX,
int centerY, Size
const & size, RGB888
const & color, Rect & updateRect)
906 const int clipX1 = paintState().absClippingRect.X1;
907 const int clipY1 = paintState().absClippingRect.Y1;
908 const int clipX2 = paintState().absClippingRect.X2;
909 const int clipY2 = paintState().absClippingRect.Y2;
911 const int halfWidth = size.width / 2;
912 const int halfHeight = size.height / 2;
914 updateRect = updateRect.merge(Rect(centerX - halfWidth, centerY - halfHeight, centerX + halfWidth, centerY + halfHeight));
915 hideSprites(updateRect);
917 const int a2 = halfWidth * halfWidth;
918 const int b2 = halfHeight * halfHeight;
919 const int crit1 = -(a2 / 4 + halfWidth % 2 + b2);
920 const int crit2 = -(b2 / 4 + halfHeight % 2 + a2);
921 const int crit3 = -(b2 / 4 + halfHeight % 2);
922 const int d2xt = 2 * b2;
923 const int d2yt = 2 * a2;
928 int dxt = 2 * b2 * x;
929 int dyt = -2 * a2 * y;
931 while (y >= 0 && x <= halfWidth) {
932 if (t + b2 * x <= crit1 || t + a2 * y <= crit3) {
938 int col1 = centerX - x;
939 int col2 = centerX - x +
width - 1;
940 if (col1 <= clipX2 && col2 >= clipX1) {
941 col1 = iclamp(col1, clipX1, clipX2);
942 col2 = iclamp(col2, clipX1, clipX2);
943 int row1 = centerY - y;
944 int row2 = centerY + y;
945 if (row1 >= clipY1 && row1 <= clipY2)
946 rawFillRow(row1, col1, col2, color);
947 if (y != 0 && row2 >= clipY1 && row2 <= clipY2)
948 rawFillRow(row2, col1, col2, color);
950 if (t - a2 * y <= crit2) {
962 if (halfHeight == 0 && centerY >= clipY1 && centerY <= clipY2)
963 rawFillRow(centerY, iclamp(centerX - halfWidth, clipX1, clipX2), iclamp(centerX - halfWidth + 2 * halfWidth + 1, clipX1, clipX2), color);
967void IRAM_ATTR BitmappedDisplayController::renderGlyphsBuffer(GlyphsBufferRenderInfo
const & glyphsBufferRenderInfo, Rect & updateRect)
969 int itemX = glyphsBufferRenderInfo.itemX;
970 int itemY = glyphsBufferRenderInfo.itemY;
972 int glyphsWidth = glyphsBufferRenderInfo.glyphsBuffer->glyphsWidth;
973 int glyphsHeight = glyphsBufferRenderInfo.glyphsBuffer->glyphsHeight;
975 uint32_t
const * mapItem = glyphsBufferRenderInfo.glyphsBuffer->map + itemX + itemY * glyphsBufferRenderInfo.glyphsBuffer->columns;
977 GlyphOptions glyphOptions = glyphMapItem_getOptions(mapItem);
978 auto fgColor = glyphMapItem_getFGColor(mapItem);
979 auto bgColor = glyphMapItem_getBGColor(mapItem);
982 glyph.X = (int16_t) (itemX * glyphsWidth * (glyphOptions.doubleWidth ? 2 : 1));
983 glyph.Y = (int16_t) (itemY * glyphsHeight);
984 glyph.width = glyphsWidth;
985 glyph.height = glyphsHeight;
986 glyph.data = glyphsBufferRenderInfo.glyphsBuffer->glyphsData + glyphMapItem_getIndex(mapItem) * glyphsHeight * ((glyphsWidth + 7) / 8);;
988 drawGlyph(glyph, glyphOptions, fgColor, bgColor, updateRect);
992void IRAM_ATTR BitmappedDisplayController::drawPath(Path
const & path, Rect & updateRect)
994 RGB888 color = getActualPenColor();
996 const int clipX1 = paintState().absClippingRect.X1;
997 const int clipY1 = paintState().absClippingRect.Y1;
998 const int clipX2 = paintState().absClippingRect.X2;
999 const int clipY2 = paintState().absClippingRect.Y2;
1001 int origX = paintState().origin.X;
1002 int origY = paintState().origin.Y;
1005 int maxX = clipX2 + 1;
1008 for (
int i = 0; i < path.pointsCount; ++i) {
1009 int py = path.points[i].Y + origY;
1015 minY = tmax(clipY1, minY);
1016 maxY = tmin(clipY2, maxY);
1018 int hw = paintState().penWidth / 2;
1019 updateRect = updateRect.merge(Rect(minX - hw, minY - hw, maxX + hw, maxY + hw));
1020 hideSprites(updateRect);
1023 for (; i < path.pointsCount - 1; ++i) {
1024 const int x1 = path.points[i].X + origX;
1025 const int y1 = path.points[i].Y + origY;
1026 const int x2 = path.points[i + 1].X + origX;
1027 const int y2 = path.points[i + 1].Y + origY;
1028 absDrawLine(x1, y1, x2, y2, color);
1030 const int x1 = path.points[i].X + origX;
1031 const int y1 = path.points[i].Y + origY;
1032 const int x2 = path.points[0].X + origX;
1033 const int y2 = path.points[0].Y + origY;
1034 absDrawLine(x1, y1, x2, y2, color);
1036 if (path.freePoints)
1037 m_primDynMemPool.free((
void*)path.points);
1041void IRAM_ATTR BitmappedDisplayController::fillPath(Path
const & path, RGB888
const & color, Rect & updateRect)
1043 const int clipX1 = paintState().absClippingRect.X1;
1044 const int clipY1 = paintState().absClippingRect.Y1;
1045 const int clipX2 = paintState().absClippingRect.X2;
1046 const int clipY2 = paintState().absClippingRect.Y2;
1048 const int origX = paintState().origin.X;
1049 const int origY = paintState().origin.Y;
1052 int maxX = clipX2 + 1;
1055 for (
int i = 0; i < path.pointsCount; ++i) {
1056 int py = path.points[i].Y + origY;
1062 minY = tmax(clipY1, minY);
1063 maxY = tmin(clipY2, maxY);
1065 updateRect = updateRect.merge(Rect(minX, minY, maxX, maxY));
1066 hideSprites(updateRect);
1068 int16_t nodeX[path.pointsCount];
1070 for (
int pixelY = minY; pixelY <= maxY; ++pixelY) {
1073 int j = path.pointsCount - 1;
1074 for (
int i = 0; i < path.pointsCount; ++i) {
1075 int piy = path.points[i].Y + origY;
1076 int pjy = path.points[j].Y + origY;
1077 if ((piy < pixelY && pjy >= pixelY) || (pjy < pixelY && piy >= pixelY)) {
1078 int pjx = path.points[j].X + origX;
1079 int pix = path.points[i].X + origX;
1080 int a = (pixelY - piy) * (pjx - pix);
1081 int b = (pjy - piy);
1082 nodeX[nodes++] = pix + a / b + (((a < 0) ^ (b > 0)) && (a % b));
1088 while (i < nodes - 1) {
1089 if (nodeX[i] > nodeX[i + 1]) {
1090 tswap(nodeX[i], nodeX[i + 1]);
1097 for (
int i = 0; i < nodes; i += 2) {
1098 if (nodeX[i] >= maxX)
1100 if (nodeX[i + 1] > minX) {
1101 if (nodeX[i] < minX)
1103 if (nodeX[i + 1] > maxX)
1104 nodeX[i + 1] = maxX;
1105 rawFillRow(pixelY, nodeX[i], nodeX[i + 1] - 1, color);
1110 if (path.freePoints)
1111 m_primDynMemPool.free((
void*)path.points);
1115void IRAM_ATTR BitmappedDisplayController::absDrawThickLine(
int X1,
int Y1,
int X2,
int Y2,
int penWidth, RGB888
const & color)
1118 const int origX = paintState().origin.X;
1119 const int origY = paintState().origin.Y;
1127 const double angle = atan2(
Y2 -
Y1,
X2 -
X1);
1128 const double pw = (double)penWidth / 2.0;
1129 const int ofs1 = lround(pw * cos(angle + M_PI_2));
1130 const int ofs2 = lround(pw * sin(angle + M_PI_2));
1131 const int ofs3 = lround(pw * cos(angle - M_PI_2));
1132 const int ofs4 = lround(pw * sin(angle - M_PI_2));
1133 pts[0].X =
X1 + ofs1;
1134 pts[0].Y =
Y1 + ofs2;
1135 pts[1].X =
X1 + ofs3;
1136 pts[1].Y =
Y1 + ofs4;
1137 pts[2].X =
X2 + ofs3;
1138 pts[2].Y =
Y2 + ofs4;
1139 pts[3].X =
X2 + ofs1;
1140 pts[3].Y =
Y2 + ofs2;
1143 Path path = { pts, 4,
false };
1144 fillPath(path, color, updateRect);
1146 switch (paintState().lineEnds) {
1147 case LineEnds::Circle:
1148 if ((penWidth & 1) == 0)
1150 fillEllipse(
X1,
Y1, Size(penWidth, penWidth), color, updateRect);
1151 fillEllipse(
X2,
Y2, Size(penWidth, penWidth), color, updateRect);
1159void IRAM_ATTR BitmappedDisplayController::drawBitmap(BitmapDrawingInfo
const & bitmapDrawingInfo, Rect & updateRect)
1161 int x = bitmapDrawingInfo.X + paintState().origin.X;
1162 int y = bitmapDrawingInfo.Y + paintState().origin.Y;
1163 updateRect = updateRect.merge(Rect(x, y, x + bitmapDrawingInfo.bitmap->width - 1, y + bitmapDrawingInfo.bitmap->height - 1));
1164 hideSprites(updateRect);
1165 absDrawBitmap(x, y, bitmapDrawingInfo.bitmap,
nullptr,
false);
1169void IRAM_ATTR BitmappedDisplayController::absDrawBitmap(
int destX,
int destY, Bitmap
const * bitmap,
void * saveBackground,
bool ignoreClippingRect)
1171 const int clipX1 = ignoreClippingRect ? 0 : paintState().absClippingRect.X1;
1172 const int clipY1 = ignoreClippingRect ? 0 : paintState().absClippingRect.Y1;
1173 const int clipX2 = ignoreClippingRect ? getViewPortWidth() - 1 : paintState().absClippingRect.X2;
1174 const int clipY2 = ignoreClippingRect ? getViewPortHeight() - 1 : paintState().absClippingRect.Y2;
1176 if (destX > clipX2 || destY > clipY2)
1179 int width = bitmap->width;
1180 int height = bitmap->height;
1185 if (destX < clipX1) {
1186 X1 = clipX1 - destX;
1192 if (destX + XCount > clipX2 + 1)
1193 XCount = clipX2 + 1 - destX;
1200 if (destY < clipY1) {
1201 Y1 = clipY1 - destY;
1207 if (destY + YCount > clipY2 + 1)
1208 YCount = clipY2 + 1 - destY;
1212 switch (bitmap->format) {
1214 case PixelFormat::Undefined:
1217 case PixelFormat::Native:
1218 rawDrawBitmap_Native(destX, destY, bitmap,
X1,
Y1, XCount, YCount);
1221 case PixelFormat::Mask:
1222 rawDrawBitmap_Mask(destX, destY, bitmap, saveBackground,
X1,
Y1, XCount, YCount);
1225 case PixelFormat::RGBA2222:
1226 rawDrawBitmap_RGBA2222(destX, destY, bitmap, saveBackground,
X1,
Y1, XCount, YCount);
1229 case PixelFormat::RGBA8888:
1230 rawDrawBitmap_RGBA8888(destX, destY, bitmap, saveBackground,
X1,
Y1, XCount, YCount);
This file contains fabgl::BitmappedDisplayController definition.
#define FABGLIB_PRIMITIVES_DYNBUFFERS_SIZE
#define FABGLIB_DEFAULT_DISPLAYCONTROLLER_QUEUE_SIZE
This file contains some utility classes and functions.
Color
This enum defines named colors.
CursorName
This enum defines a set of predefined mouse cursors.
PixelFormat
This enum defines a pixel format.