Bodmer / JPEGDecoder

A JPEG decoder library
Other
220 stars 64 forks source link

How to scale image? #47

Closed multiholle closed 4 years ago

multiholle commented 4 years ago

I'm loading the image to display from a web server and would like to scale the image to fit the screen. Simple scaling by 1/2 or 1/4 would be sufficient.

my code

```cpp #include #include #include #include TFT_eSPI tft = TFT_eSPI(); const char* ssid = "****"; const char* password = "****"; // JPEG decoder library #include const int picBufferSize = 640*480; void *picBuffer = NULL; // Return the minimum of two values a and b #define minimum(a,b) (((a) < (b)) ? (a) : (b)) //#################################################################################################### // Draw a JPEG on the TFT, images will be cropped on the right/bottom sides if they do not fit //#################################################################################################### // This function assumes xpos,ypos is a valid screen coordinate. For convenience images that do not // fit totally on the screen are cropped to the nearest MCU size and may leave right/bottom borders. void renderJPEG(int xpos, int ypos) { // retrieve infomration about the image uint16_t *pImg; uint16_t mcu_w = JpegDec.MCUWidth; uint16_t mcu_h = JpegDec.MCUHeight; uint32_t max_x = JpegDec.width; uint32_t max_y = JpegDec.height; // Jpeg images are draw as a set of image block (tiles) called Minimum Coding Units (MCUs) // Typically these MCUs are 16x16 pixel blocks // Determine the width and height of the right and bottom edge image blocks uint32_t min_w = minimum(mcu_w, max_x % mcu_w); uint32_t min_h = minimum(mcu_h, max_y % mcu_h); // save the current image block size uint32_t win_w = mcu_w; uint32_t win_h = mcu_h; // record the current time so we can measure how long it takes to draw an image uint32_t drawTime = millis(); // save the coordinate of the right and bottom edges to assist image cropping // to the screen size max_x += xpos; max_y += ypos; // read each MCU block until there are no more while (JpegDec.readSwappedBytes()) { // save a pointer to the image block pImg = JpegDec.pImage ; // calculate where the image block should be drawn on the screen int mcu_x = JpegDec.MCUx * mcu_w + xpos; // Calculate coordinates of top left corner of current MCU int mcu_y = JpegDec.MCUy * mcu_h + ypos; // check if the image block size needs to be changed for the right edge if (mcu_x + mcu_w <= max_x) win_w = mcu_w; else win_w = min_w; // check if the image block size needs to be changed for the bottom edge if (mcu_y + mcu_h <= max_y) win_h = mcu_h; else win_h = min_h; // copy pixels into a contiguous block if (win_w != mcu_w) { uint16_t *cImg; int p = 0; cImg = pImg + win_w; for (int h = 1; h < win_h; h++) { p += mcu_w; for (int w = 0; w < win_w; w++) { *cImg = *(pImg + w + p); cImg++; } } } // draw image MCU block only if it will fit on the screen if (( mcu_x + win_w ) <= tft.width() && ( mcu_y + win_h ) <= tft.height()) { tft.pushRect(mcu_x, mcu_y, win_w, win_h, pImg); } else if ( (mcu_y + win_h) >= tft.height()) JpegDec.abort(); // Image has run off bottom of screen so abort decoding } // calculate how long it took to draw the image drawTime = millis() - drawTime; // print the results to the serial port Serial.print(F( "Total render time was : ")); Serial.print(drawTime); Serial.println(F(" ms")); Serial.println(F("")); } //#################################################################################################### // Draw a JPEG on the TFT pulled from a program memory array //#################################################################################################### void drawArrayJpeg(const uint8_t arrayname[], uint32_t array_size, int xpos, int ypos) { int x = xpos; int y = ypos; JpegDec.decodeArray(arrayname, array_size); renderJPEG(x, y); Serial.println("#########################"); } void setup(void) { Serial.begin(115200); tft.init(); tft.fillScreen(TFT_BLACK); delay(10); Serial.println(); Serial.println(); Serial.print("Connecting to "); Serial.println(ssid); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(""); Serial.println("WiFi connected"); Serial.println("IP address: "); Serial.println(WiFi.localIP()); picBuffer = malloc(picBufferSize); HTTPClient http; http.begin("https://placebear.com/128/128"); //Specify the URL int httpCode = http.GET(); //Make the request if (httpCode > 0) { //Check for the returning code //String payload = http.getString(); Serial.println(httpCode); //Serial.println(payload); int len = http.getSize(); Serial.printf("[HTTP] size: %d\n", len); WiFiClient * stream = http.getStreamPtr(); // read all data from server uint8_t* p = (uint8_t*) picBuffer; int l = len; while (http.connected() && (l > 0 || len == -1)) { // get available data size size_t size = stream->available(); if (size) { int c = stream->readBytes(p, size); p += size; Serial.printf("[HTTP] read: %d\n", c); } } Serial.println(); Serial.print("[HTTP] connection closed.\n"); drawArrayJpeg((uint8_t*) picBuffer, len, 0, 16); } else { Serial.println("Error on HTTP request"); } http.end(); } void loop(){ } ```

Bodmer commented 4 years ago

There is no scaling capability built into this library. Simple methods are to skip pixels. Best is to blend NxN pixel blocks to find the average colour and plot as a single pixel.

The TJpg_Decoder library does handle scaling down by pixel skipping and has higher overall performance on 32 bit processors. Sketches are simplified too.