ac8b790b52359addb010405ff4c8861dc60bc1d8
[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                   ColorAlpha * palette = bitmap.Quantize(0, 255);
67                   bitmapNotIndexed = { };
68                   bitmapNotIndexed.Copy(bitmap);
69                   bitmapNotIndexed.Convert(null, pixelFormat888, null);
70
71                   /*
72                   eBitmap_Convert(null, bitmap, PixelFormat8, palette);
73                   bitmap.allocatePalette = true;
74                   */
75
76                   imageModeColorTableItem.disabled = false;
77                   Update(null);
78                   modifiedDocument = true;
79                }
80                return true;
81             }
82          };
83          MenuItem imageModeRGBItem
84          {
85             modeMenu, $"RGB Color", r, isRadio = true;
86             bool NotifySelect(MenuItem selection, Modifiers mods)
87             {
88                if(bitmap)
89                   bitmap.Convert(null, pixelFormat888, null);
90                delete bitmapNotIndexed;
91                imageModeColorTableItem.disabled = true;
92                Update(null);
93                modifiedDocument = true;
94                return true;
95             }
96          };
97          MenuDivider { modeMenu };
98          MenuItem imageModeColorTableItem
99          {
100             modeMenu, $"Color Table", r;
101             bool NotifySelect(MenuItem selection, Modifiers mods)
102             {
103                if(bitmap)
104                {
105                   PictureEditColorTable colorTable { master = this };
106                   colorTable.Modal();
107                   Update(null);
108                }
109                return true;
110             }
111          };
112          #ifdef _DEBUG
113          MenuDivider { imageMenu };
114          MenuItem adjustHSVItem
115          {
116             imageMenu, $"Adjust Hue, Saturation, Value", h;
117             bool NotifySelect(MenuItem selection, Modifiers mods)
118             {
119                if(bitmap)
120                {
121                   AdjustHSV adjustHSV { master = this };
122                   adjustHSV.Modal();
123                   Update(null);
124                }
125                return true;
126             }
127          };
128          #endif
129
130    property const char * bitmapFile
131    {
132       set
133       {
134          if(value)
135          {
136             strcpy(fileName, value);
137          }
138          if(fileName[0])
139          {
140             bitmap = Bitmap {};
141             if(bitmap.Load(fileName, null, null))
142             {
143                if(bitmap.pixelFormat == pixelFormatRGBA)
144                {
145                   bitmap.alphaBlend = true;
146                   bitmap.Convert(null, pixelFormat888, null);
147                }
148                if(bitmap.pixelFormat == pixelFormat8)
149                {
150                   bitmapNotIndexed = { };
151                   bitmapNotIndexed.Copy(bitmap);
152                   bitmapNotIndexed.Convert(null, pixelFormat888, null);
153                }
154                //if(!eWindow_GetStartWidth(window) || !eWindow_GetStartHeight(window))
155                {
156                   Size size = initSize;  // what's the use of retrieving initSize
157                   size.w = bitmap.width;
158                   size.h = bitmap.height;
159                   clientSize = size;
160
161                   /*
162                   Move(eWindow_GetStartX(window), eWindow_GetStartY(window),
163                      (!eWindow_GetStartWidth(window)) ? (A_CLIENT|bitmap.width) : eWindow_GetStartWidth(window),
164                      (!eWindow_GetStartHeight(window)) ? (A_CLIENT|bitmap.height) : eWindow_GetStartHeight(window));
165                   */
166
167                   /*
168                   Move(eWindow_GetStartX(window), eWindow_GetStartY(window),
169                      (!) ? (A_CLIENT|bitmap.width) : eWindow_GetStartWidth(window),
170                      (!eWindow_GetStartHeight(window)) ? (A_CLIENT|bitmap.height) : eWindow_GetStartHeight(window));
171                   */
172                }
173                scrollArea = Size { bitmap.width, bitmap.height };
174             }
175             else
176                delete bitmap;
177          }
178
179          if(bitmap)
180          {
181             switch(bitmap.pixelFormat)
182             {
183                case pixelFormat8:
184                   imageModeIndexedItem.checked = true;
185                   break;
186                case pixelFormat888:
187                   imageModeRGBItem.checked = true;
188                   imageModeColorTableItem.disabled = true;
189                   break;
190             }
191          }
192       }
193    }
194
195    void OnRedraw(Surface surface)
196    {
197       Bitmap bmp = (bitmapNotIndexed && displaySystem.pixelFormat != pixelFormat8) ? bitmapNotIndexed : bitmap;
198       if(bmp)
199       {
200          int w = (int)(bmp.width * zoomFactor);
201          int h = (int)(bmp.height * zoomFactor);
202          if(w == bmp.width && h == bmp.height)
203          {
204             surface.Blit(bmp,
205                Max(0, (clientSize.w - w) / 2), Max(0, (clientSize.h - h) / 2),
206                scroll.x, scroll.y, w, h);
207          }
208          else
209          {
210             surface.Filter(bmp,
211                Max(0, (clientSize.w - w) / 2), Max(0, (clientSize.h - h) / 2),
212                (int)(scroll.x / zoomFactor), (int)(scroll.y / zoomFactor), w, h,
213                bmp.width, bmp.height);
214          }
215       }
216    }
217
218    void OnScroll(ScrollBarAction action, int position, Key key)
219    {
220       Update(null);
221    }
222
223    bool OnKeyHit(Key key, unichar ch)
224    {
225       switch(key)
226       {
227          case equal:
228          case keyPadPlus:
229             if(bitmap && zoomFactor < 25)
230             {
231                float x = 0.5f, y = 0.5f;
232                if(bitmap.width * zoomFactor > clientSize.w)
233                   x = scroll.x / (bitmap.width * zoomFactor - clientSize.w);
234                if(bitmap.height * zoomFactor > clientSize.h)
235                   y = scroll.y / (bitmap.height * zoomFactor - clientSize.h);
236
237                zoomFactor *= 1.5;
238                scrollArea = Size { bitmap.width * zoomFactor,  bitmap.height * zoomFactor };
239
240                scroll = Point {
241                      (int)(Max(0, bitmap.width * zoomFactor - clientSize.w) * x),
242                      (int)(Max(0, bitmap.height * zoomFactor - clientSize.h) * y) };
243
244                Update(null);
245             }
246             break;
247          case minus:
248          case keyPadMinus:
249             if(bitmap && zoomFactor > 0.05)
250             {
251                float x = 0.5f, y = 0.5f;
252                if(bitmap.width * zoomFactor > clientSize.w)
253                   x = scroll.x / (bitmap.width * zoomFactor - clientSize.w);
254                if(bitmap.height * zoomFactor > clientSize.w)
255                   y = scroll.y / (bitmap.height * zoomFactor - clientSize.h);
256
257                zoomFactor /= 1.5;
258                scrollArea = Size { bitmap.width * zoomFactor, bitmap.height * zoomFactor };
259
260                scroll = Point {
261                      (int)(Max(0, bitmap.width * zoomFactor - clientSize.w) * x),
262                      (int)(Max(0, bitmap.height * zoomFactor - clientSize.h) * y) };
263
264                Update(null);
265             }
266             break;
267       }
268       return true;
269    }
270
271    bool OnSaveFile(const char * fileName)
272    {
273       bool result = false;
274       if(bitmap)
275       {
276          if(bitmap.Save(fileName,
277             ((FileType *)pictureEditFileDialog.types)[pictureEditFileDialog.fileType].typeExtension, (void *) bool::true))
278          {
279             modifiedDocument = false;
280             result = true;
281          }
282       }
283       return result;
284    }
285
286    PictureEdit()
287    {
288       zoomFactor = 1.0f;
289       return true;
290    }
291
292    ~PictureEdit()
293    {
294       delete bitmap;
295       delete bitmapNotIndexed;
296    }
297 }
298
299 class PictureEditColorTable : Window
300 {
301    hasClose = true;
302    text = $"Color Table";
303    background = formColor;
304    minClientSize = Size { 400, 400 };
305
306    Button button
307    {
308       parent = this, hotKey = escape, size = { 80 }, text = $"Close";
309       anchor = Anchor { right = 10, bottom = 10 };
310       NotifyClicked = ButtonCloseDialog;
311    };
312
313    void OnRedraw(Surface surface)
314    {
315       PictureEdit picture = (PictureEdit)master;
316       Bitmap bitmap = picture.bitmap;
317       int c;
318       for(c = 0; c < 256; c++)
319       {
320          int x = (c % 16) * 16;
321          int y = (c / 16) * 16;
322          surface.SetBackground(bitmap.palette[c]);
323          surface.Area(10 + x, 30 + y, 10 + x + 15, 30 + y + 15);
324       }
325    }
326 }
327
328 class ColorBox : Window
329 {
330    size = { 32, 32 };
331    borderStyle = deepContour;
332 }
333
334 #ifdef _DEBUG
335 class AdjustHSV : Window
336 {
337    size = { 400, 300 };
338
339    background = formColor;
340    ColorHSV target;
341    ColorHSV replace;
342    replace = { 248, 100, 71 }; //Color { 26, 0, 183 };
343    target = { 207, 61, 71 };
344    hasClose = true;
345
346    Button button1
347    {
348       this, text = $"Go", position = { 296, 104 }, isDefault = true;
349
350       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
351       {
352          PictureEdit picture = (PictureEdit)master;
353          Bitmap bitmap = picture.bitmap;
354          double h = 1.0f, s = 0.80f, v = 1.24f;
355          double tolH = 1;
356          double tolS = 1;
357          double tolV = 1;
358
359          h = target.h - replace.h;
360          s = target.s / replace.s;
361          v = target.v / replace.v;
362
363          for(y = 0; y<bitmap.height; y++)
364          {
365             for(x = 0; x<bitmap.width; x++)
366             {
367                ColorAlpha color = ((ColorAlpha *)bitmap.picture)[y * bitmap.stride + x];
368                ColorHSV hsv = color;
369                double diffH = Abs(hsv.h/360 - replace.h/360);
370                double diffS = Abs(hsv.s - replace.s)/100.0;
371                double diffV = Abs(hsv.v - replace.v)/100.0;
372                if(diffH <= tolH && diffS <= tolS && diffV <= tolV)
373                {
374                   hsv.h += h;
375                   hsv.s *= s;
376                   hsv.v *= v;
377                   color.color = hsv;
378                   ((ColorAlpha *)bitmap.picture)[y * bitmap.stride + x] = color;
379                }
380             }
381          }
382          picture.Update(null);
383          picture.modifiedDocument = true;
384          Destroy(0);
385          return true;
386       }
387    };
388
389    EditBox hBox { this, text = "H", size = { 78, 19 }, position = { 176, 80 } };
390    EditBox sBox { this, text = "S", size = { 78, 19 }, position = { 176, 120 } };
391    EditBox vBox { this, text = "V", size = { 78, 19 }, position = { 176, 160 } };
392    Label label1 { this, size = { 7, 13 }, position = { 152, 80 }, labeledWindow = hBox };
393    Label label2 { this, size = { 6, 13 }, position = { 152, 120 }, labeledWindow = sBox };
394    Label label3 { this, size = { 6, 13 }, position = { 152, 168 }, labeledWindow = vBox };
395    ColorBox original { this, position = { 10, 10 }, background = replace };
396    ColorBox result { this, position = { 10, 100 }, background = target };
397 }
398 #endif