Skip to content

Commit 09b7e56

Browse files
refactor savemap (#7)
* Store row pixel data in the heap * Added `frequency` as a argument to saveMap()
1 parent d00f7db commit 09b7e56

File tree

3 files changed

+68
-49
lines changed

3 files changed

+68
-49
lines changed

README.md

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,16 @@ The downloaded tile cache gets large very quickly -128kB per tile- so a ESP32 wi
2525
void setResolution(uint16_t w, uint16_t h);
2626
```
2727
28+
- If no resolution is set, a 320 by 240 map will be returned by `fetchMap`.
29+
2830
#### Resize cache
2931
3032
```c++
3133
bool resizeTilesCache(uint8_t numberOfTiles);
3234
```
33-
**Note**: Each tile is 128 kB.
35+
36+
- The cache is cleared before resizing.
37+
- Each tile is 128 kB.
3438

3539
#### Free the memory used by the tile cache
3640

@@ -47,11 +51,14 @@ bool fetchMap(LGFX_Sprite &map, double longitude, double latitude, uint8_t zoom)
4751
#### Save a map to SD card
4852
4953
```c++
50-
bool saveMap(const char *filename, LGFX_Sprite &display, String &result, uint8_t sdPin = SS)
54+
bool saveMap(const char *filename, LGFX_Sprite &map, String &result,
55+
uint8_t sdPin = SS, uint32_t frequency = 4000000)
5156
```
52-
`filename` should start with `/` for example `/map.bmp`
53-
`sdPin` is optional and used to set a `SS/CS` pin for the SD slot.
54-
`result` returns something like `SD Card mount failed!` or `Screenshot saved`.
57+
58+
- `filename` should start with `/` for example `/map.bmp` or `/images/map.bmp`
59+
- `result` returns something like `SD Card mount failed` or `Screenshot saved`.
60+
- `sdPin` is optional and used to set a `SS/CS` pin for the SD slot.
61+
- `frequency` is optional and used to set the SD speed.
5562

5663
## License differences between this library and the map data
5764

src/OpenStreetMap-esp32.cpp

Lines changed: 55 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -412,88 +412,100 @@ bool OpenStreetMap::downloadAndDecodeTile(CachedTile &tile, uint32_t x, uint32_t
412412
return true;
413413
}
414414

415-
bool OpenStreetMap::saveMap(const char *filename, LGFX_Sprite &map, String &result, uint8_t sdPin)
415+
bool OpenStreetMap::saveMap(const char *filename, LGFX_Sprite &map, String &result, uint8_t sdPin, uint32_t frequency)
416416
{
417417
log_i("Saving map, this may take a while...");
418418

419419
if (!map.getBuffer())
420420
{
421-
result = "No data in map!";
421+
result = "No data in map";
422422
return false;
423423
}
424424

425-
if (!SD.begin(sdPin))
425+
if (!SD.begin(sdPin, SPI, frequency))
426426
{
427-
result = "SD Card mount failed!";
427+
result = "SD Card mount failed";
428428
return false;
429429
}
430430

431431
File file = SD.open(filename, FILE_WRITE);
432432
if (!file)
433433
{
434-
result = "Failed to open file!";
434+
result = "Failed to open file";
435435
SD.end();
436436
return false;
437437
}
438438

439-
// BMP header (54 bytes)
440-
uint16_t bfType = 0x4D42; // "BM"
441-
uint32_t bfSize = 54 + map.width() * map.height() * 3; // Header + pixel data (3 bytes per pixel for RGB888)
442-
uint16_t bfReserved = 0;
443-
uint32_t bfOffBits = 54; // Offset to pixel data
439+
// BMP Header (54 bytes)
440+
uint16_t bfType = 0x4D42; // "BM"
441+
uint32_t biSizeImage = map.width() * map.height() * 3; // 3 bytes per pixel (RGB888)
442+
uint32_t bfSize = 54 + biSizeImage; // Total file size
443+
uint32_t bfOffBits = 54; // Offset to pixel data
444444

445445
uint32_t biSize = 40; // Info header size
446446
int32_t biWidth = map.width();
447-
int32_t biHeight = -map.height(); // Negative to flip vertically
447+
int32_t biHeight = -map.height(); // Negative to store in top-down order
448448
uint16_t biPlanes = 1;
449449
uint16_t biBitCount = 24; // RGB888 format
450450
uint32_t biCompression = 0;
451-
uint32_t biSizeImage = map.width() * map.height() * 3; // 3 bytes per pixel
452451
int32_t biXPelsPerMeter = 0;
453452
int32_t biYPelsPerMeter = 0;
454453
uint32_t biClrUsed = 0;
455454
uint32_t biClrImportant = 0;
456455

457-
// Write BMP header
458-
file.write(reinterpret_cast<const uint8_t *>(&bfType), sizeof(bfType));
459-
file.write(reinterpret_cast<const uint8_t *>(&bfSize), sizeof(bfSize));
460-
file.write(reinterpret_cast<const uint8_t *>(&bfReserved), sizeof(bfReserved));
461-
file.write(reinterpret_cast<const uint8_t *>(&bfOffBits), sizeof(bfOffBits));
462-
463-
file.write(reinterpret_cast<const uint8_t *>(&biSize), sizeof(biSize));
464-
file.write(reinterpret_cast<const uint8_t *>(&biWidth), sizeof(biWidth));
465-
file.write(reinterpret_cast<const uint8_t *>(&biHeight), sizeof(biHeight));
466-
file.write(reinterpret_cast<const uint8_t *>(&biPlanes), sizeof(biPlanes));
467-
file.write(reinterpret_cast<const uint8_t *>(&biBitCount), sizeof(biBitCount));
468-
file.write(reinterpret_cast<const uint8_t *>(&biCompression), sizeof(biCompression));
469-
file.write(reinterpret_cast<const uint8_t *>(&biSizeImage), sizeof(biSizeImage));
470-
file.write(reinterpret_cast<const uint8_t *>(&biXPelsPerMeter), sizeof(biXPelsPerMeter));
471-
file.write(reinterpret_cast<const uint8_t *>(&biYPelsPerMeter), sizeof(biYPelsPerMeter));
472-
file.write(reinterpret_cast<const uint8_t *>(&biClrUsed), sizeof(biClrUsed));
473-
file.write(reinterpret_cast<const uint8_t *>(&biClrImportant), sizeof(biClrImportant));
456+
// Write BMP header (Ensuring little-endian format)
457+
auto writeLE = [&](uint32_t value, uint8_t size)
458+
{
459+
for (uint8_t i = 0; i < size; i++)
460+
file.write(static_cast<uint8_t>(value >> (8 * i)));
461+
};
462+
463+
writeLE(bfType, 2);
464+
writeLE(bfSize, 4);
465+
writeLE(0, 2); // bfReserved
466+
writeLE(0, 2);
467+
writeLE(bfOffBits, 4);
468+
469+
writeLE(biSize, 4);
470+
writeLE(biWidth, 4);
471+
writeLE(biHeight, 4);
472+
writeLE(biPlanes, 2);
473+
writeLE(biBitCount, 2);
474+
writeLE(biCompression, 4);
475+
writeLE(biSizeImage, 4);
476+
writeLE(biXPelsPerMeter, 4);
477+
writeLE(biYPelsPerMeter, 4);
478+
writeLE(biClrUsed, 4);
479+
writeLE(biClrImportant, 4);
480+
481+
MemoryBuffer rowBuffer(map.width() * 3);
482+
if (!rowBuffer.isAllocated())
483+
{
484+
result = "Row buffer allocation failed";
485+
file.close();
486+
SD.end();
487+
return false;
488+
}
474489

490+
uint8_t *buf = rowBuffer.get();
475491
for (int y = 0; y < map.height(); y++)
476492
{
477493
for (int x = 0; x < map.width(); x++)
478494
{
479-
uint16_t rgb565Color = map.readPixel(x, y); // Read pixel color (RGB565 format)
480-
uint8_t red5 = (rgb565Color >> 11) & 0x1F;
481-
uint8_t green6 = (rgb565Color >> 5) & 0x3F;
482-
uint8_t blue5 = rgb565Color & 0x1F;
483-
484-
// Convert RGB565 to RGB888
485-
uint8_t red8 = (red5 * 255) / 31;
486-
uint8_t green8 = (green6 * 255) / 63;
487-
uint8_t blue8 = (blue5 * 255) / 31;
488-
489-
file.write(blue8);
490-
file.write(green8);
491-
file.write(red8);
495+
uint16_t rgb565Color = map.readPixel(x, y);
496+
uint8_t red8 = ((rgb565Color >> 11) & 0x1F) * 255 / 31;
497+
uint8_t green8 = ((rgb565Color >> 5) & 0x3F) * 255 / 63;
498+
uint8_t blue8 = (rgb565Color & 0x1F) * 255 / 31;
499+
500+
buf[x * 3] = blue8;
501+
buf[x * 3 + 1] = green8;
502+
buf[x * 3 + 2] = red8;
492503
}
504+
file.write(buf, rowBuffer.size()); // Write entire row at once
493505
}
494506

495507
file.close();
496508
SD.end();
497509
result = "Screenshot saved";
498510
return true;
499-
}
511+
}

src/OpenStreetMap-esp32.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ class OpenStreetMap
5656
bool resizeTilesCache(uint8_t numberOfTiles);
5757
void freeTilesCache();
5858
bool fetchMap(LGFX_Sprite &sprite, double longitude, double latitude, uint8_t zoom);
59-
bool saveMap(const char *filename, LGFX_Sprite &map, String &result, uint8_t sdPin = SS);
59+
bool saveMap(const char *filename, LGFX_Sprite &map, String &result, uint8_t sdPin = SS, uint32_t frequency = 4000000);
6060

6161
private:
6262
static OpenStreetMap *currentInstance;

0 commit comments

Comments
 (0)