switched to GPLv3 ONLY, because i don't trust FSF anymore
[amper.git] / utils / bmpview.d
blob71d7613ca00bac75a2c60ad8ff346e0b5b8ea1a8
1 module bmpx is aliced;
3 import arsd.color;
4 import arsd.simpledisplay;
5 import arsd.image;
7 import iv.cmdcon;
8 import iv.cmdcongl;
10 import iv.vfs;
12 //version = use_framebuffer;
15 // ////////////////////////////////////////////////////////////////////////// //
16 uint c2i (uint v) pure nothrow @safe @nogc { pragma(inline, true); return (v&0xff_00ff00u)|((v>>16)&0x00_0000ffu)|((v<<16)&0x00_ff0000u); }
19 // ////////////////////////////////////////////////////////////////////////// //
20 public class BmpWindow : SimpleWindow {
21 TrueColorImage img;
22 Image backbuf;
23 Image fontbuf;
24 Pixmap pximg;
25 int pximgwdt, pximghgt, pximgscl;
26 int mScale = 5;
27 int xofs = 0, yofs;
28 int rcx0, rcy0, rcx1, rcy1;
29 bool hasrc;
30 bool msdown;
31 bool msdrag;
32 int dragxofs, dragyofs;
33 int dragmx0, dragmy0;
35 // minsize will be taken from aampw
36 // if resizestep is zero, size on that dimension is fixed
37 this (MemoryImage aimg) {
38 if (aimg is null) assert(0, "wtf?!");
39 img = aimg.getAsTrueColorImage;
40 sdpyWindowClass = "BMPVIEW_WINDOW";
41 super(img.width, img.height, "Bitmap Viewer", OpenGlOptions.no, Resizability.allowResizing);
42 setMinSize(img.width, img.height);
43 createPixmap();
44 //backbuf = new Image(img.width*scale, img.height*scale);
45 //glconBackBuffer = this.backbuf;
46 setupHandlers();
47 glconCtlWindow = this;
48 //glconResize(img.width, img.height);
49 glconCtlWindow.addEventListener((GLConScreenRebuildEvent evt) { redraw(true); });
50 glconCtlWindow.addEventListener((GLConScreenRepaintEvent evt) { redraw(false); });
53 final @property int scale () const pure nothrow @safe @nogc { pragma(inline, true); return mScale; }
54 final @property void scale (int v) pure nothrow @safe @nogc { pragma(inline, true); if (v < 1) v = 1; else if (v > 16) v = 16; mScale = v; }
56 final void freePixmap () {
57 if (!closed) {
58 if (pximg) { XFreePixmap(impl.display, pximg); pximg = 0; flushGui(); }
59 delete backbuf;
60 delete fontbuf;
64 override void close () {
65 freePixmap();
66 super.close();
69 final int rcpixel (int x, int y) nothrow @safe @nogc {
70 if (!hasrc) return 0;
71 immutable int x0 = (rcx0 <= rcx1 ? rcx0 : rcx1);
72 immutable int x1 = (rcx0 <= rcx1 ? rcx1 : rcx0);
73 immutable int y0 = (rcy0 <= rcy1 ? rcy0 : rcy1);
74 immutable int y1 = (rcy0 <= rcy1 ? rcy1 : rcy0);
75 if (x < x0 || y < y0 || x > x1 || y > y1) return 0;
76 int pixnum = -1;
77 if (y == y0) pixnum = (x >= x0 && x <= x1 ? x-x0 : -1);
78 else if (x == x1) pixnum = (y > y0 && y <= y1 ? (x1-x0+1)+(y-y0-1) : -1);
79 else if (y == y1) pixnum = (x >= x0 && x < x1 ? (x1-x0+1)+(y1-y0+1)+(x1-x) : -1);
80 else if (x == x0) pixnum = (y > y0 && y < y1 ? (x1-x0+1)*2+(y1-y0+1)+(y1-y-1) : -1);
81 return (pixnum >= 0 ? pixnum%2+1 : 0);
84 final int rcwidth () nothrow @safe @nogc {
85 immutable int x0 = (rcx0 <= rcx1 ? rcx0 : rcx1);
86 immutable int x1 = (rcx0 <= rcx1 ? rcx1 : rcx0);
87 return (x0 <= x1 ? x1-x0+1 : 0);
90 final int rcheight () nothrow @safe @nogc {
91 immutable int y0 = (rcy0 <= rcy1 ? rcy0 : rcy1);
92 immutable int y1 = (rcy0 <= rcy1 ? rcy1 : rcy0);
93 return (y0 <= y1 ? y1-y0+1 : 0);
96 final void drawChar (int x, int y, char ch, Color clr) {
97 immutable int bbwdt = fontbuf.width;
98 immutable int bbhgt = fontbuf.height;
99 if (x < 0 || y < 0 || x > bbwdt-10 || y > bbhgt-10) return;
100 uint v = clr.asUint.c2i;
101 //uint* destp = (cast(uint*)backbuf.getDataPointer)+y*bbwdt+x;
102 uint* destp = (cast(uint*)fontbuf.getDataPointer)+y*bbwdt+x;
103 foreach (immutable dy; 0..10) {
104 ushort vv = glConFont10.ptr[cast(uint)ch*10+dy];
105 auto dp = destp;
106 foreach (immutable dx; 0..10) {
107 if (vv&0x8000) *dp = v;
108 vv <<= 1;
109 ++dp;
111 destp += bbwdt;
115 final void drawStr (int x, int y, const(char)[] s, Color clr) {
116 foreach (char ch; s) {
117 drawChar(x, y, ch, clr);
118 x += 10;
122 final void drawStrOut (int x, int y, const(char)[] s, Color clr, Color outline=Color.black) {
123 foreach (immutable int dy; -1..2) {
124 foreach (immutable int dx; -1..2) {
125 if (dx || dy) drawStr(x+dx, y+dy, s, outline);
128 drawStr(x, y, s, clr);
131 final void createPixmap (bool forceredraw=false) {
132 if (mScale < 1) mScale = 1; else if (mScale > 16) mScale = 16;
133 immutable int scl = mScale;
134 immutable int iwdt = img.width;
135 immutable int ihgt = img.height;
136 //conwriteln("mScale=", mScale, "; scl=", scl, "; pximgscl=", pximgscl);
137 if (pximgwdt != iwdt*scl || pximghgt != ihgt*scl || pximg == 0 || backbuf is null) {
138 if (pximg) { XFreePixmap(impl.display, pximg); pximg = 0; }
139 pximgwdt = iwdt*scl;
140 pximghgt = ihgt*scl;
141 pximgscl = scl;
142 auto obb = backbuf;
143 backbuf = new Image(pximgwdt, pximghgt, true);
144 pximg = XCreatePixmap(display, cast(Drawable)window, pximgwdt, pximghgt, 24);
145 flushGui();
146 delete obb;
147 forceredraw = true;
148 } else if (pximgscl != scl) {
149 pximgscl = scl;
150 forceredraw = true;
152 if (forceredraw) {
153 //conwriteln("mScale=", mScale, "; scl=", scl, "; pximgscl=", pximgscl, "; pw=", pximgwdt, "; ph=", pximghgt, "; w=", iwdt, "; h=", ihgt, "; bw=", backbuf.width, "; bh=", backbuf.height);
154 auto srcp = cast(const(Color)*)img.imageData.colors.ptr;
155 auto destp = cast(uint*)backbuf.getDataPointer;
156 Color clr;
157 foreach (immutable int sy; 0..ihgt) {
158 auto dp = destp;
159 destp += pximgwdt*scl;
160 foreach (immutable int sx; 0..iwdt) {
161 if (auto pn = rcpixel(sx, sy)) {
162 clr = Color(pn == 1 ? 255 : 0, 0, pn != 1 ? 255 : 0);
163 } else {
164 clr = *srcp;
166 immutable uint v = clr.asUint.c2i;
167 foreach (immutable int dy; 0..scl) dp[pximgwdt*dy..pximgwdt*dy+scl] = v;
168 dp += scl;
169 ++srcp;
172 if (backbuf.usingXshm) {
173 XShmPutImage(impl.display, cast(Drawable)pximg, impl.gc, backbuf.handle, 0, 0, 0, 0, pximgwdt, pximghgt, false);
174 } else {
175 XPutImage(impl.display, cast(Drawable)pximg, impl.gc, backbuf.handle, 0, 0, 0, 0, pximgwdt, pximghgt);
177 if (hasrc) {
178 if (fontbuf is null) fontbuf = new Image(32*10+20, 10+4, true);
179 auto fbu = cast(uint*)fontbuf.getDataPointer;
180 fbu[0..fontbuf.width*fontbuf.height] = 0;
181 import core.stdc.stdio : snprintf;
182 char[128] buf = void;
183 auto len = snprintf(buf.ptr, buf.length, "pos: %d,%d size: %d,%d", rcx0, rcy0, rcwidth, rcheight);
184 drawStrOut(2, 2, buf[0..len], Color(0, 255, 0));
186 //flushGui();
190 void redraw (bool forceredraw) {
191 if (closed || backbuf is null) return;
192 createPixmap(forceredraw);
193 XCopyArea(impl.display, cast(Drawable)pximg, cast(Drawable)impl.window, impl.gc, 0, 0, pximgwdt, pximghgt, xofs*mScale, yofs*mScale);
194 Color clr = Color(128, 128, 128);
195 XSetForeground(impl.display, impl.gc, cast(uint)((clr.r<<16)|(clr.g<<8)|clr.b));
196 XSetBackground(impl.display, impl.gc, cast(uint)((clr.r<<16)|(clr.g<<8)|clr.b));
197 int sx = xofs*mScale;
198 int sy = yofs*mScale;
199 int ex = sx+pximgwdt;
200 int ey = sy+pximghgt;
201 if (sx < 0) sx = 0;
202 if (sy < 0) sy = 0;
203 if (ex < 0) ex = 0;
204 if (ey < 0) ey = 0;
205 if (sx > 0) XFillRectangle(impl.display, cast(Drawable)impl.window, impl.gc, 0, 0, sx, height);
206 if (sy > 0) XFillRectangle(impl.display, cast(Drawable)impl.window, impl.gc, sx, 0, width-sx, sy);
207 if (ex < width) XFillRectangle(impl.display, cast(Drawable)impl.window, impl.gc, ex, 0, width-ex, height); else ex = width;
208 if (ey < height) XFillRectangle(impl.display, cast(Drawable)impl.window, impl.gc, 0, ey, ex, height-ey);
209 if (hasrc && fontbuf !is null) {
210 if (fontbuf.usingXshm) {
211 XShmPutImage(impl.display, cast(Drawable)impl.window, impl.gc, fontbuf.handle, 0, 0, 0, 0, fontbuf.width, fontbuf.height, false);
212 } else {
213 XPutImage(impl.display, cast(Drawable)impl.window, impl.gc, fontbuf.handle, 0, 0, 0, 0, fontbuf.width, fontbuf.height);
216 //glconDraw();
219 auto painter = this.draw();
220 painter.drawImage(Point(0, 0), backbuf);
223 flushGui();
226 protected void setupHandlers () {
227 handleExpose = delegate (int x, int y, int wdt, int hgt, int eventsLeft) {
228 if (eventsLeft == 0) redraw(false);
229 return true;
231 visibilityChanged = delegate (bool visible) {
232 if (visible) createPixmap(); else freePixmap();
234 handleKeyEvent = delegate (KeyEvent event) {
235 scope(exit) if (!conQueueEmpty()) glconPostDoConCommands();
236 //if (glconKeyEvent(event)) { glconPostScreenRepaint(); return; }
237 if (isQuitRequested) { close(); return; }
238 if ((event.modifierState&ModifierState.numLock) == 0) {
239 switch (event.key) {
240 case Key.Pad0: event.key = Key.Insert; break;
241 case Key.Pad1: event.key = Key.End; break;
242 case Key.Pad2: event.key = Key.Down; break;
243 case Key.Pad3: event.key = Key.PageDown; break;
244 case Key.Pad4: event.key = Key.Left; break;
245 //case Key.Pad5: event.key = Key.Insert; break;
246 case Key.Pad6: event.key = Key.Right; break;
247 case Key.Pad7: event.key = Key.Home; break;
248 case Key.Pad8: event.key = Key.Up; break;
249 case Key.Pad9: event.key = Key.PageUp; break;
250 case Key.PadEnter: event.key = Key.Enter; break;
251 case Key.PadDot: event.key = Key.Delete; break;
252 default: break;
254 } else {
255 if (event.key == Key.PadEnter) event.key = Key.Enter;
257 if (event.pressed) {
258 //conwriteln(event.toStr);
259 if (event == "C-Q") { close(); return; }
260 if (event == "Escape") { msdown = false; hasrc = false; glconPostScreenRepaint(); return; }
261 if (event == "Plus") { scale = scale+1; glconPostScreenRepaint(); return; }
262 if (event == "Minus") { scale = scale-1; glconPostScreenRepaint(); return; }
263 if (event == "Left") { xofs += 1; glconPostScreenRepaint(); return; }
264 if (event == "Right") { xofs -= 1; glconPostScreenRepaint(); return; }
265 if (event == "Up") { yofs += 1; glconPostScreenRepaint(); return; }
266 if (event == "Down") { yofs -= 1; glconPostScreenRepaint(); return; }
267 if (event == "Home") { xofs = yofs = 0; glconPostScreenRepaint(); return; }
271 handleMouseEvent = delegate (MouseEvent event) {
272 scope(exit) if (!conQueueEmpty()) glconPostDoConCommands();
273 if (isQuitRequested) { close(); return; }
274 immutable int scl = mScale;
275 immutable int mx = event.x/scl-xofs;
276 immutable int my = event.y/scl-yofs;
277 if (event.type == MouseEventType.buttonPressed) {
278 if (event.modifierState&ModifierState.ctrl) {
279 if (event.button == MouseButton.wheelUp) { scale = scale+1; glconPostScreenRepaint(); return; }
280 if (event.button == MouseButton.wheelDown) { scale = scale-1; glconPostScreenRepaint(); return; }
282 if (event.button == MouseButton.left) {
283 if (event.modifierState&(ModifierState.ctrl|ModifierState.alt)) {
284 msdrag = true;
285 msdown = false;
286 dragxofs = xofs;
287 dragyofs = yofs;
288 dragmx0 = event.x;
289 dragmy0 = event.y;
290 glconPostScreenRepaint();
291 } else {
292 msdrag = false;
293 msdown = true;
294 hasrc = true;
295 //conwriteln("pos: ", mx, ", ", my);
296 rcx0 = rcx1 = mx;
297 rcy0 = rcy1 = my;
298 glconPostScreenRepaint();
301 if (event.button == MouseButton.right) conwriteln(mx, ", ", my);
302 return;
304 if (event.type == MouseEventType.buttonReleased) {
305 if (event.button == MouseButton.left) { msdown = msdrag = false; glconPostScreenRepaint(); }
306 return;
308 if (event.type == MouseEventType.motion && msdown) {
309 immutable int ow = rcwidth, oh = rcheight;
310 rcx1 = mx;
311 rcy1 = my;
312 if (ow != rcwidth || oh != rcheight) glconPostScreenRepaint();
313 return;
315 if (event.type == MouseEventType.motion && msdrag) {
316 immutable int dx = (event.x-dragmx0)/scl;
317 immutable int dy = (event.y-dragmy0)/scl;
318 immutable int nxofs = dragxofs+dx;
319 immutable int nyofs = dragyofs+dy;
320 if (nxofs != xofs || nyofs != yofs) {
321 xofs = nxofs;
322 yofs = nyofs;
323 glconPostScreenRepaint();
325 return;
329 handleCharEvent = delegate (dchar ch) {
330 scope(exit) if (!conQueueEmpty()) glconPostDoConCommands();
331 //if (glconCharEvent(ch)) { glconPostScreenRepaint(); return; }
332 if (isQuitRequested) { close(); return; }
335 windowResized = delegate (int wdt, int hgt) {
336 scope(exit) if (!conQueueEmpty()) glconPostDoConCommands();
337 if (isQuitRequested) { close(); return; }
338 if (wdt < 1) wdt = 1;
339 if (hgt < 1) hgt = 1;
340 //glconResize(wdt, hgt);
341 if (backbuf.width != wdt || backbuf.height != hgt) {
342 //createPixmap();
343 //backbuf = new Image(wdt, hgt);
344 //glconBackBuffer = this.backbuf;
346 redraw(false);
349 onFocusChange = delegate (bool focused) { msdown = msdrag = false; };
354 void main (string[] args) {
355 glconAllowOpenGLRender = false;
356 if (args.length == 1) args ~= "MAIN.bmp";
358 conProcessArgs!true(args);
359 conProcessQueue(int.max/4);
361 auto win = new BmpWindow(loadImageFromFile(VFile(args[1])));
363 flushGui();
364 win.eventLoop(0);
365 flushGui();
366 conProcessQueue(int.max/4);