/* * XEvil(TM) Copyright (C) 1994,2000 Steve Hardt and Michael Judge * http://www.xevil.com * satan@xevil.com * * This program 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. * * This program 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, the file "gpl.txt"; if not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA, or visit http://www.gnu.org. */ // "draw.cpp" // All init_x(), and draw() functions. Specific to X11. // Include Files #include "utils.h" #include "coord.h" #include "world.h" #include "locator.h" #include "physical.h" #include "actual.h" // Only for Viewport::get_reduce_draw(). Should put get_reduce_draw() // in Xvars instead. #include "viewport.h" // Draw functions for World void World::draw_square(Drawable buffer,Xvars &xvars,int dpyNum,const Loc &l, int x,int y,Boolean reduceDraw) { if (!inside(l)) { // World::draw_outside() will take care of it. return; } int blockNum = themes[themeIndex].blockIndices[map[l.r][l.c]]; if (unionSquares[l.r][l.c]) { assert(map[l.r][l.c] == Wempty || map[l.r][l.c] == Wwall); // Draw poster. if (unionSquares[l.r][l.c]->type == UN_POSTER) { if (!reduceDraw) { PosterSquare *pSquare = &unionSquares[l.r][l.c]->pSquare; Pixmap pix = xdata.posterPixmaps[dpyNum][pSquare->poster]; Pixmap mask = xdata.posterMasks[dpyNum][pSquare->poster]; if (!pix || !mask) { return; } // Now draw using clip mask XSetClipMask(xvars.dpy[dpyNum],xvars.gc[dpyNum],mask); XSetClipOrigin(xvars.dpy[dpyNum],xvars.gc[dpyNum], xvars.stretch_x(x - pSquare->loc.c * WSQUARE_WIDTH), xvars.stretch_y(y - pSquare->loc.r * WSQUARE_HEIGHT)); XCopyArea(xvars.dpy[dpyNum],pix, buffer,xvars.gc[dpyNum], xvars.stretch_x(pSquare->loc.c * WSQUARE_WIDTH), xvars.stretch_y(pSquare->loc.r * WSQUARE_HEIGHT), xvars.stretch_x(WSQUARE_WIDTH), xvars.stretch_y(WSQUARE_HEIGHT), xvars.stretch_x(x), xvars.stretch_y(y)); // Restore gc to initial state. XSetClipMask(xvars.dpy[dpyNum],xvars.gc[dpyNum],None); XSetClipOrigin(xvars.dpy[dpyNum],xvars.gc[dpyNum],0,0); } // reduceDraw } // Draw door. else if (unionSquares[l.r][l.c]->type == UN_DOOR) { int topBottom = unionSquares[l.r][l.c]->dSquare.topBottom; int doorNum = themes[themeIndex].doorBase + topBottom; XCopyArea(xvars.dpy[dpyNum], xdata.doorPixmaps[dpyNum][doorNum], buffer,xvars.gc[dpyNum],0,0, xvars.stretch_x(WSQUARE_WIDTH), xvars.stretch_y(WSQUARE_HEIGHT), xvars.stretch_x(x), xvars.stretch_y(y)); } // Draw moverSquare. else if (unionSquares[l.r][l.c]->type == UN_MOVER) { // If moversquare goes through a wall, draw the wall first. if (map[l.r][l.c] == Wwall) { // Hack, rip out union square and draw just wall. UnionSquare *tmp = unionSquares[l.r][l.c]; unionSquares[l.r][l.c] = NULL; // go recursive draw_square(buffer,xvars,dpyNum,l,x,y,reduceDraw); unionSquares[l.r][l.c] = tmp; } int mSquareNum = themes[themeIndex].moverSquareBase + unionSquares[l.r][l.c]->mSquare.orientation; // Now draw using clip mask if (!reduceDraw) { XSetClipMask(xvars.dpy[dpyNum],xvars.gc[dpyNum], xdata.moverSquareMasks[dpyNum][mSquareNum]); XSetClipOrigin(xvars.dpy[dpyNum],xvars.gc[dpyNum], xvars.stretch_x(x), xvars.stretch_y(y)); } XCopyArea(xvars.dpy[dpyNum], xdata.moverSquarePixmaps[dpyNum][mSquareNum], buffer,xvars.gc[dpyNum],0,0, xvars.stretch_x(WSQUARE_WIDTH), xvars.stretch_y(WSQUARE_HEIGHT), xvars.stretch_x(x), xvars.stretch_y(y)); // Restore gc to initial state. if (!reduceDraw) { XSetClipMask(xvars.dpy[dpyNum],xvars.gc[dpyNum],None); XSetClipOrigin(xvars.dpy[dpyNum],xvars.gc[dpyNum],0,0); } } // Unknown UnionSquare. else { assert(0); } } // Draw regular square. else { // World::draw_background() takes care of drawing empty squares. if (blockNum != Wempty) { // Only use transparent CopyArea for certain bricks. Boolean useTransparent = blockUseTransparent[map[l.r][l.c]] && !reduceDraw; if (useTransparent) { XSetClipMask(xvars.dpy[dpyNum],xvars.gc[dpyNum], xdata.blockMasks[dpyNum][blockNum]); XSetClipOrigin(xvars.dpy[dpyNum],xvars.gc[dpyNum], xvars.stretch_x(x), xvars.stretch_y(y)); } XCopyArea(xvars.dpy[dpyNum], xdata.blockPixmaps[dpyNum][blockNum], buffer,xvars.gc[dpyNum],0,0, xvars.stretch_x(WSQUARE_WIDTH), xvars.stretch_y(WSQUARE_HEIGHT), xvars.stretch_x(x), xvars.stretch_y(y)); if (useTransparent) { // Restore gc to initial state. XSetClipMask(xvars.dpy[dpyNum],xvars.gc[dpyNum],None); XSetClipOrigin(xvars.dpy[dpyNum],xvars.gc[dpyNum],0,0); } } } } void World::draw_mover(CMN_DRAWABLE buffer,Xvars &xvars,int dpyNum, MoverP /* mover */,int x,int y) { int moverNum = themes[themeIndex].moverIndex; XSetClipMask(xvars.dpy[dpyNum],xvars.gc[dpyNum], xdata.moverMasks[dpyNum][moverNum]); XSetClipOrigin(xvars.dpy[dpyNum],xvars.gc[dpyNum], xvars.stretch_x(x), xvars.stretch_y(y)); XCopyArea(xvars.dpy[dpyNum], xdata.moverPixmaps[dpyNum][moverNum], buffer,xvars.gc[dpyNum],0,0, xvars.stretch_x(moverSize.width), xvars.stretch_y(moverSize.height), xvars.stretch_x(x), xvars.stretch_y(y)); // Restore gc to initial state. XSetClipMask(xvars.dpy[dpyNum],xvars.gc[dpyNum],None); XSetClipOrigin(xvars.dpy[dpyNum],xvars.gc[dpyNum],0,0); } void World::draw_outside_offset(CMN_DRAWABLE dest, Xvars &xvars,int dpyNum,Size sourceOffset, const Area &destArea) { if (!xvars.is_valid(xValid)) { init_x(xvars,IX_INIT,NULL); } // Everything is already in window coordinates. Pos destPos = destArea.get_pos(); Size destSize = destArea.get_size(); XCopyArea(xvars.dpy[dpyNum],xdata.outsidePixmaps[dpyNum][outsideIndex], dest,xvars.gc[dpyNum], sourceOffset.width,sourceOffset.height, destSize.width,destSize.height, destPos.x,destPos.y); } void World::draw_background(CMN_DRAWABLE buffer,Xvars &xvars,int dpyNum, Area area,Boolean background3D) { if (!xvars.is_valid(xValid)) { init_x(xvars,IX_INIT,NULL); } // backgrounds.size should really be given in unstretched coordinates // to avoid having to divide by two. Size bSize = backgrounds[backgroundIndex].size; // Size of the background in stretched coordinates. Size bgSize; bgSize.set(xvars.stretch_x(bSize.width >> 1), xvars.stretch_y(bSize.height >> 1)); Pos pos = xvars.stretch_pos(area.get_pos()); Size size = xvars.stretch_size(area.get_size()); // Move background at half the speed of the foreground. if (background3D) { pos.x /= W_BACKGROUNDRATE; pos.y /= W_BACKGROUNDRATE; } // The origin to use for tiling with the background. Pos tsOrigin(-(pos.x % bgSize.width), -(pos.y % bgSize.height)); // Set GC to fill with tile of the background. XSetTSOrigin(xvars.dpy[dpyNum],xvars.gc[dpyNum], tsOrigin.x,tsOrigin.y); XSetFillStyle(xvars.dpy[dpyNum],xvars.gc[dpyNum], FillTiled); XSetTile(xvars.dpy[dpyNum],xvars.gc[dpyNum], xdata.backgroundPixmaps[dpyNum][backgroundIndex]); // Fill buffer, tiled with the background. XFillRectangle(xvars.dpy[dpyNum],buffer,xvars.gc[dpyNum], 0,0,size.width,size.height); // Revert GC to former state. // // Not going to bother reverting the tile in the GC, we don't use // it anywhere else anyway. Only applies when fill_style is FillSolid. // Tried setting it to "None" or to the value returned from XGetGCValues // on the initial GC. Both gave errors. // XSetTSOrigin(xvars.dpy[dpyNum],xvars.gc[dpyNum], 0,0); XSetFillStyle(xvars.dpy[dpyNum],xvars.gc[dpyNum], FillSolid); } // Fill the background of pixmaps[] (as specified by masks[]) with // the given color. void World_fill_background(Xvars& xvars,int dpyNum, Pixmap pixmaps[],Pixmap masks[], const Size& size,int pixNum, Pixel color) { Pixmap scratch = XCreatePixmap(xvars.dpy[dpyNum],xvars.root[dpyNum], size.width,size.height,xvars.depth[dpyNum]); if (!scratch) { return; } XSetForeground(xvars.dpy[dpyNum],xvars.gc[dpyNum],color); for (int n = 0; n < pixNum; n++) { // Fill scratch with color. XFillRectangle(xvars.dpy[dpyNum],scratch,xvars.gc[dpyNum],0,0, size.width,size.height); // Copy pixmaps[n] onto scratch using appropriate mask. XSetClipMask(xvars.dpy[dpyNum],xvars.gc[dpyNum],masks[n]); XCopyArea(xvars.dpy[dpyNum],pixmaps[n],scratch,xvars.gc[dpyNum], 0,0,size.width,size.height,0,0); XSetClipMask(xvars.dpy[dpyNum],xvars.gc[dpyNum],None); // Now copy scratch back onto pixmaps[n] without using mask. XCopyArea(xvars.dpy[dpyNum],scratch,pixmaps[n],xvars.gc[dpyNum], 0,0,size.width,size.height,0,0); } XSetForeground(xvars.dpy[dpyNum],xvars.gc[dpyNum],xvars.black[dpyNum]); } void World::init_x(Xvars &xvars,IXCommand,void*) { for (int dpyNum = 0; dpyNum < xvars.dpyMax; dpyNum++) { xdata.background[dpyNum] = xvars.alloc_named_color(dpyNum,Xvars_BACKGROUND); // Regular blocks. int n; for (n = 0; n < W_ALL_BLOCKS_NUM; n++) { Pixmap* mask = NULL; // Only create the mask if necessary. So, check to see if this block is // used as a transparent block in any theme. Inefficient, but doesn't // really matter here. for (int block = 0; (mask == NULL) && (block < W_BLOCKS_NUM); block++) { if (blockUseTransparent[block]) { for (int th = 0; th < W_THEME_NUM; th++) { if (themes[th].blockIndices[block] == n) { mask = &xdata.blockMasks[dpyNum][n]; break; } } } } // Load pixmap and possibly mask. Boolean val = xvars.load_pixmap(&xdata.blockPixmaps[dpyNum][n], mask,dpyNum, (char**)blocksBits[n]); if (!val) { cerr << "Failed to load block " << n << endl; } } // for n // For reduce draw, we won't use masks for XCopyArea, so fill background // with black. if (Viewport::get_reduce_draw()) { Size blockSize; blockSize.set(xvars.stretch_x(WSQUARE_WIDTH), xvars.stretch_y(WSQUARE_HEIGHT)); World_fill_background(xvars,dpyNum,xdata.blockPixmaps[dpyNum], xdata.blockMasks[dpyNum],blockSize, W_ALL_BLOCKS_NUM, xvars.black[dpyNum]); } // Create posterPixmaps, blocks must be created first. // // Use W_ALL_POSTERS_NUM to get the title poster. for (n = 0; n < W_ALL_POSTERS_NUM; n++) { Boolean val = xvars.load_pixmap(&xdata.posterPixmaps[dpyNum][n], &xdata.posterMasks[dpyNum][n], dpyNum,(char**)posters[n].id); if (!val) { cerr << "Failed to load poster " << n << endl; } } // Create doorPixmaps. for (n = 0; n < W_ALL_DOORS_NUM; n++) { Boolean val = xvars.load_pixmap(&xdata.doorPixmaps[dpyNum][n],NULL,dpyNum, (char**)doorPixmapBits[n]); if (!val) { cerr << "Failed to load door block " << n << endl; } } // Mover squares. for (n = 0; n < W_ALL_MOVER_SQUARES_NUM; n++) { Boolean val = xvars.load_pixmap(&xdata.moverSquarePixmaps[dpyNum][n], &xdata.moverSquareMasks[dpyNum][n], dpyNum, (char**)moverSquarePixmapBits[n]); if (!val) { cerr << "Failed to mover square " << n << endl; } } // For reduce draw, we won't use masks for XCopyArea, so fill background // with black. if (Viewport::get_reduce_draw()) { Size blockSize; blockSize.set(xvars.stretch_x(WSQUARE_WIDTH), xvars.stretch_y(WSQUARE_HEIGHT)); World_fill_background(xvars,dpyNum,xdata.moverSquarePixmaps[dpyNum], xdata.moverSquareMasks[dpyNum],blockSize, W_ALL_MOVER_SQUARES_NUM, xvars.black[dpyNum]); } // Mover. for (n = 0; n < W_ALL_MOVERS_NUM; n++) { Boolean val = xvars.load_pixmap(&xdata.moverPixmaps[dpyNum][n], &xdata.moverMasks[dpyNum][n], dpyNum, (char**)moverPixmapBits[n]); if (!val) { cerr << "Failed to load mover " << n << endl; } } // Backgrounds. for (n = 0; n < W_ALL_BACKGROUNDS_NUM; n++) { Boolean val = xvars.load_pixmap(&xdata.backgroundPixmaps[dpyNum][n],NULL,dpyNum, (char**)backgrounds[n].id); if (!val) { cerr << "Failed to load background " << n << endl; } } // Outsides. for (n = 0; n < W_ALL_OUTSIDES_NUM; n++) { Boolean val = xvars.load_pixmap(&xdata.outsidePixmaps[dpyNum][n],NULL,dpyNum, (char**)outsides[n].id); if (!val) { cerr << "Failed to load outside " << n << endl; } } } // for dpyNum xvars.mark_valid(xValid); } void Locator::draw_buffered(CMN_DRAWABLE window,Xvars &xvars, int dpyNum,const Box &room) { // Don't draw if outside the world. if (!world->overlap(room)) { return; } if (!xvars.is_valid(xValid)) { init_x(xvars,IX_INIT,NULL); } // Draw new stuff GLoc gridStart; gridStart.horiz = (int)floor((float)room.loc.c / (float)OL_GRID_COL_MAX) - 1; gridStart.vert = (int)floor((float)room.loc.r / (float)OL_GRID_ROW_MAX) - 1; GLoc gridFinish; gridFinish.horiz = (int)ceil((float)(room.loc.c + room.dim.colMax) / (float) OL_GRID_COL_MAX) + 1; gridFinish.vert = (int)ceil((float)(room.loc.r + room.dim.rowMax) / (float) OL_GRID_ROW_MAX) + 1; GLoc gloc; // Don't need to clease the wasDrawn flag if not using the wasDrawn flag. if (drawingAlgorithm == DRAW_MERGE_AREAS) { // Clear the was_drawn flag for all gridEntries and shadowEntries for (gloc.vert = gridStart.vert; gloc.vert < gridFinish.vert; gloc.vert++) { for (gloc.horiz = gridStart.horiz; gloc.horiz < gridFinish.horiz; gloc.horiz++) if (valid(gloc)) { OLshadowEntry *se = shadows[gloc.vert][gloc.horiz]->get_next(); while (se) { se->set_was_drawn(False); se = se->get_next(); } OLgridEntry *ge = grid[gloc.vert][gloc.horiz]->get_next(); while (ge) { ge->set_was_drawn(False); ge = ge->get_next(); } } } } // DRAW_MERGE_AREAS // Go through all shadows in room. for (gloc.vert = gridStart.vert; gloc.vert < gridFinish.vert; gloc.vert++) { for (gloc.horiz = gridStart.horiz; gloc.horiz < gridFinish.horiz; gloc.horiz++) { if (valid(gloc)) { OLshadowEntry *se = shadows[gloc.vert][gloc.horiz]->get_next(); while (se) { if (!se->get_was_drawn()) { // don't draw if already done. const Area &shadowArea = se->get_area(); // Orphaned shadows must always draw themselves, there // is no OLgridEntry that might take care of them. if (se->get_orphaned()) { // Draw the shadow area. if (drawingAlgorithm == DRAW_MERGE_AREAS) { // Set was_drawn before call to merge_draw_area so // that area doesn't waste time merging with itself. se->set_was_drawn(True); merge_draw_area(window,xvars,dpyNum,room, shadowArea,gloc); } else { draw_area(window,xvars,dpyNum,room,shadowArea,gloc); } } // Not an orphan. Shadow draws self if it doesn't overlap // the current location of the object. else { OLgridEntry *ge = se->get_grid_entry(); // Regular grid entry will draw shadow. if (shadowArea.overlap(ge->get_area())) { se->set_draw_self(False); } // Draw the shadow area. else { se->set_draw_self(True); if (drawingAlgorithm == DRAW_MERGE_AREAS) { se->set_was_drawn(True); merge_draw_area(window,xvars,dpyNum,room, shadowArea,gloc); } else { draw_area(window,xvars,dpyNum,room, shadowArea,gloc); } } } } // se->get_was_drawn() is False. else { // Shadow implicitly took care of drawing itself. se->set_draw_self(True); } se = se->get_next(); } // while se } // valid } // for gloc.horiz } // for gloc.vert // Go through all grid entries in room. for (gloc.vert = gridStart.vert; gloc.vert < gridFinish.vert; gloc.vert++) { for (gloc.horiz = gridStart.horiz; gloc.horiz < gridFinish.horiz; gloc.horiz++) { if (valid(gloc)) { OLgridEntry *ge = grid[gloc.vert][gloc.horiz]->get_next(); while (ge) { OLentry *ent = ge->get_entry(); assert (ent->gloc == gloc); if ((ge->get_mapped() || ge->get_flash())) { OLshadowEntry *se = ge->get_shadow_entry(); const Area &shadowArea = se->get_area(); if (se->draw_self() || se->get_was_drawn() || // Don't try to combine areas if the shadow is // not in the room. // Edge effects taken care of by extra +1 in // looking at grid squares of the room. !shadowArea.overlap(room)) { // At this point we know we don't need to worry about the shadow. if (!ge->get_was_drawn()) { if (drawingAlgorithm == DRAW_MERGE_AREAS) { ge->set_was_drawn(True); merge_draw_area(window,xvars,dpyNum,room, ge->get_area(),gloc); } else { draw_area(window,xvars,dpyNum,room, ge->get_area(),gloc); } } } else if (!ge->get_was_drawn()) { // Need to draw both grid entry and shadow. if (drawingAlgorithm == DRAW_MERGE_AREAS) { ge->set_was_drawn(True); se->set_was_drawn(True); // Try to combine areas of gridEntry and shadow. Area combined = shadowArea.combine(ge->get_area()); Size size = combined.get_size(); if (size.width <= OL_GRID_WIDTH && size.height <= OL_GRID_HEIGHT) { // Draw once, combining areas. merge_draw_area(window,xvars,dpyNum,room, combined,gloc); } else { // Combined area is too big, so draw areas separately. merge_draw_area(window,xvars,dpyNum,room, ge->get_area(),gloc); merge_draw_area(window,xvars,dpyNum,room, shadowArea,gloc); } } else { draw_area(window,xvars,dpyNum,room, shadowArea.combine(ge->get_area()),gloc); } } else { // gridEntry was drawn, but not shadow, so draw shadow here. if (drawingAlgorithm == DRAW_MERGE_AREAS) { se->set_was_drawn(True); merge_draw_area(window,xvars,dpyNum,room, shadowArea, gloc); } else { draw_area(window,xvars,dpyNum,room, shadowArea,gloc); } } } ge = ge->get_next(); } // while ge } // valid } // for gloc.horiz } // for gloc.vert } Drawable Locator::get_scratch_buffer(Pos& pos,Xvars&,int dpyNum) { pos.set_zero(); return xdata.scratchBuffer[dpyNum]; } void Locator::init_x(Xvars &xvars,IXCommand command,void*) { assert(command == IX_INIT); assert(!xvars.is_valid(xValid)); for (int dpyNum = 0; dpyNum < xvars.dpyMax; dpyNum++) { // Floating backbuffer. xdata.buffer[dpyNum] = XCreatePixmap(xvars.dpy[dpyNum],xvars.root[dpyNum], xvars.stretch_x(OL_GRID_COL_MAX * WSQUARE_WIDTH), xvars.stretch_y(OL_GRID_ROW_MAX * WSQUARE_HEIGHT), xvars.depth[dpyNum]); // Scratch buffer, supplied to Physical objects that want to do // drawing tricks. xdata.scratchBuffer[dpyNum] = XCreatePixmap(xvars.dpy[dpyNum],xvars.root[dpyNum], xvars.stretch_x(OL_GRID_COL_MAX * WSQUARE_WIDTH), xvars.stretch_y(OL_GRID_ROW_MAX * WSQUARE_HEIGHT), xvars.depth[dpyNum]); // Tick marks // Much of this code directly from Windows Locator::init_x(), but I don't // see an easy way to share. // // Load base tick bitmaps onto surface. // Also compute max tick size while we're doing a loop anyway. TickType tt; int hp; Size tickMax; // stretched coordinates. tickMax.set_zero(); for (hp = 0; hp < CO_DIR_HALF_PURE; hp++) { tickMax.width = Utils::maximum(tickMax.width,xvars.stretch_x(tickSizes[hp].width)); tickMax.height = Utils::maximum(tickMax.height,xvars.stretch_y(tickSizes[hp].height)); for (tt = 0; tt < TICK_MAX; tt++) { Dir dir = Coord::half_pure_to_pure(hp); if (Transform2D::is_base(dir,NULL)) { assert(tickPixmapBits[tt][hp] != PH_AUTO_GEN); xvars.load_pixmap(&xdata.tickPixmaps[dpyNum][tt][hp], &xdata.tickMasks[dpyNum][tt][hp], dpyNum,(char**)tickPixmapBits[tt][hp]); } } } // For efficiency, only create one scratch surface for doing the // transformations. Pass it into generate_pixmap_from_transform(). Pixmap scratch = XCreatePixmap(xvars.dpy[dpyNum],xvars.root[dpyNum], tickMax.width,tickMax.height, xvars.depth[dpyNum]); Pixmap maskScratch = XCreatePixmap(xvars.dpy[dpyNum],xvars.root[dpyNum], tickMax.width,tickMax.height, 1); // Load auto-generated ticks. for (tt = 0; tt < TICK_MAX; tt++) { for (hp = 0; hp < CO_DIR_HALF_PURE; hp++) { // Need "pure" dire for Transform2D, not Dir dir = Coord::half_pure_to_pure(hp); if (!Transform2D::is_base(dir,NULL)) { assert(tickPixmapBits[tt][hp] == PH_AUTO_GEN); Dir base = Transform2D::get_base(dir,NULL); int baseHP = Coord::pure_to_half_pure(base); int tNum; const TransformType* transforms = Transform2D::get_transforms(tNum,dir,NULL); // Create the empty pixmap. xdata.tickPixmaps[dpyNum][tt][hp] = XCreatePixmap(xvars.dpy[dpyNum],xvars.root[dpyNum], xvars.stretch_x(tickSizes[hp].width), xvars.stretch_y(tickSizes[hp].height), xvars.depth[dpyNum]); // Transform from base pixmap to auto-generated one. xvars.generate_pixmap_from_transform(dpyNum, xdata.tickPixmaps[dpyNum][tt][hp], xdata.tickPixmaps[dpyNum][tt][baseHP], xvars.stretch_size(tickSizes[baseHP]), scratch, transforms,tNum, xvars.depth[dpyNum]); // Create the empty mask. xdata.tickMasks[dpyNum][tt][hp] = XCreatePixmap(xvars.dpy[dpyNum],xvars.root[dpyNum], xvars.stretch_x(tickSizes[hp].width), xvars.stretch_y(tickSizes[hp].height), 1); // Transform from base mask to auto-generated one. xvars.generate_pixmap_from_transform(dpyNum, xdata.tickMasks[dpyNum][tt][hp], xdata.tickMasks[dpyNum][tt][baseHP], xvars.stretch_size(tickSizes[baseHP]), maskScratch, transforms,tNum, 1); } } // hp } // tt // Kill scratch pixmaps. XFreePixmap(xvars.dpy[dpyNum],scratch); XFreePixmap(xvars.dpy[dpyNum],maskScratch); } // dpyNum xvars.mark_valid(xValid); } void Locator::merge_draw_area(Drawable window,Xvars &xvars,int dpyNum, const Box &room,const Area &area, const GLoc &gloc) { // Trivial reject. if (!area.overlap(room)) { return; } Area newArea = area; GLoc check; for (check.horiz = gloc.horiz - 1; check.horiz <= gloc.horiz + 1; check.horiz++) { for (check.vert = gloc.vert - 1; check.vert <= gloc.vert + 1; check.vert++) { if (valid(check)&& possible_overlap(area,check)) { // Try to merge in gridEntries OLgridEntry *checkGe = grid[check.vert][check.horiz]->get_next(); while (checkGe) { if (!checkGe->get_was_drawn() && newArea.overlap(checkGe->get_area())) { // Try to combine areas. Area tryMe = newArea.combine(checkGe->get_area()); Size size = tryMe.get_size(); if (size.width <= OL_GRID_WIDTH && size.height <= OL_GRID_HEIGHT) { // Combine was successful. Rah, rah. checkGe->set_was_drawn(True); newArea = tryMe; // Since we merged in the gridEntry, check the associated // shadow entry as well. OLshadowEntry *se = checkGe->get_shadow_entry(); if (!se->get_was_drawn()) { // Try to combine areas. Area tryMe2 = newArea.combine(se->get_area()); Size size = tryMe2.get_size(); if (size.width <= OL_GRID_WIDTH && size.height <= OL_GRID_HEIGHT) { // Combined the shadow as well. se->set_was_drawn(True); newArea = tryMe2; } } } } checkGe = checkGe->get_next(); } // while (checkGe) // Try to merge in shadowEntries OLshadowEntry *se = shadows[check.vert][check.horiz]->get_next(); while (se) { if (!se->get_was_drawn() && newArea.overlap(se->get_area())) { // Try to combine areas. Area tryMe = newArea.combine(se->get_area()); Size size = tryMe.get_size(); if (size.width <= OL_GRID_WIDTH && size.height <= OL_GRID_HEIGHT) { // Combine was successful. Rah, rah. se->set_was_drawn(True); newArea = tryMe; // Since we merged in the shadow, check the associated grid // entry as well. if (!se->get_orphaned()) { // else no entry to check. OLgridEntry *ge = se->get_grid_entry(); if (!ge->get_was_drawn()) { // Try to combine areas. Area tryMe2 = newArea.combine(ge->get_area()); Size size = tryMe2.get_size(); if (size.width <= OL_GRID_WIDTH && size.height <= OL_GRID_HEIGHT) { // Combined the shadow's gridEntry as well. ge->set_was_drawn(True); newArea = tryMe2; } } } } } se = se->get_next(); } // while (se) } // if valid } // for } // for // Do actual drawing on merged area. draw_area(window,xvars,dpyNum,room,newArea,compute_gloc(newArea)); } void Locator::draw_area(Drawable window,Xvars &xvars,int dpyNum, const Box &room,const Area &area,const GLoc &gloc) { ///// Unnecessary since all calls come from merge_draw_area which already ///// does this, and areas don't get smaller. // Trivial reject. if (drawingAlgorithm != DRAW_MERGE_AREAS) { if (!area.overlap(room)) return; } // reduceDraw is always False for X // // If we are using Locator::draw_area, we are not doing smooth scrolling. // So we don't use the the background3D effect. // Locator::draw_directly() doesn't use Locator::draw_area(). world->draw(xdata.buffer[dpyNum],xvars,dpyNum,area,False,False); // Iterate over grid squares + and - 1 in both directions. GLoc start,finish; start.horiz = gloc.horiz - 1; start.vert = gloc.vert - 1; finish.horiz = gloc.horiz + 2; finish.vert = gloc.vert + 2; // Avoid double drawing composite objects. clear_already_drawn_flag(start,finish); for (int dLevel = 0; dLevel < DRAWING_LEVELS; dLevel++) { GLoc check; for (check.horiz = start.horiz; check.horiz < finish.horiz; check.horiz++) { for (check.vert = start.vert; check.vert < finish.vert; check.vert++) { if (valid(check) && possible_overlap(area,check)) { OLgridEntry *checkGe = grid[check.vert][check.horiz]->get_next(); while (checkGe) { if ((checkGe->get_drawing_level() == dLevel) && (checkGe->get_flash() || checkGe->get_mapped())) { PhysicalP checkP = checkGe->get_physical(); const Area &checkArea = checkP->get_area(); if (checkArea.overlap(area)) { draw_chain(xdata.buffer[dpyNum],xvars,dpyNum,area,checkP); } } checkGe = checkGe->get_next(); } // while } // if } // for vert } // for horiz } // for dLevel Pos pos; Size size; area.get_rect(pos,size); // Copy the backbuffer section onto the actual window. XCopyArea(xvars.dpy[dpyNum],xdata.buffer[dpyNum],window,xvars.gc[dpyNum], 0,0, xvars.stretch_x(size.width), xvars.stretch_y(size.height), xvars.stretch_x(pos.x - room.loc.c * WSQUARE_WIDTH), xvars.stretch_x(pos.y - room.loc.r * WSQUARE_HEIGHT)); // For debugging, if the -draw_rects argument was passed in. if (drawRects) { XSetForeground(xvars.dpy[dpyNum],xvars.gc[dpyNum],xvars.red[dpyNum]); XDrawRectangle(xvars.dpy[dpyNum],window,xvars.gc[dpyNum], xvars.stretch_x(pos.x - room.loc.c * WSQUARE_WIDTH), xvars.stretch_y(pos.y - room.loc.r * WSQUARE_HEIGHT), xvars.stretch_x(size.width) - 1, xvars.stretch_y(size.height) - 1); XSetForeground(xvars.dpy[dpyNum],xvars.gc[dpyNum],xvars.black[dpyNum]); } } Boolean Locator::draw_tick(TickType tt,CMN_DRAWABLE window,Xvars &xvars, int dpyNum, const Size &windowSize, Dir tickDir,int offset) { if (!xvars.is_valid(xValid)) { init_x(xvars,IX_INIT,NULL); } int hp = Coord::pure_to_half_pure(tickDir); // Stretched coordinates. Pos destPos; Size tSize = xvars.stretch_size(tickSizes[hp]); // Set X coordinate of destPos. switch (tickDir) { case CO_R: case CO_DN_R: case CO_UP_R: destPos.x = windowSize.width - tSize.width; break; case CO_DN: case CO_UP: destPos.x = offset - (tSize.width >> 1); break; case CO_DN_L: case CO_L: case CO_UP_L: destPos.x = 0; break; default: assert(0); } // Set Y coordinate of destPos. switch (tickDir) { case CO_R: case CO_L: destPos.y = offset - (tSize.height >> 1); break; case CO_DN_R: case CO_DN: case CO_DN_L: destPos.y = windowSize.height - tSize.height; break; case CO_UP_L: case CO_UP: case CO_UP_R: destPos.y = 0; break; default: assert(0); } // Copy tick onto surface. XSetClipMask(xvars.dpy[dpyNum],xvars.gc[dpyNum], xdata.tickMasks[dpyNum][tt][hp]); XSetClipOrigin(xvars.dpy[dpyNum],xvars.gc[dpyNum], destPos.x,destPos.y); XCopyArea(xvars.dpy[dpyNum],xdata.tickPixmaps[dpyNum][tt][hp], window,xvars.gc[dpyNum],0,0, xvars.stretch_x(tickSizes[hp].width), xvars.stretch_y(tickSizes[hp].height), destPos.x,destPos.y); // Restore gc to initial state. XSetClipMask(xvars.dpy[dpyNum],xvars.gc[dpyNum],None); XSetClipOrigin(xvars.dpy[dpyNum],xvars.gc[dpyNum],0,0); return True; } void Fire::init_x(Xvars &xvars,IXCommand command,void*) { assert(!xvars.is_valid(xdata.valid)); assert(command == IX_INIT); int dpyNum; for (dpyNum = 0; dpyNum < xvars.dpyMax; dpyNum++) { Boolean val = xvars.load_pixmap(&xdata.pixmap[dpyNum], &xdata.mask[dpyNum], dpyNum,(char**)fireBits); if (!val) { cerr << "Failed to load fire graphics." << endl; } } xvars.mark_valid(xdata.valid); } // Draw functions for Physcial. void Protection::draw(Drawable buffer,Xvars &xvars,int dpyNum, const Area &bufArea) { if (!xvars.is_valid(pXdata->valid)) { init_x(xvars,IX_INIT,NULL,*prc,*pXdata); } Pos pos; Size size; area.get_rect(pos,size); Size offset = area - bufArea; XSetForeground(xvars.dpy[dpyNum],xvars.gc[dpyNum],pXdata->color[dpyNum]); XDrawRectangle(xvars.dpy[dpyNum],buffer,xvars.gc[dpyNum], xvars.stretch_x(offset.width), xvars.stretch_y(offset.height), xvars.stretch_x(size.width - 1), xvars.stretch_y(size.height - 1)); XSetForeground(xvars.dpy[dpyNum],xvars.gc[dpyNum],xvars.black[dpyNum]); } void Moving::draw(Drawable buffer,Xvars &xvars,int dpyNum, const Area &bufArea) { // Initialize X variables if necessary. if (!xvars.is_valid(movingXdata->valid)) { init_x(xvars,IX_INIT,NULL,*mc,*movingXdata); } Pos pos; Size size; area.get_rect(pos,size); Size offset = area - bufArea; Pixmap scratch; if (is_invisible()) { LocatorP locator = get_locator(); Pos dummy; scratch = locator->get_scratch_buffer(dummy,xvars,dpyNum); // Which way to distort the image. // We will draw the image twice to get the wrap-around. // Copy portion of area into scratch buffer. XCopyArea(xvars.dpy[dpyNum],buffer,scratch,xvars.gc[dpyNum], xvars.stretch_x(offset.width), xvars.stretch_y(offset.height), xvars.stretch_x(size.width), xvars.stretch_y(size.height), xvars.stretch_x(0), xvars.stretch_y(size.height - 3)); XCopyArea(xvars.dpy[dpyNum],buffer,scratch,xvars.gc[dpyNum], xvars.stretch_x(offset.width), xvars.stretch_y(offset.height), xvars.stretch_x(size.width), xvars.stretch_y(size.height), xvars.stretch_x(0), xvars.stretch_y(-3)); } CMN_IMAGEDATA imageData; get_pixmap_mask(xvars,dpyNum,imageData,dir,movingAnimNum); Pixmap pixmap = imageData.pixmap; Pixmap mask = imageData.mask; XSetClipMask(xvars.dpy[dpyNum],xvars.gc[dpyNum],mask); XSetClipOrigin(xvars.dpy[dpyNum],xvars.gc[dpyNum], xvars.stretch_x(offset.width), xvars.stretch_y(offset.height)); Drawable srcPix; if (is_invisible()) { // Draw from the scratch surface. srcPix = scratch; } else { // Use supplied pixmap. srcPix = pixmap; } XCopyArea(xvars.dpy[dpyNum],srcPix, buffer,xvars.gc[dpyNum],0,0, xvars.stretch_x(size.width), xvars.stretch_y(size.height), xvars.stretch_x(offset.width), xvars.stretch_y(offset.height)); // Restore gc to initial state. XSetClipMask(xvars.dpy[dpyNum],xvars.gc[dpyNum],None); XSetClipOrigin(xvars.dpy[dpyNum],xvars.gc[dpyNum],0,0); // Haven't fixed this for stretching yet. #if 0 ColorNum colorNum; if (draw_outline(colorNum) && // Don't draw for XPM images. !mc->useXPM ) { XSetForeground(xvars.dpy[dpyNum],xvars.gc[dpyNum], xvars.humanColors[dpyNum][colorNum]); XPoint points[12]; points[0].x = offset.width; points[0].y = offset.height; points[1].x = offset.width + 1; points[1].y = offset.height; points[2].x = offset.width; points[2].y = offset.height + 1; points[3].x = offset.width + size.width - 1; points[3].y = offset.height; points[4].x = offset.width + size.width - 2; points[4].y = offset.height; points[5].x = offset.width + size.width - 1; points[5].y = offset.height + 1; points[6].x = offset.width; points[6].y = offset.height + size.height - 1; points[7].x = offset.width; points[7].y = offset.height + size.height - 2; points[8].x = offset.width + 1; points[8].y = offset.height + size.height - 1; points[9].x = offset.width + size.width - 1; points[9].y = offset.height + size.height - 1; points[10].x = offset.width + size.width - 2; points[10].y = offset.height + size.height - 1; points[11].x = offset.width + size.width - 1; points[11].y = offset.height + size.height - 2; XDrawPoints(xvars.dpy[dpyNum],buffer,xvars.gc[dpyNum],points, 12,CoordModeOrigin); XSetForeground(xvars.dpy[dpyNum],xvars.gc[dpyNum], xvars.black[dpyNum]); } #endif } void Moving::get_pixmap_mask(Xvars &,int dpyNum,CMN_IMAGEDATA &imageData, Dir dir,int animNum) { imageData.pixmap = movingXdata->pixmaps[dpyNum][dir][animNum]; imageData.mask = movingXdata->masks[dpyNum][dir][animNum]; } // Draw functions for Actual void Explosion::draw(Drawable buffer,Xvars &xvars,int dpyNum, const Area &bufArea) { Pos pos; Size size; area.get_rect(pos,size); Size offset = area - bufArea; XFillArc(xvars.dpy[dpyNum],buffer,xvars.gc[dpyNum], xvars.stretch_x(offset.width), xvars.stretch_y(offset.height), xvars.stretch_x(size.width), xvars.stretch_y(size.height), 0,23040); } void Fire::draw(Drawable buffer,Xvars &xvars,int dpyNum, const Area &bufArea) { // Initialize X variables if necessary. if (!xvars.is_valid(xdata.valid)) { init_x(xvars,IX_INIT,NULL); } const Area &area = get_area(); Pos pos; Size size; area.get_rect(pos,size); Size offset = area - bufArea; Size sizeMax = Fire::get_size_max(); // Grab a random part of the source bitmap. // World coordinates. Pos srcPos(Utils::choose(sizeMax.width - size.width + 1), Utils::choose(sizeMax.height - size.height + 1)); XSetClipMask(xvars.dpy[dpyNum],xvars.gc[dpyNum],xdata.mask[dpyNum]); XSetClipOrigin(xvars.dpy[dpyNum],xvars.gc[dpyNum], xvars.stretch_x(offset.width), xvars.stretch_y(offset.height)); XCopyArea(xvars.dpy[dpyNum],xdata.pixmap[dpyNum],buffer, xvars.gc[dpyNum], xvars.stretch_x(srcPos.x),xvars.stretch_y(srcPos.y), xvars.stretch_x(size.width),xvars.stretch_y(size.height), xvars.stretch_x(offset.width),xvars.stretch_y(offset.height)); // Restore gc to initial state. XSetClipMask(xvars.dpy[dpyNum],xvars.gc[dpyNum],None); XSetClipOrigin(xvars.dpy[dpyNum],xvars.gc[dpyNum],0,0); #if 0 XSetForeground(xvars.dpy[dpyNum],xvars.gc[dpyNum],xvars.red[dpyNum]); XFillArc(xvars.dpy[dpyNum],buffer,xvars.gc[dpyNum], xvars.stretch_x(offset.width), xvars.stretch_y(offset.height), xvars.stretch_x(size.width), xvars.stretch_y(size.height), 0,23040); XSetForeground(xvars.dpy[dpyNum],xvars.gc[dpyNum],xvars.black[dpyNum]); #endif } void Protection::init_x(Xvars &xvars,IXCommand,void*, const ProtectionContext &prc, ProtectionXdata &pXdata) { assert(!xvars.is_valid(pXdata.valid)); for (int dpyNum = 0; dpyNum < xvars.dpyMax; dpyNum++) { pXdata.color[dpyNum] = xvars.alloc_named_color(dpyNum,prc.colorName,xvars.black[dpyNum]); } xvars.mark_valid(pXdata.valid); } void Moving::init_x(Xvars &xvars,IXCommand command,void*, const MovingContext &mc,MovingXdata &movingXdata) { assert(!xvars.is_valid(movingXdata.valid)); assert(command == IX_INIT); // Load all non-autogenerated bitmaps. // This should load ALL the base bitmaps. int dpyNum; for (dpyNum = 0; dpyNum < xvars.dpyMax; dpyNum++) { for (int n = 0; n < CO_DIR_MAX; n++) { if (mc.animMax[n] == 0 || mc.pixmapBits[n][0] == PH_AUTO_GEN) { continue; } for (int m = 0; m < mc.animMax[n]; m++) { assert(mc.useXPM); // Load an XPM image Boolean val = xvars.load_pixmap(&movingXdata.pixmaps[dpyNum][n][m], &movingXdata.masks[dpyNum][n][m], dpyNum,(char**)mc.pixmapBits[n][m]); if (!val) { cerr << "load_pixmap() failed for " << mc.physicalContext.className << " dir=" << n << " animNum=" << m << endl; } } // for m } // for n } // for dpyNum // Auto generate the PH_AUTO_GEN pixmaps and masks. for (dpyNum = 0; dpyNum < xvars.dpyMax; dpyNum++) { // For efficiency, only create one scratch surface for doing the // transformations. Pass it into generate_pixmap_from_transform(). Pixmap scratch = XCreatePixmap(xvars.dpy[dpyNum],xvars.root[dpyNum], xvars.stretch_x(mc.physicalContext.sizeMax.width), xvars.stretch_y(mc.physicalContext.sizeMax.height), xvars.depth[dpyNum]); Pixmap maskScratch = XCreatePixmap(xvars.dpy[dpyNum],xvars.root[dpyNum], xvars.stretch_x(mc.physicalContext.sizeMax.width), xvars.stretch_y(mc.physicalContext.sizeMax.height), 1); // Loop over all dirs. for (int n = 0; n < CO_DIR_MAX; n++) { if (mc.animMax[n] == 0 || mc.pixmapBits[n][0] != PH_AUTO_GEN) { continue; } // Lookup the necessary transformations. Dir base = Transform2D::get_base(n,mc.transformOverride); int tNum; const TransformType* transforms = Transform2D::get_transforms(tNum,n,mc.transformOverride); // Loop over all animation frames. for (int m = 0; m < mc.animMax[n]; m++) { // Create the empty pixmap. movingXdata.pixmaps[dpyNum][n][m] = XCreatePixmap(xvars.dpy[dpyNum],xvars.root[dpyNum], xvars.stretch_x(mc.sizes[n].width), xvars.stretch_y(mc.sizes[n].height), xvars.depth[dpyNum]); // Transform from base pixmap to auto-generated one. xvars.generate_pixmap_from_transform(dpyNum, movingXdata.pixmaps[dpyNum][n][m], movingXdata.pixmaps[dpyNum][base][m], xvars.stretch_size(mc.sizes[base]), scratch, transforms,tNum, xvars.depth[dpyNum]); // Create the empty mask. movingXdata.masks[dpyNum][n][m] = XCreatePixmap(xvars.dpy[dpyNum],xvars.root[dpyNum], xvars.stretch_x(mc.sizes[n].width), xvars.stretch_y(mc.sizes[n].height), 1); // Transform from base mask to auto-generated one. xvars.generate_pixmap_from_transform(dpyNum, movingXdata.masks[dpyNum][n][m], movingXdata.masks[dpyNum][base][m], xvars.stretch_size(mc.sizes[base]), maskScratch, transforms,tNum, 1); } // for m } // for n // Kill scratch pixmaps. XFreePixmap(xvars.dpy[dpyNum],scratch); XFreePixmap(xvars.dpy[dpyNum],maskScratch); } // for dpyNum xvars.mark_valid(movingXdata.valid); }