NXEngine v1.0.0.2
[NXEngine.git] / sifedit / EditArea.cpp
blobff0db2d0e326dc97eefe7d3d5b58ae3df9e8793e
2 #include <wx/wx.h>
3 #include "sifedit.h"
4 #include "MainWindow.h"
5 #include "EditArea.fdh"
7 #define SWAP(A, B) { A ^= B; B ^= A; A ^= B; }
8 static int zoomstops[] = { 2, 4, 8, 16, 32, -1 };
10 BEGIN_EVENT_TABLE(EditArea, wxPanel)
12 EVT_PAINT(EditArea::OnPaint)
13 EVT_ERASE_BACKGROUND(EditArea::OnErase)
14 EVT_TIMER(100, EditArea::OnTimer)
16 EVT_LEFT_DOWN(EditArea::OnMouseDown)
17 EVT_LEFT_UP(EditArea::OnMouseUp)
18 EVT_RIGHT_DOWN(EditArea::OnMouseDown)
19 EVT_RIGHT_UP(EditArea::OnMouseUp)
20 EVT_MOTION(EditArea::OnMouseMove)
21 EVT_MOUSEWHEEL(EditArea::OnMouseWheel)
23 END_EVENT_TABLE()
26 void c------------------------------() {}
29 EditArea::EditArea(wxWindow *parent, wxWindowID id, const wxPoint &pos, const wxSize &size)
30 : wxPanel(parent, id, pos, size)
32 fTimer = new wxTimer(this, 100);
33 fTimer->Start(125);
35 fBackground = NULL;
36 fZoomedSprite = NULL;
38 fZoom = 16;
39 fOffset = 1;
40 fBlinkState = true;
42 fDrag.dragging = false;
43 fShift.mode = SM_NO_SHIFT;
46 EditArea::~EditArea()
48 delete fBackground;
49 delete fZoomedSprite;
53 void c------------------------------() {}
56 bool EditArea::SetBackground(const char *filename)
58 delete fBackground;
59 fBackground = new wxImage;
61 if (!fBackground->LoadFile(wxString(PrefixPath(filename), wxConvUTF8)))
63 stat("SetBackground: failed to load file '%s'", filename);
64 return 1;
67 return 0;
70 void EditArea::SetZoom(int factor)
72 if (factor < 1) factor = 1;
73 if (factor > 32) factor = 32 ;
75 if (fZoom != factor)
77 fZoom = factor;
78 CreateZoomedSprite();
82 void EditArea::NextZoomStop(int dir)
84 int curstop;
85 for(curstop=0;zoomstops[curstop] != -1;curstop++)
86 if (zoomstops[curstop] == fZoom) break;
88 curstop += (dir > 0) ? 1 : -1;
90 if (zoomstops[curstop] == -1) curstop--;
91 if (curstop < 0) curstop = 0;
93 fZoom = zoomstops[curstop];
96 void EditArea::NextOffsStop()
98 fOffset *= 2;
99 if (fOffset > 16)
100 fOffset = 1;
102 RefreshNow();
106 void c------------------------------() {}
109 void EditArea::OnTimer(wxTimerEvent &event)
111 if (gd.curmode != EM_VIEW)
113 fBlinkState ^= 1;
114 RefreshNow();
116 else fBlinkState = 1;
119 void EditArea::OnPaint(wxPaintEvent &event)
121 wxPaintDC dc(this);
122 Draw(&dc);
125 void EditArea::Draw(wxDC *dc)
127 if (!fZoomedSprite || !fZoomedSpriteSettings.equ(GetZoomedSpriteSettings()))
129 if (CreateZoomedSprite())
130 return;
133 // create a backbuffer
134 wxBitmap bbbmp(GetSize().GetWidth(), GetSize().GetHeight());
135 wxMemoryDC bbdc(bbbmp);
137 // clear everything
138 bbdc.SetBrush(*wxLIGHT_GREY_BRUSH);
139 bbdc.SetPen(*wxTRANSPARENT_PEN);
140 bbdc.DrawRectangle(0, 0, bbbmp.GetWidth(), bbbmp.GetHeight());
142 // draw sprite onto backbuffer
143 bbdc.DrawBitmap(wxBitmap(*fZoomedSprite), fOffset*fZoom, fOffset*fZoom);
145 // draw overlays
146 bbdc.SetPen(fBlinkState ? *wxRED_PEN : *wxBLACK_PEN);
147 bbdc.SetBrush(*wxTRANSPARENT_BRUSH);
149 if (fShift.mode != SM_NO_SHIFT)
152 else if (fDrag.dragging)
154 DrawRect(&bbdc, &fDrag.draggedrect);
156 else if (gd.curmode == EM_VIEW)
158 SIFSprite *sprite = CurSprite();
159 SIFDir *dir = CurDir();
161 bbdc.SetPen(*wxCYAN_PEN);
162 DrawRect(&bbdc, &sprite->solidbox);
164 bbdc.SetPen(*wxRED_PEN);
165 DrawRect(&bbdc, &sprite->bbox);
166 DrawPoints(&bbdc, &sprite->block_l.point[0], sprite->block_l.count);
167 DrawPoints(&bbdc, &sprite->block_r.point[0], sprite->block_r.count);
168 DrawPoints(&bbdc, &sprite->block_u.point[0], sprite->block_u.count);
169 DrawPoints(&bbdc, &sprite->block_d.point[0], sprite->block_d.count);
171 bbdc.SetPen(*wxGREEN_PEN);
172 if (!dir->actionpoint.equ(0, 0)) DrawPoints(&bbdc, &dir->actionpoint, 1);
173 if (!dir->actionpoint2.equ(0, 0)) DrawPoints(&bbdc, &dir->actionpoint2, 1);
175 bbdc.SetPen(*wxLIGHT_GREY_PEN);
176 if (!sprite->spawn_point.equ(0, 0)) DrawPoints(&bbdc, &sprite->spawn_point, 1);
178 bbdc.SetPen(*wxCYAN_PEN);
179 if (!dir->drawpoint.equ(0, 0)) DrawPoints(&bbdc, &dir->drawpoint, 1);
181 else
183 SIFRect *bbox = GetCurrentBBox();
184 if (bbox) DrawRect(&bbdc, bbox);
186 int npoints;
187 SIFPoint *points = GetCurrentPointList(&npoints);
188 if (points)
190 if (gd.curmode == EM_ACTIONPT || gd.curmode == EM_ACTIONPT2)
192 bbdc.SetPen(fBlinkState ? *wxGREEN_PEN : *wxBLACK_PEN);
195 DrawPoints(&bbdc, points, npoints);
199 // blit contents of the backbuffer to the screen
200 dc->Blit(0, 0, bbdc.GetSize().GetWidth(), bbdc.GetSize().GetHeight(), &bbdc, 0, 0);
202 // draw coordinates when dragging
203 if (fShift.mode)
205 switch(fShift.mode)
207 case SM_SIZE:
208 DrawGuideText(dc, stprintf("%d x %d", \
209 CurSprite()->w, CurSprite()->h));
210 break;
212 case SM_SHEET_OFFSET:
213 DrawGuideText(dc, stprintf("[%d, %d]", \
214 CurDir()->sheet_offset.x, CurDir()->sheet_offset.y));
215 break;
217 case SM_OFFSET_ALL:
218 DrawGuideText(dc, stprintf("+[%d, %d]", \
219 fShift.totaldx, fShift.totaldy));
220 break;
225 // prevent horrific flicker under Windows--keep it from
226 // trying to erase the background every time we paint.
227 void EditArea::OnErase(wxEraseEvent &event)
231 void EditArea::DrawRect(wxDC *dc, SIFRect *rect)
233 int x = (fOffset * fZoom) + (rect->x1 * fZoom);
234 int y = (fOffset * fZoom) + (rect->y1 * fZoom);
235 int w = ((rect->x2 - rect->x1) + 1) * fZoom;
236 int h = ((rect->y2 - rect->y1) + 1) * fZoom;
238 dc->DrawRectangle(x, y, w, h);
241 void EditArea::DrawPoints(wxDC *dc, SIFPoint *point, int count)
243 for(int i=0;i<count;i++)
245 int x = (fOffset * fZoom) + (point[i].x * fZoom);
246 int y = (fOffset * fZoom) + (point[i].y * fZoom);
248 dc->DrawRectangle(x, y, fZoom, fZoom);
249 dc->DrawLine(x, y, x+(fZoom-1), y+(fZoom-1));
253 void EditArea::DrawGuideText(wxDC *dc, const char *str)
255 int x, y;
256 wxCoord tew, teh;
258 dc->SetFont(wxFont(14, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
259 dc->SetTextForeground(wxColour(0, 0, 0));
260 dc->SetTextBackground(wxColour(255, 255, 255));
261 dc->SetBackgroundMode(wxSOLID);
263 wxString wxstr(str, wxConvUTF8);
264 dc->GetTextExtent(wxstr, &tew, &teh);
266 x = (fOffset * fZoom) + ((CurSprite()->w * fZoom) / 2) - (tew / 2);
267 y = (fOffset * fZoom) + (CurSprite()->h * fZoom) + 8;
269 dc->DrawText(wxstr, x, y);
273 void c------------------------------() {}
276 // converts from e.g. mouse coords on the window to sprite coords on the (zoomed) sprite
277 void EditArea::PixelToSpriteCoords(int x, int y, int *x_inout, int *y_inout)
279 x -= (fOffset * fZoom);
280 y -= (fOffset * fZoom);
281 if (x < 0) x -= fZoom;
282 if (y < 0) y -= fZoom;
283 x /= fZoom;
284 y /= fZoom;
286 if (x_inout) *x_inout = x;
287 if (y_inout) *y_inout = y;
290 // converts from sprite coords to actual window coordinates relative to the editarea
291 // returns the upper-left corner of the enlarged pixel.
292 void EditArea::SpriteToPixelCoords(int x, int y, int *x_inout, int *y_inout)
294 x *= fZoom;
295 y *= fZoom;
296 x += (fOffset * fZoom);
297 y += (fOffset * fZoom);
299 if (x_inout) *x_inout = x;
300 if (y_inout) *y_inout = y;
304 void c------------------------------() {}
307 // returns a pointer the bbox which is currently being edited, based on the edit mode, or NULL.
308 SIFRect *EditArea::GetCurrentBBox()
310 switch(gd.curmode)
312 case EM_BBOX: return &CurSprite()->bbox;
313 case EM_SOLIDBOX: return &CurSprite()->solidbox;
314 case EM_PFBOX: return &CurDir()->pf_bbox;
317 return NULL;
320 // returns a pointer to the first point in the pointlist (or single point in some modes)
321 // which is currently being edited, and the number of points, based on the edit mode, or NULL.
322 SIFPoint *EditArea::GetCurrentPointList(int *count_out, SIFPointList **outerstruct)
324 int dummy1;
325 SIFPointList *dummy2;
326 if (!count_out) count_out = &dummy1;
327 if (!outerstruct) outerstruct = &dummy2;
329 *outerstruct = NULL;
331 switch(gd.curmode)
333 case EM_BLOCKL:
334 *outerstruct = &CurSprite()->block_l;
335 break;
337 case EM_BLOCKR:
338 *outerstruct = &CurSprite()->block_r;
339 break;
341 case EM_BLOCKU:
342 *outerstruct = &CurSprite()->block_u;
343 break;
345 case EM_BLOCKD:
346 *outerstruct = &CurSprite()->block_d;
347 break;
349 case EM_SPAWNPT: *count_out = 1; return &CurSprite()->spawn_point;
351 case EM_ACTIONPT: *count_out = 1; return &CurDir()->actionpoint;
352 case EM_ACTIONPT2: *count_out = 1; return &CurDir()->actionpoint2;
353 case EM_DRAWPT: *count_out = 1; return &CurDir()->drawpoint;
356 if (outerstruct && *outerstruct)
358 *count_out = (*outerstruct)->count;
359 return &((*outerstruct)->point[0]);
362 return NULL;
366 void c------------------------------() {}
369 void EditArea::OnMouseDown(wxMouseEvent &event)
371 fShift.buttondown = event.m_leftDown;
372 if (fShift.mode) return;
374 int x, y;
375 PixelToSpriteCoords(event.m_x, event.m_y, &x, &y);
377 int button = event.m_leftDown ? LEFT : RIGHT;
379 // begin drag if allowed
380 SIFRect *bbox = GetCurrentBBox();
381 if (bbox)
383 HandleBeginDrag(event, button);
385 else // else maybe they're trying to add/remove a point
387 HandlePointEdit(x, y, button);
392 void EditArea::OnMouseMove(wxMouseEvent &event)
394 if (fShift.mode)
396 HandleShiftMode(event.m_x, event.m_y);
397 return;
400 if (!fDrag.dragging)
402 OfferAppropriateDragCursor(event.m_x, event.m_y);
403 return;
406 SetCursorForDragMode(fDrag.mode);
407 if (!fDrag.mouse_moved)
409 if (event.m_x != fDrag.orig_px || event.m_y != fDrag.orig_py)
411 fDrag.mouse_moved = true;
415 int x, y;
416 PixelToSpriteCoords(event.m_x, event.m_y, &x, &y);
418 if (x == fDrag.lastx && y == fDrag.lasty)
419 return;
421 // update the displayed bbox as they drag
422 if (fDrag.mode == DM_CREATE_NEW)
423 DragUpdateNewBBox(x, y);
424 else
425 DragUpdateAdjustedBBox(x, y);
427 // update screen
428 RefreshNow();
430 fDrag.lastx = x;
431 fDrag.lasty = y;
435 void EditArea::OnMouseUp(wxMouseEvent &event)
437 fShift.buttondown = event.m_leftDown;
438 if (fShift.mode) return;
440 if (fDrag.dragging)
442 // apply the new bbox
443 if (fDrag.mouse_moved)
445 SIFRect *bbox = GetCurrentBBox();
446 if (bbox)
448 *bbox = fDrag.draggedrect;
450 if (mainwin->fLinkBBoxCheckbox->GetValue() && \
451 gd.curmode == EM_BBOX)
453 CurSprite()->solidbox = fDrag.draggedrect;
458 fDrag.dragging = false;
459 SetCursor(*wxSTANDARD_CURSOR);
463 void EditArea::OnMouseWheel(wxMouseEvent &event)
465 int amt = event.m_wheelRotation / event.m_wheelDelta;
466 if (amt == 0) return;
468 NextZoomStop(amt);
469 mainwin->UpdateDisplay();
473 void c------------------------------() {}
476 void EditArea::DragUpdateNewBBox(int x, int y)
478 SIFRect rect;
479 rect.x1 = fDrag.anchor_x;
480 rect.y1 = fDrag.anchor_y;
481 rect.x2 = x;
482 rect.y2 = y;
484 if (rect.x1 > rect.x2) SWAP(rect.x1, rect.x2);
485 if (rect.y1 > rect.y2) SWAP(rect.y1, rect.y2);
487 fDrag.draggedrect = rect;
490 void EditArea::DragUpdateAdjustedBBox(int x, int y)
492 // get distance from anchor
493 int distx = (x - fDrag.anchor_x);
494 int disty = (y - fDrag.anchor_y);
496 SIFRect rect = fDrag.origrect;
498 if (fDrag.mode & DM_ADJUST_X1) rect.x1 += distx;
499 if (fDrag.mode & DM_ADJUST_X2) rect.x2 += distx;
500 if (fDrag.mode & DM_ADJUST_Y1) rect.y1 += disty;
501 if (fDrag.mode & DM_ADJUST_Y2) rect.y2 += disty;
503 fDrag.draggedrect = rect;
507 // returns which drag mode would be used if you pressed the left button
508 // right now at (unzoomed) pixel position x, y.
509 int EditArea::SelectDragMode(int px, int py)
511 SIFRect *bbox;
512 int x1, y1, x2, y2;
514 if (!(bbox = GetCurrentBBox()))
515 return DM_CREATE_NEW; // not in bbox-edit mode, so always use standard cursor
517 x1 = bbox->x1;
518 y1 = bbox->y1;
519 x2 = bbox->x2;
520 y2 = bbox->y2;
522 // zoom the bbox's coords to get it's real, zoomed, coords on the screen
523 SpriteToPixelCoords(x1, y1, &x1, &y1);
524 SpriteToPixelCoords(x2, y2, &x2, &y2);
525 x2 += (fZoom - 1); // get lower-left corner
526 y2 += (fZoom - 1); // instead of upper-left
528 // find out whether we are closest to the left, right, top or bottom edge of the bbox.
529 int xclosest = 9000;
530 int yclosest = 9000;
531 int xwinner = 0;
532 int ywinner = 0;
533 int nc;
535 if (py >= y1 && py <= y2)
537 if ((nc = abs(px - x1)) < xclosest) { xclosest = nc; xwinner = DM_ADJUST_X1; }
538 if ((nc = abs(px - x2)) < xclosest) { xclosest = nc; xwinner = DM_ADJUST_X2; }
541 if (px >= x1 && px <= x2)
543 if ((nc = abs(py - y1)) < yclosest) { yclosest = nc; ywinner = DM_ADJUST_Y1; }
544 if ((nc = abs(py - y2)) < yclosest) { yclosest = nc; ywinner = DM_ADJUST_Y2; }
547 // check if we're close enough to count
548 #define CLOSE_DIST 5
550 int result = 0; // equivalent to DM_CREATE_NEW
551 if (xclosest <= CLOSE_DIST) result |= xwinner;
552 if (yclosest <= CLOSE_DIST) result |= ywinner;
554 // offset whole bbox
555 if (result == 0 && px >= x1 && px <= x2 && py >= y1 && py <= y2)
556 result = (DM_ADJUST_X1 | DM_ADJUST_X2 | DM_ADJUST_Y1 | DM_ADJUST_Y2);
558 return result;
561 // called from OnMouseMove, if they are close to one of the sides of
562 // the bbox it sets the mouse cursor to a sizing cursor to offer to drag that side.
563 void EditArea::OfferAppropriateDragCursor(int px, int py)
565 int dm = DM_CREATE_NEW;
567 if (!fDrag.dragging)
568 dm = SelectDragMode(px, py);
570 SetCursorForDragMode(dm);
573 // sets the cursor to that used when doing or offering the given drag mode
574 void EditArea::SetCursorForDragMode(int mode)
576 switch(mode)
578 case DM_ADJUST_X1:
579 case DM_ADJUST_X2: SetCursor(wxCursor(wxCURSOR_SIZEWE)); return;
581 case DM_ADJUST_Y1:
582 case DM_ADJUST_Y2: SetCursor(wxCursor(wxCURSOR_SIZENS)); return;
584 case DM_ADJUST_X1 | DM_ADJUST_Y1:
585 case DM_ADJUST_X2 | DM_ADJUST_Y2:
586 SetCursor(wxCursor(wxCURSOR_SIZENWSE));
587 return;
589 case DM_ADJUST_X2 | DM_ADJUST_Y1:
590 case DM_ADJUST_X1 | DM_ADJUST_Y2:
591 SetCursor(wxCursor(wxCURSOR_SIZENESW));
592 return;
594 case DM_ADJUST_X1 | DM_ADJUST_X2 | DM_ADJUST_Y1 | DM_ADJUST_Y2:
596 // don't OFFER it, only set it if we're doing it now via right mouse button
597 if (fDrag.dragging)
599 SetCursor(wxCursor(wxCURSOR_SIZING));
600 return;
603 break;
606 SetCursor(*wxSTANDARD_CURSOR);
610 void c------------------------------() {}
613 void EditArea::HandleBeginDrag(wxMouseEvent &event, int button)
615 int x, y;
617 SIFRect *bbox = GetCurrentBBox();
618 if (!bbox) return;
620 PixelToSpriteCoords(event.m_x, event.m_y, &x, &y);
622 fDrag.mode = SelectDragMode(event.m_x, event.m_y);
623 if (fDrag.mode == (DM_ADJUST_X1 | DM_ADJUST_Y1 | DM_ADJUST_X2 | DM_ADJUST_Y2))
625 if (button != RIGHT)
626 fDrag.mode = DM_CREATE_NEW;
629 fDrag.origrect = *bbox;
630 fDrag.anchor_x = x;
631 fDrag.anchor_y = y;
632 fDrag.dragging = true;
634 fDrag.orig_px = event.m_x;
635 fDrag.orig_py = event.m_y;
636 fDrag.mouse_moved = false;
638 fDrag.lastx = -1;
639 fDrag.lasty = -1;
641 OnMouseMove(event);
644 void EditArea::HandlePointEdit(int x, int y, int button)
646 SIFPointList *list;
647 SIFPoint *point;
648 int npoints;
650 stat("HandlePointEdit mouse click at %d,%d btn %d", x, y, button);
652 point = GetCurrentPointList(&npoints, &list);
653 if (!point) return;
655 if (list)
656 { // dealing with a list, such as blockl/r/u/d
658 // delete any point currently at this position
659 // for RIGHT this is the action requested
660 // for LEFT it prevents adding more than one block point in the same spot
661 SIFPointList newlist;
662 newlist.count = 0;
663 for(int i=0;i<npoints;i++)
665 if (list->point[i].x != x || list->point[i].y != y)
667 newlist.point[newlist.count].x = list->point[i].x;
668 newlist.point[newlist.count].y = list->point[i].y;
669 newlist.count++;
672 *list = newlist;
674 if (button == LEFT)
676 if (list->count < SIF_MAX_BLOCK_POINTS)
678 list->point[list->count].x = x;
679 list->point[list->count].y = y;
680 list->count++;
682 else blip();
685 else
686 { // dealing with a single point, such as spawn_point
687 if (button == RIGHT) return;
688 point->x = x;
689 point->y = y;
692 RefreshNow();
693 mainwin->UpdateTitle();
697 void c------------------------------() {}
700 void EditArea::UpdateCtrlShiftStates()
702 int oldShiftMode = fShift.mode;
703 fShift.mode = SM_NO_SHIFT;
705 if (gd.ctrl)
707 fShift.mode = (!gd.shift) ? SM_SHEET_OFFSET : SM_OFFSET_ALL;
709 else if (gd.shift)
711 fShift.mode = SM_SIZE;
714 UpdateShiftStatusText();
716 if (fShift.mode != oldShiftMode)
718 if (fShift.mode)
720 fDrag.dragging = false;
721 fShift.havefirst = false;
722 fShift.buttondown = false;
723 fShift.totaldx = fShift.totaldy = 0;
725 fShift.expand_bbox = CurSprite()->bbox.equ(0, 0, CurSprite()->w-1, CurSprite()->h-1);
726 fShift.expand_solidbox = CurSprite()->solidbox.equ(0, 0, CurSprite()->w-1, CurSprite()->h-1);
728 SetCursor(wxCursor(wxCURSOR_SIZING));
730 else
732 SetCursor(*wxSTANDARD_CURSOR);
735 RefreshNow();
739 void EditArea::HandleShiftMode(int px, int py)
741 SetCursor(wxCursor(wxCURSOR_SIZING));
743 if (!fShift.buttondown)
745 fShift.havefirst = false;
746 return;
749 if (!fShift.havefirst)
751 fShift.lastpx = px;
752 fShift.lastpy = py;
753 fShift.totaldx = fShift.totaldy = 0;
754 fShift.havefirst = true;
755 stat("HandleShiftMode(%d, %d): Took first.", px, py);
756 return;
759 const int SENSITIVITY = 2;
760 int deltax = (px - fShift.lastpx) / SENSITIVITY;
761 int deltay = (py - fShift.lastpy) / SENSITIVITY;
763 SpriteRecord *sprite = CurSprite();
764 SIFDir *dir = CurDir();
766 if (deltax)
768 switch(fShift.mode)
770 case SM_SIZE:
772 sprite->w += deltax;
773 if (sprite->w < 1) sprite->w = 1;
774 if (sprite->w > 255) sprite->w = 255;
776 break;
778 case SM_SHEET_OFFSET:
780 dir->sheet_offset.x += deltax;
781 if (dir->sheet_offset.x < 0) dir->sheet_offset.x = 0;
783 break;
785 case SM_OFFSET_ALL:
787 OffsetAllFrames(deltax, 0);
789 break;
792 fShift.lastpx = px;
795 if (deltay)
797 switch(fShift.mode)
799 case SM_SIZE:
801 sprite->h += deltay;
802 if (sprite->h < 1) sprite->h = 1;
803 if (sprite->h > 255) sprite->h = 255;
805 break;
807 case SM_SHEET_OFFSET:
809 dir->sheet_offset.y += deltay;
810 if (dir->sheet_offset.y < 0) dir->sheet_offset.y = 0;
812 break;
814 case SM_OFFSET_ALL:
816 OffsetAllFrames(0, deltay);
818 break;
821 fShift.lastpy = py;
824 if (deltax || deltay)
826 if (fShift.mode == SM_SIZE)
828 if (fShift.expand_bbox)
829 CurSprite()->bbox.set(0, 0, CurSprite()->w-1, CurSprite()->h-1);
831 if (fShift.expand_solidbox)
832 CurSprite()->solidbox.set(0, 0, CurSprite()->w-1, CurSprite()->h-1);
835 fShift.totaldx += deltax;
836 fShift.totaldy += deltay;
838 mainwin->UpdateDisplay();
839 UpdateShiftStatusText();
843 void EditArea::UpdateShiftStatusText()
845 char *status = "Press CTRL=Sheet Offset, Shift=Size, CTRL+Shift=Offset All";
847 switch(fShift.mode)
849 case SM_SHEET_OFFSET:
850 status = stprintf("Adjusting sheet offset: [%d, %d]",
851 CurDir()->sheet_offset.x, CurDir()->sheet_offset.y);
852 break;
854 case SM_OFFSET_ALL:
855 status = "Adjusting sheet offset of ALL frames in current sprite...drag!";
856 break;
858 case SM_SIZE:
859 status = stprintf("Sizing current sprite: %dx%d",
860 CurSprite()->w, CurSprite()->h);
861 break;
864 mainwin->SetStatusText(wxString(status, wxConvUTF8));
868 void EditArea::OffsetAllFrames(int dx, int dy)
870 SpriteRecord *sprite = CurSprite();
871 int nframes = CurFrameCount();
873 for(int i=0;i<nframes;i++)
875 sprite->frame[i].dir[gd.curdir].sheet_offset.x += dx;
876 sprite->frame[i].dir[gd.curdir].sheet_offset.y += dy;
881 void c------------------------------() {}
884 bool EditArea::CreateZoomedSprite()
886 delete fZoomedSprite;
887 fZoomedSprite = NULL;
889 // cut out a wximage from the spritesheet containing just the sprite we are interested in
890 wxImage *sprite = sheetmgr.GetSprite(gd.cursprite, gd.curframe, gd.curdir);
891 if (!sprite)
893 staterr("CreateZoomedSprite: failed to GetSprite()");
894 return 1;
897 // add alpha to the sprite by setting all black pixels to transparent
898 int alphasize = (sprite->GetWidth() * sprite->GetHeight());
899 uint8_t *alpha = (uint8_t *)malloc(alphasize);
900 uint8_t *data = sprite->GetData();
901 int i, j;
902 for(i=j=0;i<alphasize;i++)
904 if (data[j] == 0 && data[j+1] == 0 && data[j+2] == 0)
906 alpha[i] = 0;
908 else
910 alpha[i] = 255;
913 j += 3;
916 sprite->SetAlpha(alpha);
918 // zoom the sprite to the desired size
919 int zoomw = CurSprite()->w * fZoom;
920 int zoomh = CurSprite()->h * fZoom;
921 sprite->Rescale(zoomw, zoomh);
923 // create a temporary offscreen memory DC of the appropriate size
924 wxBitmap membmp(zoomw, zoomh);
925 wxMemoryDC memdc(membmp);
927 // blit the background and sprite into the memory DC
928 FillBackground(&memdc);
929 memdc.DrawBitmap(wxBitmap(*sprite), 0, 0);
931 // obtain the image from the bitmap associated with the memory DC
932 fZoomedSprite = new wxImage(membmp.ConvertToImage());
934 fZoomedSpriteSettings = GetZoomedSpriteSettings();
935 delete sprite;
936 return 0;
939 ZoomedSpriteSettings EditArea::GetZoomedSpriteSettings()
941 ZoomedSpriteSettings set;
943 set.zoom = fZoom;
944 set.sheet_x = CurDir()->sheet_offset.x;
945 set.sheet_y = CurDir()->sheet_offset.y;
946 set.sheetno = CurSprite()->spritesheet;
947 set.w = CurSprite()->w;
948 set.h = CurSprite()->h;
950 return set;
953 bool ZoomedSpriteSettings::equ(ZoomedSpriteSettings other)
955 if (sheet_x != other.sheet_x) return false;
956 if (sheet_y != other.sheet_y) return false;
957 if (w != other.w) return false;
958 if (h != other.h) return false;
959 if (sheetno != other.sheetno) return false;
960 if (zoom != other.zoom) return false;
962 return true;
966 void c------------------------------() {}
969 void EditArea::FillBackground(wxDC *dc)
971 if (!fBackground) return;
972 wxBitmap bg(*fBackground);
974 int w = dc->GetSize().GetWidth();
975 int h = dc->GetSize().GetHeight();
977 for(int y=0;y<h;y+=bg.GetWidth())
978 for(int x=0;x<w;x+=bg.GetHeight())
980 dc->DrawBitmap(bg, x, y);
984 void EditArea::RefreshNow()
986 #ifdef WIN32
987 wxClientDC dc(this);
988 Draw(&dc);
989 #else
990 Refresh();
991 //Update();
992 #endif