c232ffbaa838b9e41bb4501a8b8a089c9fc5178b
[sdk] / ide / src / documents / PictureEdit.ec
1 /****************************************************************************
2    ECERE IDE
3
4    Copyright (c) 2003 Jerome Jacovella-St-Louis
5    All Rights Reserved.
6
7    pictureEdit.ec - Picture Editor Control
8 ****************************************************************************/
9 #ifdef ECERE_STATIC
10 import static "ecere"
11 #else
12 import "ecere"
13 #endif
14
15 #define ID_IMAGE_MODE_COLORTABLE 9
16 #define ID_IMAGE_MODE_INDEXED    10
17 #define ID_IMAGE_MODE_RGB        11
18
19 static Array<FileFilter> filters
20 { [
21    { $"Image Files (*.jpg, *.jpeg, *.bmp, *.pcx, *.png, *.gif)", "jpg, jpeg, bmp, pcx, png, gif" },
22    { $"All files", null }
23 ] };
24
25 static Array<FileType> types
26 { [
27    { $"Based on extension", null,  never },
28    { $"JPG Image",          "jpg", always },
29    { $"BMP Image",          "bmp", always },
30    { $"PCX Image",          "pcx", always },
31    { $"PNG Image",          "png", always },
32    { $"GIF Image",          "gif", always }
33 ] };
34
35 FileDialog pictureEditFileDialog { filters = filters.array, sizeFilters = filters.count * sizeof(FileFilter), types = types.array, sizeTypes = types.count * sizeof(FileType) };
36
37 class PictureEdit : Window
38 {
39    background = black;
40    isDocument = true;
41    isActiveClient = true;
42    menu = Menu { };
43
44    OnHScroll = OnScroll;
45    OnVScroll = OnScroll;
46
47    float zoomFactor;
48    char fileName[MAX_LOCATION];
49    Bitmap bitmap;
50    Bitmap bitmapNotIndexed;
51
52    //saveDialog = pictureEditFileDialog;
53
54    Menu fileMenu { menu, $"File", f };
55       MenuItem { fileMenu, $"Save", s, ctrlS, NotifySelect = MenuFileSave };
56       MenuItem { fileMenu, $"Save As...", a, NotifySelect = MenuFileSaveAs };
57    Menu imageMenu { menu, $"Image", i };
58       Menu modeMenu { imageMenu, $"Mode", m };
59          MenuItem imageModeIndexedItem
60          {
61             modeMenu, $"Indexed Color...", i, isRadio = true;
62             bool NotifySelect(MenuItem selection, Modifiers mods)
63             {
64                if(bitmap)
65                {
66                   displaySystem.Lock();
67                   OnUnloadGraphics();
68                   bitmap.Quantize(0, 255);
69                   OnLoadGraphics();
70                   displaySystem.Unlock();
71
72                   imageModeColorTableItem.disabled = false;
73                   Update(null);
74                   modifiedDocument = true;
75                }
76                return true;
77             }
78          };
79          MenuItem imageModeRGBItem
80          {
81             modeMenu, $"RGB Color", r, isRadio = true;
82             bool NotifySelect(MenuItem selection, Modifiers mods)
83             {
84                if(bitmap)
85                {
86                   displaySystem.Lock();
87                   OnUnloadGraphics();
88                   bitmap.Convert(null, pixelFormat888, null);
89                   OnLoadGraphics();
90                   displaySystem.Unlock();
91
92                   imageModeColorTableItem.disabled = true;
93                   Update(null);
94                   modifiedDocument = true;
95                }
96                return true;
97             }
98          };
99          MenuDivider { modeMenu };
100          MenuItem imageModeColorTableItem
101          {
102             modeMenu, $"Color Table", r;
103             bool NotifySelect(MenuItem selection, Modifiers mods)
104             {
105                if(bitmap)
106                {
107                   PictureEditColorTable colorTable { master = this };
108                   colorTable.Modal();
109                   Update(null);
110                }
111                return true;
112             }
113          };
114          #ifdef _DEBUG
115          MenuDivider { imageMenu };
116          MenuItem adjustHSVItem
117          {
118             imageMenu, $"Adjust Hue, Saturation, Value", h;
119             bool NotifySelect(MenuItem selection, Modifiers mods)
120             {
121                if(bitmap)
122                {
123                   AdjustHSV adjustHSV { master = this };
124                   adjustHSV.Modal();
125                   Update(null);
126                }
127                return true;
128             }
129          };
130          #endif
131
132    property const char * bitmapFile
133    {
134       set
135       {
136          if(value)
137          {
138             strcpy(fileName, value);
139          }
140          if(fileName[0])
141          {
142             bitmap = Bitmap {};
143             if(bitmap.Load(fileName, null, null))
144             {
145                if(bitmap.pixelFormat == pixelFormatRGBA)
146                {
147                   bitmap.alphaBlend = true;
148                   bitmap.Convert(null, pixelFormat888, null);
149                }
150                //if(!eWindow_GetStartWidth(window) || !eWindow_GetStartHeight(window))
151                {
152                   Size size = initSize;  // what's the use of retrieving initSize
153                   size.w = bitmap.width;
154                   size.h = bitmap.height;
155                   clientSize = size;
156
157                   /*
158                   Move(eWindow_GetStartX(window), eWindow_GetStartY(window),
159                      (!eWindow_GetStartWidth(window)) ? (A_CLIENT|bitmap.width) : eWindow_GetStartWidth(window),
160                      (!eWindow_GetStartHeight(window)) ? (A_CLIENT|bitmap.height) : eWindow_GetStartHeight(window));
161                   */
162
163                   /*
164                   Move(eWindow_GetStartX(window), eWindow_GetStartY(window),
165                      (!) ? (A_CLIENT|bitmap.width) : eWindow_GetStartWidth(window),
166                      (!eWindow_GetStartHeight(window)) ? (A_CLIENT|bitmap.height) : eWindow_GetStartHeight(window));
167                   */
168                   bitmap.keepData = true;
169                }
170                scrollArea = Size { bitmap.width, bitmap.height };
171             }
172             else
173                delete bitmap;
174          }
175
176          if(bitmap)
177          {
178             switch(bitmap.pixelFormat)
179             {
180                case pixelFormat8:
181                   imageModeIndexedItem.checked = true;
182                   imageModeRGBItem.disabled = true;
183                   break;
184                case pixelFormat888:
185                   imageModeRGBItem.checked = true;
186                   imageModeColorTableItem.disabled = true;
187                   break;
188             }
189          }
190       }
191    }
192
193    void OnUnloadGraphics()
194    {
195       void * picture = bitmap.picture, * palette = bitmap.palette;
196       bitmap.picture = null;
197       bitmap.palette = null;
198       bitmap.Free();
199       bitmap.picture = picture;
200       bitmap.palette = palette;
201       delete bitmapNotIndexed;
202    }
203
204    bool OnLoadGraphics()
205    {
206       if(bitmap.pixelFormat == pixelFormat8)
207       {
208          bitmapNotIndexed = { };
209          bitmapNotIndexed.Copy(bitmap);
210          bitmapNotIndexed.Convert(null, pixelFormat888, null);
211          bitmapNotIndexed.MakeDD(displaySystem);
212       }
213       else
214          bitmap.MakeDD(displaySystem);
215       return true;
216    }
217
218    void OnRedraw(Surface surface)
219    {
220       Bitmap bmp = (bitmapNotIndexed && displaySystem.pixelFormat != pixelFormat8) ? bitmapNotIndexed : bitmap;
221       if(bmp)
222       {
223          int w = (int)(bmp.width * zoomFactor);
224          int h = (int)(bmp.height * zoomFactor);
225          if(w == bmp.width && h == bmp.height)
226          {
227             surface.Blit(bmp,
228                Max(0, (clientSize.w - w) / 2), Max(0, (clientSize.h - h) / 2),
229                scroll.x, scroll.y, w, h);
230          }
231          else
232          {
233             surface.Filter(bmp,
234                Max(0, (clientSize.w - w) / 2), Max(0, (clientSize.h - h) / 2),
235                (int)(scroll.x / zoomFactor), (int)(scroll.y / zoomFactor), w, h,
236                bmp.width, bmp.height);
237          }
238       }
239    }
240
241    void OnScroll(ScrollBarAction action, int position, Key key)
242    {
243       Update(null);
244    }
245
246    bool OnKeyHit(Key key, unichar ch)
247    {
248       switch(key)
249       {
250          case equal:
251          case keyPadPlus:
252             if(bitmap && zoomFactor < 25)
253             {
254                float x = 0.5f, y = 0.5f;
255                if(bitmap.width * zoomFactor > clientSize.w)
256                   x = scroll.x / (bitmap.width * zoomFactor - clientSize.w);
257                if(bitmap.height * zoomFactor > clientSize.h)
258                   y = scroll.y / (bitmap.height * zoomFactor - clientSize.h);
259
260                zoomFactor *= 1.5;
261                scrollArea = Size { bitmap.width * zoomFactor,  bitmap.height * zoomFactor };
262
263                scroll = Point {
264                      (int)(Max(0, bitmap.width * zoomFactor - clientSize.w) * x),
265                      (int)(Max(0, bitmap.height * zoomFactor - clientSize.h) * y) };
266
267                Update(null);
268             }
269             break;
270          case minus:
271          case keyPadMinus:
272             if(bitmap && zoomFactor > 0.05)
273             {
274                float x = 0.5f, y = 0.5f;
275                if(bitmap.width * zoomFactor > clientSize.w)
276                   x = scroll.x / (bitmap.width * zoomFactor - clientSize.w);
277                if(bitmap.height * zoomFactor > clientSize.w)
278                   y = scroll.y / (bitmap.height * zoomFactor - clientSize.h);
279
280                zoomFactor /= 1.5;
281                scrollArea = Size { bitmap.width * zoomFactor, bitmap.height * zoomFactor };
282
283                scroll = Point {
284                      (int)(Max(0, bitmap.width * zoomFactor - clientSize.w) * x),
285                      (int)(Max(0, bitmap.height * zoomFactor - clientSize.h) * y) };
286
287                Update(null);
288             }
289             break;
290       }
291       return true;
292    }
293
294    bool OnSaveFile(const char * fileName)
295    {
296       bool result = false;
297       if(bitmap)
298       {
299          if(bitmap.Save(fileName,
300             ((FileType *)pictureEditFileDialog.types)[pictureEditFileDialog.fileType].typeExtension, (void *) bool::true))
301          {
302             modifiedDocument = false;
303             result = true;
304          }
305       }
306       return result;
307    }
308
309    PictureEdit()
310    {
311       zoomFactor = 1.0f;
312       return true;
313    }
314
315    ~PictureEdit()
316    {
317       delete bitmap;
318       delete bitmapNotIndexed;
319    }
320 }
321
322 class PictureEditColorTable : Window
323 {
324    hasClose = true;
325    text = $"Color Table";
326    background = formColor;
327    minClientSize = Size { 400, 400 };
328
329    Button button
330    {
331       parent = this, hotKey = escape, size = { 80 }, text = $"Close";
332       anchor = Anchor { right = 10, bottom = 10 };
333       NotifyClicked = ButtonCloseDialog;
334    };
335
336    void OnRedraw(Surface surface)
337    {
338       PictureEdit picture = (PictureEdit)master;
339       Bitmap bitmap = picture.bitmap;
340       int c;
341       for(c = 0; c < 256; c++)
342       {
343          int x = (c % 16) * 16;
344          int y = (c / 16) * 16;
345          surface.SetBackground(bitmap.palette[c]);
346          surface.Area(10 + x, 30 + y, 10 + x + 15, 30 + y + 15);
347       }
348    }
349 }
350
351 class ColorBox : Window
352 {
353    size = { 32, 32 };
354    borderStyle = deepContour;
355 }
356
357 #ifdef _DEBUG
358 class AdjustHSV : Window
359 {
360    size = { 400, 300 };
361
362    background = formColor;
363    ColorHSV target;
364    ColorHSV replace;
365    replace = { 248, 100, 71 }; //Color { 26, 0, 183 };
366    target = { 207, 61, 71 };
367    hasClose = true;
368
369    Button button1
370    {
371       this, text = $"Go", position = { 296, 104 }, isDefault = true;
372
373       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
374       {
375          PictureEdit picture = (PictureEdit)master;
376          Bitmap bitmap = picture.bitmap;
377          double h = 1.0f, s = 0.80f, v = 1.24f;
378          double tolH = 1;
379          double tolS = 1;
380          double tolV = 1;
381
382          h = target.h - replace.h;
383          s = target.s / replace.s;
384          v = target.v / replace.v;
385
386          for(y = 0; y<bitmap.height; y++)
387          {
388             for(x = 0; x<bitmap.width; x++)
389             {
390                ColorAlpha color = ((ColorAlpha *)bitmap.picture)[y * bitmap.stride + x];
391                ColorHSV hsv = color;
392                double diffH = Abs(hsv.h/360 - replace.h/360);
393                double diffS = Abs(hsv.s - replace.s)/100.0;
394                double diffV = Abs(hsv.v - replace.v)/100.0;
395                if(diffH <= tolH && diffS <= tolS && diffV <= tolV)
396                {
397                   hsv.h += h;
398                   hsv.s *= s;
399                   hsv.v *= v;
400                   color.color = hsv;
401                   ((ColorAlpha *)bitmap.picture)[y * bitmap.stride + x] = color;
402                }
403             }
404          }
405          picture.Update(null);
406          picture.modifiedDocument = true;
407          Destroy(0);
408          return true;
409       }
410    };
411
412    EditBox hBox { this, text = "H", size = { 78, 19 }, position = { 176, 80 } };
413    EditBox sBox { this, text = "S", size = { 78, 19 }, position = { 176, 120 } };
414    EditBox vBox { this, text = "V", size = { 78, 19 }, position = { 176, 160 } };
415    Label label1 { this, size = { 7, 13 }, position = { 152, 80 }, labeledWindow = hBox };
416    Label label2 { this, size = { 6, 13 }, position = { 152, 120 }, labeledWindow = sBox };
417    Label label3 { this, size = { 6, 13 }, position = { 152, 168 }, labeledWindow = vBox };
418    ColorBox original { this, position = { 10, 10 }, background = replace };
419    ColorBox result { this, position = { 10, 100 }, background = target };
420 }
421 #endif