/* Starshatter OpenSource Distribution Copyright (c) 1997-2004, Destroyer Studios LLC. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name "Destroyer Studios" nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. SUBSYSTEM: nGenEx.lib FILE: Bitmap.cpp AUTHOR: John DiCamillo OVERVIEW ======== Bitmap Resource class */ #include "MemDebug.h" #include "Bitmap.h" #include "Video.h" #include "Color.h" #include "Game.h" // +--------------------------------------------------------------------+ DWORD GetRealTime(); static inline void swap(int& a, int& b) { int tmp=a; a=b; b=tmp; } static inline void sort(int& a, int& b) { if (a>b) swap(a,b); } static inline void swap(double& a, double& b) { double tmp=a; a=b; b=tmp; } static inline void sort(double& a, double& b) { if (a>b) swap(a,b); } static void draw_strip(BYTE* dst, int pitch, int pixsize, int x, int y, int len, Color color); static void draw_vline(BYTE* dst, int pitch, int pixsize, int x, int y, int len, Color color); class WinPlot { public: WinPlot(Bitmap* bmp); void plot(int x, int y, DWORD val, int exch=0); private: BYTE* s; int pitch, pixsize; }; WinPlot::WinPlot(Bitmap* bmp) { s = bmp->GetSurface(); pitch = bmp->Pitch(); pixsize = bmp->PixSize(); } void WinPlot::plot(int x, int y, DWORD val, int exch) { if (exch) swap(x,y); BYTE* dst = s + y*pitch + x*pixsize; switch (pixsize) { case 1: *dst = (BYTE) val; break; case 2: { LPWORD dst2 = (LPWORD) dst; *dst2 = (WORD) val; } break; case 4: { LPDWORD dst4 = (LPDWORD) dst; *dst4 = (DWORD) val; } break; } } // +--------------------------------------------------------------------+ Bitmap::Bitmap() : type(BMP_SOLID), width(0), height(0), ownpix(false), alpha_loaded(false), texture(false), pix(0), hipix(0), mapsize(0), last_modified(0) { strcpy_s(filename, "Bitmap()"); } Bitmap::Bitmap(int w, int h, ColorIndex* p, int t) : type(t), width(w), height(h), ownpix(false), alpha_loaded(false), texture(false), pix(p), hipix(0), mapsize(w*h), last_modified(GetRealTime()) { sprintf_s(filename, "Bitmap(%d, %d, index, type=%d)", w, h, (int) t); } Bitmap::Bitmap(int w, int h, Color* p, int t) : type(t), width(w), height(h), ownpix(false), alpha_loaded(false), texture(false), pix(0), hipix(p), mapsize(w*h), last_modified(GetRealTime()) { sprintf_s(filename, "Bitmap(%d, %d, hicolor, type=%d)", w, h, (int) t); } // +--------------------------------------------------------------------+ Bitmap::~Bitmap() { if (ownpix) { delete [] pix; delete [] hipix; } } // +--------------------------------------------------------------------+ int Bitmap::BmpSize() const { return mapsize * PixSize(); } int Bitmap::RowSize() const { return width; } int Bitmap::Pitch() const { return width * PixSize(); } int Bitmap::PixSize() const { if (hipix) return sizeof(Color); else if (pix) return sizeof(ColorIndex); return 0; } BYTE* Bitmap::GetSurface() { if (ownpix) { if (hipix) return (BYTE*) hipix; return (BYTE*) pix; } return 0; } // +--------------------------------------------------------------------+ void Bitmap::BitBlt(int x, int y, const Bitmap& srcBmp, int sx, int sy, int w, int h, bool blend) { if (!ownpix || x < 0 || y < 0 || x >= width || y >= height) return; if (sx < 0 || sy < 0 || sx >= srcBmp.Width() || sy >= srcBmp.Height()) return; if (hipix) { if (srcBmp.HiPixels()) { int dpitch = width; int spitch = srcBmp.Width(); int rowlen = w * sizeof(Color); Color* dst = hipix + (y*dpitch) + x; Color* src = srcBmp.HiPixels() + (sy*spitch) + sx; if (!blend) { for (int i = 0; i < h; i++) { memcpy(dst, src, rowlen); dst += dpitch; src += spitch; } } else { for (int i = 0; i < h; i++) { Color* ps = src; Color* pd = dst; for (int n = 0; n < w; n++) { if (ps->Value()) pd->Set(Color::FormattedBlend(ps->Value(), pd->Value())); ps++; pd++; } dst += dpitch; src += spitch; } } } else { int dpitch = width; int spitch = srcBmp.Width(); Color* dst = hipix + (y*dpitch) + x; ColorIndex* src = srcBmp.Pixels() + (sy*spitch) + sx; if (!blend) { for (int j = 0; j < h; j++) { for (int i = 0; i < w; i++) { dst[i].Set(src[i].Formatted()); } dst += dpitch; src += spitch; } } else { for (int i = 0; i < h; i++) { ColorIndex* ps = src; Color* pd = dst; for (int n = 0; n < w; n++) { if (ps->Index()) pd->Set(Color::FormattedBlend(ps->Formatted(), pd->Value())); ps++; pd++; } } dst += dpitch; src += spitch; } } } else if (pix) { if (srcBmp.Pixels()) { int dpitch = width; int spitch = srcBmp.Width(); int rowlen = w; Color* dst = hipix + (y*dpitch) + x; Color* src = srcBmp.HiPixels() + (sy*spitch) + sx; for (int i = 0; i < h; i++) { #pragma warning(suppress: 28183) memcpy(dst, src, rowlen); dst += dpitch; src += spitch; } } } alpha_loaded = srcBmp.alpha_loaded; last_modified = GetRealTime(); } // +--------------------------------------------------------------------+ void Bitmap::CopyBitmap(const Bitmap& rhs) { if (ownpix) { delete [] pix; delete [] hipix; pix = 0; hipix = 0; } type = rhs.type; width = rhs.width; height = rhs.height; alpha_loaded = rhs.alpha_loaded; texture = rhs.texture; ownpix = true; mapsize = width * height; if (rhs.pix) { pix = new(__FILE__,__LINE__) ColorIndex[mapsize]; if (!pix) { width = 0; height = 0; mapsize = 0; } else { memcpy(pix, rhs.pix, mapsize*sizeof(ColorIndex)); } } if (rhs.hipix) { hipix = new(__FILE__,__LINE__) Color[mapsize]; if (!hipix && !pix) { width = 0; height = 0; mapsize = 0; } else { memcpy(hipix, rhs.hipix, mapsize*sizeof(Color)); } } last_modified = GetRealTime(); } // +--------------------------------------------------------------------+ void Bitmap::ClearImage() { if (ownpix) { delete [] pix; delete [] hipix; pix = 0; hipix = 0; } type = BMP_SOLID; width = 0; height = 0; mapsize = 0; ownpix = false; texture = false; last_modified = GetRealTime(); } // +--------------------------------------------------------------------+ void Bitmap::CopyImage(int w, int h, BYTE* p, int t) { if (ownpix) { delete [] pix; pix = 0; } else { hipix = 0; } type = t; width = w; height = h; ownpix = true; texture = false; mapsize = w * h; pix = new(__FILE__,__LINE__) ColorIndex[mapsize]; if (!pix) { width = 0; height = 0; mapsize = 0; } else { memcpy(pix, p, mapsize); } last_modified = GetRealTime(); } // +--------------------------------------------------------------------+ void Bitmap::CopyHighColorImage(int w, int h, DWORD* p, int t) { if (ownpix) { delete [] hipix; hipix = 0; } else { pix = 0; } type = t; width = w; height = h; ownpix = true; texture = false; mapsize = w * h; hipix = new(__FILE__,__LINE__) Color[mapsize]; if (!hipix) { width = 0; height = 0; mapsize = 0; } else { memcpy(hipix, p, mapsize*sizeof(DWORD)); } last_modified = GetRealTime(); } // +--------------------------------------------------------------------+ void Bitmap::CopyAlphaImage(int w, int h, BYTE* a) { if (!hipix || width != w || height != h) return; type = BMP_TRANSLUCENT; alpha_loaded = true; Color* p = hipix; for (int i = 0; i < mapsize; i++) { p->SetAlpha(*a); p++; a++; } last_modified = GetRealTime(); } void Bitmap::CopyAlphaRedChannel(int w, int h, DWORD* a) { if (!hipix || width != w || height != h) return; type = BMP_TRANSLUCENT; alpha_loaded = true; Color* p = hipix; for (int i = 0; i < mapsize; i++) { p->SetAlpha((BYTE) ((*a & Color::RMask) >> Color::RShift)); p++; a++; } last_modified = GetRealTime(); } // +--------------------------------------------------------------------+ void Bitmap::AutoMask(DWORD mask) { if (!hipix || !mapsize || alpha_loaded) return; type = BMP_TRANSLUCENT; alpha_loaded = true; Color* p = hipix; DWORD m = mask & Color::RGBMask; for (int i = 0; i < mapsize; i++) { if ((p->Value() & Color::RGBMask) == m) p->SetAlpha(0); else p->SetAlpha(255); p++; } last_modified = GetRealTime(); } // +--------------------------------------------------------------------+ void Bitmap::FillColor(Color c) { if (!width || !height) return; if (pix) { ColorIndex* p = pix; BYTE index = c.Index(); for (int i = 0; i < mapsize; i++) *p++ = index; } if (hipix) { Color* p = hipix; DWORD value = c.Value(); for (int i = 0; i < mapsize; i++) { p->Set(value); p++; } } last_modified = GetRealTime(); } // +--------------------------------------------------------------------+ void Bitmap::ScaleTo(int w, int h) { if (w < 1 || h < 1) return; double dx = (double) width / (double) w; double dy = (double) height / (double) h; bool mem_ok = true; if (hipix) { Color* src = hipix; Color* buf = new(__FILE__,__LINE__) Color[w*h]; Color* dst = buf; if (!buf) { mem_ok = false; } else { for (int y = 0; y < h; y++) { int y_offset = (int) (y * dy); for (int x = 0; x < w; x++) { int x_offset = (int) (x * dx); src = hipix + (y_offset * width) + x_offset; *dst++ = *src; } } if (ownpix) delete [] hipix; hipix = buf; ownpix = true; } } if (pix) { ColorIndex* src = pix; ColorIndex* buf = new(__FILE__,__LINE__) ColorIndex[w*h]; ColorIndex* dst = buf; if (!buf) { mem_ok = false; } else { for (int y = 0; y < h; y++) { int y_offset = (int) (y * dy); for (int x = 0; x < w; x++) { int x_offset = (int) (x * dx); src = pix + (y_offset * width) + x_offset; *dst++ = *src; } } if (ownpix) delete [] pix; pix = buf; ownpix = true; } } if (mem_ok) { width = w; height = h; mapsize = width * height; } } // +--------------------------------------------------------------------+ void Bitmap::MakeIndexed() { if (hipix) { if (pix && ownpix) delete [] pix; pix = new(__FILE__,__LINE__) ColorIndex[mapsize]; if (pix) { Color* src = hipix; ColorIndex* dst = pix; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { *dst++ = src->Index(); src++; } } if (!ownpix) hipix = 0; ownpix = true; } } } // +--------------------------------------------------------------------+ void Bitmap::MakeHighColor() { if (pix) { if (hipix && ownpix) delete [] hipix; hipix = new(__FILE__,__LINE__) Color[mapsize]; if (hipix) { ColorIndex* src = pix; Color* dst = hipix; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { *dst++ = src->Index(); src++; } } if (!ownpix) pix = 0; ownpix = true; } } } // +--------------------------------------------------------------------+ static int FindBestTexSize(int n, int max_size) { int delta = 100000; int best = 1; for (int i = 0; i < 12; i++) { int size = 1 << i; if (size > max_size) break; int dx = abs(n-size); if (size < n) dx *= 4; if (dx < delta) { delta = dx; best = size; } } return best; } void Bitmap::MakeTexture() { if (width < 1 || height < 1 || (!pix && !hipix)) { if (ownpix) { delete [] pix; delete [] hipix; } width = 0; height = 0; pix = 0; hipix = 0; texture = false; return; } // texture surface format is 32-bit RGBA: if (pix && !hipix) { MakeHighColor(); } // check size and aspect ratio: int max_tex_size = Game::MaxTexSize(); int max_tex_aspect = Game::MaxTexAspect(); int best_width = FindBestTexSize(width, max_tex_size); int best_height = FindBestTexSize(height, max_tex_size); int aspect = 1; // correct sizes for aspect if necessary: if (best_width > best_height) { aspect = best_width / best_height; if (aspect > max_tex_aspect) best_height = best_width / max_tex_aspect; } else { aspect = best_height / best_width; if (aspect > max_tex_aspect) best_width = best_height / max_tex_aspect; } // rescale if necessary: if (width != best_width || height != best_height) ScaleTo(best_width, best_width); texture = true; } // +--------------------------------------------------------------------+ ColorIndex Bitmap::GetIndex(int x, int y) const { ColorIndex result(0); if (x < 0 || y < 0 || x > width-1 || y > height-1) return result; if (pix) { result = *(pix + y*width + x); } else if (hipix) { result = (hipix + y*width + x)->Index(); } return result; } // +--------------------------------------------------------------------+ Color Bitmap::GetColor(int x, int y) const { Color result = Color::Black; if (x < 0 || y < 0 || x > width-1 || y > height-1) return result; if (pix) { result = (pix + y*width + x)->Index(); } else if (hipix) { result = *(hipix + y*width + x); } return result; } // +--------------------------------------------------------------------+ void Bitmap::SetIndex(int x, int y, ColorIndex c) { if (x < 0 || y < 0 || x > width || y > height) return; if (pix) { *(pix + y*width + x) = c; } else if (hipix) { *(hipix + y*width + x) = c.Index(); } last_modified = GetRealTime(); } // +--------------------------------------------------------------------+ void Bitmap::SetColor(int x, int y, Color c) { if (x < 0 || y < 0 || x > width || y > height) return; if (pix) { *(pix + y*width + x) = c.Index(); } else if (hipix) { *(hipix + y*width + x) = c; } last_modified = GetRealTime(); } // +--------------------------------------------------------------------+ void Bitmap::SetFilename(const char* s) { if (s) { int n = strlen(s); if (n >= 60) { ZeroMemory(filename, sizeof(filename)); strcpy_s(filename, "..."); strcat_s(filename, s + n - 59); filename[63] = 0; } else { strcpy_s(filename, s); } } } // +--------------------------------------------------------------------+ Bitmap* Bitmap::Default() { static Bitmap def; if (!def.width) { def.width = def.height = 64; def.mapsize = 64*64; def.ownpix = true; def.pix = new(__FILE__,__LINE__) ColorIndex[def.mapsize]; // 8 bit palette mode if (def.pix) { ColorIndex* p = def.pix; for (int y = 0; y < 64; y++) { for (int x = 0; x < 64; x++) { double distance = sqrt((x-32.0)*(x-32.0) + (y-32.0)*(y-32.0)); if (distance > 31.0) distance = 31.0; BYTE color = 24 + (BYTE) distance; if (x == 0 || y == 0) color = 255; else if (x == 32 || y == 32) color = 251; *p++ = color; } } } } return &def; } // +--------------------------------------------------------------------+ static List bitmap_cache; Bitmap* Bitmap::GetBitmapByID(HANDLE bmp_id) { for (int i = 0; i < bitmap_cache.size(); i++) { if (bitmap_cache[i]->Handle() == bmp_id) { return bitmap_cache[i]; } } return 0; } Bitmap* Bitmap::CheckCache(const char* filename) { for (int i = 0; i < bitmap_cache.size(); i++) { if (!_stricmp(bitmap_cache[i]->GetFilename(), filename)) { return bitmap_cache[i]; } } return 0; } void Bitmap::AddToCache(Bitmap* bmp) { bitmap_cache.append(bmp); } void Bitmap::CacheUpdate() { for (int i = 0; i < bitmap_cache.size(); i++) { Bitmap* bmp = bitmap_cache[i]; if (bmp->IsTexture()) bmp->MakeTexture(); } } void Bitmap::ClearCache() { bitmap_cache.destroy(); } DWORD Bitmap::CacheMemoryFootprint() { DWORD result = sizeof(bitmap_cache); result += bitmap_cache.size() * sizeof(Bitmap*); for (int i = 0; i < bitmap_cache.size(); i++) { Bitmap* bmp = bitmap_cache[i]; if (bmp->pix) result += bmp->mapsize * sizeof(ColorIndex); if (bmp->hipix) result += bmp->mapsize * sizeof(Color); } return result; } // +--------------------------------------------------------------------+ bool Bitmap::ClipLine(int& x1, int& y1, int& x2, int& y2) { // vertical lines: if (x1==x2) { clip_vertical: sort(y1,y2); if (x1 < 0 || x1 >= width) return false; if (y1 < 0) y1 = 0; if (y2 >= height) y2 = height; return true; } // horizontal lines: if (y1==y2) { clip_horizontal: sort(x1,x2); if (y1 < 0 || y1 >= height) return false; if (x1 < 0) x1 = 0; if (x2 > width) x2 = width; return true; } // general lines: // sort left to right: if (x1 > x2) { swap(x1,x2); swap(y1,y2); } double m = (double)(y2-y1) / (double)(x2-x1); double b = (double) y1 - (m * x1); // clip: if (x1 < 0) { x1 = 0; y1 = (int) b; } if (x1 >= width) return false; if (x2 < 0) return false; if (x2 > width-1) { x2 = width-1; y2 = (int) (m * x2 + b); } if (y1 < 0 && y2 < 0) return false; if (y1 >= height && y2 >= height) return false; if (y1 < 0) { y1 = 0; x1 = (int) (-b/m); } if (y1 >= height) { y1 = height-1; x1 = (int) ((y1-b)/m); } if (y2 < 0) { y2 = 0; x2 = (int) (-b/m); } if (y2 >= height) { y2 = height-1; x2 = (int) ((y2-b)/m); } if (x1 == x2) goto clip_vertical; if (y1 == y2) goto clip_horizontal; return true; } // +--------------------------------------------------------------------+ bool Bitmap::ClipLine(double& x1, double& y1, double& x2, double& y2) { // vertical lines: if (x1==x2) { clip_vertical: sort(y1,y2); if (x1 < 0 || x1 >= width) return false; if (y1 < 0) y1 = 0; if (y2 >= height) y2 = height; return true; } // horizontal lines: if (y1==y2) { clip_horizontal: sort(x1,x2); if (y1 < 0 || y1 >= height) return false; if (x1 < 0) x1 = 0; if (x2 > width) x2 = width; return true; } // general lines: // sort left to right: if (x1 > x2) { swap(x1,x2); swap(y1,y2); } double m = (double)(y2-y1) / (double)(x2-x1); double b = (double) y1 - (m * x1); // clip: if (x1 < 0) { x1 = 0; y1 = b; } if (x1 >= width) return false; if (x2 < 0) return false; if (x2 > width-1) { x2 = width-1; y2 = (m * x2 + b); } if (y1 < 0 && y2 < 0) return false; if (y1 >= height && y2 >= height) return false; if (y1 < 0) { y1 = 0; x1 = (-b/m); } if (y1 >= height) { y1 = height-1; x1 = ((y1-b)/m); } if (y2 < 0) { y2 = 0; x2 = (-b/m); } if (y2 >= height) { y2 = height-1; x2 = ((y2-b)/m); } if (x1 == x2) goto clip_vertical; if (y1 == y2) goto clip_horizontal; return true; } // +--------------------------------------------------------------------+ void Bitmap::DrawLine(int x1, int y1, int x2, int y2, Color color) { BYTE* s = GetSurface(); if (!s) return; last_modified = GetRealTime(); // vertical lines: if (x1==x2) { draw_vertical: sort(y1,y2); int fh = y2-y1; if (x1 < 0 || x1 >= width) return; if (y1 < 0) y1 = 0; if (y2 >= height) y2 = height; fh = y2-y1; draw_vline(s, Pitch(), PixSize(), x1, y1, fh, color); return; } // horizontal lines: if (y1==y2) { draw_horizontal: sort(x1,x2); int fw = x2-x1; if (y1 < 0 || y1 >= height) return; if (x1 < 0) x1 = 0; if (x2 > width) x2 = width; fw = x2-x1; draw_strip(s, Pitch(), PixSize(), x1, y1, fw, color); return; } // general lines: // sort left to right: if (x1 > x2) { swap(x1,x2); swap(y1,y2); } double m = (double)(y2-y1) / (double)(x2-x1); double b = (double) y1 - (m * x1); // clip: if (x1 < 0) { x1 = 0; y1 = (int) b; } if (x1 >= width) return; if (x2 < 0) return; if (x2 > width-1) { x2 = width-1; y2 = (int) (m * x2 + b); } if (y1 < 0 && y2 < 0) return; if (y1 >= height && y2 >= height) return; if (y1 < 0) { y1 = 0; x1 = (int) (-b/m); } if (y1 >= height) { y1 = height-1; x1 = (int) ((y1-b)/m); } if (y2 < 0) { y2 = 0; x2 = (int) (-b/m); } if (y2 >= height) { y2 = height-1; x2 = (int) ((y2-b)/m); } if (x1 > x2) return; if (x1 == x2) goto draw_vertical; if (y1 == y2) goto draw_horizontal; // plot the line using /* Symmetric Double Step Line Algorithm by Brian Wyvill from "Graphics Gems", Academic Press, 1990 */ WinPlot plotter(this); DWORD pix = color.Value(); int sign_x=1, sign_y=1, step, reflect; int i, inc1, inc2, c, D, x_end, pixleft; int dx = x2 - x1; int dy = y2 - y1; if (dx < 0) { sign_x = -1; dx *= -1; } if (dy < 0) { sign_y = -1; dy *= -1; } // decide increment sign by the slope sign if (sign_x == sign_y) step = 1; else step = -1; if (dy > dx) { // chooses axis of greatest movement (make * dx) swap(x1, y1); swap(x2, y2); swap(dx, dy); reflect = 1; } else reflect = 0; if (x1 > x2) { // start from the smaller coordinate swap(x1,x2); swap(y1,y2); } /* Note dx=n implies 0 - n or (dx+1) pixels to be set */ /* Go round loop dx/4 times then plot last 0,1,2 or 3 pixels */ /* In fact (dx-1)/4 as 2 pixels are already plottted */ x_end = (dx - 1) / 4; pixleft = (dx - 1) % 4; /* number of pixels left over at the end */ plotter.plot(x1, y1, pix, reflect); plotter.plot(x2, y2, pix, reflect); /* plot first two points */ inc2 = 4 * dy - 2 * dx; if (inc2 < 0) { /* slope less than 1/2 */ c = 2 * dy; inc1 = 2 * c; D = inc1 - dx; for (i = 0; i < x_end; i++) { /* plotting loop */ ++x1; --x2; if (D < 0) { /* pattern 1 forwards */ plotter.plot(x1, y1, pix, reflect); plotter.plot(++x1, y1, pix, reflect); /* pattern 1 backwards */ plotter.plot(x2, y2, pix, reflect); plotter.plot(--x2, y2, pix, reflect); D += inc1; } else { if (D < c) { /* pattern 2 forwards */ plotter.plot(x1, y1, pix, reflect); plotter.plot(++x1, y1 += step, pix, reflect); /* pattern 2 backwards */ plotter.plot(x2, y2, pix, reflect); plotter.plot(--x2, y2 -= step, pix, reflect); } else { /* pattern 3 forwards */ plotter.plot(x1, y1 += step, pix, reflect); plotter.plot(++x1, y1, pix, reflect); /* pattern 3 backwards */ plotter.plot(x2, y2 -= step, pix, reflect); plotter.plot(--x2, y2, pix, reflect); } D += inc2; } } /* end for */ /* plot last pattern */ if (pixleft) { if (D < 0) { plotter.plot(++x1, y1, pix, reflect); /* pattern 1 */ if (pixleft > 1) plotter.plot(++x1, y1, pix, reflect); if (pixleft > 2) plotter.plot(--x2, y2, pix, reflect); } else { if (D < c) { plotter.plot(++x1, y1, pix, reflect); /* pattern 2 */ if (pixleft > 1) plotter.plot(++x1, y1 += step, pix, reflect); if (pixleft > 2) plotter.plot(--x2, y2, pix, reflect); } else { /* pattern 3 */ plotter.plot(++x1, y1 += step, pix, reflect); if (pixleft > 1) plotter.plot(++x1, y1, pix, reflect); if (pixleft > 2) plotter.plot(--x2, y2 -= step, pix, reflect); } } } /* end if pixleft */ } /* end slope < 1/2 */ else { /* slope greater than 1/2 */ c = 2 * (dy - dx); inc1 = 2 * c; D = inc1 + dx; for (i = 0; i < x_end; i++) { ++x1; --x2; if (D > 0) { /* pattern 4 forwards */ plotter.plot(x1, y1 += step, pix, reflect); plotter.plot(++x1, y1 += step, pix, reflect); /* pattern 4 backwards */ plotter.plot(x2, y2 -= step, pix, reflect); plotter.plot(--x2, y2 -= step, pix, reflect); D += inc1; } else { if (D < c) { /* pattern 2 forwards */ plotter.plot(x1, y1, pix, reflect); plotter.plot(++x1, y1 += step, pix, reflect); /* pattern 2 backwards */ plotter.plot(x2, y2, pix, reflect); plotter.plot(--x2, y2 -= step, pix, reflect); } else { /* pattern 3 forwards */ plotter.plot(x1, y1 += step, pix, reflect); plotter.plot(++x1, y1, pix, reflect); /* pattern 3 backwards */ plotter.plot(x2, y2 -= step, pix, reflect); plotter.plot(--x2, y2, pix, reflect); } D += inc2; } } /* end for */ /* plot last pattern */ if (pixleft) { if (D > 0) { plotter.plot(++x1, y1 += step, pix, reflect); /* pattern 4 */ if (pixleft > 1) plotter.plot(++x1, y1 += step, pix, reflect); if (pixleft > 2) plotter.plot(--x2, y2 -= step, pix, reflect); } else { if (D < c) { plotter.plot(++x1, y1, pix, reflect); /* pattern 2 */ if (pixleft > 1) plotter.plot(++x1, y1 += step, pix, reflect); if (pixleft > 2) plotter.plot(--x2, y2, pix, reflect); } else { /* pattern 3 */ plotter.plot(++x1, y1 += step, pix, reflect); if (pixleft > 1) plotter.plot(++x1, y1, pix, reflect); if (pixleft > 2) { if (D > c) /* step 3 */ plotter.plot(--x2, y2 -= step, pix, reflect); else /* step 2 */ plotter.plot(--x2, y2, pix, reflect); } } } } } } // +--------------------------------------------------------------------+ void Bitmap::DrawRect(int x1, int y1, int x2, int y2, Color color) { sort(x1,x2); sort(y1,y2); int fw = x2-x1; int fh = y2-y1; if (fw == 0 || fh == 0) return; // perform clip int left = (x1 >= 0); int right = (x2 <= width); int top = (y1 >= 0); int bottom = (y2 <= height); BYTE* s = GetSurface(); if (!s) return; int pitch = Pitch(); int pixsize = PixSize(); if (left) draw_vline(s, pitch, pixsize, x1, y1, fh, color); if (right) draw_vline(s, pitch, pixsize, x2, y1, fh, color); if (top) draw_strip(s, pitch, pixsize, x1, y1, fw, color); if (bottom) draw_strip(s, pitch, pixsize, x1, y2, fw, color); last_modified = GetRealTime(); } // +--------------------------------------------------------------------+ void Bitmap::DrawRect(const Rect& r, Color color) { if (r.w == 0 || r.h == 0) return; int x1 = r.x; int y1 = r.y; int x2 = r.x + r.w; int y2 = r.y + r.h; // perform clip int left = (x1 >= 0); int right = (x2 <= width); int top = (y1 >= 0); int bottom = (y2 <= height); BYTE* s = GetSurface(); if (!s) return; int pitch = Pitch(); int pixsize = PixSize(); if (left) draw_vline(s, pitch, pixsize, x1, y1, r.h, color); if (right) draw_vline(s, pitch, pixsize, x2, y1, r.h, color); if (top) draw_strip(s, pitch, pixsize, x1, y1, r.w, color); if (bottom) draw_strip(s, pitch, pixsize, x1, y2, r.w, color); last_modified = GetRealTime(); } // +--------------------------------------------------------------------+ void Bitmap::FillRect(int x1, int y1, int x2, int y2, Color color) { // perform clip if (x1 < 0) x1 = 0; if (x2 > width-1) x2 = width-1; if (y1 < 0) y1 = 0; if (y2 > height) y2 = height; int fw = x2-x1; int fh = y2-y1; if (fw == 0 || fh == 0) return; BYTE* s = GetSurface(); if (!s) return; int pitch = Pitch(); int pixsize = PixSize(); for (int i = 0; i < fh; i++) draw_strip(s, pitch, pixsize, x1, y1+i, fw, color); last_modified = GetRealTime(); } // +--------------------------------------------------------------------+ void Bitmap::FillRect(const Rect& r, Color color) { int x1 = r.x; int y1 = r.y; int x2 = r.x + r.w; int y2 = r.y + r.h; // perform clip if (x1 < 0) x1 = 0; if (x2 > width-1) x2 = width-1; if (y1 < 0) y1 = 0; if (y2 > height) y2 = height; int fw = x2-x1; int fh = y2-y1; if (fw == 0 || fh == 0) return; BYTE* s = GetSurface(); if (!s) return; int pitch = Pitch(); int pixsize = PixSize(); for (int i = 0; i < fh; i++) draw_strip(s, pitch, pixsize, x1, y1+i, fw, color); last_modified = GetRealTime(); } // +--------------------------------------------------------------------+ void Bitmap::DrawEllipse(int x1, int y1, int x2, int y2, Color color, BYTE quad) { BYTE* orig = GetSurface(); BYTE* s = orig; if (!s) return; sort(x1,x2); sort(y1,y2); int fw = x2-x1; int fh = y2-y1; if (fw < 1 || fh < 1) return; // clip: if (x1 >= width || x2 < 0) return; if (y1 >= height || y2 < 0) return; double a = fw / 2.0; double b = fh / 2.0; double a2 = a*a; double b2 = b*b; int x = 0; int y = (int) b; int x0 = (x1+x2)/2; int y0 = (y1+y2)/2; // clip super-giant ellipses: if (x1 < 0 && y1 < 0 && x2 > width && y2 > height) { double r2 = (a2 32 * height) return; double ul = (x1-x0)*(x1-x0) + (y1-y0)*(y1-y0); double ur = (x2-x0)*(x2-x0) + (y1-y0)*(y1-y0); double ll = (x1-x0)*(x1-x0) + (y2-y0)*(y2-y0); double lr = (x2-x0)*(x2-x0) + (y2-y0)*(y2-y0); if (ul > r2 && ur > r2 && ll > r2 && lr > r2) return; } DrawEllipsePoints(x0,y0,x,y,color,quad); // region 1 double d1 = (b2)-(a2*b)+(0.25*a2); while ((a2)*(y-0.5) > (b2)*(x+1)) { if (d1 < 0) d1 += b2*(2*x+3); else { d1 += b2*(2*x+3) + a2*(-2*y+2); y--; } x++; DrawEllipsePoints(x0,y0,x,y,color,quad); } // region 2 double d2 = b2*(x+0.5)*(x+0.5) + a2*(y-1)*(y-1) - a2*b2; while (y > 0) { if (d2 < 0) { d2 += b2*(2*x+2) + a2*(-2*y+3); x++; } else d2 += a2*(-2*y+3); y--; DrawEllipsePoints(x0,y0,x,y,color,quad); } last_modified = GetRealTime(); } void Bitmap::DrawEllipsePoints(int x0, int y0, int x, int y, Color c, BYTE quad) { BYTE* s = GetSurface(); if (!s) return; int pitch = Pitch(); int pixsize = PixSize(); int left = x0-x; int right = x0+x+1; int top = y0-y; int bottom = y0+y+1; // clip: if (left >= width || right < 0) return; if (top >= height || bottom < 0) return; BYTE* dst = 0; DWORD cf = c.Value(); if (left >= 0 && top >= 0 && quad&1) { dst = s + top*pitch + left*pixsize; switch (pixsize) { case 1: *dst = (BYTE) cf; break; case 2: { LPWORD sw = (LPWORD) dst; *sw = (WORD) cf; } break; case 4: { LPDWORD sd = (LPDWORD) dst; *sd = (DWORD) cf; } break; } } if (right < width && top >= 0 && quad&2) { dst = s + top*pitch + right*pixsize; switch (pixsize) { case 1: *dst = (BYTE) cf; break; case 2: { LPWORD sw = (LPWORD) dst; *sw = (WORD) cf; } break; case 4: { LPDWORD sd = (LPDWORD) dst; *sd = (DWORD) cf; } break; } } if (left >= 0 && bottom < height && quad&4) { dst = s + bottom*pitch + left*pixsize; switch (pixsize) { case 1: *dst = (BYTE) cf; break; case 2: { LPWORD sw = (LPWORD) dst; *sw = (WORD) cf; } break; case 4: { LPDWORD sd = (LPDWORD) dst; *sd = (DWORD) cf; } break; } } if (right < width && bottom < height && quad&4) { dst = s + bottom*pitch + right*pixsize; switch (pixsize) { case 1: *dst = (BYTE) cf; break; case 2: { LPWORD sw = (LPWORD) dst; *sw = (WORD) cf; } break; case 4: { LPDWORD sd = (LPDWORD) dst; *sd = (DWORD) cf; } break; } } } // +--------------------------------------------------------------------+ static void draw_strip(BYTE* s, int pitch, int pixsize, int x, int y, int len, Color color) { if (!s) return; s += y*pitch + x*pixsize; DWORD value = color.Formatted(); switch (pixsize) { case 1: { if (len > 1) memset(s, (BYTE) value, len); } break; case 2: { LPWORD sw = (LPWORD) s; for (int x = 0; x < len; x++) { *sw++ = (WORD) value; } } break; case 4: { Color* sd = (Color*) s; for (int x = 0; x < len; x++) { *sd++ = color; } } break; } } // +--------------------------------------------------------------------+ static void draw_vline(BYTE* s, int pitch, int pixsize, int x, int y, int len, Color color) { if (!s) return; s += (y)*pitch + (x)*pixsize; DWORD value = color.Formatted(); switch (pixsize) { case 1: { for (int y = 0; y < len; y++) { *s = (BYTE) value; s += pitch; } } break; case 2: { LPWORD sw = (LPWORD) s; pitch /= 2; for (int y = 0; y < len; y++) { *sw = (WORD) value; sw += pitch; } } break; case 4: { Color* sd = (Color*) s; pitch /= 4; for (int y = 0; y < len; y++) { *sd = color; sd += pitch; } } break; } }