/* * mogltk * Copyright (C) 1999-2004 Nicolas "Pixel" Noble * * 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; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* $Id: shape.cc,v 1.22 2006-10-28 16:50:46 pixel Exp $ */ #include #include #include #include #include #include "engine.h" #include "base.h" #include "Shape.h" #include "Texture.h" #include "Font.h" #define ENTER bool flag = Enter() #define LEAVE Leave(flag) mogltk::fillwalker::fillwalker() { } mogltk::fillwalker::~fillwalker() { } void mogltk::fillwalker::step(int x, int y) { } mogltk::segwalker::segwalker() { } mogltk::segwalker::~segwalker() { } void mogltk::segwalker::step(int x1, int y1, int x2, int y2) { } mogltk::Fill::Fill() : minX(INT_MAX), minY(INT_MAX), maxX(INT_MIN), maxY(INT_MIN), cached(0), scached(0), header(0) { } mogltk::Fill::~Fill() { if (header) delete header; if (cached) delete cached; if (scached) delete scached; } void mogltk::Fill::walk(fillwalker * w) { if (header) header->walk(w); } void mogltk::Fill::swalk(segwalker * s) { std::vector::iterator i; for (i = segments.begin(); i != segments.end(); i++) { s->step(i->x1, i->y1, i->x2, i->y2); } } void mogltk::Fill::insert(int x, int y) { if (cached) { delete cached; cached = 0; } if (scached) { delete scached; scached = 0; } if (!header) { new sline(y, this); } header->insert(x, y); if (x > maxX) maxX = x; if (x < minX) minX = x; if (y > maxY) maxY = y; if (y < minY) minY = y; } void mogltk::Fill::insert(int x1, int y1, int x2, int y2) { int dx, dy, y; double x, i, i2; struct segment s; s.x1 = x1; s.y1 = y1; s.x2 = x2; s.y2 = y2; segments.push_back(s); if (y1 == y2) return; if (y2 < y1) { SWAP(x1, x2); SWAP(y1, y2); } dx = x1 - x2; dy = y1 - y2; x = x1; i = ((double) dx) / ((double) dy); i2 = i / 2; for (y = y1; y < y2; y++, x += i) insert(x + i2, y); } void mogltk::Fill::insertfix(int x, int y) { if (!header) return; header->insertfix(x, y); } int mogltk::Fill::GetMaxX() const { return maxX; } int mogltk::Fill::GetMaxY() const { return maxY; } int mogltk::Fill::GetMinX() const { return minX; } int mogltk::Fill::GetMinY() const { return minY; } mogltk::Texture * mogltk::Fill::GetTexture() { return cached; } mogltk::Texture * mogltk::Fill::GetSTexture() { return scached; } mogltk::Texture * mogltk::Fill::Talloc() { if (cached) return cached; int x; int y; for (x = 1; x <= (maxX - minX); x <<= 1); for (y = 1; y <= (maxY - minY); y <<= 1); cached = new Texture(x, y, true); cached->name = "Fill-Texture - cached"; return cached; } mogltk::Texture * mogltk::Fill::STalloc() { if (scached) return scached; int x; int y; for (x = 1; x <= (maxX - minX); x <<= 1); for (y = 1; y <= (maxY - minY); y <<= 1); scached = new Texture(x, y, true); cached->name = "Fill-Texture - scached"; return scached; } mogltk::Fill::sline::sline(int _y, Fill * _header) : y(_y), header(_header), pheader(0) { if (!header->header) { header->header = this; } else { if (header->header->y > y) { next = header->header; header->header = this; } else { sline * p; for (p = header->header; p->next; p = p->next) { if (p->next->y > y) { next = p->next; p->next = this; return; } } p->next = this; next = 0; } } } mogltk::Fill::sline::~sline() { if (pheader) delete pheader; if (next) delete next; } int mogltk::Fill::sline::GetY() const { return y; } void mogltk::Fill::sline::insert(int ax, int ay) { sline * f; if (ay == y) { if (!pheader) new point(ax, this); else if (!pheader->look(ax)) new point(ax, this); } else { f = header->header->look(ay); if (!f) { f = new sline(ay, header); } f->insert(ax, ay); } } void mogltk::Fill::sline::insertfix(int ax, int ay) { sline * f; if (ay == y) { if (count() & 1) { insert(ax, ay); } } else { f = header->header->look(ay); if (!f) return; f->insertfix(ax, ay); } } int mogltk::Fill::sline::count() const { int r = 0; if (next) r = next->count(); return r + 1; } void mogltk::Fill::sline::walk(fillwalker * w) { if (pheader) pheader->walk(w); if (next) next->walk(w); } mogltk::Fill::sline::point::point(int _x, sline * _header) : x(_x), header(_header) { if (!header->pheader) { header->pheader = this; } else { if (header->pheader->x > x) { next = header->pheader; header->pheader = this; } else { point * p; for (p = header->pheader; p->next; p = p->next) { if (p->next->x > x) { next = p->next; p->next = this; return; } } p->next = this; next = 0; } } } mogltk::Fill::sline::point::~point() { if (next) delete next; } int mogltk::Fill::sline::point::GetX() const { return x; } int mogltk::Fill::sline::point::GetY() const { return header->GetY(); } void mogltk::Fill::sline::point::walk(fillwalker * w) { w->step(GetX(), GetY()); if (next) next->walk(w); } mogltk::Fill::sline::point * mogltk::Fill::sline::point::look(int _x) { if (x > _x) return 0; if (x == _x) return this; if (!next) return 0; return next->look(_x); } mogltk::Fill::sline * mogltk::Fill::sline::look(int _y) { if (y > _y) return 0; if (y == _y) return this; if (!next) return 0; return next->look(_y); } mogltk::Shape::Shape(SDL_Surface * _surf) : surf(_surf) { if (!surf) surf = mogltk::engine::base_o->getsurface(); } SDL_Surface * mogltk::Shape::GetSurf() { return surf; } void mogltk::Shape::box(int x1, int y1, int x2, int y2, ColorP c) { ENTER; SDL_Rect rect; rect.x = x1; rect.y = y1; rect.w = x2 - x1 + 1; rect.h = y2 - y1 + 1; SDL_FillRect(surf, &rect, c.toSDL(surf->format)); LEAVE; } void mogltk::Shape::hline(int x1, int x2, int y, ColorP c) { for (; x1 <= x2; x1++) { pixel(x1, y, c); } } void mogltk::Shape::hline3d(int x1, int x2, int y, ColorP shade1, ColorP shade2, bool bevel) { ENTER; if (!bevel) { hline(x1, x2, y, shade2); hline(x1, x2, y + 1, shade1); } else { hline(x1, x2, y, shade1); hline(x1, x2, y + 1, shade2); } LEAVE; } void mogltk::Shape::vline(int x, int y1, int y2, ColorP c) { for (; y1 <= y2; y1++) { pixel(x, y1, c); } } void mogltk::Shape::vline3d(int x, int y1, int y2, ColorP shade1, ColorP shade2, bool bevel) { ENTER; if (!bevel) { vline(x, y1, y2, shade2); vline(x + 1, y1, y2, shade1); } else { vline(x, y1, y2, shade1); vline(x + 1, y1, y2, shade2); } LEAVE; } void mogltk::Shape::bsubline_1(int x1, int y1, int x2, int y2, ColorP c) { int x, y, ddx, ddy, e; ddx = abs(x2 - x1); ddy = abs(y2 - y1) << 1; e = ddx - ddy; ddx <<= 1; if (x1 > x2) { SWAP(x1, x2); SWAP(y1, y2); } ENTER; for (x = x1, y = y1; x <= x2; x++) { pixel(x, y, c); if (e < 0) { y++; e += ddx - ddy; } else { e -= ddy; } } LEAVE; } void mogltk::Shape::bsubline_2(int x1, int y1, int x2, int y2, ColorP c) { int x, y, ddx, ddy, e; ddx = abs(x2 - x1) << 1; ddy = abs(y2 - y1); e = ddy - ddx; ddy <<= 1; if (y1 > y2) { SWAP(x1, x2); SWAP(y1, y2); } ENTER; for (y = y1, x = x1; y <= y2; y++) { pixel(x, y, c); if (e < 0) { x++; e += ddy - ddx; } else { e -= ddx; } } LEAVE; } void mogltk::Shape::bsubline_3(int x1, int y1, int x2, int y2, ColorP c) { int x, y, ddx, ddy, e; ddx = abs(x1 - x2) << 1; ddy = abs(y2 - y1); e = ddy - ddx; ddy <<= 1; if (y1 > y2) { SWAP(x1, x2); SWAP(y1, y2); } ENTER; for (y = y1, x = x1; y <= y2; y++) { pixel(x, y, c); if (e < 0) { x--; e += ddy - ddx; } else { e -= ddx; } } LEAVE; } void mogltk::Shape::bsubline_4(int x1, int y1, int x2, int y2, ColorP c) { int x, y, ddx, ddy, e; ddy = abs(y2 - y1) << 1; ddx = abs(x1 - x2); e = ddx - ddy; ddx <<= 1; if (x1 > x2) { SWAP(x1, x2); SWAP(y1, y2); } for (x = x1, y = y1; x <= x2; x++) { pixel(x, y, c); if (e < 0) { y--; e += ddx - ddy; } else { e -= ddy; } } } void mogltk::Shape::line(int x1, int y1, int x2, int y2, ColorP c) { if ((x1 == x2) && (y1 == y2)) { printm(M_INFO, "Line is a pixel...\n"); pixel(x1, y1, c); return; } if (x1 == x2) { vline(x1, MIN(y1, y2), MAX(y1, y2), c); return; } if (y1 == y2) { hline(MIN(x1, x2), MAX(x1, x2), y1, c); return; } float k = float(y2 - y1) / float(x2 - x1); if ((k >= 0) && (k <= 1)) { bsubline_1(x1, y1, x2, y2, c); } else if (k > 1) { bsubline_2(x1, y1, x2, y2, c); } else if ((k < 0) && (k >= -1)) { bsubline_4(x1, y1, x2, y2, c); } else { bsubline_3(x1, y1, x2, y2, c); } } void mogltk::Shape::pixel(int x, int y, ColorP c) { ENTER; int bpp = surf->format->BytesPerPixel; Uint8 *p = (Uint8 *)surf->pixels + y * surf->pitch + x * bpp; Uint32 pixel = c.toSDL(surf->format); if ((x < 0) || (y < 0) || (x >= surf->w) || (y >= surf->h)) { printm(M_INFO, "Pixel culling, out of bounds.\n"); return; } switch(bpp) { case 1: *p = pixel; break; case 2: *(Uint16 *)p = pixel; break; case 3: if(SDL_BYTEORDER == SDL_BIG_ENDIAN) { p[0] = (pixel >> 16) & 0xff; p[1] = (pixel >> 8) & 0xff; p[2] = pixel & 0xff; } else { p[0] = pixel & 0xff; p[1] = (pixel >> 8) & 0xff; p[2] = (pixel >> 16) & 0xff; } break; case 4: *(Uint32 *)p = pixel; break; } LEAVE; } void mogltk::Shape::circle(int x0, int y0, int r, ColorP c) { ENTER; int x = 0; int y = r - 1; int d = 3 - 2 * r; int dI = 10 - 4 * r; int rI = 6; while (x <= y) { pixel(x0 + x, y0 + y, c); pixel(x0 - x, y0 + y, c); pixel(x0 + x, y0 - y, c); pixel(x0 - x, y0 - y, c); pixel(x0 + y, y0 + x, c); pixel(x0 - y, y0 + x, c); pixel(x0 + y, y0 - x, c); pixel(x0 - y, y0 - x, c); if (d >= 0) { d += dI; dI += 8; y -= 1; } else { d += rI; dI += 4; } rI += 4; x += 1; } LEAVE; } mogltk::Fill * mogltk::Shape::fcircle(int x0, int y0, int r) { int x = 0; int y = r - 1; int d = 3 - 2 * r; int dI = 10 - 4 * r; int rI = 6; mogltk::Fill * f = new Fill(); bool t = false; int ox = 0, oy = 0; while (x <= y) { /* if (t) { f->insert(x0 - x, y0 - y); f->insert(x0 + x, y0 - y); f->insert(x0 - x, y0 + y); f->insert(x0 + x, y0 + y); } f->insert(x0 - y, y0 - x); f->insert(x0 + y, y0 - x); f->insert(x0 - y, y0 + x); f->insert(x0 + y, y0 + x); */ if (t) { f->insert(x0 - ox, y0 - oy, x0 - x, y0 - y); f->insert(x0 + ox, y0 - oy, x0 + x, y0 - y); f->insert(x0 - ox, y0 + oy, x0 - x, y0 + y); f->insert(x0 + ox, y0 + oy, x0 + x, y0 + y); f->insert(x0 - oy, y0 - ox, x0 - y, y0 - x); f->insert(x0 + oy, y0 - ox, x0 + y, y0 - x); f->insert(x0 - oy, y0 + ox, x0 - y, y0 + x); f->insert(x0 + oy, y0 + ox, x0 + y, y0 + x); } ox = x; oy = y; t = true; if (d >= 0) { d += dI; dI += 8; y -= 1; } else { d += rI; dI += 4; } rI += 4; x += 1; } return f; } void mogltk::Shape::pcircle(int x, int y, int r, ColorP c) { Fill * f = fcircle(x, y, r); fdraw(f, c); delete f; } mogltk::filldrawer::filldrawer(Fill * _f, Texture * _t, ColorP _c) : f(_f), t(_t), c(_c), oldx(-1), oldy(-1) { } mogltk::filldrawer::~filldrawer() { } void mogltk::filldrawer::step(int x, int y) { if (oldy != y) { oldx = -1; } if (oldx == -1) { oldx = x; } else { /* s->hline(oldx, x, y, c); */ Uint32 * p = t->GetPixels(); int i, first = t->GetWidth() * (y - f->GetMinY()) + oldx - f->GetMinX(), last = first - oldx + x; SDL_PixelFormat * format = t->GetFormat(); for (i = first; i <= last; i++) { if (i > (t->GetWidth() * t->GetHeight())) { printm(M_ERROR, "Big problem here. i = %i and W, H = %i, %i\n", i, t->GetWidth(), t->GetHeight()); printm(M_ERROR, "Initially, wanted to draw hline(%i, %i, %i)\n", oldx, x, y); printm(M_ERROR, "With MinX = %i and MinY = %i --> hline(%i, %i, %i)\n", f->GetMinX(), f->GetMinY(), oldx - f->GetMinX(), x - f->GetMinX(), y - f->GetMinY()); printm(M_ERROR, "For info, MaxX = %i and MaxY = %i\n", f->GetMaxX(), f->GetMaxY()); exit(-1); } p[i] = c.toSDL(format); } oldx = -1; } oldy = y; } void mogltk::Shape::fdraw(Fill * f, ColorP c, int sx, int sy) { ENTER; if (!f) return; if (!f->GetTexture()) { filldrawer * w = new filldrawer(f, f->Talloc(), c); f->walk(w); delete w; f->last = c.c; } SDL_PixelFormat * format = f->GetTexture()->GetFormat(); if (f->last == c.c) { Uint32 * p = f->GetTexture()->GetPixels(); int i, n = f->GetTexture()->GetWidth() * f->GetTexture()->GetHeight(); for (i = 0; i < n; i++) if (p[i] & 0xff000000) p[i] = c.toSDL(format); } SDL_Rect r; r.x = f->GetMinX() + sx; r.y = f->GetMinY() + sy; SDL_BlitSurface(f->GetTexture()->GetSurface(), 0, surf, &r); f->last = c.c; LEAVE; } mogltk::segdrawer::segdrawer(Fill * _f, Texture * _t, ColorP _c) : f(_f), t(_t), c(_c), sh(new Shape(_t->GetSurface())) { } mogltk::segdrawer::~segdrawer() { delete sh; } void mogltk::segdrawer::step(int x1, int y1, int x2, int y2) { sh->line(x1 - f->GetMinX(), y1 - f->GetMinY(), x2 - f->GetMinX(), y2 - f->GetMinY(), c); } void mogltk::Shape::sdraw(Fill * f, ColorP c, int sx, int sy) { ENTER; if (!f) return; if (!f->GetSTexture()) { segdrawer * w = new segdrawer(f, f->STalloc(), c); f->swalk(w); delete w; f->last = c.c; } SDL_PixelFormat * format = f->GetSTexture()->GetFormat(); if (f->last == c.c) { Uint32 * p = f->GetSTexture()->GetPixels(); int i, n = f->GetSTexture()->GetWidth() * f->GetSTexture()->GetHeight(); for (i = 0; i < n; i++) if (p[i] & 0xff000000) p[i] = c.toSDL(format); } SDL_Rect r; r.x = f->GetMinX() + sx; r.y = f->GetMinY() + sy; SDL_BlitSurface(f->GetSTexture()->GetSurface(), 0, surf, &r); f->last = c.c; LEAVE; } void mogltk::Shape::obox(int x1, int y1, int x2, int y2, ColorP c) { ENTER; hline(x1, x2, y1, c); hline(x1, x2, y2, c); vline(x1, y1 + 1, y2 - 1, c); vline(x2, y1 + 1, y2 - 1, c); LEAVE; } void mogltk::Shape::box3d(int x1, int y1, int x2, int y2, ColorP face, ColorP shade1, ColorP shade2, int depth, bool bevel) { ENTER; int i; for (i = 0; i < depth; i++) { hline(x1 + i, x2 - i, y1 + i, bevel ? shade2 : shade1); vline(x1 + i, y1 + i, y2 - i, bevel ? shade2 : shade1); } for (i = 0; i < depth; i++) { hline(x1 + i, x2 - i, y2 - i, bevel ? shade1 : shade2); vline(x2 - i, y1 + i, y2 - i, bevel ? shade1 : shade2); } box(x1 + depth, y1 + depth, x2 - depth, y2 - depth, face); LEAVE; } void mogltk::Shape::obox3d(int x1, int y1, int x2, int y2, ColorP shade1, ColorP shade2, bool bevel) { ENTER; if (!bevel) { obox(x1 + 1, y1 + 1, x2, y2, shade1); obox(x1, y1, x2 - 1, y2 - 1, shade2); } else { obox(x1, y1, x2 - 1, y2 - 1, shade1); obox(x1 + 1, y1 + 1, x2, y2, shade2); } LEAVE; } void mogltk::Shape::window(int x1, int y1, int x2, int y2, const String & title, ColorP titlecolor, ColorP titlebackcolor, ColorP front, ColorP shade1, ColorP shade2) { ENTER; box3d(x1, y1, x2, y2, front, shade1, shade2); hline3d(x1 + 2, x2 - 2, y1 + 19, shade1, shade2, 1); box(x1 + 2, y1 + 2, x2 - 2, y1 + 18, titlebackcolor); text((x1 + x2) / 2, y1 + 2, title, titlecolor, CENTER); LEAVE; } void mogltk::Shape::text(int x, int y, const String & text, ColorP textcolor, align_t align, mogltk::Font * _font) { int tsize = _font->singletextsize(text); switch (align) { case LEFT: _font->putcursor(x, y); break; case CENTER: _font->putcursor(x - (tsize / 2), y); break; case RIGHT: _font->putcursor(x - tsize, y); break; } _font->setcolor(textcolor); _font->setshadow(0); _font->printf("%s", text.to_charp()); } void mogltk::Shape::text3d(int x, int y, const String & atext, ColorP textcolor, ColorP shade1, ColorP shade2, align_t align, bool bevel, mogltk::Font * _font) { ENTER; _font->setwspace(1); if (!bevel) { text(x - 1, y - 1, atext, shade2, align); text(x + 1, y + 1, atext, shade1, align); } else { text(x - 1, y - 1, atext, shade1, align); text(x + 1, y + 1, atext, shade2, align); } text(x, y, atext, textcolor, align); _font->setwspace(0); LEAVE; } void mogltk::Shape::button(int x1, int y1, int x2, int y2, const String & atext, bool bevel, ColorP front, ColorP shade1, ColorP shade2, ColorP round, ColorP textcolor, ColorP tshade1, ColorP tshade2) { ENTER; box3d(x1, y1, x2, y2, front, shade1, shade2, 2, bevel); hline(x1, x2, y1 - 1, round); hline(x1, x2, y2 + 1, round); vline(x1 - 1, y1, y2, round); vline(x2 + 1, y1, y2, round); pixel(x1, y1, round); pixel(x1, y2, round); pixel(x2, y1, round); pixel(x2, y2, round); text3d((x1 + x2) / 2, (y1 + y2) / 2 - 8, atext, textcolor, tshade1, tshade2, CENTER, bevel); LEAVE; } bool mogltk::Shape::Enter() { if (SDL_MUSTLOCK(surf)) { SDL_LockSurface(surf); return true; } else { return false; } } void mogltk::Shape::Leave(bool locked) { if (locked) SDL_UnlockSurface(surf); } void mogltk::Shape::tbox(Texture *, int x1, int y1, int x2, int y2, int tx, int ty, double f, ColorP c) { } void mogltk::Shape::tbox(Texture *, int x1, int y1, int x2, int y2, int tx1, int ty1, int tx2, int ty2, ColorP c) { } void mogltk::Shape::tbox(Texture *, int x1, int y1, int x2, int y2, ColorP, ColorP, ColorP, ColorP, int tx, int ty, double f) { } void mogltk::Shape::tbox(Texture *, int x1, int y1, int x2, int y2, ColorP, ColorP, ColorP, ColorP, int tx1, int ty1, int tx2, int ty2) { } void mogltk::Shape::vline(int x, int y1, int y2, ColorP c1, ColorP c2) { } void mogltk::Shape::hline(int x1, int x2, int y, ColorP c1, ColorP c2) { } void mogltk::Shape::box(int x1, int y1, int x2, int y2, ColorP c1, ColorP c2, ColorP c3, ColorP c4) { } void mogltk::Shape::obox(int x1, int y1, int x2, int y2, ColorP c1, ColorP c2, ColorP c3, ColorP c4) { }