1 /**
2 The root widget to inherit from for a flat UI.
3 
4 Copyright: Guillaume Piolat 2015-2018.
5 Copyright: Ethan Reker 2017.
6 License:   $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
7 */
8 module dplug.flatwidgets.flatbackgroundgui;
9 
10 import dplug.math.box;
11 import dplug.core.nogc;
12 import dplug.core.file;
13 import dplug.graphics;
14 
15 import dplug.core.nogc;
16 import dplug.gui.graphics;
17 import dplug.gui.element;
18 public import dplug.gui.sizeconstraints;
19 
20 
21 /// FlatBackgroundGUI provides a background that is loaded from a PNG or JPEG
22 /// image. The string for backgroundPath should be in "stringImportPaths"
23 /// specified in dub.json
24 class FlatBackgroundGUI(string backgroundPath,
25                         string absoluteGfxDirectory = null // for UI development only
26                         ) : GUIGraphics
27 {
28 public:
29 nothrow:
30 @nogc:
31 
32     this(int width, int height)
33     {
34         this(makeSizeConstraintsFixed(width, height));
35     }
36 
37     this(SizeConstraints sizeConstraints)
38     {
39         super(sizeConstraints, flagRaw | flagAnimated);
40         _backgroundImageResized = mallocNew!(OwnedImage!RGBA)();
41         loadBackgroundImageFromStaticData();
42     }
43     
44     ~this()
45     {
46         freeBackgroundImage();
47         _backgroundImageResized.destroyFree();
48     }
49     
50     override void reflow()
51     {
52     }
53     
54     override void onDrawRaw(ImageRef!RGBA rawMap, box2i[] dirtyRects)
55     {
56         // Resize resources to match actual size.
57         {
58             int W = position.width;
59             int H = position.height;
60             if (_forceResizeUpdate || _backgroundImageResized.w != W || _backgroundImageResized.h != H)
61             {
62                 _backgroundImageResized.size(W, H);
63                 ImageResizer resizer;
64                 resizer.resizeImage_sRGBNoAlpha(_backgroundImage.toRef, _backgroundImageResized.toRef);
65                 _forceResizeUpdate = false;
66             }
67         }
68 
69         ImageRef!RGBA backgroundRef = _backgroundImageResized.toRef();
70 
71         foreach(dirtyRect; dirtyRects)
72         {
73             ImageRef!RGBA croppedRawIn = backgroundRef.cropImageRef(dirtyRect);
74             ImageRef!RGBA croppedRawOut = rawMap.cropImageRef(dirtyRect);
75             croppedRawIn.blitTo(croppedRawOut);
76         }
77     }
78 
79     // Development purposes. 
80     // In debug mode, pressing ENTER reload the backgrounds
81     debug
82     {
83         override bool onKeyDown(Key key)
84         {
85             if (super.onKeyDown(key))
86                 return true;
87             
88             if (key == Key.enter)
89             {
90                 reloadImageAtRuntime();
91                 return true;
92             }
93 
94             return false;
95         }
96     }
97     
98 private:
99     OwnedImage!RGBA _backgroundImage, _backgroundImageResized;
100 
101     /// Where pixel data is taken in the image, expressed in _background coordinates.
102     box2i _sourceRect;
103 
104     /// Where it is deposited. Same size than _sourceRect. Expressed in _position coordinates.
105     box2i _destRect;
106 
107     /// Offset from source to dest.
108     vec2i _offset;
109 
110     /// Force resize of source image in order to display changes while editing files.
111     bool _forceResizeUpdate;
112 
113     static immutable string backgroundPathAbs = absoluteGfxDirectory ~ backgroundPath;
114 
115     final void loadBackgroundImageFromStaticData()
116     {
117         auto backgroundData = cast(ubyte[])(import(backgroundPath));
118         loadBackgroundImage(backgroundData);
119     }
120 
121     // Reloads image for UI development.
122     final void reloadImageAtRuntime()
123     {
124         // reading images with an absolute path since we don't know 
125         // which is the current directory from the host
126         ubyte[] backgroundData = readFile(backgroundPathAbs);
127 
128         if (backgroundData)
129         {
130             // Reload images from disk and update the UI
131             freeBackgroundImage();
132             loadBackgroundImage(backgroundData);
133             _forceResizeUpdate = true;
134             setDirtyWhole();
135         }
136         else
137         {
138             // Note: if you fail here, the absolute path you provided in your gui.d was incorrect.
139             // The background files cannot be loaded at runtime, and you have to fix your pathes.
140             assert(false);
141         }
142 
143         freeSlice(backgroundData);
144     }
145 
146     final void loadBackgroundImage(ubyte[] backgroundData)
147     {
148         _backgroundImage = loadOwnedImage(backgroundData);
149     }
150 
151     void freeBackgroundImage()
152     {
153         if (_backgroundImage)
154         {
155             _backgroundImage.destroyFree();
156             _backgroundImage = null;
157         }
158     }
159 }