1+ /*
2+ * Aurora: https://github.com/pixelmatix/aurora
3+ * Copyright (c) 2014 Jason Coon
4+ *
5+ * Portions of this code are adapted from Adafruit's spitftbitmap example: https://github.com/adafruit/Adafruit_ILI9340/blob/master/examples/spitftbitmap/spitftbitmap.ino
6+ * Written by Limor Fried/Ladyada for Adafruit Industries: http://www.adafruit.com
7+ *
8+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
9+ * this software and associated documentation files (the "Software"), to deal in
10+ * the Software without restriction, including without limitation the rights to
11+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
12+ * the Software, and to permit persons to whom the Software is furnished to do so,
13+ * subject to the following conditions:
14+ *
15+ * The above copyright notice and this permission notice shall be included in all
16+ * copies or substantial portions of the Software.
17+ *
18+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
20+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
21+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
22+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
23+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24+ */
25+
26+ #ifndef BitmapPlayer_H
27+ #define BitmapPlayer_H
28+
29+ #define BUFFPIXEL 64
30+
31+ class BitmapPlayer {
32+ private:
33+ // These read 16- and 32-bit types from the SD card file.
34+ // BMP data is stored little-endian, Arduino is little-endian too.
35+ // May need to reverse subscript order if porting elsewhere.
36+
37+ uint16_t read16 (File& f) {
38+ uint16_t result;
39+ ((uint8_t *) &result)[0 ] = f.read (); // LSB
40+ ((uint8_t *) &result)[1 ] = f.read (); // MSB
41+ return result;
42+ }
43+
44+ uint32_t read32 (File& f) {
45+ uint32_t result;
46+ ((uint8_t *) &result)[0 ] = f.read (); // LSB
47+ ((uint8_t *) &result)[1 ] = f.read ();
48+ ((uint8_t *) &result)[2 ] = f.read ();
49+ ((uint8_t *) &result)[3 ] = f.read (); // MSB
50+ return result;
51+ }
52+
53+ public:
54+ uint32_t drawBitmap (char *filename, uint8_t x = 0 , uint8_t y = 0 ) {
55+ File bmpFile;
56+ int bmpWidth, bmpHeight; // W+H in pixels
57+ uint8_t bmpDepth; // Bit depth (currently must be 24)
58+ uint32_t bmpImageoffset; // Start of image data in file
59+ uint32_t rowSize; // Not always = bmpWidth; may have padding
60+ uint8_t sdbuffer[3 * BUFFPIXEL]; // pixel buffer (R+G+B per pixel)
61+ uint8_t buffidx = sizeof (sdbuffer); // Current position in sdbuffer
62+ boolean goodBmp = false ; // Set to true on valid header parse
63+ boolean flip = true ; // BMP is stored bottom-to-top
64+ int w, h, row, col;
65+ // uint8_t r, g, b;
66+ uint32_t pos = 0 , startTime = millis ();
67+
68+ if ((x >= matrix.getScreenWidth ()) || (y >= matrix.getScreenHeight ())) return 0 ;
69+
70+ Serial.println ();
71+ Serial.print (" Loading image '" );
72+ Serial.print (filename);
73+ Serial.println (' \' ' );
74+
75+ // Open requested file on SD card
76+ bmpFile = SD.open (filename, O_READ);
77+ if (!bmpFile) {
78+ return 0 ;
79+ }
80+
81+ // Parse BMP header
82+ if (read16 (bmpFile) == 0x4D42 ) { // BMP signature
83+ Serial.print (" File size: " );
84+ Serial.println (read32 (bmpFile));
85+ read32 (bmpFile); // Read & ignore creator bytes
86+ bmpImageoffset = read32 (bmpFile); // Start of image data
87+ Serial.print (" Image Offset: " );
88+ Serial.println (bmpImageoffset, DEC);
89+ // Read DIB header
90+ Serial.print (" Header size: " );
91+ Serial.println (read32 (bmpFile));
92+ bmpWidth = read32 (bmpFile);
93+ bmpHeight = read32 (bmpFile);
94+ if (read16 (bmpFile) == 1 ) { // # planes -- must be '1'
95+ bmpDepth = read16 (bmpFile); // bits per pixel
96+ Serial.print (" bmpDepth: " );
97+ Serial.println (bmpDepth);
98+ if ((bmpDepth == 24 ) && (read32 (bmpFile) == 0 )) { // 0 = uncompressed
99+
100+ goodBmp = true ; // Supported BMP format -- proceed!
101+ Serial.print (" Image size: " );
102+ Serial.print (bmpWidth);
103+ Serial.print (' x' );
104+ Serial.println (bmpHeight);
105+
106+ // BMP rows are padded (if needed) to 4-byte boundary
107+ rowSize = (bmpWidth * 3 + 3 ) & ~3 ;
108+
109+ // If bmpHeight is negative, image is in top-down order.
110+ // This is not canon but has been observed in the wild.
111+ if (bmpHeight < 0 ) {
112+ bmpHeight = -bmpHeight;
113+ flip = false ;
114+ }
115+
116+ // Crop area to be loaded
117+ w = bmpWidth;
118+ h = bmpHeight;
119+
120+ if ((x + w - 1 ) >= matrix.getScreenWidth ()) w = matrix.getScreenWidth () - x;
121+ if ((y + h - 1 ) >= matrix.getScreenHeight ()) h = matrix.getScreenWidth () - y;
122+
123+ for (row = 0 ; row < h; row++) { // For each scanline...
124+ if (flip) // Bitmap is stored bottom-to-top order (normal BMP)
125+ pos = bmpImageoffset + (bmpHeight - 1 - row) * rowSize;
126+ else // Bitmap is stored top-to-bottom
127+ pos = bmpImageoffset + row * rowSize;
128+ if (bmpFile.position () != pos) { // Need seek?
129+ bmpFile.seek (pos);
130+ buffidx = sizeof (sdbuffer); // Force buffer reload
131+ }
132+
133+ // optimize by setting pins now
134+ for (col = 0 ; col < w; col++) { // For each pixel...
135+ // Time to read more pixel data?
136+ if (buffidx >= sizeof (sdbuffer)) { // Indeed
137+ bmpFile.read (sdbuffer, sizeof (sdbuffer));
138+ buffidx = 0 ; // Set index to beginning
139+ }
140+
141+ // Convert pixel from BMP to Matrix format, push to display
142+ rgb24 color;
143+ color.blue = sdbuffer[buffidx++];
144+ color.green = sdbuffer[buffidx++];
145+ color.red = sdbuffer[buffidx++];
146+
147+ matrix.drawPixel (x + col, y + row, color);
148+
149+ } // end pixel
150+ } // end scanline
151+ // matrix.swapBuffers(true);
152+ Serial.print (" Loaded in " );
153+ Serial.print (millis () - startTime);
154+ Serial.println (" ms" );
155+ } // end goodBmp
156+ }
157+ }
158+
159+ bmpFile.close ();
160+
161+ if (!goodBmp) {
162+ Serial.println (" BMP format not recognized." );
163+ return 0 ;
164+ }
165+ else {
166+ return millis () - startTime;
167+ }
168+ }
169+ };
170+
171+ #endif
0 commit comments