It can, although maybe not as direct. I have played around with different variations of picture viewers, that I use mostly to test out different boards and displays...
That supports BMP, JPEG, PNG. The last two are through the libries JPEGDEC, PNGdec.
I have played around with a couple variations of it on the GIGA with the GIGA display.
Which most of the code is up in my github project: KurtE/Arduino_GIGA-stuff: This is my GIGA test sketches and other like stuff for Arduino GIGA boards
LIke, the one tft_picture_view_sd_giga_shield_24Bit.
The main parts of the JPEG support in the section like:
#ifdef __JPEGDEC__
JPEGDEC jpeg;
void processJPGFile(WrapperFile &jpgFile, const char *name, bool fErase) {
int image_size = jpgFile.size();
jpgFile.seek(0);
Serial.println();
Serial.print((uint32_t)&jpgFile, HEX);
Serial.print(F(" Loading JPG image '"));
Serial.print(name);
Serial.print("' ");
Serial.println(image_size, DEC);
uint8_t scale = 1;
if (jpeg.open((void *)&jpgFile, image_size, nullptr, myReadJPG, mySeekJPG, JPEGDraw)) {
int image_width = jpeg.getWidth();
int image_height = jpeg.getHeight();
int decode_options = 0;
Serial.print("Image size: ");
Serial.print(image_width);
Serial.print("x");
Serial.print(image_height);
// Try setting to 24 bit mode
jpeg.setPixelType(RGB8888);
switch (g_JPGScale) {
case 1:
scale = 1;
decode_options = 0;
break;
case 2:
scale = 2;
decode_options = JPEG_SCALE_HALF;
break;
case 4:
scale = 4;
decode_options = JPEG_SCALE_QUARTER;
break;
case 8:
scale = 8;
decode_options = JPEG_SCALE_EIGHTH;
break;
default:
{
if ((image_width > g_jpg_scale_x_above[SCL_16TH]) || (image_height > g_jpg_scale_y_above[SCL_16TH])) {
decode_options = JPEG_SCALE_EIGHTH | JPEG_SCALE_HALF;
scale = 16;
} else if ((image_width > g_jpg_scale_x_above[SCL_EIGHTH]) || (image_height > g_jpg_scale_y_above[SCL_EIGHTH])) {
decode_options = JPEG_SCALE_EIGHTH;
scale = 8;
} else if ((image_width > g_jpg_scale_x_above[SCL_QUARTER]) || (image_height > g_jpg_scale_y_above[SCL_QUARTER])) {
decode_options = JPEG_SCALE_QUARTER;
scale = 4;
} else if ((image_width > g_jpg_scale_x_above[SCL_HALF]) || (image_height > g_jpg_scale_y_above[SCL_HALF])) {
decode_options = JPEG_SCALE_HALF;
scale = 2;
}
}
}
Display.beginDraw();
if (fErase && ((image_width / scale < g_tft_width) || (image_height / scale < g_tft_height))) {
fillScreen((uint16_t)g_background_color, false);
}
if (g_center_image) {
g_image_offset_x = (g_tft_width - image_width / scale) / 2;
g_image_offset_y = (g_tft_height - image_height / scale) / 2;
} else {
g_image_offset_x = 0;
g_image_offset_y = 0;
}
g_image_scale = scale;
Serial.print("Scale: 1/");
Serial.print(g_image_scale);
Serial.print(" Image Offsets (");
Serial.print(g_image_offset_x);
Serial.print(", ");
Serial.print(g_image_offset_y), Serial.println(")");
jpeg.decode(0, 0, decode_options);
jpeg.close();
//Display.endDraw();
} else {
Serial.println("Was not a valid jpeg file");
}
jpgFile.close();
}
int32_t myReadJPG(JPEGFILE *pjpegfile, uint8_t *buffer, int32_t length) {
if (!pjpegfile || !pjpegfile->fHandle) return 0;
return ((WrapperFile *)(pjpegfile->fHandle))->read(buffer, length);
}
int32_t mySeekJPG(JPEGFILE *pjpegfile, int32_t position) {
if (!pjpegfile || !pjpegfile->fHandle) return 0;
return ((WrapperFile *)(pjpegfile->fHandle))->seek(position);
}
int JPEGDraw(JPEGDRAW *pDraw) {
if (g_debug_output) {
Serial.print("jpeg draw: x,y=");
Serial.print(pDraw->x);
Serial.print(",");
Serial.print(pDraw->y);
Serial.print(", cx,cy = ");
Serial.print(pDraw->iWidth);
Serial.print(",");
Serial.println(pDraw->iHeight);
}
writeClippedRect24(pDraw->x, pDraw->y, pDraw->iWidth, pDraw->iHeight, (uint32_t *)pDraw->pPixels);
return 1;
}
#endif
Note: my sketch has the ability read in a quick and dirty options file, that allows me to hard code scale, or to give hints... But the main things are:
After calling jpeg.open, you can the call the jpeg.getwidth() and jpeg.gitHeight(), and
use those values to decide if you wish to scale and/or offset the image.
I don't use the library to do the offset. Instead when the library does a callback for us to draw something, in my case JPEGDraw method, this method calls another
of my internal functions writeClippedRect24, which adds on the offsets, and
then puts the pixels out to the display.
Hope that makes sense.