/* * Image Editor * ------------ * This program allows the direct editing of * GIF and IMG format images. The IMG format is * the built-in format of the GraphApp package. */ #include #include typedef struct ImageEditorClass * ImageEditor; struct ImageEditorClass { window win; /* image editor main window */ rgb bgcolour; /* background colour for the window */ menubar mbar; /* menubar */ menu file_menu; menuitem new_item, open_item, save_item; menuitem line_1; menuitem quit_item; menu edit_menu; menuitem undo_item; menuitem line_2; menuitem sort_palette; menu tool_menu; menuitem pencil, sampler; control pixedit; /* the pixel editor */ scrollbar vert, horiz; control paledit; /* the palette editor */ control display; /* real-size display */ field pixval; /* colour editor */ checkbox transparent; field red, green, blue; button add, change; char *filename; /* current file */ image prev_img; /* the image before the current modification */ image img; /* the image being edited */ int size; /* size of one pixel when expanded */ int x, y; /* current x and y of top-left displayed */ int xsize, ysize; /* page sizes */ int changed; /* has this image been changed? */ int new_width; /* pixel width of new images */ int new_height; /* pixel height of new images */ int new_depth; /* pixel depth of new images */ window new_image_window; /* for setting a new image's size */ field new_width_input; /* new image's width input field */ field new_height_input; /* new image's height input field */ }; /* * Functions: */ ImageEditor find_image_editor(control c) { window win = parentwindow(c); ImageEditor editor = (ImageEditor) getdata(win); return editor; } void update_colour_editor(ImageEditor editor, int entry) { rgb colour; rgb *palette; if (entry >= getpalettesize(editor->img)) return; palette = getpalette(editor->img); colour = palette[entry]; settext(editor->pixval, int_to_string(entry)); if (getalpha(colour) > 0x7F) check(editor->transparent); else uncheck(editor->transparent); settext(editor->red, int_to_string(getred(colour))); settext(editor->green, int_to_string(getgreen(colour))); settext(editor->blue, int_to_string(getblue(colour))); draw(editor->paledit); } void prepare_for_change(ImageEditor editor) { image img_copy; img_copy = copyimage(editor->img); del(editor->prev_img); editor->prev_img = img_copy; } void image_changed(ImageEditor editor) { if (editor->changed == 0) { enable(editor->undo_item); editor->changed = 1; } } void draw_pixel_block(rgb colour, rect r) { setcolour(colour); fillrect(r); if ((colour & White) == White) { /* white */ setcolour(LightGrey); drawrect(r); } if (getalpha(colour) > 0x7F) { /* transparent */ setcolour(Black); drawline(pt(r.x+r.width/2-1,r.y+r.height/2-1), pt(r.x+r.width/2+2,r.y+r.height/2-1)); drawline(pt(r.x+r.width/2,r.y+r.height/2-1), pt(r.x+r.width/2,r.y+r.height/2+2)); } } void redraw_pixedit(control pixedit, rect pix_rect) { ImageEditor editor; image img; int x, y, width, height; int w, h, size; int maxy, maxx; rect r; byte *pixels; rgb *palette; int palsize; byte pixval; rgb colour; editor = find_image_editor(pixedit); img = editor->img; width = getwidth(img); height = getheight(img); size = editor->size; pixels = getpixels(img); palette = getpalette(img); palsize = getpalettesize(img); maxy = editor->ysize; if (maxy > height) maxy = height; maxx = editor->xsize; if (maxx > width) maxx = width; for (y=0; y < maxy; y++) { for (x=0; x < maxx; x++) { pixval = pixels[(y+editor->y)*width + (x+editor->x)]; r = rect(x*size+1,y*size+1,size-1,size-1); if (pixval >= palsize) { /* not in palette! */ setcolour(Black); drawline(topleft(r), bottomright(r)); drawline(bottomleft(r), topright(r)); } else { /* correct palette entry */ colour = palette[pixval]; draw_pixel_block(colour, r); } } } setcolour(Black); drawrect(pix_rect); } void handle_pixedit_drag(control pixedit, int buttons, point p) { ImageEditor editor; image img; int x, y, width, height; int w, h, size; int maxy, maxx; rect r; byte *pixels; rgb *palette; int palsize; byte pixval; rgb colour; editor = find_image_editor(pixedit); img = editor->img; width = getwidth(img); height = getheight(img); size = editor->size; pixels = getpixels(img); palette = getpalette(img); palsize = getpalettesize(img); maxy = editor->ysize; if (maxy > height) maxy = height; maxx = editor->xsize; if (maxx > width) maxx = width; x = (p.x - 1) / editor->size; y = (p.y - 1) / editor->size; if (x >= maxx) return; if (y >= maxy) return; pixval = pixels[(y+editor->y)*width + (x+editor->x)]; r = rect(x*size+1,y*size+1,size-1,size-1); if (ischecked(editor->sampler)) { update_colour_editor(editor, pixval); } else if (ischecked(editor->pencil)) { if (pixval == atoi(gettext(editor->pixval))) return; pixval = atoi(gettext(editor->pixval)); pixels[(y+editor->y)*width + (x+editor->x)] = pixval; /* redraw this pixel */ colour = palette[pixval]; draw_pixel_block(colour, r); draw(editor->display); image_changed(editor); } } void handle_pixedit_click(control pixedit, int buttons, point p) { ImageEditor editor; editor = find_image_editor(pixedit); prepare_for_change(editor); handle_pixedit_drag(pixedit, buttons, p); } void redraw_display(control display, rect r) { ImageEditor editor; bitmap b; editor = find_image_editor(display); drawto(display); r = getrect(editor->img); drawimage(editor->img, rect(0,0,r.width,r.height), r); setcolour(Black); drawrect(getrect(display)); } void redraw_paledit(control paledit, rect r) { ImageEditor editor; int i, rowsize, palsize, selected, wrong = 0; int height; rgb *palette; rgb colour; editor = find_image_editor(paledit); palette = getpalette(editor->img); palsize = getpalettesize(editor->img); selected = atoi(gettext(editor->pixval)); if (selected < 0) selected = 0, wrong = 1; if (selected >= palsize) selected = palsize - 1, wrong = 1; if (wrong) update_colour_editor(editor, selected); rowsize = (r.width - 2) / 10; height = (r.height - 2) / 10; for (i=0; i < palsize; i++) { colour = palette[i]; r = rect(2+10*(i%rowsize),2+10*(i/rowsize),8,8); draw_pixel_block(colour, r); if (i == selected) setcolour(Black); else setcolour(White); drawrect(insetr(r,-1)); } setcolour(White); for (; i < rowsize * height; i++) { r = rect(2+10*(i%rowsize),2+10*(i/rowsize),8,8); fillrect(insetr(r,-1)); } setcolour(Black); drawrect(getrect(paledit)); } void handle_paledit_click(control paledit, int buttons, point p) { ImageEditor editor; int entry, rowsize, palsize; rgb *palette; rect r; r = insetr(getrect(paledit),1); if (! ptinr(p,r)) return; p = pt(p.x-r.x,p.y-r.y); rowsize = r.width / 10; if (p.x > rowsize*10) return; editor = find_image_editor(paledit); palette = getpalette(editor->img); palsize = getpalettesize(editor->img); entry = (p.x / 10) + (p.y / 10) * rowsize; if (entry >= palsize) return; update_colour_editor(editor, entry); } int find_colour_component(field which) { int wrong = 0; int value; value = atoi(gettext(which)); if (value < 0) value = 0, wrong = 1; if (value > 255) value = 255, wrong = 1; if (wrong) settext(which, int_to_string(value)); return value; } void add_colour(button btn) { ImageEditor editor; int i, t, r, g, b; image img; rgb *palette; rgb palette2[256]; int palsize; rgb colour; editor = find_image_editor(btn); img = editor->img; palette = getpalette(img); palsize = getpalettesize(img); if (palsize == 2<transparent)) t = 0xFF; else t = 0x00; r = find_colour_component(editor->red); g = find_colour_component(editor->green); b = find_colour_component(editor->blue); colour = rgb(r,g,b) | (((rgb)t)<<24); palette2[palsize] = colour; prepare_for_change(editor); setpalette(img, palsize+1, palette2); draw(editor->paledit); draw(editor->display); image_changed(editor); } void change_colour(button btn) { ImageEditor editor; int i, t, r, g, b; image img; rgb *palette; rgb palette2[256]; int palsize; rgb colour; editor = find_image_editor(btn); img = editor->img; palette = getpalette(img); palsize = getpalettesize(img); for (i=0; i < palsize; i++) palette2[i] = palette[i]; if (ischecked(editor->transparent)) t = 0xFF; else t = 0x00; r = find_colour_component(editor->red); g = find_colour_component(editor->green); b = find_colour_component(editor->blue); i = atoi(gettext(editor->pixval)); if (i < 0) return; if (i >= palsize) return; colour = rgb(r,g,b) | (((rgb)t)<<24); palette2[i] = colour; prepare_for_change(editor); setpalette(img, palsize, palette2); draw(editor->paledit); draw(editor->pixedit); draw(editor->display); image_changed(editor); } void update_scrollbars(ImageEditor editor) { int max; rect r = getrect(editor->pixedit); editor->ysize = (r.height - 2) / editor->size; if (editor->ysize > getheight(editor->img)) editor->ysize = getheight(editor->img); max = getheight(editor->img) - editor->ysize; changescrollbar(editor->vert, editor->y, max, editor->ysize); editor->xsize = (r.width - 2) / editor->size; if (editor->xsize > getwidth(editor->img)) editor->xsize = getwidth(editor->img); max = getwidth(editor->img) - editor->xsize; changescrollbar(editor->horiz, editor->x, max, editor->xsize); } void update_editor(ImageEditor editor) { update_scrollbars(editor); redraw(editor->pixedit); redraw(editor->display); redraw(editor->paledit); } void reset_editor(ImageEditor editor) { editor->x = editor->y = 0; update_colour_editor(editor, 0); update_editor(editor); } void move_x(scrollbar s, int value) { ImageEditor editor; editor = find_image_editor(s); if (editor->x != value) { editor->x = value; redraw(editor->pixedit); } } void move_y(scrollbar s, int value) { ImageEditor editor; editor = find_image_editor(s); if (editor->y != value) { editor->y = value; redraw(editor->pixedit); } } int save_image(ImageEditor editor, char *filename) { if (! filename) /* cancel */ return 0; saveimage(editor->img, filename); /* change the editor filename */ del_string(editor->filename); editor->filename = new_string(filename); settext(editor->win, filename); /* change the settings */ editor->changed = 0; disable(editor->undo_item); prepare_for_change(editor); return 1; } int open_image(ImageEditor editor, char *filename) { image img; img = loadimage(filename); if (! img) { askok("Unable to load image."); return 0; } /* delete the old image from memory */ del(editor->img); /* change the editor filename */ del_string(editor->filename); editor->filename = new_string(filename); settext(editor->win, filename); /* change the settings */ editor->img = img; editor->changed = 0; disable(editor->undo_item); prepare_for_change(editor); /* redraw the various panels */ reset_editor(editor); return 1; } int new_image(ImageEditor editor) { int i, x, y, width, height; image img; byte *pixels; rgb palette[5] = {Black, White, Red, Green, Blue}; int palsize = 5; width = editor->new_width; height = editor->new_height; img = newimage(width, height, editor->new_depth); if (! img) { askok("Insufficient memory to create a new image."); return 0; } pixels = getpixels(img); for (y=0; y < height; y++) for (x=0; x < width; x++) pixels[y*width + x] = 1; setpalette(img, palsize, palette); /* delete the old image from memory */ del(editor->img); /* change the editor filename */ del_string(editor->filename); editor->filename = new_string("untitled"); settext(editor->win, editor->filename); /* change the settings */ editor->img = img; editor->changed = 0; disable(editor->undo_item); prepare_for_change(editor); /* redraw the various panels */ reset_editor(editor); return 1; } int ask_save_changed_image(ImageEditor editor) { int result; char *savename; /* save existing modified image */ if (editor->changed) { result = askyesnocancel("Image has changed. Save changes?"); if (result == CANCEL) /* cancel */ return CANCEL; if (result == YES) { /* save changes */ savename = askfilesave("Save As:", editor->filename); if (! savename) /* cancel */ return CANCEL; saveimage(editor->img, savename); editor->changed = 0; disable(editor->undo_item); prepare_for_change(editor); } } return YES; } void set_image_size(button b) { ImageEditor editor = find_image_editor(b); editor->new_width = abs(atoi(gettext(editor->new_width_input))); editor->new_height = abs(atoi(gettext(editor->new_height_input))); new_image(editor); hide(parentwindow(b)); } void ask_new_image_size(ImageEditor editor) { int h; char buf[30]; if (! editor->new_image_window) { h = getheight(SystemFont) + 5; editor->new_image_window = newwindow("New Image Size", rect(50,50,200,h+h+h+70),Titlebar+Modal); setdata(editor->new_image_window, editor); newlabel("Pixel width:", rect(10,10,120,h+10), AlignLeft); newlabel("Pixel height:", rect(10,h+30,120,h+10),AlignLeft); editor->new_width_input = newfield("", rect(135,10,50,h+10)); editor->new_height_input = newfield("", rect(135,h+30,50,h+10)); newbutton("Okay", rect(60,h+h+50,80,h+10), set_image_size); } sprintf(buf, "%d", editor->new_width); settext(editor->new_width_input, buf); sprintf(buf, "%d", editor->new_height); settext(editor->new_height_input, buf); show(editor->new_image_window); } void ask_new_image(menuitem mi) { char *filename; ImageEditor editor; editor = find_image_editor(mi); if (ask_save_changed_image(editor) == CANCEL) return; ask_new_image_size(editor); } void ask_open_image(menuitem mi) { char *filename; ImageEditor editor; editor = find_image_editor(mi); if (ask_save_changed_image(editor) == CANCEL) return; filename = askfilename("Open File:", ""); if (filename) open_image(editor, filename); } void ask_save_image(menuitem mi) { char *filename; ImageEditor editor; editor = find_image_editor(mi); filename = askfilesave("Save As:", editor->filename); if (filename) save_image(editor, filename); } void do_quit_program(menuitem mi) { ImageEditor editor; editor = find_image_editor(mi); if (ask_save_changed_image(editor) == CANCEL) return; exitapp(); } void use_pencil(menuitem mi) { ImageEditor editor; editor = find_image_editor(mi); check(editor->pencil); uncheck(editor->sampler); } void use_sampler(menuitem mi) { ImageEditor editor; editor = find_image_editor(mi); uncheck(editor->pencil); check(editor->sampler); } void do_undo(menuitem mi) { ImageEditor editor; image new_img; editor = find_image_editor(mi); if (editor->changed) { new_img = copyimage(editor->prev_img); del(editor->img); editor->img = new_img; update_editor(editor); } } void do_sort_palette(menuitem mi) { ImageEditor editor; editor = find_image_editor(mi); prepare_for_change(editor); sortpalette(editor->img); reset_editor(editor); image_changed(editor); } ImageEditor create_image_editor() { ImageEditor editor = NULL; rect r; int x, h, minw, maxw; editor = (ImageEditor) malloc(sizeof(ImageEditorClass)); if (! editor) return editor; editor->filename = new_string("Untitled"); editor->x = editor->y = 0; editor->size = 10; editor->changed = 0; editor->new_width = 32; editor->new_height = 32; editor->new_depth = 8; editor->bgcolour = LightGrey; r = rect(50,50,560,450); editor->win = newwindow("Image Editor", r, StandardWindow); setdata(editor->win, editor); setclose(editor->win, do_quit_program); editor->file_menu = newmenu("File"); editor->new_item = newmenuitem("New...", 'N', ask_new_image); editor->open_item = newmenuitem("Open...", 'O', ask_open_image); editor->save_item = newmenuitem("Save As...", 'S', ask_save_image); editor->line_1 = newmenuitem("-", 0, NULL); editor->quit_item = newmenuitem("Exit", 'Q', do_quit_program); editor->file_menu = newmenu("Edit"); editor->undo_item = newmenuitem("Undo", 'Z', do_undo); disable(editor->undo_item); editor->line_2 = newmenuitem("-", 0, NULL); editor->sort_palette = newmenuitem("Sort Palette", 0, do_sort_palette); editor->tool_menu = newmenu("Tools"); editor->pencil = newmenuitem("Pencil", 'P', use_pencil); check(editor->pencil); editor->sampler = newmenuitem("Colour Sampler", 'C', use_sampler); r = rect(10,10,322,322); editor->pixedit = newcontrol("Pixel Editor", r); setredraw(editor->pixedit, redraw_pixedit); setmousedown(editor->pixedit, handle_pixedit_click); setmousedrag(editor->pixedit, handle_pixedit_drag); editor->ysize = 32; editor->vert = newscrollbar(rect(r.x+r.width+2,r.y,16,r.height), 0, 32, move_y); editor->xsize = 32; editor->horiz = newscrollbar(rect(r.x,r.y+r.height+2,r.width,16), 0, 32, move_x); x = r.x + r.width + 30; h = getheight(SystemFont); maxw = strwidth(SystemFont, "<>"); minw = strwidth(SystemFont, "456789") + 5; if (maxw < minw + 5) maxw = minw + 5; r = rect(x,r.y,150,h); setbackground(newlabel("Colour:", r, AlignLeft), editor->bgcolour); r = rect(x,r.y+r.height+5,minw,h+15); editor->pixval = newfield("0", r); disable(editor->pixval); r.x = r.x + maxw + 5; r.width = strwidth(SystemFont, "Transparent ") + 30; editor->transparent = newcheckbox("Transparent", r, NULL); setbackground(editor->transparent, editor->bgcolour); r = rect(x, r.y+r.height+5, maxw, h); setbackground(newlabel("Red", r, AlignLeft), editor->bgcolour); editor->red = newfield("0", rect(r.x,r.y+h+5,minw,h+15)); r.x = r.x + r.width + 5; setbackground(newlabel("Green", r, AlignLeft), editor->bgcolour); editor->green = newfield("0", rect(r.x,r.y+h+5,minw,h+15)); r.x = r.x + r.width + 5; setbackground(newlabel("Blue", r, AlignLeft), editor->bgcolour); editor->blue = newfield("0", rect(r.x,r.y+h+5,minw,h+15)); r = rect(x,r.y + r.height + h + 20, 76, h+5); editor->add = newbutton("Add", r, add_colour); r.x = r.x + r.width + 10; editor->change = newbutton("Change", r, change_colour); r = rect(x,r.y + r.height + 5, 150, h); setbackground(newlabel("Palette:", r, AlignLeft), editor->bgcolour); r = rect(x,r.y+r.height+5,162,162); editor->paledit = newcontrol("Palette Editor", r); setredraw(editor->paledit, redraw_paledit); setmouseup(editor->paledit, handle_paledit_click); r = rect(x,r.y+r.height+10,66,66); editor->display = newcontrol("Display", r); setredraw(editor->display, redraw_display); setbackground(editor->win, editor->bgcolour); editor->prev_img = NULL; editor->img = NULL; editor->new_image_window = NULL; if (! new_image(editor)) return NULL; update_scrollbars(editor); prepare_for_change(editor); return editor; } void show_image_editor(ImageEditor editor) { show(editor->win); } int main(int argc, char *argv[]) { ImageEditor editor; if (initapp(argc, argv) == 0) return 1; editor = create_image_editor(); if (argv && argv[1]) open_image(editor, argv[1]); show_image_editor(editor); mainloop(); return 0; }