11package com .thecoderscorner .menu .editorui .gfxui .imgedit ;
22
3+ import com .thecoderscorner .embedcontrol .core .controlmgr .color .ControlColor ;
4+ import com .thecoderscorner .menu .domain .util .PortablePalette ;
5+ import com .thecoderscorner .menu .editorui .gfxui .pixmgr .BmpDataManager ;
36import javafx .scene .canvas .Canvas ;
47import javafx .scene .canvas .GraphicsContext ;
5- import javafx .scene .image .Image ;
68import javafx .scene .paint .Color ;
79
810public class ImageDrawingGrid extends Canvas {
9- private final Image image ;
11+ public enum DrawingMode { NONE , DOT , LINE , OUTLINE_RECT , FILLED_RECT }
12+ private final BmpDataManager bitmap ;
13+ private final PortablePalette palette ;
1014 private final boolean editMode ;
15+ private DrawingMode mode = DrawingMode .NONE ;
16+ private DrawingMode currentShape = DrawingMode .LINE ;
17+ private int colorIndex = 0 ;
1118 private double fitWidth ;
1219 private double fitHeight ;
20+ private int xStart , yStart ;
21+ private int xNow , yNow ;
22+ private boolean dirty = false ;
1323
14- public ImageDrawingGrid (Image image , boolean editMode ) {
15- this .image = image ;
24+ public ImageDrawingGrid (BmpDataManager bitmap , PortablePalette palette , boolean editMode ) {
25+ this .bitmap = bitmap ;
26+ this .palette = palette ;
1627 this .editMode = editMode ;
28+
29+ if (editMode ) {
30+ setOnMousePressed (event -> {
31+ xStart = (int ) (event .getX () / fitWidth * bitmap .getPixelWidth ());
32+ yStart = (int ) (event .getY () / fitHeight * bitmap .getPixelHeight ());
33+ mode = DrawingMode .DOT ;
34+ });
35+
36+ setOnMouseDragged (event -> {
37+ mode = currentShape ;
38+
39+ xNow = (int ) (event .getX () / fitWidth * bitmap .getPixelWidth ());
40+ yNow = (int ) (event .getY () / fitHeight * bitmap .getPixelHeight ());
41+ if (xNow >= bitmap .getPixelWidth () || yNow >= bitmap .getPixelHeight ()) return ;
42+ onPaintSurface (getGraphicsContext2D ());
43+ });
44+
45+ setOnMouseReleased (event -> {
46+ int xEnd = (int ) (event .getX () / fitWidth * bitmap .getPixelWidth ());
47+ int yEnd = (int ) (event .getY () / fitHeight * bitmap .getPixelHeight ());
48+ if (xEnd >= bitmap .getPixelWidth () || yEnd >= bitmap .getPixelHeight ()) return ;
49+ if (mode == DrawingMode .DOT ) {
50+ bitmap .setDataAt (xEnd , yEnd , colorIndex );
51+ recordChange ();
52+ onPaintSurface (getGraphicsContext2D ());
53+ } else if (mode == DrawingMode .FILLED_RECT ) {
54+ recordChange ();
55+ filledRectangle (bitmap , xEnd , yEnd );
56+ } else if (mode == DrawingMode .OUTLINE_RECT ) {
57+ recordChange ();
58+ drawLine (xStart , yStart , xEnd , yStart );
59+ drawLine (xEnd , yStart , xEnd , yEnd );
60+ drawLine (xStart , yEnd , xEnd , yEnd );
61+ drawLine (xStart , yStart , xStart , yEnd );
62+ } else if (mode == DrawingMode .LINE ) {
63+ recordChange ();
64+ drawLine (xStart , yStart , xEnd , yEnd );
65+ }
66+ mode = DrawingMode .NONE ;
67+ onPaintSurface (getGraphicsContext2D ());
68+ });
69+ }
70+ }
71+
72+ private void recordChange () {
73+ dirty = true ;
74+ }
75+
76+ void setCurrentShape (DrawingMode shape ) {
77+ currentShape = shape ;
78+ }
79+
80+ private void filledRectangle (BmpDataManager bitmap , int xEnd , int yEnd ) {
81+ int xMin = Math .min (xStart , xEnd );
82+ int yMin = Math .min (yStart , yEnd );
83+ int xMax = Math .max (xStart , xEnd );
84+ int yMax = Math .max (yStart , yEnd );
85+ for (int x = xMin ; x <= xMax ; x ++) {
86+ for (int y = yMin ; y <= yMax ; y ++) {
87+ bitmap .setDataAt (x , y , colorIndex );
88+ }
89+ }
90+ }
91+
92+ private void drawLine (int x0 , int y0 , int x1 , int y1 ) {
93+ // roughly copied from Adafruit_GFX, Thanks!
94+ boolean steep = Math .abs (y1 - y0 ) > Math .abs (x1 - x0 );
95+ if (steep ) {
96+ var tmp = y0 ;
97+ y0 = x0 ;
98+ x0 = tmp ;
99+
100+ tmp = y1 ;
101+ y1 = x1 ;
102+ x1 = tmp ;
103+ }
104+
105+ if (x0 > x1 ) {
106+ var tmp = x0 ;
107+ x0 = x1 ;
108+ x1 = tmp ;
109+
110+ tmp = y0 ;
111+ y0 = y1 ;
112+ y1 = tmp ;
113+ }
114+
115+ int dx , dy ;
116+ dx = x1 - x0 ;
117+ dy = Math .abs (y1 - y0 );
118+
119+ int err = dx / 2 ;
120+ int ystep ;
121+
122+ if (y0 < y1 ) {
123+ ystep = 1 ;
124+ } else {
125+ ystep = -1 ;
126+ }
127+
128+ for (; x0 <= x1 ; x0 ++) {
129+ if (steep ) {
130+ bitmap .setDataAt (y0 , x0 , colorIndex );
131+ } else {
132+ bitmap .setDataAt (x0 , y0 , colorIndex );
133+ }
134+ err -= dy ;
135+ if (err < 0 ) {
136+ y0 += ystep ;
137+ err += dx ;
138+ }
139+ }
140+ }
141+
142+ public void setCurrentColor (int paletteIdx ) {
143+ colorIndex = paletteIdx ;
17144 }
18145
19146 public boolean isResizable () {
20147 return true ;
21148 }
22149
23150 void prepareRatios () {
24- double ratio = image . getWidth () / image . getHeight ();
25- if (image . getWidth () > image . getHeight ()) {
151+ double ratio = ( double ) bitmap . getPixelWidth () / ( double ) bitmap . getPixelHeight ();
152+ if (bitmap . getPixelWidth () > bitmap . getPixelHeight ()) {
26153 double maxWid = this .getWidth ();
27154 fitWidth = maxWid ;
28155 fitHeight = (maxWid / ratio );
@@ -35,31 +162,50 @@ void prepareRatios() {
35162
36163 protected void onPaintSurface (GraphicsContext gc ) {
37164 prepareRatios ();
38- var perSquareX = fitWidth / image . getWidth ();
39- var perSquareY = fitHeight / image . getHeight ();
165+ var perSquareX = fitWidth / bitmap . getPixelWidth ();
166+ var perSquareY = fitHeight / bitmap . getPixelHeight ();
40167
41168 // first render the image as it appears.
42- var pixReader = image .getPixelReader ();
43- for (int y = 0 ; y < image .getHeight (); y ++) {
44- for (int x = 0 ; x < image .getWidth (); x ++) {
45- gc .setFill (pixReader .getColor (x , y ));
46- gc .fillRect (x * perSquareX , y * perSquareY , perSquareX , perSquareY );
169+ for (int y = 0 ; y < bitmap .getPixelHeight (); y ++) {
170+ for (int x = 0 ; x < bitmap .getPixelWidth (); x ++) {
171+ gc .setFill (ControlColor .asFxColor (palette .getColorAt (bitmap .getDataAt (x , y ))));
172+ gc .fillRect (x * perSquareX , y * perSquareY , perSquareX + 0.6 , perSquareY + 0.6 );
47173 }
48174 }
49175
176+ if (!editMode ) return ;
177+
178+ gc .setStroke (Color .BLUE );
179+ gc .setLineWidth (3 );
180+ gc .setLineDashes (10 , 3 );
181+
182+ double wid = ((xNow - xStart ) + 1 ) * perSquareX ;
183+ double hei = ((yNow - yStart ) + 1 ) * perSquareY ;
184+ if (mode == DrawingMode .FILLED_RECT || mode == DrawingMode .OUTLINE_RECT ) {
185+ gc .strokeRect (xStart * perSquareX , yStart * perSquareY , wid , hei );
186+ } else if (mode == DrawingMode .LINE ) {
187+ gc .strokeLine (xStart * perSquareX , yStart * perSquareY , xNow * perSquareX , yNow * perSquareY );
188+ }
189+
50190 // only use grid squares if the magnification is sufficient.
51191 if (perSquareX > 3 && perSquareY > 3 ) {
192+ gc .setLineDashes ();
193+ gc .setStroke (Color .LIGHTGRAY );
194+ gc .setLineWidth (1 );
195+ gc .setLineDashes (4 , 2 );
196+
52197 // now draw the grid ines into the image
53- gc .setFill (Color .GRAY );
54- for (int y = 0 ; y < image .getHeight (); y ++) {
55- gc .moveTo (0 , y * perSquareY );
56- gc .lineTo (this .getHeight (), y * perSquareY );
198+ for (int y = 0 ; y < bitmap .getPixelHeight (); y ++) {
199+ gc .strokeLine (0 , y * perSquareY , fitWidth , y * perSquareY );
57200 }
58201
59- for (int x = 0 ; x < image .getWidth (); x ++) {
60- gc .moveTo (x * perSquareX , 0 );
61- gc .lineTo (x * perSquareX , this .getWidth ());
202+ for (int x = 0 ; x < bitmap .getPixelWidth (); x ++) {
203+ gc .strokeLine (x * perSquareX , 0 , x * perSquareX , fitHeight );
62204 }
63205 }
64206 }
207+
208+ public boolean isModified () {
209+ return dirty ;
210+ }
65211}
0 commit comments