|
|
|
|
@@ -72,6 +72,16 @@ void GfxRenderer::drawPixel(const int x, const int y, const bool state) const {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GfxRenderer::drawPixelGray(const int x, const int y, const uint8_t val2bit) const {
|
|
|
|
|
if (renderMode == BW && val2bit < 3) {
|
|
|
|
|
drawPixel(x, y);
|
|
|
|
|
} else if (renderMode == GRAYSCALE_MSB && (val2bit == 1 || val2bit == 2)) {
|
|
|
|
|
drawPixel(x, y, false);
|
|
|
|
|
} else if (renderMode == GRAYSCALE_LSB && val2bit == 1) {
|
|
|
|
|
drawPixel(x, y, false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int GfxRenderer::getTextWidth(const int fontId, const char* text, const EpdFontFamily::Style style) const {
|
|
|
|
|
if (fontMap.count(fontId) == 0) {
|
|
|
|
|
Serial.printf("[%lu] [GFX] Font %d not found\n", millis(), fontId);
|
|
|
|
|
@@ -422,12 +432,20 @@ void GfxRenderer::drawBitmap(const Bitmap& bitmap, const int x, const int y, con
|
|
|
|
|
Serial.printf("[%lu] [GFX] Cropping %dx%d by %dx%d pix, is %s\n", millis(), bitmap.getWidth(), bitmap.getHeight(),
|
|
|
|
|
cropPixX, cropPixY, bitmap.isTopDown() ? "top-down" : "bottom-up");
|
|
|
|
|
|
|
|
|
|
if (maxWidth > 0 && (1.0f - cropX) * bitmap.getWidth() > maxWidth) {
|
|
|
|
|
scale = static_cast<float>(maxWidth) / static_cast<float>((1.0f - cropX) * bitmap.getWidth());
|
|
|
|
|
const float effectiveWidth = (1.0f - cropX) * bitmap.getWidth();
|
|
|
|
|
const float effectiveHeight = (1.0f - cropY) * bitmap.getHeight();
|
|
|
|
|
|
|
|
|
|
// Calculate scale factor: supports both downscaling and upscaling when both constraints are provided
|
|
|
|
|
if (maxWidth > 0 && maxHeight > 0) {
|
|
|
|
|
const float scaleX = static_cast<float>(maxWidth) / effectiveWidth;
|
|
|
|
|
const float scaleY = static_cast<float>(maxHeight) / effectiveHeight;
|
|
|
|
|
scale = std::min(scaleX, scaleY);
|
|
|
|
|
isScaled = (scale < 0.999f || scale > 1.001f);
|
|
|
|
|
} else if (maxWidth > 0 && effectiveWidth > static_cast<float>(maxWidth)) {
|
|
|
|
|
scale = static_cast<float>(maxWidth) / effectiveWidth;
|
|
|
|
|
isScaled = true;
|
|
|
|
|
}
|
|
|
|
|
if (maxHeight > 0 && (1.0f - cropY) * bitmap.getHeight() > maxHeight) {
|
|
|
|
|
scale = std::min(scale, static_cast<float>(maxHeight) / static_cast<float>((1.0f - cropY) * bitmap.getHeight()));
|
|
|
|
|
} else if (maxHeight > 0 && effectiveHeight > static_cast<float>(maxHeight)) {
|
|
|
|
|
scale = static_cast<float>(maxHeight) / effectiveHeight;
|
|
|
|
|
isScaled = true;
|
|
|
|
|
}
|
|
|
|
|
Serial.printf("[%lu] [GFX] Scaling by %f - %s\n", millis(), scale, isScaled ? "scaled" : "not scaled");
|
|
|
|
|
@@ -448,12 +466,17 @@ void GfxRenderer::drawBitmap(const Bitmap& bitmap, const int x, const int y, con
|
|
|
|
|
for (int bmpY = 0; bmpY < (bitmap.getHeight() - cropPixY); bmpY++) {
|
|
|
|
|
// The BMP's (0, 0) is the bottom-left corner (if the height is positive, top-left if negative).
|
|
|
|
|
// Screen's (0, 0) is the top-left corner.
|
|
|
|
|
int screenY = -cropPixY + (bitmap.isTopDown() ? bmpY : bitmap.getHeight() - 1 - bmpY);
|
|
|
|
|
const int logicalY = -cropPixY + (bitmap.isTopDown() ? bmpY : bitmap.getHeight() - 1 - bmpY);
|
|
|
|
|
int screenYStart, screenYEnd;
|
|
|
|
|
if (isScaled) {
|
|
|
|
|
screenY = std::floor(screenY * scale);
|
|
|
|
|
screenYStart = static_cast<int>(std::floor(logicalY * scale)) + y;
|
|
|
|
|
screenYEnd = static_cast<int>(std::floor((logicalY + 1) * scale)) + y;
|
|
|
|
|
} else {
|
|
|
|
|
screenYStart = logicalY + y;
|
|
|
|
|
screenYEnd = screenYStart + 1;
|
|
|
|
|
}
|
|
|
|
|
screenY += y; // the offset should not be scaled
|
|
|
|
|
if (screenY >= getScreenHeight()) {
|
|
|
|
|
|
|
|
|
|
if (screenYStart >= getScreenHeight()) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -464,7 +487,7 @@ void GfxRenderer::drawBitmap(const Bitmap& bitmap, const int x, const int y, con
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (screenY < 0) {
|
|
|
|
|
if (screenYEnd <= 0) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -473,27 +496,42 @@ void GfxRenderer::drawBitmap(const Bitmap& bitmap, const int x, const int y, con
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const int syStart = std::max(screenYStart, 0);
|
|
|
|
|
const int syEnd = std::min(screenYEnd, getScreenHeight());
|
|
|
|
|
|
|
|
|
|
for (int bmpX = cropPixX; bmpX < bitmap.getWidth() - cropPixX; bmpX++) {
|
|
|
|
|
int screenX = bmpX - cropPixX;
|
|
|
|
|
const int outX = bmpX - cropPixX;
|
|
|
|
|
int screenXStart, screenXEnd;
|
|
|
|
|
if (isScaled) {
|
|
|
|
|
screenX = std::floor(screenX * scale);
|
|
|
|
|
screenXStart = static_cast<int>(std::floor(outX * scale)) + x;
|
|
|
|
|
screenXEnd = static_cast<int>(std::floor((outX + 1) * scale)) + x;
|
|
|
|
|
} else {
|
|
|
|
|
screenXStart = outX + x;
|
|
|
|
|
screenXEnd = screenXStart + 1;
|
|
|
|
|
}
|
|
|
|
|
screenX += x; // the offset should not be scaled
|
|
|
|
|
if (screenX >= getScreenWidth()) {
|
|
|
|
|
|
|
|
|
|
if (screenXStart >= getScreenWidth()) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (screenX < 0) {
|
|
|
|
|
if (screenXEnd <= 0) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const uint8_t val = outputRow[bmpX / 4] >> (6 - ((bmpX * 2) % 8)) & 0x3;
|
|
|
|
|
|
|
|
|
|
if (renderMode == BW && val < 3) {
|
|
|
|
|
drawPixel(screenX, screenY);
|
|
|
|
|
} else if (renderMode == GRAYSCALE_MSB && (val == 1 || val == 2)) {
|
|
|
|
|
drawPixel(screenX, screenY, false);
|
|
|
|
|
} else if (renderMode == GRAYSCALE_LSB && val == 1) {
|
|
|
|
|
drawPixel(screenX, screenY, false);
|
|
|
|
|
const int sxStart = std::max(screenXStart, 0);
|
|
|
|
|
const int sxEnd = std::min(screenXEnd, getScreenWidth());
|
|
|
|
|
|
|
|
|
|
for (int sy = syStart; sy < syEnd; sy++) {
|
|
|
|
|
for (int sx = sxStart; sx < sxEnd; sx++) {
|
|
|
|
|
if (renderMode == BW && val < 3) {
|
|
|
|
|
drawPixel(sx, sy);
|
|
|
|
|
} else if (renderMode == GRAYSCALE_MSB && (val == 1 || val == 2)) {
|
|
|
|
|
drawPixel(sx, sy, false);
|
|
|
|
|
} else if (renderMode == GRAYSCALE_LSB && val == 1) {
|
|
|
|
|
drawPixel(sx, sy, false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@@ -506,11 +544,16 @@ void GfxRenderer::drawBitmap1Bit(const Bitmap& bitmap, const int x, const int y,
|
|
|
|
|
const int maxHeight) const {
|
|
|
|
|
float scale = 1.0f;
|
|
|
|
|
bool isScaled = false;
|
|
|
|
|
if (maxWidth > 0 && bitmap.getWidth() > maxWidth) {
|
|
|
|
|
// Calculate scale factor: supports both downscaling and upscaling when both constraints are provided
|
|
|
|
|
if (maxWidth > 0 && maxHeight > 0) {
|
|
|
|
|
const float scaleX = static_cast<float>(maxWidth) / static_cast<float>(bitmap.getWidth());
|
|
|
|
|
const float scaleY = static_cast<float>(maxHeight) / static_cast<float>(bitmap.getHeight());
|
|
|
|
|
scale = std::min(scaleX, scaleY);
|
|
|
|
|
isScaled = (scale < 0.999f || scale > 1.001f);
|
|
|
|
|
} else if (maxWidth > 0 && bitmap.getWidth() > maxWidth) {
|
|
|
|
|
scale = static_cast<float>(maxWidth) / static_cast<float>(bitmap.getWidth());
|
|
|
|
|
isScaled = true;
|
|
|
|
|
}
|
|
|
|
|
if (maxHeight > 0 && bitmap.getHeight() > maxHeight) {
|
|
|
|
|
} else if (maxHeight > 0 && bitmap.getHeight() > maxHeight) {
|
|
|
|
|
scale = std::min(scale, static_cast<float>(maxHeight) / static_cast<float>(bitmap.getHeight()));
|
|
|
|
|
isScaled = true;
|
|
|
|
|
}
|
|
|
|
|
@@ -538,20 +581,37 @@ void GfxRenderer::drawBitmap1Bit(const Bitmap& bitmap, const int x, const int y,
|
|
|
|
|
|
|
|
|
|
// Calculate screen Y based on whether BMP is top-down or bottom-up
|
|
|
|
|
const int bmpYOffset = bitmap.isTopDown() ? bmpY : bitmap.getHeight() - 1 - bmpY;
|
|
|
|
|
int screenY = y + (isScaled ? static_cast<int>(std::floor(bmpYOffset * scale)) : bmpYOffset);
|
|
|
|
|
if (screenY >= getScreenHeight()) {
|
|
|
|
|
int screenYStart, screenYEnd;
|
|
|
|
|
if (isScaled) {
|
|
|
|
|
screenYStart = static_cast<int>(std::floor(bmpYOffset * scale)) + y;
|
|
|
|
|
screenYEnd = static_cast<int>(std::floor((bmpYOffset + 1) * scale)) + y;
|
|
|
|
|
} else {
|
|
|
|
|
screenYStart = bmpYOffset + y;
|
|
|
|
|
screenYEnd = screenYStart + 1;
|
|
|
|
|
}
|
|
|
|
|
if (screenYStart >= getScreenHeight()) {
|
|
|
|
|
continue; // Continue reading to keep row counter in sync
|
|
|
|
|
}
|
|
|
|
|
if (screenY < 0) {
|
|
|
|
|
if (screenYEnd <= 0) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const int syStart = std::max(screenYStart, 0);
|
|
|
|
|
const int syEnd = std::min(screenYEnd, getScreenHeight());
|
|
|
|
|
|
|
|
|
|
for (int bmpX = 0; bmpX < bitmap.getWidth(); bmpX++) {
|
|
|
|
|
int screenX = x + (isScaled ? static_cast<int>(std::floor(bmpX * scale)) : bmpX);
|
|
|
|
|
if (screenX >= getScreenWidth()) {
|
|
|
|
|
int screenXStart, screenXEnd;
|
|
|
|
|
if (isScaled) {
|
|
|
|
|
screenXStart = static_cast<int>(std::floor(bmpX * scale)) + x;
|
|
|
|
|
screenXEnd = static_cast<int>(std::floor((bmpX + 1) * scale)) + x;
|
|
|
|
|
} else {
|
|
|
|
|
screenXStart = bmpX + x;
|
|
|
|
|
screenXEnd = screenXStart + 1;
|
|
|
|
|
}
|
|
|
|
|
if (screenXStart >= getScreenWidth()) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (screenX < 0) {
|
|
|
|
|
if (screenXEnd <= 0) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -561,7 +621,13 @@ void GfxRenderer::drawBitmap1Bit(const Bitmap& bitmap, const int x, const int y,
|
|
|
|
|
// For 1-bit source: 0 or 1 -> map to black (0,1,2) or white (3)
|
|
|
|
|
// val < 3 means black pixel (draw it)
|
|
|
|
|
if (val < 3) {
|
|
|
|
|
drawPixel(screenX, screenY, true);
|
|
|
|
|
const int sxStart = std::max(screenXStart, 0);
|
|
|
|
|
const int sxEnd = std::min(screenXEnd, getScreenWidth());
|
|
|
|
|
for (int sy = syStart; sy < syEnd; sy++) {
|
|
|
|
|
for (int sx = sxStart; sx < sxEnd; sx++) {
|
|
|
|
|
drawPixel(sx, sy, true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// White pixels (val == 3) are not drawn (leave background)
|
|
|
|
|
}
|
|
|
|
|
|