Skip to content

Commit c5f5be0

Browse files
committed
#446 font creator editing inflight, commit to work on urgent patch
1 parent 61c84bb commit c5f5be0

File tree

14 files changed

+364
-105
lines changed

14 files changed

+364
-105
lines changed

.idea/runConfigurations/TcMenu_Designer_UI.xml

Lines changed: 4 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ A menu library and designer UI for Arduino and mbed that is modular enough to su
44

55
Initially, you can use the menu designer UI that is packaged with every release, and available for Windows, macOS, and Linux. The designer UI takes care of building the core menu code and putting any callback functions into your sketch file. Think of the designer like a form designer in the desktop domain. Furthermore, It's non destructive on the sketch file, so can be round tripped during development.
66

7-
TheCodersCorner.com invest a lot of time and resources into making this open source product which is used by literally thousands of users. We don't presently sell hardware or have any other income streams from it, we ask that especially commercial users consider making a voluntary contribution to help keep us going using the sponsor button.
7+
TheCodersCorner.com invest a lot of time and resources into making this open source product which is used by literally thousands of users. We offer both [commercial support]() and [C++/Java/Dart consultancy](), or if you just want to say thanks, you can also make a donation via [GitHub](https://github.com/davetcc/tcMenu) (this repository).
88

99
In any fork, please ensure all text up to here is left unaltered.
1010

@@ -18,7 +18,9 @@ In any fork, please ensure all text up to here is left unaltered.
1818

1919
## Questions, community forum and support
2020

21-
You can ask questions either in the discussions section of this repo, or using the Arduino forum. We generally answer most questions, but the rules of engagement are: **this is my hobby, I make it available because it helps others**. Don't expect immediate answers, make sure you've recreated the problem in a simple sketch that you can send to me. Please consider making at least a one time donation using the sponsor link if we do help you out.
21+
Community questions can be asked in the discussions section of this repo, or using the Arduino forum. We generally answer most community questions but the responses will not be timely, for commercial use we recommend you purchase support.
22+
23+
Before posting into the community make sure you've recreated the problem in a simple sketch, and please consider making at least a one time donation using the sponsor link if we do help you out.
2224

2325
* Discussions section of this git repo (available from top menu of github page).
2426
* [Arduino discussion forum](https://forum.arduino.cc/) where questions can be asked, please tag me using `@davetcc`.

tcMenuGenerator/src/main/java/com/thecoderscorner/menu/editorui/gfxui/CreateBitmapWidgetController.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ private void refreshGridComponents() {
9696
var imageView = new SimpleImagePane(img.bmpData(), img.pixelFormat(),false, img.palette(), buttons);
9797

9898
editButton.setOnAction(_ -> {
99-
var editor = new SimpleImageEditor(img.bmpData(), img.pixelFormat(), img.palette());
99+
var editor = new SimpleImageEditor(img.bmpData(), img.pixelFormat(), img.palette(), true);
100100
if(editor.presentUI(editorUI)) {
101101
imageView.invalidate();
102102
}

tcMenuGenerator/src/main/java/com/thecoderscorner/menu/editorui/gfxui/CreateFontUtilityController.java

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import com.thecoderscorner.menu.editorui.dialog.SelectUnicodeRangesDialog;
77
import com.thecoderscorner.menu.editorui.generator.core.VariableNameGenerator;
88
import com.thecoderscorner.menu.editorui.gfxui.TcUnicodeFontExporter.TcUnicodeFontBlock;
9+
import com.thecoderscorner.menu.editorui.gfxui.imgedit.SimpleImageEditor;
910
import com.thecoderscorner.menu.editorui.gfxui.imgedit.SimpleImagePane;
1011
import com.thecoderscorner.menu.editorui.gfxui.pixmgr.BmpDataManager;
1112
import com.thecoderscorner.menu.editorui.gfxui.pixmgr.NativeBmpBitPacker;
@@ -21,8 +22,10 @@
2122
import javafx.scene.control.MenuItem;
2223
import javafx.scene.control.TextField;
2324
import javafx.scene.control.*;
25+
import javafx.scene.image.ImageView;
2426
import javafx.scene.layout.BorderPane;
2527
import javafx.scene.layout.GridPane;
28+
import javafx.scene.layout.HBox;
2629
import javafx.scene.layout.Pane;
2730
import javafx.stage.Stage;
2831

@@ -347,8 +350,23 @@ public FontGlyphDataControl(int code, BmpDataManager data) {
347350

348351
public Pane getUI() {
349352
BorderPane pane = new BorderPane();
350-
pane.setCenter(new SimpleImagePane(data, NativePixelFormat.MONO_BITMAP, false, FONT_PALETTE, List.of()));
353+
SimpleImagePane imgView = new SimpleImagePane(data, NativePixelFormat.MONO_BITMAP, false, FONT_PALETTE, List.of());
354+
pane.setCenter(imgView);
351355
pane.setBottom(new Label(STR."U\{code} : \{new String(Character.toChars(code))}"));
356+
357+
var box = new HBox(2);
358+
var editButton = new Button();
359+
editButton.setGraphic(new ImageView(getClass().getResource("/img/edit-pencil.png").toString()));
360+
editButton.setOnAction(_ -> {
361+
var editor = new SimpleImageEditor(data, NativePixelFormat.MONO_BITMAP, FONT_PALETTE, false);
362+
if(editor.presentUI(editorUI)) {
363+
imgView.invalidate();
364+
}
365+
});
366+
var selectCheck = new CheckBox();
367+
selectCheck.setOnAction(event -> selected = selectCheck.isSelected());
368+
box.getChildren().addAll(selectCheck, editButton);
369+
pane.setTop(box);
352370
return pane;
353371
}
354372

tcMenuGenerator/src/main/java/com/thecoderscorner/menu/editorui/gfxui/NativeFreeFontLoadedFont.java

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@
77
import java.util.Optional;
88
import java.util.Set;
99

10+
import static java.lang.System.Logger.Level.*;
11+
1012
public class NativeFreeFontLoadedFont implements LoadedFont {
13+
public final System.Logger logger = System.getLogger(getClass().getSimpleName());
14+
1115
private final Path fontPath;
1216
private final MethodHandle fontClose;
1317
private final MethodHandle fontGetGlyph;
@@ -19,6 +23,8 @@ public class NativeFreeFontLoadedFont implements LoadedFont {
1923
private int fontHandle;
2024

2125
public NativeFreeFontLoadedFont(Path path, int dpi) {
26+
logger.log(INFO, "Loading the freetype library and finding methods");
27+
2228
fontPath = path;
2329
fontHandle = 0;
2430
System.loadLibrary("fontGlyphGenerator");
@@ -54,30 +60,36 @@ public NativeFreeFontLoadedFont(Path path, int dpi) {
5460
);
5561

5662
try (Arena arena = Arena.ofConfined()) {
63+
logger.log(INFO, "Trying to initialise the library");
5764
int retCode = (int)fontLibInit.invoke();
58-
if(retCode != 0) throw new IllegalArgumentException("Font Library not loaded");
65+
if(retCode != 0) throw new IllegalArgumentException("Font Library init failed");
5966
setPixelsPerInch.invoke(dpi);
67+
logger.log(INFO, "All initialisation complete");
6068
} catch (Throwable e) {
69+
logger.log(ERROR, "Unable to load font library", e);
6170
}
6271
}
6372

6473
public void dispose() {
6574
try {
75+
logger.log(INFO, "Closing the freetype library");
6676
fontLibDestroy.invokeExact();
6777
} catch (Throwable e) {
78+
logger.log(ERROR, "Unable to close font library", e);
6879
}
6980
}
7081

7182
@Override
7283
public Optional<ConvertedFontGlyph> getConvertedGlyph(int code) {
7384
try(Arena arena = Arena.ofConfined()) {
85+
logger.log(DEBUG, "Get Glyph from library for {}", code);
7486
var data = arena.allocate(2200);
7587
var result = (int) fontGetGlyph.invoke(fontHandle, code, data);
7688
if(result == 0) {
7789
return Optional.of(parseDataBlock(data));
7890
}
7991
} catch (Throwable e) {
80-
throw new RuntimeException(e);
92+
logger.log(ERROR, "Unable to get glyph, will return empty", e);
8193
}
8294
return Optional.empty();
8395
}
@@ -116,20 +128,22 @@ public boolean canDisplay(int code) {
116128
try {
117129
return (boolean) canDisplayFn.invoke(fontHandle, code);
118130
} catch (Throwable e) {
131+
logger.log(ERROR, "Unable to check if code is displayable", e);
119132
return false;
120133
}
121134
}
122135

123136
@Override
124137
public void deriveFont(FontStyle fontStyle, int size, Set<UnicodeBlockMapping> newMappings, AntiAliasMode aliasMode) {
125138
try(Arena arena = Arena.ofConfined()) {
139+
logger.log(INFO, "Derive font size {} mappings {} path {}", size, newMappings.size(), fontPath);
126140
if(fontHandle != 0) {
127141
fontClose.invoke(fontHandle);
128142
}
129143
var cstrName = arena.allocateUtf8String(fontPath.toString());
130144
fontHandle = (int) fontLibCreateFont.invoke(cstrName, 0, size);
131145
} catch (Throwable ex) {
132-
146+
logger.log(ERROR, "Unable to derive font", ex);
133147
}
134148
}
135149
}

tcMenuGenerator/src/main/java/com/thecoderscorner/menu/editorui/gfxui/imgedit/ImageDrawingGrid.java

Lines changed: 64 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,18 @@
1515
* are going to be used later potentially on native displays.
1616
*/
1717
public class ImageDrawingGrid extends Canvas {
18-
public enum DrawingMode { NONE, DOT, LINE, OUTLINE_RECT, FILLED_RECT, OUTLINE_CIRCLE, FLOOD_FILL }
18+
public enum DrawingMode { NONE, DOT, LINE, OUTLINE_RECT, FILLED_RECT, OUTLINE_CIRCLE, FLOOD_FILL, SELECTION }
1919
private final BmpDataManager bitmap;
2020
private final PortablePalette palette;
2121
private final boolean editMode;
2222
private BiConsumer<Integer, Integer> positionConsumer;
2323
private DrawingMode mode = DrawingMode.NONE;
24-
private DrawingMode currentShape = DrawingMode.LINE;
24+
private DrawingMode currentShape = DrawingMode.DOT;
2525
private int colorIndex = 0;
2626
private double fitWidth;
2727
private double fitHeight;
2828
private int xStart, yStart;
29+
private OrderedRect imageSelection = null;
2930
private int xNow, yNow;
3031
private boolean dirty = false;
3132

@@ -36,6 +37,7 @@ public ImageDrawingGrid(BmpDataManager bitmap, PortablePalette palette, boolean
3637

3738
if(editMode) {
3839
setOnMousePressed(event -> {
40+
imageSelection = null;
3941
xStart = (int) (event.getX() / fitWidth * bitmap.getPixelWidth());
4042
yStart = (int) (event.getY() / fitHeight * bitmap.getPixelHeight());
4143
mode = currentShape == DrawingMode.FLOOD_FILL ? DrawingMode.FLOOD_FILL : DrawingMode.DOT;
@@ -47,13 +49,19 @@ public ImageDrawingGrid(BmpDataManager bitmap, PortablePalette palette, boolean
4749
xNow = (int) (event.getX() / fitWidth * bitmap.getPixelWidth());
4850
yNow = (int) (event.getY() / fitHeight * bitmap.getPixelHeight());
4951
if (xNow >= bitmap.getPixelWidth() || yNow >= bitmap.getPixelHeight()) return;
52+
if(mode == DrawingMode.SELECTION) {
53+
imageSelection = new OrderedRect(xStart, yStart, xNow, yNow);
54+
}
55+
5056
onPaintSurface(getGraphicsContext2D());
5157
});
5258

5359
setOnMouseMoved(event -> {
5460
int xEnd = (int) (event.getX() / fitWidth * bitmap.getPixelWidth());
5561
int yEnd = (int) (event.getY() / fitHeight * bitmap.getPixelHeight());
56-
if(positionConsumer != null) positionConsumer.accept(xEnd, yEnd);
62+
if(positionConsumer != null) {
63+
positionConsumer.accept(xEnd, yEnd);
64+
}
5765
});
5866

5967
setOnMouseReleased(event -> {
@@ -66,7 +74,7 @@ public ImageDrawingGrid(BmpDataManager bitmap, PortablePalette palette, boolean
6674
onPaintSurface(getGraphicsContext2D());
6775
} else if(mode == DrawingMode.FILLED_RECT) {
6876
recordChange();
69-
filledRectangle(bitmap, xEnd, yEnd);
77+
filledRectangle(bitmap, xStart, yStart, xEnd, yEnd);
7078
} else if(mode == DrawingMode.OUTLINE_CIRCLE) {
7179
recordChange();
7280
int halfX = (xEnd - xStart) / 2;
@@ -87,7 +95,7 @@ public ImageDrawingGrid(BmpDataManager bitmap, PortablePalette palette, boolean
8795
}
8896
}
8997

90-
private void floodFill(int x, int y, int startingCol) {
98+
public void floodFill(int x, int y, int startingCol) {
9199
// make sure we are inside bounds, and still able to fill, IE colour is still the starting colour
92100
if(x < 0 || y < 0 || x >= bitmap.getPixelWidth() || y >= bitmap.getPixelHeight()) return;
93101
if(bitmap.getDataAt(x, y) != startingCol) return;
@@ -99,14 +107,14 @@ private void floodFill(int x, int y, int startingCol) {
99107
floodFill(x + 1, y, startingCol);
100108
}
101109

102-
private void drawBoxOutline(int xEnd, int yEnd) {
110+
public void drawBoxOutline(int xEnd, int yEnd) {
103111
drawLine(xStart, yStart, xEnd, yStart);
104112
drawLine(xEnd, yStart, xEnd, yEnd);
105113
drawLine(xStart, yEnd, xEnd, yEnd);
106114
drawLine(xStart, yStart, xStart, yEnd);
107115
}
108116

109-
private void drawCircle(int x0, int y0, int r) {
117+
public void drawCircle(int x0, int y0, int r) {
110118
int f = 1 - r;
111119
int ddF_x = 1;
112120
int ddF_y = -2 * r;
@@ -143,23 +151,20 @@ private void recordChange() {
143151
dirty = true;
144152
}
145153

146-
void setCurrentShape(DrawingMode shape) {
154+
public void setCurrentShape(DrawingMode shape) {
147155
currentShape = shape;
148156
}
149157

150-
private void filledRectangle(BmpDataManager bitmap, int xEnd, int yEnd) {
151-
int xMin = Math.min(xStart, xEnd);
152-
int yMin = Math.min(yStart, yEnd);
153-
int xMax = Math.max(xStart, xEnd);
154-
int yMax = Math.max(yStart, yEnd);
155-
for (int x = xMin; x <= xMax; x++) {
156-
for (int y = yMin; y <= yMax; y++) {
158+
public void filledRectangle(BmpDataManager bitmap, int xStart, int yStart, int xEnd, int yEnd) {
159+
OrderedRect r = new OrderedRect(xStart, yStart, xEnd, yEnd);
160+
for (int x = r.getXMin(); x <= r.getXMax(); x++) {
161+
for (int y = r.getYMin(); y <= r.getYMax(); y++) {
157162
bitmap.setDataAt(x, y, colorIndex);
158163
}
159164
}
160165
}
161166

162-
private void drawLine(int x0, int y0, int x1, int y1) {
167+
public void drawLine(int x0, int y0, int x1, int y1) {
163168
// roughly copied from Adafruit_GFX, Thanks!
164169
boolean steep = Math.abs(y1 - y0) > Math.abs(x1 - x0);
165170
if (steep) {
@@ -255,6 +260,12 @@ protected void onPaintSurface(GraphicsContext gc) {
255260
gc.strokeRect(xStart * perSquareX, yStart * perSquareY, wid, hei);
256261
} else if(mode == DrawingMode.LINE) {
257262
gc.strokeLine(xStart * perSquareX, yStart * perSquareY, xNow * perSquareX, yNow * perSquareY);
263+
} else if(currentShape == DrawingMode.SELECTION && imageSelection != null) {
264+
gc.setStroke(Color.GREENYELLOW);
265+
gc.setLineWidth(4);
266+
gc.setLineDashes(10, 10);
267+
gc.strokeRect(imageSelection.getXMin() * perSquareX, imageSelection.getYMin() * perSquareY,
268+
imageSelection.width() * perSquareX, imageSelection.height() * perSquareY);
258269
}
259270

260271
// only use grid squares if the magnification is sufficient.
@@ -282,4 +293,41 @@ public boolean isModified() {
282293
public void setPositionUpdateListener(BiConsumer<Integer, Integer> consumer) {
283294
positionConsumer = consumer;
284295
}
296+
297+
public OrderedRect getImageSelection() {
298+
return imageSelection;
299+
}
300+
301+
public class OrderedRect {
302+
private final int xMin;
303+
private final int yMin;
304+
private final int xMax;
305+
private final int yMax;
306+
307+
public OrderedRect(int xStart, int yStart, int xEnd, int yEnd) {
308+
xMin = Math.min(xStart, xEnd);
309+
yMin = Math.min(yStart, yEnd);
310+
xMax = Math.max(xStart, xEnd);
311+
yMax = Math.max(yStart, yEnd);
312+
}
313+
314+
public int getXMin() {
315+
return xMin;
316+
}
317+
318+
public int getYMin() {
319+
return yMin;
320+
}
321+
322+
public int getXMax() {
323+
return xMax;
324+
}
325+
326+
public int getYMax() {
327+
return yMax;
328+
}
329+
330+
int width() { return xMax - xMin; }
331+
int height() { return yMax - yMin; }
332+
}
285333
}

0 commit comments

Comments
 (0)