added mousewheel support for UV Image window
[blender.git] / source / blender / src / drawimage.c
1 /**
2  * $Id$
3  *
4  * ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version. The Blender
10  * Foundation also sells licenses for use in proprietary software under
11  * the Blender License.  See http://www.blender.org/BL/ for information
12  * about this.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22  *
23  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
24  * All rights reserved.
25  *
26  * The Original Code is: all of this file.
27  *
28  * Contributor(s): none yet.
29  *
30  * ***** END GPL/BL DUAL LICENSE BLOCK *****
31  */
32
33 #include <math.h>
34
35 #ifdef HAVE_CONFIG_H
36 #include <config.h>
37 #endif
38
39 #ifdef WIN32
40 #include <io.h>
41 #include "BLI_winstuff.h"
42 #else
43 #include <unistd.h>
44 #endif   
45 #include "MEM_guardedalloc.h"
46
47 #include "BLI_blenlib.h"
48 #include "BLI_editVert.h"
49
50 #include "IMB_imbuf_types.h"
51
52 #include "DNA_image_types.h"
53 #include "DNA_mesh_types.h"
54 #include "DNA_packedFile_types.h"
55 #include "DNA_screen_types.h"
56 #include "DNA_space_types.h"
57 #include "DNA_userdef_types.h"
58
59 #include "BKE_utildefines.h"
60 #include "BKE_global.h"
61 #include "BKE_mesh.h"
62 #include "BKE_image.h"
63
64 #include "BDR_editface.h"
65
66 #include "BIF_gl.h"
67 #include "BIF_space.h"
68 #include "BIF_screen.h"
69 #include "BIF_mywindow.h"
70 #include "BIF_drawimage.h"
71
72 /* Modules used */
73 #include "mydevice.h"
74 #include "render.h"
75
76
77 void rectwrite_part(int winxmin, int winymin, int winxmax, int winymax, int x1, int y1, int xim, int yim, float zoomx, float zoomy, unsigned int *rect)
78 {
79         int cx, cy, oldxim, x2, y2;
80         
81         oldxim= xim;
82                 
83         /* coordinaten hoe 't op scherm komt */
84         x2= x1+ zoomx*xim;
85         y2= y1+ zoomy*yim;
86
87         /* partiele clip */
88         if(x1<winxmin) {
89                 /* recten bij OpenGL mogen niet links/onder van windowrand beginnen */
90                 cx= winxmin-x1+(int)zoomx;
91                 /* zorg ervoor dat de rect pixelnauwkeurig wordt neergezet */
92                 cx/= zoomx;
93                 cx++;
94                 x1+= zoomx*cx;
95                 xim-= cx;
96                 rect+= cx;
97         }
98         if(y1<winymin) {
99                 cy= winymin-y1+(int)zoomy;
100                 cy/= zoomy;
101                 cy++;
102                 y1+= zoomy*cy;
103                 rect+= cy*oldxim;
104                 yim-= cy;
105         }
106         if(x2>=winxmax) {
107                 cx= x2-winxmax;
108                 cx/= zoomx;
109                 xim-= cx+3;
110         }
111         if(y2>=winymax) {
112                 cy= y2-winymax;
113                 cy/= zoomy;
114                 yim-= cy+3;
115         }
116         
117         if(xim<=0) return;
118         if(yim<=0) return;
119
120         mywinset(G.curscreen->mainwin);
121         glScissor(winxmin, winymin, winxmax-winxmin+1, winymax-winymin+1);
122         
123         glPixelStorei(GL_UNPACK_ROW_LENGTH,  oldxim);
124         
125         glPixelZoom(zoomx,  zoomy);
126
127         glRasterPos2i(x1, y1);
128         glDrawPixels(xim, yim, GL_RGBA, GL_UNSIGNED_BYTE,  rect);
129
130         glPixelZoom(1.0,  1.0);
131
132         glPixelStorei(GL_UNPACK_ROW_LENGTH,  0);
133         
134         mywinset(curarea->win);
135 }
136
137 /**
138  * Sets up the fields of the View2D member of the SpaceImage struct
139  * This routine can be called in two modes:
140  * mode == 'f': float mode ???
141  * mode == 'p': pixel mode ???
142  *
143  * @param     sima  the image space to update
144  * @param     mode  the mode to use for the update
145  * @return    void
146  *   
147  */
148 void calc_image_view(SpaceImage *sima, char mode)
149 {
150         float xim=256, yim=256;
151         float x1, y1;
152         float zoom;
153         
154         if(sima->image && sima->image->ibuf) {
155                 xim= sima->image->ibuf->x;
156                 yim= sima->image->ibuf->y;
157         }
158         
159         sima->v2d.tot.xmin= 0;
160         sima->v2d.tot.ymin= 0;
161         sima->v2d.tot.xmax= xim;
162         sima->v2d.tot.ymax= yim;
163         
164         sima->v2d.mask.xmin= sima->v2d.mask.ymin= 0;
165         sima->v2d.mask.xmax= curarea->winx;
166         sima->v2d.mask.ymax= curarea->winy;
167
168
169         /* Which part of the image space do we see? */
170         /* Same calculation as in lrectwrite: area left and down*/
171         x1= curarea->winrct.xmin+(curarea->winx-sima->zoom*xim)/2;
172         y1= curarea->winrct.ymin+(curarea->winy-sima->zoom*yim)/2;
173
174         x1-= sima->zoom*sima->xof;
175         y1-= sima->zoom*sima->yof;
176
177         /* float! */
178         zoom= sima->zoom;
179         
180         /* relatieve afbeeld links */
181         sima->v2d.cur.xmin= ((curarea->winrct.xmin - (float)x1)/zoom);
182         sima->v2d.cur.xmax= sima->v2d.cur.xmin + ((float)curarea->winx/zoom);
183         
184         /* relatieve afbeeld links */
185         sima->v2d.cur.ymin= ((curarea->winrct.ymin-(float)y1)/zoom);
186         sima->v2d.cur.ymax= sima->v2d.cur.ymin + ((float)curarea->winy/zoom);
187         
188         if(mode=='f') {         
189                 sima->v2d.cur.xmin/= xim;
190                 sima->v2d.cur.xmax/= xim;
191                 sima->v2d.cur.ymin/= yim;
192                 sima->v2d.cur.ymax/= yim;
193         }
194 }
195
196 void what_image(SpaceImage *sima)
197 {
198         extern TFace *lasttface;        /* editface.c */
199         Mesh *me;
200                 
201         if(sima->mode==SI_TEXTURE) {
202                 if(G.f & G_FACESELECT) {
203
204                         sima->image= 0;
205                         me= get_mesh((G.scene->basact) ? (G.scene->basact->object) : 0);
206                         set_lasttface();
207                         
208                         if(me && me->tface && lasttface) {
209                                 if(lasttface->mode & TF_TEX) {
210                                         sima->image= lasttface->tpage;
211                                         
212                                         if(sima->flag & SI_EDITTILE);
213                                         else sima->curtile= lasttface->tile;
214                                         
215                                         if(sima->image) {
216                                                 if(lasttface->mode & TF_TILES) sima->image->tpageflag |= IMA_TILES;
217                                                 else sima->image->tpageflag &= ~IMA_TILES;
218                                         }
219                                 }
220                         }
221                 }
222         }
223 }
224
225 void image_changed(SpaceImage *sima, int dotile)
226 {
227         TFace *tface;
228         Mesh *me;
229         int a;
230         
231         if(sima->mode==SI_TEXTURE) {
232                 
233                 if(G.f & G_FACESELECT) {
234                         me= get_mesh((G.scene->basact) ? (G.scene->basact->object) : 0);
235                         if(me && me->tface) {
236                                 tface= me->tface;
237                                 a= me->totface;
238                                 while(a--) {
239                                         if(tface->flag & TF_SELECT) {
240                                                 
241                                                 if(dotile==2) {
242                                                         tface->mode &= ~TF_TILES;
243                                                 }
244                                                 else {
245                                                         tface->tpage= sima->image;
246                                                         tface->mode |= TF_TEX;
247                                                 
248                                                         if(dotile) tface->tile= sima->curtile;
249                                                 }
250                                                 
251                                                 if(sima->image) {
252                                                         if(sima->image->tpageflag & IMA_TILES) tface->mode |= TF_TILES;
253                                                         else tface->mode &= ~TF_TILES;
254                                                 
255                                                         if(sima->image->id.us==0) sima->image->id.us= 1;
256                                                 }
257                                         }
258                                         tface++;
259                                 }
260                                 allqueue(REDRAWVIEW3D, 0);
261                                 allqueue(REDRAWBUTSGAME, 0);
262                         }
263                 }
264         }
265 }
266
267
268 void uvco_to_areaco(float *vec, short *mval)
269 {
270         float x, y;
271
272         mval[0]= 3200;
273         
274         x= (vec[0] - G.v2d->cur.xmin)/(G.v2d->cur.xmax-G.v2d->cur.xmin);
275         y= (vec[1] - G.v2d->cur.ymin)/(G.v2d->cur.ymax-G.v2d->cur.ymin);
276
277         if(x>=0.0 && x<=1.0) {
278                 if(y>=0.0 && y<=1.0) {          
279                         mval[0]= G.v2d->mask.xmin + x*(G.v2d->mask.xmax-G.v2d->mask.xmin);
280                         mval[1]= G.v2d->mask.ymin + y*(G.v2d->mask.ymax-G.v2d->mask.ymin);
281                 }
282         }
283 }
284
285 void uvco_to_areaco_noclip(float *vec, short *mval)
286 {
287         float x, y;
288
289         mval[0]= 3200;
290         
291         x= (vec[0] - G.v2d->cur.xmin)/(G.v2d->cur.xmax-G.v2d->cur.xmin);
292         y= (vec[1] - G.v2d->cur.ymin)/(G.v2d->cur.ymax-G.v2d->cur.ymin);
293
294         x= G.v2d->mask.xmin + x*(G.v2d->mask.xmax-G.v2d->mask.xmin);
295         y= G.v2d->mask.ymin + y*(G.v2d->mask.ymax-G.v2d->mask.ymin);
296         
297         mval[0]= x;
298         mval[1]= y;
299 }
300
301
302 void draw_tfaces(void)
303 {
304         TFace *tface;
305         MFace *mface;
306         Mesh *me;
307         unsigned int col;
308         int a;
309         
310         glPointSize(2.0);
311         
312         if(G.f & G_FACESELECT) {
313                 me= get_mesh((G.scene->basact) ? (G.scene->basact->object) : 0);
314                 if(me && me->tface) {
315                         
316                         calc_image_view(G.sima, 'f');   /* float */
317                         myortho2(G.v2d->cur.xmin, G.v2d->cur.xmax, G.v2d->cur.ymin, G.v2d->cur.ymax);
318
319                         tface= me->tface;
320                         mface= me->mface;
321                         a= me->totface;
322                         
323                         while(a--) {
324                                 if(mface->v3 && (tface->flag & TF_SELECT) ) {
325                                 
326                                         cpack(0x0);
327                                         glBegin(GL_LINE_LOOP);
328                                                 glVertex2fv( tface->uv[0] );
329                                                 glVertex2fv( tface->uv[1] );
330                                                 glVertex2fv( tface->uv[2] );
331                                                 if(mface->v4) glVertex2fv( tface->uv[3] );
332                                         glEnd();
333                                 
334                                         setlinestyle(2);
335                                         /* kleuren: R=x G=y */
336                                         
337                                         if(tface->flag & TF_ACTIVE) cpack(0xFF00); else cpack(0xFFFFFF);
338         
339                                         glBegin(GL_LINE_STRIP);
340                                                 glVertex2fv( tface->uv[0] );
341                                                 glVertex2fv( tface->uv[1] );
342                                         glEnd();
343                                         
344                                         if(tface->flag & TF_ACTIVE) cpack(0xFF); else cpack(0xFFFFFF);
345         
346                                         glBegin(GL_LINE_STRIP);
347                                                 glVertex2fv( tface->uv[0] );
348                                                 if(mface->v4) glVertex2fv( tface->uv[3] ); else glVertex2fv( tface->uv[2] );
349                                         glEnd();
350                                         
351                                         cpack(0xFFFFFF);
352         
353                                         glBegin(GL_LINE_STRIP);
354                                                 glVertex2fv( tface->uv[1] );
355                                                 glVertex2fv( tface->uv[2] );
356                                                 if(mface->v4) glVertex2fv( tface->uv[3] );
357                                         glEnd();
358                                         
359                                         setlinestyle(0);
360                                         
361                                         glBegin(GL_POINTS);
362                                         
363                                         if(tface->flag & TF_SEL1) col= 0x77FFFF; else col= 0xFF70FF;
364                                         cpack(col);
365                                         glVertex2fv(tface->uv[0]);
366                                         
367                                         if(tface->flag & TF_SEL2) col= 0x77FFFF; else col= 0xFF70FF;
368                                         cpack(col);
369                                         glVertex2fv(tface->uv[1]);
370                                         
371                                         if(tface->flag & TF_SEL3) col= 0x77FFFF; else col= 0xFF70FF;
372                                         cpack(col);
373                                         glVertex2fv(tface->uv[2]);
374                                         
375                                         if(mface->v4) {
376                                                 if(tface->flag & TF_SEL4) col= 0x77FFFF; else col= 0xFF70FF;
377                                                 cpack(col);
378                                                 glVertex2fv(tface->uv[3]);
379                                         }
380                                         glEnd();
381                                 }
382                                         
383                                 tface++;
384                                 mface++;
385                         }
386                 }
387         }
388         glPointSize(1.0);
389 }
390
391 static unsigned int *get_part_from_ibuf(ImBuf *ibuf, short startx, short starty, short endx, short endy)
392 {
393         unsigned int *rt, *rp, *rectmain;
394         short y, heigth, len;
395
396         /* de juiste offset in rectot */
397
398         rt= ibuf->rect+ (starty*ibuf->x+ startx);
399
400         len= (endx-startx);
401         heigth= (endy-starty);
402
403         rp=rectmain= MEM_mallocN(heigth*len*sizeof(int), "rect");
404         
405         for(y=0; y<heigth; y++) {
406                 memcpy(rp, rt, len*4);
407                 rt+= ibuf->x;
408                 rp+= len;
409         }
410         return rectmain;
411 }
412
413 void drawimagespace(void)
414 {
415         ImBuf *ibuf= NULL;
416         unsigned int *rect;
417         int x1, y1, xmin, xmax, ymin, ymax;
418         short sx, sy, dx, dy;
419         
420         glClearColor(.1875, .1875, .1875, 0.0); 
421         glClear(GL_COLOR_BUFFER_BIT);
422
423         curarea->win_swap= WIN_BACK_OK;
424         
425         xmin= curarea->winrct.xmin; xmax= curarea->winrct.xmax;
426         ymin= curarea->winrct.ymin; ymax= curarea->winrct.ymax;
427         
428         what_image(G.sima);
429         
430         if(G.sima->image) {
431         
432                 if(G.sima->image->ibuf==0) {
433                         load_image(G.sima->image, IB_rect, G.sce, G.scene->r.cfra);
434                 }       
435                 ibuf= G.sima->image->ibuf;
436         }
437         
438         if(ibuf==0 || ibuf->rect==0) {
439                 calc_image_view(G.sima, 'f');
440                 myortho2(G.v2d->cur.xmin, G.v2d->cur.xmax, G.v2d->cur.ymin, G.v2d->cur.ymax);
441                 cpack(0x404040);
442                 glRectf(0.0, 0.0, 1.0, 1.0);
443                 draw_tfaces();
444                 
445                 return;
446         }
447
448         /* plek berekenen */
449         x1= xmin+(curarea->winx-G.sima->zoom*ibuf->x)/2;
450         y1= ymin+(curarea->winy-G.sima->zoom*ibuf->y)/2;
451
452         x1-= G.sima->zoom*G.sima->xof;
453         y1-= G.sima->zoom*G.sima->yof;
454
455         
456         if(G.sima->flag & SI_EDITTILE) {
457                 rectwrite_part(xmin, ymin, xmax, ymax, x1, y1, ibuf->x, ibuf->y, (float)G.sima->zoom, (float)G.sima->zoom, ibuf->rect);
458                 
459                 dx= ibuf->x/G.sima->image->xrep;
460                 dy= ibuf->y/G.sima->image->yrep;
461                 sy= (G.sima->curtile / G.sima->image->xrep);
462                 sx= G.sima->curtile - sy*G.sima->image->xrep;
463
464                 sx*= dx;
465                 sy*= dy;
466                 
467                 calc_image_view(G.sima, 'p');   /* pixel */
468                 myortho2(G.v2d->cur.xmin, G.v2d->cur.xmax, G.v2d->cur.ymin, G.v2d->cur.ymax);
469                 
470                 cpack(0x0);
471                 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); glRects(sx,  sy,  sx+dx-1,  sy+dy-1); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
472                 cpack(0xFFFFFF);
473                 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); glRects(sx+1,  sy+1,  sx+dx,  sy+dy); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
474         }
475         else if(G.sima->mode==SI_TEXTURE) {
476                 if(G.sima->image->tpageflag & IMA_TILES) {
477                         
478                         
479                         /* eventjes laten staan */
480                         if(G.sima->image->xrep<1) return;
481                         if(G.sima->image->yrep<1) return;
482                         
483                         if(G.sima->curtile >= G.sima->image->xrep*G.sima->image->yrep) 
484                                 G.sima->curtile = G.sima->image->xrep*G.sima->image->yrep - 1; 
485                         
486                         dx= ibuf->x/G.sima->image->xrep;
487                         dy= ibuf->y/G.sima->image->yrep;
488                         
489                         sy= (G.sima->curtile / G.sima->image->xrep);
490                         sx= G.sima->curtile - sy*G.sima->image->xrep;
491         
492                         sx*= dx;
493                         sy*= dy;
494                         
495                         rect= get_part_from_ibuf(ibuf, sx, sy, sx+dx, sy+dy);
496                         
497                         /* rect= ibuf->rect; */
498                         for(sy= 0; sy+dy<=ibuf->y; sy+= dy) {
499                                 for(sx= 0; sx+dx<=ibuf->x; sx+= dx) {
500                                         
501                                         rectwrite_part(xmin, ymin, xmax, ymax, 
502                                                 x1+sx*G.sima->zoom, y1+sy*G.sima->zoom, dx, dy, (float)G.sima->zoom, (float)G.sima->zoom, rect);
503                                 }
504                         }
505                         
506                         MEM_freeN(rect);
507                 }
508                 else 
509                         rectwrite_part(xmin, ymin, xmax, ymax, x1, y1, ibuf->x, ibuf->y, (float)G.sima->zoom,(float)G.sima->zoom, ibuf->rect);
510         
511                 draw_tfaces();
512         }
513
514         calc_image_view(G.sima, 'f');   /* float */
515 }
516
517 void image_viewmove(void)
518 {
519         short mval[2], mvalo[2], xof, yof;
520         
521         getmouseco_sc(mvalo);
522         
523         while(get_mbut()&(L_MOUSE|M_MOUSE)) {
524
525                 getmouseco_sc(mval);
526                 
527                 xof= (mvalo[0]-mval[0])/G.sima->zoom;
528                 yof= (mvalo[1]-mval[1])/G.sima->zoom;
529                 
530                 if(xof || yof) {
531                         
532                         G.sima->xof+= xof;
533                         G.sima->yof+= yof;
534                         
535                         mvalo[0]= mval[0];
536                         mvalo[1]= mval[1];
537                         
538                         scrarea_do_windraw(curarea);
539                         screen_swapbuffers();
540                 }               
541                 else BIF_wait_for_statechange();
542         }
543 }
544
545 void image_viewzoom(unsigned short event)
546 {
547         SpaceImage *sima= curarea->spacedata.first;
548         int width, height;
549
550         if(U.uiflag & WHEELZOOMDIR) {
551                 if (event==WHEELDOWNMOUSE || event == PADPLUSKEY) {
552                         sima->zoom *= 2;
553                 } else {
554                         sima->zoom /= 2;
555                         /* Check if the image will still be visible after zooming out */
556                         if (sima->zoom < 1) {
557                                 calc_image_view(G.sima, 'p');
558                                 if (sima->image) {
559                                         if (sima->image->ibuf) {
560                                                 width = sima->image->ibuf->x * sima->zoom;
561                                                 height = sima->image->ibuf->y * sima->zoom;
562                                                 if ((width < 4) && (height < 4)) {
563                                                         /* Image will become too small, reset value */
564                                                         sima->zoom *= 2;
565                                                 }
566                                         }
567                                 }
568                         }
569                 }
570         } else {
571                 if (event==WHEELUPMOUSE || event == PADPLUSKEY) {
572                         sima->zoom *= 2;
573                 } else {
574                         sima->zoom /= 2;
575                         /* Check if the image will still be visible after zooming out */
576                         if (sima->zoom < 1) {
577                                 calc_image_view(G.sima, 'p');
578                                 if (sima->image) {
579                                         if (sima->image->ibuf) {
580                                                 width = sima->image->ibuf->x * sima->zoom;
581                                                 height = sima->image->ibuf->y * sima->zoom;
582                                                 if ((width < 4) && (height < 4)) {
583                                                         /* Image will become too small, reset value */
584                                                         sima->zoom *= 2;
585                                                 }
586                                         }
587                                 }
588                         }
589                 }
590         }
591 }
592
593 /**
594  * Updates the fields of the View2D member of the SpaceImage struct.
595  * Default behavior is to reset the position of the image and set the zoom to 1
596  * If the image will not fit within the window rectangle, the zoom is adjusted
597  *
598  * @return    void
599  *   
600  */
601 void image_home(void)
602 {
603         int width, height;
604         float zoomX, zoomY;
605
606         if (curarea->spacetype != SPACE_IMAGE) return;
607         if ((G.sima->image == 0) || (G.sima->image->ibuf == 0)) return;
608
609         /* Check if the image will fit in the image with zoom==1 */
610         width = curarea->winx;
611         height = curarea->winy;
612         if (((G.sima->image->ibuf->x >= width) || (G.sima->image->ibuf->y >= height)) && 
613                 ((width > 0) && (height > 0))) {
614                 /* Find the zoom value that will fit the image in the image space */
615                 zoomX = ((float)width) / ((float)G.sima->image->ibuf->x);
616                 zoomY = ((float)height) / ((float)G.sima->image->ibuf->y);
617                 G.sima->zoom= MIN2(zoomX, zoomY);
618
619                 /* Now make it a power of 2 */
620                 G.sima->zoom = 1 / G.sima->zoom;
621                 G.sima->zoom = log(G.sima->zoom) / log(2);
622                 G.sima->zoom = ceil(G.sima->zoom);
623                 G.sima->zoom = pow(2, G.sima->zoom);
624                 G.sima->zoom = 1 / G.sima->zoom;
625         }
626         else {
627                 G.sima->zoom= (float)1;
628         }
629
630         G.sima->xof= G.sima->yof= 0;
631         
632         calc_image_view(G.sima, 'p');
633         
634         scrarea_queue_winredraw(curarea);
635 }
636