sleep screen filled with cover image's perimeter dominant color prior to being drawn
This commit is contained in:
@@ -262,3 +262,90 @@ BmpReaderError Bitmap::rewindToData() const {
|
||||
|
||||
return BmpReaderError::Ok;
|
||||
}
|
||||
|
||||
bool Bitmap::detectPerimeterIsBlack() const {
|
||||
// Detect if the 1-pixel perimeter of the image is mostly black or white.
|
||||
// Returns true if mostly black (luminance < 128), false if mostly white.
|
||||
|
||||
if (width <= 0 || height <= 0) return false;
|
||||
|
||||
auto* rowBuffer = static_cast<uint8_t*>(malloc(rowBytes));
|
||||
if (!rowBuffer) return false;
|
||||
|
||||
int blackCount = 0;
|
||||
int whiteCount = 0;
|
||||
|
||||
// Helper lambda to get luminance from a pixel at position x in rowBuffer
|
||||
auto getLuminance = [&](int x) -> uint8_t {
|
||||
switch (bpp) {
|
||||
case 32: {
|
||||
const uint8_t* p = rowBuffer + x * 4;
|
||||
return (77u * p[2] + 150u * p[1] + 29u * p[0]) >> 8;
|
||||
}
|
||||
case 24: {
|
||||
const uint8_t* p = rowBuffer + x * 3;
|
||||
return (77u * p[2] + 150u * p[1] + 29u * p[0]) >> 8;
|
||||
}
|
||||
case 8:
|
||||
return paletteLum[rowBuffer[x]];
|
||||
case 2:
|
||||
return paletteLum[(rowBuffer[x >> 2] >> (6 - ((x & 3) * 2))) & 0x03];
|
||||
case 1: {
|
||||
const uint8_t palIndex = (rowBuffer[x >> 3] & (0x80 >> (x & 7))) ? 1 : 0;
|
||||
return paletteLum[palIndex];
|
||||
}
|
||||
default:
|
||||
return 128; // Neutral if unsupported
|
||||
}
|
||||
};
|
||||
|
||||
// Helper to classify and count a pixel
|
||||
auto countPixel = [&](int x) {
|
||||
const uint8_t lum = getLuminance(x);
|
||||
if (lum < 128) {
|
||||
blackCount++;
|
||||
} else {
|
||||
whiteCount++;
|
||||
}
|
||||
};
|
||||
|
||||
// Helper to seek to a specific image row (accounting for top-down vs bottom-up)
|
||||
auto seekToRow = [&](int imageRow) -> bool {
|
||||
// In bottom-up BMP (topDown=false), row 0 in file is the bottom row of image
|
||||
// In top-down BMP (topDown=true), row 0 in file is the top row of image
|
||||
int fileRow = topDown ? imageRow : (height - 1 - imageRow);
|
||||
return file.seek(bfOffBits + static_cast<uint32_t>(fileRow) * rowBytes);
|
||||
};
|
||||
|
||||
// Sample top row (image row 0) - all pixels
|
||||
if (seekToRow(0) && file.read(rowBuffer, rowBytes) == rowBytes) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
countPixel(x);
|
||||
}
|
||||
}
|
||||
|
||||
// Sample bottom row (image row height-1) - all pixels
|
||||
if (height > 1) {
|
||||
if (seekToRow(height - 1) && file.read(rowBuffer, rowBytes) == rowBytes) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
countPixel(x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sample left and right edges from intermediate rows
|
||||
for (int y = 1; y < height - 1; y++) {
|
||||
if (seekToRow(y) && file.read(rowBuffer, rowBytes) == rowBytes) {
|
||||
countPixel(0); // Left edge
|
||||
countPixel(width - 1); // Right edge
|
||||
}
|
||||
}
|
||||
|
||||
free(rowBuffer);
|
||||
|
||||
// Rewind file position for subsequent drawing
|
||||
rewindToData();
|
||||
|
||||
// Return true if perimeter is mostly black
|
||||
return blackCount > whiteCount;
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ class Bitmap {
|
||||
BmpReaderError parseHeaders();
|
||||
BmpReaderError readNextRow(uint8_t* data, uint8_t* rowBuffer) const;
|
||||
BmpReaderError rewindToData() const;
|
||||
bool detectPerimeterIsBlack() const;
|
||||
int getWidth() const { return width; }
|
||||
int getHeight() const { return height; }
|
||||
bool isTopDown() const { return topDown; }
|
||||
|
||||
Reference in New Issue
Block a user