Skip to content

Commit 3f4112a

Browse files
The-EGthinkyhead
andcommitted
💄 Improve Ender3 v2 DWIN MarlinUI (#23369)
Co-authored-by: Scott Lahteine <thinkyhead@users.noreply.github.com>
1 parent 31ec8f2 commit 3f4112a

File tree

140 files changed

+3022
-97
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

140 files changed

+3022
-97
lines changed

Marlin/src/lcd/e3v2/marlinui/dwin_lcd.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@
6464
#define ICON_UpArrow 14
6565
#define ICON_DownArrow 15
6666
#define ICON_BedLine 16
67+
#define ICON_BedLevelOff 17
68+
#define ICON_BedLevelOn 18
6769

6870
#include "../common/dwin_font.h"
6971

Marlin/src/lcd/e3v2/marlinui/ui_common.cpp

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,6 @@ bool MarlinUI::detected() { return true; }
8181
// Initialize or re-initialize the LCD
8282
void MarlinUI::init_lcd() {
8383
DWIN_Startup();
84-
85-
// Load the assets JPG (currently just the status screen 'icon')
86-
DWIN_JPG_CacheTo1(DWIN_MarlinUI_Assets);
8784
}
8885

8986
// This LCD should clear where it will draw anew

Marlin/src/lcd/e3v2/marlinui/ui_status_480x272.cpp

Lines changed: 166 additions & 94 deletions
Large diffs are not rendered by default.

Marlin/src/lcd/marlinui.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,7 @@ constexpr uint8_t epps = ENCODER_PULSES_PER_STEP;
244244

245245
#if IS_DWIN_MARLINUI
246246
bool MarlinUI::did_first_redraw;
247+
bool MarlinUI::old_is_printing;
247248
#endif
248249

249250
// Encoder Handling

Marlin/src/lcd/marlinui.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,7 @@ class MarlinUI {
468468

469469
#if IS_DWIN_MARLINUI
470470
static bool did_first_redraw;
471+
static bool old_is_printing;
471472
#endif
472473

473474
#if EITHER(BABYSTEP_ZPROBE_GFX_OVERLAY, MESH_EDIT_GFX_OVERLAY)
Lines changed: 342 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,342 @@
1+
# DWIN_ICO
2+
# - Dissect and create DWIN .ico files for their LCD displays.
3+
#
4+
# Copyright (c) 2020 Brent Burton
5+
#
6+
# This program is free software: you can redistribute it and/or modify
7+
# it under the terms of the GNU General Public License as published by
8+
# the Free Software Foundation, either version 3 of the License, or
9+
# (at your option) any later version.
10+
#
11+
# This program is distributed in the hope that it will be useful,
12+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
# GNU General Public License for more details.
15+
#
16+
# You should have received a copy of the GNU General Public License
17+
# along with this program. If not, see <https://www.gnu.org/licenses/>.
18+
#----------------------------------------------------------------
19+
#
20+
# This is not a normal Microsoft .ICO file, but it has a similar
21+
# structure for containing a number of icon images. Each icon is
22+
# a small JPG file.
23+
#
24+
# The file has a directory header containing fixed-length
25+
# records, and each record points to its data at an offset later
26+
# in the file.
27+
#
28+
# The directory entries are 16 bytes each, and the entire
29+
# directory is 4KB (0 - 0x1000). This supports 256 entries.
30+
#
31+
# Multibyte values are in Big Endian format.
32+
#
33+
# Header: (offset 0x0)
34+
# W H offset ?? len ?? ??
35+
# Entry 0: xxxx xxxx 00001000 xx 10a2 00 00000000
36+
# Entry 1: xxxx xxxx 000020a2 xx 0eac 00 00000000
37+
# Entry 2: xxxx xxxx 00002f4e xx 0eaa 00 00000000
38+
# ...
39+
# 0x00001000: ffd8 ffe1 0018 ... jpeg exif and data follow .. ffd9
40+
# 0x000020a2: ffd8 ffe1 ...
41+
# ...rest of ICO entries' data...
42+
#
43+
# Header structure:
44+
# Offset Len What
45+
# 0 2 width
46+
# 2 2 height
47+
# 4 4 file byte position from SEEK_BEG
48+
# 8 3 length of data
49+
# 11 5 ??? all zeroes
50+
#
51+
# Other notes:
52+
# * The index of each icon corresponds to the Icon number in dwin.h
53+
# * One exception is number 39: that header entry is blank, and dwin.h
54+
# does not define a name for 39. This is specially handled to
55+
# prevent reordering stock icons.
56+
57+
import os
58+
import struct
59+
from PIL import Image
60+
61+
def getJpegResolution(jpegFile):
62+
"""Returns a 2-tuple containing the jpegFile's (width, height).
63+
"""
64+
img = Image.open(jpegFile)
65+
return img.size
66+
67+
class DWIN_ICO_File():
68+
def __init__(self):
69+
self.entries = [] # list of header entries
70+
71+
def splitFile(self, filename, outDir):
72+
if not filename[-4:].lower() == '.ico':
73+
raise RuntimeError('Input file must end in .ico')
74+
75+
with open(filename, 'rb') as infile:
76+
self._parseHeader(infile)
77+
self._splitEntryData(infile, outDir)
78+
79+
return
80+
81+
def _parseHeader(self, infile):
82+
maxEntries = 256
83+
count = 0
84+
validEntries = 0
85+
while count < maxEntries:
86+
rawBytes = infile.read(16)
87+
entry = Entry()
88+
entry.parseRawData(rawBytes)
89+
# check that it is valid: is offset nonzero?
90+
# Special case: treat 39 as valid
91+
if (entry.offset > 0) or (count == 39):
92+
validEntries += 1
93+
self.entries.append(entry)
94+
count += 1
95+
return
96+
97+
def _splitEntryData(self, infile, outDir):
98+
print('Splitting Entry Data...')
99+
if 0 == len(self.entries):
100+
raise RuntimeError('.ico file is not loaded yet')
101+
102+
# check for output dir:
103+
if not os.path.exists(outDir):
104+
os.mkdir(outDir)
105+
106+
# keep a count
107+
count = 0
108+
for entry in self.entries:
109+
# Skip any empty entries. (Special handling of 39.)
110+
if entry.length == 0:
111+
count += 1
112+
continue
113+
# Seek file position, read length bytes, and write to new output file.
114+
print('%02d: offset: 0x%06x len: 0x%04x width: %d height: %d' %
115+
(count, entry.offset, entry.length, entry.width, entry.height))
116+
outfilename = os.path.join(outDir,
117+
'%03d-%s.jpg' % (count, _iconNames[count]))
118+
with open(outfilename, 'wb') as outfile:
119+
infile.seek(entry.offset)
120+
blob = infile.read(entry.length)
121+
outfile.write(blob)
122+
print('Wrote %d bytes to %s' % (entry.length, outfilename))
123+
124+
count += 1
125+
return
126+
127+
def createFile(self, iconDir, filename):
128+
'''Create a new .ico file from the contents of iconDir.
129+
130+
The contents of iconDir are processed to get image
131+
resolution, and a new entry is created for each.
132+
133+
Each filename must have a leading number followed by a
134+
dash, which is the icon index. E.g., "071-ICON_StepX.jpg".
135+
'''
136+
self.entries = [Entry() for i in range(0,256)]
137+
# 1. Scan icon directory and record all valid files
138+
print('Scanning icon directory', iconDir)
139+
count = 0
140+
for dirEntry in os.scandir(iconDir):
141+
if not dirEntry.is_file():
142+
print('...Ignoring', dirEntry.path)
143+
continue
144+
# process each file:
145+
try:
146+
index = int(dirEntry.name[0:3])
147+
if (index < 0) or (index > 255):
148+
print('...Ignoring invalid index on', dirEntry.path)
149+
continue
150+
#dirEntry.path is iconDir/name
151+
w,h = getJpegResolution(dirEntry.path)
152+
length = dirEntry.stat().st_size
153+
e = self.entries[index]
154+
e.width = w
155+
e.height = h
156+
e.length = length
157+
e.filename = dirEntry.path
158+
count += 1
159+
except Exception as e:
160+
print('Whoops: ', e)
161+
pass
162+
print('...Scanned %d icon files' % (count))
163+
164+
# 2. Scan over valid header entries and update offsets
165+
self._updateHeaderOffsets()
166+
167+
# 3. Write out header to .ico file, the append each icon file
168+
self._combineAndWriteIcoFile(filename)
169+
print('Scanning done. %d icons included.' % (count))
170+
171+
def _updateHeaderOffsets(self):
172+
"""Iterate over all header entries and update their offsets.
173+
"""
174+
offset = 256 * 16
175+
for i in range(0,256):
176+
e = self.entries[i]
177+
if e.length == 0:
178+
continue
179+
e.offset = offset
180+
offset += e.length
181+
#print('%03d: (%d x %d) len=%d off=%d' %
182+
# (i, e.width, e.height, e.length, e.offset))
183+
return
184+
185+
def _combineAndWriteIcoFile(self, filename):
186+
"""Write out final .ico file.
187+
All header entries are updated, so write out
188+
the final header contents, and concat each icon
189+
file to the .ico.
190+
"""
191+
with open(filename, 'wb') as outfile:
192+
# 1. Write header directory
193+
for e in self.entries:
194+
outfile.write( e.serialize() )
195+
if outfile.tell() != 4096:
196+
raise RuntimeError('Header directory write failed. Not 4096 bytes')
197+
# 2. For each entry, concat the icon file data
198+
for e in self.entries:
199+
if 0 == e.length: continue
200+
guts = self._getFileContents(e.filename, e.length)
201+
outfile.write(guts)
202+
return
203+
204+
def _getFileContents(self, filename, length):
205+
"""Read contents of filename, and return bytes"""
206+
with open(filename, 'rb') as infile:
207+
contents = infile.read(length)
208+
if len(contents) != length:
209+
raise RuntimeError('Failed to read contents of', filename)
210+
return contents
211+
212+
class Entry():
213+
'''Entry objects record resolution and size information
214+
about each icon stored in an ICO file.
215+
'''
216+
__slots__ = ('width', 'height', 'offset', 'length', 'filename')
217+
218+
def __init__(self, w=0, h=0, length=0, offset=0):
219+
self.width = w
220+
self.height = h
221+
self.offset = offset
222+
self.length = length
223+
self.filename = None
224+
225+
def parseRawData(self, rawEntryBytes):
226+
if len(rawEntryBytes) != 16:
227+
raise RuntimeError('Entry: data must be 16 bytes long')
228+
229+
# Split data into bigendian fields
230+
(w, h, off, len3, len21, b1,b2,b3,b4,b5) = \
231+
struct.unpack('>HHLBHBBBBB', rawEntryBytes)
232+
self.width = w
233+
self.height = h
234+
self.offset = off
235+
self.length = len3 * 65536 + len21
236+
return
237+
238+
def serialize(self):
239+
"""Convert this Entry's information into a 16-byte
240+
.ico directory entry record. Return bytes object.
241+
"""
242+
len21 = self.length % 65536
243+
len3 = self.length // 65536
244+
rawdata = struct.pack('>HHLBHBBBBB', self.width, self.height,
245+
self.offset, len3, len21,
246+
0, 0, 0, 0, 0)
247+
return rawdata
248+
249+
_iconNames = {
250+
0 : 'ICON_LOGO',
251+
1 : 'ICON_Print_0',
252+
2 : 'ICON_Print_1',
253+
3 : 'ICON_Prepare_0',
254+
4 : 'ICON_Prepare_1',
255+
5 : 'ICON_Control_0',
256+
6 : 'ICON_Control_1',
257+
7 : 'ICON_Leveling_0',
258+
8 : 'ICON_Leveling_1',
259+
9 : 'ICON_HotendTemp',
260+
10 : 'ICON_BedTemp',
261+
11 : 'ICON_Speed',
262+
12 : 'ICON_Zoffset',
263+
13 : 'ICON_Back',
264+
14 : 'ICON_File',
265+
15 : 'ICON_PrintTime',
266+
16 : 'ICON_RemainTime',
267+
17 : 'ICON_Setup_0',
268+
18 : 'ICON_Setup_1',
269+
19 : 'ICON_Pause_0',
270+
20 : 'ICON_Pause_1',
271+
21 : 'ICON_Continue_0',
272+
22 : 'ICON_Continue_1',
273+
23 : 'ICON_Stop_0',
274+
24 : 'ICON_Stop_1',
275+
25 : 'ICON_Bar',
276+
26 : 'ICON_More',
277+
27 : 'ICON_Axis',
278+
28 : 'ICON_CloseMotor',
279+
29 : 'ICON_Homing',
280+
30 : 'ICON_SetHome',
281+
31 : 'ICON_PLAPreheat',
282+
32 : 'ICON_ABSPreheat',
283+
33 : 'ICON_Cool',
284+
34 : 'ICON_Language',
285+
35 : 'ICON_MoveX',
286+
36 : 'ICON_MoveY',
287+
37 : 'ICON_MoveZ',
288+
38 : 'ICON_Extruder',
289+
# no 39
290+
40 : 'ICON_Temperature',
291+
41 : 'ICON_Motion',
292+
42 : 'ICON_WriteEEPROM',
293+
43 : 'ICON_ReadEEPROM',
294+
44 : 'ICON_ResumeEEPROM',
295+
45 : 'ICON_Info',
296+
46 : 'ICON_SetEndTemp',
297+
47 : 'ICON_SetBedTemp',
298+
48 : 'ICON_FanSpeed',
299+
49 : 'ICON_SetPLAPreheat',
300+
50 : 'ICON_SetABSPreheat',
301+
51 : 'ICON_MaxSpeed',
302+
52 : 'ICON_MaxAccelerated',
303+
53 : 'ICON_MaxJerk',
304+
54 : 'ICON_Step',
305+
55 : 'ICON_PrintSize',
306+
56 : 'ICON_Version',
307+
57 : 'ICON_Contact',
308+
58 : 'ICON_StockConfiguraton',
309+
59 : 'ICON_MaxSpeedX',
310+
60 : 'ICON_MaxSpeedY',
311+
61 : 'ICON_MaxSpeedZ',
312+
62 : 'ICON_MaxSpeedE',
313+
63 : 'ICON_MaxAccX',
314+
64 : 'ICON_MaxAccY',
315+
65 : 'ICON_MaxAccZ',
316+
66 : 'ICON_MaxAccE',
317+
67 : 'ICON_MaxSpeedJerkX',
318+
68 : 'ICON_MaxSpeedJerkY',
319+
69 : 'ICON_MaxSpeedJerkZ',
320+
70 : 'ICON_MaxSpeedJerkE',
321+
71 : 'ICON_StepX',
322+
72 : 'ICON_StepY',
323+
73 : 'ICON_StepZ',
324+
74 : 'ICON_StepE',
325+
75 : 'ICON_Setspeed',
326+
76 : 'ICON_SetZOffset',
327+
77 : 'ICON_Rectangle',
328+
78 : 'ICON_BLTouch',
329+
79 : 'ICON_TempTooLow',
330+
80 : 'ICON_AutoLeveling',
331+
81 : 'ICON_TempTooHigh',
332+
82 : 'ICON_NoTips_C',
333+
83 : 'ICON_NoTips_E',
334+
84 : 'ICON_Continue_C',
335+
85 : 'ICON_Continue_E',
336+
86 : 'ICON_Cancel_C',
337+
87 : 'ICON_Cancel_E',
338+
88 : 'ICON_Confirm_C',
339+
89 : 'ICON_Confirm_E',
340+
90 : 'ICON_Info_0',
341+
91 : 'ICON_Info_1'
342+
}

0 commit comments

Comments
 (0)