Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
home:majun
arachne
arachne.c
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File arachne.c of Package arachne
/* arachne.c Author: Hayden Walles Date: 9 September 2007 A GUI based seam carving program. */ /* Copyright (C) 2007, 2008 Hayden Walles This file is part of Arachne. Arachne is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Arachne is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "arachne.h" /*I'm afraid this really is a shambles. But it is a working shambles.*/ LOOM *FirstLoom; int toolColour(int tool){ if(tool==LOOM_TOOL_PROTECT) return 255; else return 0xff0000; } int saveLoom(LOOM *loom,char *filename){ int i,j,oj; int result; SEAM_MAP *map=loom->map; IMAGE *output; int seamcount; int error; /*Copy the image without the first seamcount minimal seams.*/ if(loom->mode==LOOM_MODE_HORZ){ output=newImage(loom->span,imageHeight(loom->image),&error); if(output==NULL){ guiReportError(loom,"Out of memory saving image."); return 0; } if(loom->span<=imageWidth(loom->image)){ /*Compressing*/ seamcount=imageWidth(loom->image)-loom->span; for(i=0;i<imageHeight(loom->image);i++){ oj=0; for(j=0;j<imageWidth(loom->image);j++){ if(getMap(map,i,j)>seamcount){ setPixel(output,i,oj,getRed(loom->image,i,j),getGreen(loom->image,i,j),getBlue(loom->image,i,j)); oj++; } } } } else { /*Stretching*/ seamcount=loom->span-imageWidth(loom->image); for(i=0;i<imageHeight(loom->image);i++){ oj=0; for(j=0;j<imageWidth(loom->image)-1;j++){ setPixel(output,i,oj,getRed(loom->image,i,j),getGreen(loom->image,i,j),getBlue(loom->image,i,j)); oj++; if(getMap(map,i,j)<=seamcount){ setPixel(output,i,oj,(getRed(loom->image,i,j)+getRed(loom->image,i,j+1))/2,(getGreen(loom->image,i,j)+getGreen(loom->image,i,j+1))/2,(getBlue(loom->image,i,j)+getBlue(loom->image,i,j+1))/2); oj++; } } /*And check the final pixel, too.*/ setPixel(output,i,oj,getRed(loom->image,i,j),getGreen(loom->image,i,j),getBlue(loom->image,i,j)); oj++; if(getMap(map,i,j)<=seamcount){ setPixel(output,i,oj,getRed(loom->image,i,j),getGreen(loom->image,i,j),getBlue(loom->image,i,j)); } } } } else { output=newImage(imageWidth(loom->image),loom->span,&error); if(output==NULL){ guiReportError(loom,"Out of memory saving image."); return 0; } if(loom->span<=imageHeight(loom->image)){ /*Compressing*/ seamcount=imageHeight(loom->image)-loom->span; for(i=0;i<imageWidth(loom->image);i++){ oj=0; for(j=0;j<imageHeight(loom->image);j++){ if(getMap(map,j,i)>seamcount){ setPixel(output,oj,i,getRed(loom->image,j,i),getGreen(loom->image,j,i),getBlue(loom->image,j,i)); oj++; } } } } else { /*Stretching*/ seamcount=loom->span-imageHeight(loom->image); for(i=0;i<imageWidth(loom->image);i++){ oj=0; for(j=0;j<imageHeight(loom->image)-1;j++){ setPixel(output,oj,i,getRed(loom->image,j,i),getGreen(loom->image,j,i),getBlue(loom->image,j,i)); oj++; if(getMap(map,j,i)<=seamcount){ setPixel(output,oj,i,(getRed(loom->image,j,i)+getRed(loom->image,j+1,i))/2, (getGreen(loom->image,j,i)+getGreen(loom->image,j+1,i))/2, (getBlue(loom->image,j,i)+getBlue(loom->image,j+1,i))/2); oj++; } } /*And the last pixel of the column.*/ setPixel(output,oj,i,getRed(loom->image,j,i),getGreen(loom->image,j,i),getBlue(loom->image,j,i)); oj++; if(getMap(map,j,i)<=seamcount){ setPixel(output,oj,i,getRed(loom->image,j,i),getGreen(loom->image,j,i),getBlue(loom->image,j,i)); } } } } if(!saveImage(filename,output,&error)){ guiReportError(loom,"Could not save image."); deleteImage(output); return 0; } deleteImage(output); return 1; } void mapUpdate(int done,int of,void *user){ LOOM *sm=user; progress_set_position(sm,done,of); guiClearWaiting(); } void ergUpdate(int done,int of,void *user){ LOOM *sm=user; progress_set_position(sm,done,of); guiClearWaiting(); } void setModeWidgets(LOOM *sm,aboolean sensitive){ toolbar_set_button_enabled(sm->horzmodebtn,sensitive); toolbar_set_button_enabled(sm->vertmodebtn,sensitive); toolbar_set_button_enabled(sm->origmodebtn,sensitive); menu_set_item_enabled(sm->horzmodemenu,sensitive); menu_set_item_enabled(sm->vertmodemenu,sensitive); menu_set_item_enabled(sm->origmodemenu,sensitive); } void setToolWidgets(LOOM *sm,aboolean sensitive){ toolbar_set_button_enabled(sm->protectbtn,sensitive); toolbar_set_button_enabled(sm->exposebtn,sensitive); toolbar_set_button_enabled(sm->clearbtn,sensitive); menu_set_item_enabled(sm->protectmenu,sensitive); menu_set_item_enabled(sm->exposemenu,sensitive); menu_set_item_enabled(sm->clearmenu,sensitive); if(!sensitive){ toolbar_deactivate_toggle(sm->protectbtn); toolbar_deactivate_toggle(sm->exposebtn); toolbar_deactivate_toggle(sm->clearbtn); menu_uncheck_item(sm->protectmenu); menu_uncheck_item(sm->exposemenu); menu_uncheck_item(sm->clearmenu); } } void changeModeH(LOOM *sm){ int cwidth; if((sm->mode==LOOM_MODE_HORZ)||(sm->mode==LOOM_MODE_NONE)) return; sm->mode=LOOM_MODE_HORZ; toolbar_activate_toggle(sm->horzmodebtn); menu_check_item(sm->horzmodemenu); setToolWidgets(sm,FALSE); if((sm->map==NULL)||(sm->mapdirection==1)){ /*We need to recompute a new map.*/ if(sm->map!=NULL) mapDelete(sm->map); sm->map=NULL; computeMap(sm); guiGetWorkspace(sm,&cwidth,NULL); cwidth=MIN(cwidth,2*imageWidth(sm->image)); sm->span=MAX(imageWidth(sm->image),cwidth); preView(sm); } else { guiGetWorkspace(sm,&cwidth,NULL); cwidth=MIN(cwidth,2*imageWidth(sm->image)); sm->span=MAX(imageWidth(sm->image),cwidth); preView(sm); guiSetWindowMinimum(sm,1,imageHeight(sm->image)); } guiSetStatus(sm,"%d x %d",sm->span,imageHeight(sm->image)); } void changeModeV(LOOM *sm){ int cheight; if((sm->mode==LOOM_MODE_VERT)||(sm->mode==LOOM_MODE_NONE)) return; sm->mode=LOOM_MODE_VERT; toolbar_activate_toggle(sm->vertmodebtn); menu_check_item(sm->vertmodemenu); setToolWidgets(sm,FALSE); if((sm->map==NULL)||(sm->mapdirection==0)){ /*We need to recompute a new map.*/ if(sm->map!=NULL) mapDelete(sm->map); sm->map=NULL; // sm->span=imageHeight(sm->image); // preView(sm); // guiSetWindowMinimum(sm,imageWidth(sm->image),imageHeight(sm->image)); computeMap(sm); guiGetWorkspace(sm,NULL,&cheight); cheight=MIN(cheight,2*imageHeight(sm->image)); sm->span=MAX(imageHeight(sm->image),cheight); preView(sm); } else { guiGetWorkspace(sm,NULL,&cheight); cheight=MIN(cheight,2*imageHeight(sm->image)); sm->span=MAX(imageHeight(sm->image),cheight); preView(sm); guiSetWindowMinimum(sm,imageWidth(sm->image),1); } guiSetStatus(sm,"%d x %d",imageWidth(sm->image),sm->span); } void changeModeO(LOOM *sm){ char scratchname[32]; if((sm->mode==LOOM_MODE_ORIG)||(sm->mode==LOOM_MODE_NONE)) return; toolbar_activate_toggle(sm->origmodebtn); menu_check_item(sm->origmodemenu); setToolWidgets(sm,TRUE); setModeWidgets(sm,TRUE); guiSetStatus(sm,"%d x %d",imageWidth(sm->image),imageHeight(sm->image)); sm->mode=LOOM_MODE_ORIG; sm->tool=0; preView(sm); guiSetWindowMinimum(sm,imageWidth(sm->image),imageHeight(sm->image)); } void pickupProtectTool(LOOM *sm){ if((sm->mode==LOOM_MODE_ORIG)){ sm->tool=LOOM_TOOL_PROTECT; toolbar_activate_toggle(sm->protectbtn); menu_check_item(sm->protectmenu); } } void pickupHideTool(LOOM *sm){ if((sm->mode==LOOM_MODE_ORIG)){ sm->tool=LOOM_TOOL_HIDE; toolbar_activate_toggle(sm->exposebtn); menu_check_item(sm->exposemenu); } } void pickupClearTool(LOOM *sm){ if((sm->mode==LOOM_MODE_ORIG)){ sm->tool=LOOM_TOOL_CLEAR; toolbar_activate_toggle(sm->clearbtn); menu_check_item(sm->clearmenu); } } void switchDynamicEnergy(LOOM *sm){ /*If, in future, I add more options, then I can defer the map recomputation until the user says go. Since there is only one at the moment, we might as well just do it now.*/ if(sm->map!=NULL) mapDelete(sm->map); sm->map=NULL; if((sm->mode==LOOM_MODE_ORIG)||(sm->mode==LOOM_MODE_NONE)) return; /*If we are in one of the other modes, we should recompute the map.*/ computeMap(sm); /*And repreview*/ preView(sm); } int computeMap(LOOM *new){ SEAM_UNPICKER *pic; SEAM_MAP *newmap; int error; int i; // if(new->map!=NULL) // return 1; new->working=1; guiBeginComputation(new); pic=seamstressNewUnpicker(imageWidth(new->image),imageHeight(new->image),new->mode==LOOM_MODE_HORZ ? 0:1,/*Energy func*/0,new->flags&LOOM_DYNAMIC_ENERGY,&error); new->mapdirection=new->mode==LOOM_MODE_HORZ ? 0:1; if(pic==NULL){ guiEndComputation(new); guiReportError(NULL,"Unable to initialise seam computation."); new->working=0; return 0; } for(i=0;i<imageHeight(new->image);i++) unpickerSetRowRGB32(pic,i,new->image->pixels+(i*imageWidth(new->image)*4)); progress_set_text(new,"Computing energy"); progress_set_position(new,0,1); if(!unpickerComputeEnergy(pic,new->marks,&ergUpdate,new,&error)){ guiEndComputation(new); guiReportError(new,"Error computing energy for %s.",new->filename); new->working=0; return 0; } /*This would be best computed in another thread. Add this as an alternative later.*/ progress_set_text(new,"Computing minimal seams"); progress_set_position(new,0,1); if((new->map=unpickerMap(pic,&mapUpdate,new,&error))==NULL){ guiEndComputation(new); guiReportError(new,"Error precomuting seams for %s.",new->filename); new->working=0; return 0; } progress_set_position(new,0,1); progress_set_text(new,""); /*Now that's done we can allow the user to resize the image.*/ unpickerDelete(pic); new->working=0; if(new->mapdirection==0){ guiSetWindowMinimum(new,1,imageHeight(new->image)); } else { guiSetWindowMinimum(new,imageWidth(new->image),1); } guiEndComputation(new); return 1; } /*NB: Doesn't do anything very sensible if the window is closed during the map computation.*/ int replaceLoomFromFile(LOOM *new,char *filename){ int error; IMAGE *newimg; char *fn; newimg=loadImage(filename,&error); if(newimg==NULL){ char *msg; if(msg=lastImageErrorString()){ guiReportError(NULL,"Unable to load the image %s. %s",filename,msg); } else { guiReportError(NULL,"Unable to load the image %s.",filename); } return 0; } fn=strdup(filename); if(fn==NULL){ deleteImage(newimg); guiReportError(NULL,"Out of memory loading %s.",filename); return 0; } if(new->image!=NULL) deleteImage(new->image); new->image=newimg; if(new->filename!=NULL) free(new->filename); new->filename=fn; if(new->marks!=NULL) marksDelete(new->marks); new->marks=NULL; if(new->map!=NULL) mapDelete(new->map); new->map=NULL; new->span=imageWidth(new->image); new->toolwidth=16; if(!guiReloadLoom(new)){ //do what? } if(new->mode==LOOM_MODE_NONE){ /*Enter original mode.*/ setToolWidgets(new,TRUE); setModeWidgets(new,TRUE); new->mode=LOOM_MODE_ORIG; new->tool=0; toolbar_activate_toggle(new->origmodebtn); menu_check_item(new->origmodemenu); } if(new->mode==LOOM_MODE_ORIG){ preView(new); guiSetWindowMinimum(new,imageWidth(new->image),imageHeight(new->image)); guiSetStatus(new,"%d x %d",imageWidth(new->image),imageHeight(new->image)); } else { changeModeO(new); } return 1; } /*Loads a file, creates a new basic loom from it, creates and shows an associated window. It also links it into the global list.*/ LOOM *addLoomFromFile(char *filename){ SEAM_UNPICKER *pic; LOOM *new; IMAGE *image; int error; int i; new=malloc(sizeof(LOOM)); if(new==NULL){ guiReportError(NULL,"Out of memory loading image %s.",filename); return NULL; } if(filename!=NULL){ new->filename=strdup(filename); if(new->filename==NULL){ guiReportError(NULL,"Out of memory loading image %s.",filename); free(new); return NULL; } image=loadImage(filename,&error); if(image==NULL){ guiReportError(NULL,"Unable to load the image %s.",filename); return NULL; } new->image=image; new->mode=LOOM_MODE_ORIG; new->tool=0; } else { new->filename=NULL; new->image=NULL; new->mode=LOOM_MODE_NONE; } new->marks=NULL; new->map=NULL; new->span=0; new->flags=0; //dynamic energy would be set in here. new->map=NULL; //we'll compute this in a moment. new->toolwidth=16; new->abort=new->working=0; /*Create the GUI stuff first.*/ if(!guiInitialiseLoom(new)){ guiReportError(NULL,"Unable to load the image %s.",filename); return NULL; } if(filename!=NULL){ preView(new); //give the user something to look at while the seams are mapped. } if(new->mode==LOOM_MODE_ORIG){ /*Enter original mode.*/ setToolWidgets(new,TRUE); setModeWidgets(new,TRUE); toolbar_activate_toggle(new->origmodebtn); menu_check_item(new->origmodemenu); guiSetStatus(new,"%d x %d",imageWidth(new->image),imageHeight(new->image)); } else if(new->mode==LOOM_MODE_NONE){ setToolWidgets(new,FALSE); setModeWidgets(new,FALSE); guiSetStatus(new,"No image"); } new->next=FirstLoom; FirstLoom=new; // computeMap(new); return new; } void annotatePreView(LOOM *loom, int x, int y,int type){ int l,r,t,b; int i,j; l=x-loom->toolwidth/2; r=l+loom->toolwidth; l=MAX(0,l); r=MIN(imageWidth(loom->image),r); t=y-loom->toolwidth/2; b=t+loom->toolwidth; t=MAX(0,t); b=MIN(b,imageHeight(loom->image)); if(type==LOOM_TOOL_CLEAR){ // PIXEL pixels[r-l]; for(i=t;i<b;i++){ for(j=l;j<r;j++){ guiSetViewPixel(loom,i,j, getRed(loom->image,i,j), getGreen(loom->image,i,j), getBlue(loom->image,i,j)); // sm->private.view[(i*imageWidth(sm->image)+j)*3]=getRed(sm->image,i,j); // sm->private.view[(i*imageWidth(sm->image)+j)*3+1]=getGreen(sm->image,i,j); // sm->private.view[(i*imageWidth(sm->image)+j)*3+2]=getBlue(sm->image,i,j); } // guiSetViewPixels(loom,i,l,r-l,pixels); } } else { // PIXEL pixels[r-l]; int toolcolour, tr,tg,tb; toolcolour=toolColour(loom->tool); tr=toolcolour&0xff; tg=(toolcolour>>8)&0xff; tb=(toolcolour>>16)&0xff; for(i=t;i<b;i++){ for(j=l;j<r;j++){ guiSetViewPixel(loom,i,j, getRed(loom->image,i,j)/2+tr/2, getGreen(loom->image,i,j)/2+tg/2, getBlue(loom->image,i,j)/2+tb/2); // sm->private.view[(i*imageWidth(sm->image)+j)*3]=getRed(sm->image,i,j)/2+tr/2; // sm->private.view[(i*imageWidth(sm->image)+j)*3+1]=getGreen(sm->image,i,j)/2+tg/2; // sm->private.view[(i*imageWidth(sm->image)+j)*3+2]=getBlue(smn->image,i,j)/2+tb/2; } // guiSetViewPixels(loom,i,l,r-l,pixels); } } guiUpdateView(loom); } int preView(LOOM *loom){ int i,j,oj; int result; SEAM_MAP *map=loom->map; int seamcount; int tr[2],tg[2],tb[2]; int tool; if((loom->mode==LOOM_MODE_ORIG)||(loom->map==NULL)){ /*Just copy the bits directly over, modulated by any user annotations..*/ if(!guiPrepareView(loom,imageWidth(loom->image),imageHeight(loom->image))) return 0; tr[0]=toolColour(LOOM_TOOL_PROTECT)&0xff; tg[0]=(toolColour(LOOM_TOOL_PROTECT)>>8)&0xff; tb[0]=(toolColour(LOOM_TOOL_PROTECT)>>16)&0xff; tr[1]=toolColour(LOOM_TOOL_HIDE)&0xff; tg[1]=(toolColour(LOOM_TOOL_HIDE)>>8)&0xff; tb[1]=(toolColour(LOOM_TOOL_HIDE)>>16)&0xff; for(i=0;i<imageHeight(loom->image);i++){ for(j=0;j<imageWidth(loom->image);j++){ if((loom->marks!=NULL)&&(getByte(loom->marks,i,j)>0)){ tool = getByte(loom->marks,i,j)-1; guiSetViewPixel(loom,i,j, getRed(loom->image,i,j)/2+tr[tool]/2, getGreen(loom->image,i,j)/2+tg[tool]/2, getBlue(loom->image,i,j)/2+tb[tool]/2); } else { guiSetViewPixel(loom,i,j, getRed(loom->image,i,j), getGreen(loom->image,i,j), getBlue(loom->image,i,j)); } } } } else { //surely this is overkill - the next two conditions could be dragged up to this level. /*Copy the image without the first seamcount minimal seams.*/ if(loom->mode==LOOM_MODE_HORZ){ if(!guiPrepareView(loom,loom->span,imageHeight(loom->image))) return 0; if(loom->span<=imageWidth(loom->image)){ /*Compressing*/ seamcount=imageWidth(loom->image)-loom->span; for(i=0;i<imageHeight(loom->image);i++){ oj=0; for(j=0;j<imageWidth(loom->image);j++){ if(getMap(map,i,j)>seamcount){ guiSetViewPixel(loom,i,oj, getRed(loom->image,i,j), getGreen(loom->image,i,j), getBlue(loom->image,i,j)); oj++; } } } } else { /*Stretching*/ /*NB. I don't believe this to be the same as described in $4.3 of the Avidan and Shamir paper. They seem to advocate duplicating pixel s in the sequence [p][s][q] as [p][avg(p,s)][avg(s,q)][q]. I produce [p][s][avg(s,q)][q] instead. Perhaps I want to alter this. I should compare the results. This _is_, I believe the technique they advocate in $5, because it's easy to add to a seam map (the first technique requires modifications to existing pixels). Although I'm not using a seam map to do the stretching here, I will very likely add it to Seamstress at some point, when it would be nice to have consistency between the version here and the new version implemented in the library. I can in fact implement either here, however.*/ seamcount=loom->span-imageWidth(loom->image); for(i=0;i<imageHeight(loom->image);i++){ oj=0; for(j=0;j<imageWidth(loom->image)-1;j++){ guiSetViewPixel(loom,i,oj, getRed(loom->image,i,j), getGreen(loom->image,i,j), getBlue(loom->image,i,j)); oj++; if(getMap(map,i,j)<=seamcount){ guiSetViewPixel(loom,i,oj, (getRed(loom->image,i,j)+getRed(loom->image,i,j+1))/2, (getGreen(loom->image,i,j)+getGreen(loom->image,i,j+1))/2, (getBlue(loom->image,i,j)+getBlue(loom->image,i,j+1))/2); oj++; } } /*And check the final pixel, too.*/ guiSetViewPixel(loom,i,oj, getRed(loom->image,i,j), getGreen(loom->image,i,j), getBlue(loom->image,i,j)); oj++; if(getMap(map,i,j)<=seamcount){ guiSetViewPixel(loom,i,oj, getRed(loom->image,i,j), getGreen(loom->image,i,j), getBlue(loom->image,i,j)); } } } } else { if(!guiPrepareView(loom,imageWidth(loom->image),loom->span)) return 0; if(imageHeight(loom->image)>=loom->span){ seamcount=imageHeight(loom->image)-loom->span; for(i=0;i<imageWidth(loom->image);i++){ oj=0; for(j=0;j<imageHeight(loom->image);j++){ if(getMap(map,j,i)>seamcount){ guiSetViewPixel(loom,oj,i, getRed(loom->image,j,i), getGreen(loom->image,j,i), getBlue(loom->image,j,i)); oj++; } } } } else { /*Stretching*/ seamcount=loom->span-imageHeight(loom->image); for(i=0;i<imageWidth(loom->image);i++){ oj=0; for(j=0;j<imageHeight(loom->image)-1;j++){ guiSetViewPixel(loom,oj,i, getRed(loom->image,j,i), getGreen(loom->image,j,i), getBlue(loom->image,j,i)); oj++; if(getMap(map,j,i)<=seamcount){ guiSetViewPixel(loom,oj,i, (getRed(loom->image,j,i)+getRed(loom->image,j+1,i))/2, (getGreen(loom->image,j,i)+getGreen(loom->image,j+1,i))/2, (getBlue(loom->image,j,i)+getBlue(loom->image,j+1,i))/2); oj++; } } /*And the last pixel of the column.*/ guiSetViewPixel(loom,oj,i, getRed(loom->image,j,i), getGreen(loom->image,j,i), getBlue(loom->image,j,i)); oj++; if(getMap(map,j,i)<=seamcount){ guiSetViewPixel(loom,oj,i, getRed(loom->image,j,i), getGreen(loom->image,j,i), getBlue(loom->image,j,i)); } } } } } guiUpdateView(loom); return 1; } /*The actual entry point is defined in whichever gui module is being used. But this is guaranteed to be called sometime during the startup of the program. All the rest of our interaction is event-based.*/ int arachneStart(int argc, char **argv){ char *inname=NULL; if(argc>1){ inname=argv[1]; if(!addLoomFromFile(argv[1])){ if(!addLoomFromFile(NULL)) exit(1); } } else { if(!addLoomFromFile(NULL)) exit(1); } return 1; } void arachneResizeView(LOOM *loom,int width, int height){ if((loom->mode==LOOM_MODE_NONE)||loom->working){ return; } else if((loom->mode==LOOM_MODE_ORIG)||(loom->map==NULL)){ } else if(loom->mapdirection==0){ if(width!=loom->span){ loom->span=width; if(loom->span>2*imageWidth(loom->image)) loom->span=2*imageWidth(loom->image); guiSetStatus(loom,"%d x %d",loom->span,imageHeight(loom->image)); } } else { if(height!=loom->span){ loom->span=height; if(loom->span>2*imageHeight(loom->image)) loom->span=2*imageHeight(loom->image); guiSetStatus(loom,"%d x %d",imageWidth(loom->image),loom->span); } } preView(loom); } void arachneAnnotate(LOOM *sm, int x, int y){ int mark; if(sm->map!=NULL){ /*This is to ensure recomputation.*/ mapDelete(sm->map); sm->map=NULL; } /*Update the preview to show the effect of drawing.*/ if(sm->tool==LOOM_TOOL_PROTECT) mark=1; else if(sm->tool==LOOM_TOOL_HIDE) mark=2; else mark=0; if(sm->marks==NULL) sm->marks=seamstressNewMarks(imageWidth(sm->image),imageHeight(sm->image),NULL); if(sm->marks!=NULL){ if(marksAnnotate(sm->marks,x-sm->toolwidth/2,y-sm->toolwidth/2,x-sm->toolwidth/2+sm->toolwidth-1,y-sm->toolwidth/2+sm->toolwidth-1,mark)) annotatePreView(sm,x,y,sm->tool); } }
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor