ResidualVM logo ResidualVM website - Forums - Contact us BuildBot - Doxygen - Wiki curved edge

surface.cpp

Go to the documentation of this file.
00001 /* ScummVM - Graphic Adventure Engine
00002  *
00003  * ScummVM is the legal property of its developers, whose names
00004  * are too numerous to list here. Please refer to the COPYRIGHT
00005  * file distributed with this source distribution.
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  *
00021  */
00022 
00023 #include "common/algorithm.h"
00024 #include "common/endian.h"
00025 #include "common/util.h"
00026 #include "common/rect.h"
00027 #include "common/textconsole.h"
00028 #include "graphics/primitives.h"
00029 #include "graphics/surface.h"
00030 #include "graphics/conversion.h"
00031 
00032 namespace Graphics {
00033 
00034 template<typename T>
00035 static void plotPoint(int x, int y, int color, void *data) {
00036     Surface *s = (Surface *)data;
00037     if (x >= 0 && x < s->w && y >= 0 && y < s->h) {
00038         T *ptr = (T *)s->getBasePtr(x, y);
00039         *ptr = (T)color;
00040     }
00041 }
00042 
00043 void Surface::drawLine(int x0, int y0, int x1, int y1, uint32 color) {
00044     if (format.bytesPerPixel == 1)
00045         Graphics::drawLine(x0, y0, x1, y1, color, plotPoint<byte>, this);
00046     else if (format.bytesPerPixel == 2)
00047         Graphics::drawLine(x0, y0, x1, y1, color, plotPoint<uint16>, this);
00048     else if (format.bytesPerPixel == 4)
00049         Graphics::drawLine(x0, y0, x1, y1, color, plotPoint<uint32>, this);
00050     else
00051         error("Surface::drawLine: bytesPerPixel must be 1, 2, or 4");
00052 }
00053 
00054 void Surface::drawThickLine(int x0, int y0, int x1, int y1, int penX, int penY, uint32 color) {
00055     if (format.bytesPerPixel == 1)
00056         Graphics::drawThickLine(x0, y0, x1, y1, penX, penY, color, plotPoint<byte>, this);
00057     else if (format.bytesPerPixel == 2)
00058         Graphics::drawThickLine(x0, y0, x1, y1, penX, penY, color, plotPoint<uint16>, this);
00059     else if (format.bytesPerPixel == 4)
00060         Graphics::drawThickLine(x0, y0, x1, y1, penX, penY, color, plotPoint<uint32>, this);
00061     else
00062         error("Surface::drawThickLine: bytesPerPixel must be 1, 2, or 4");
00063 }
00064 
00065 void Surface::create(uint16 width, uint16 height, const PixelFormat &f) {
00066     free();
00067 
00068     w = width;
00069     h = height;
00070     format = f;
00071     pitch = w * format.bytesPerPixel;
00072 
00073     if (width && height) {
00074         pixels = calloc(width * height, format.bytesPerPixel);
00075         assert(pixels);
00076     }
00077 }
00078 
00079 void Surface::free() {
00080 	::free(pixels);
00081     pixels = 0;
00082     w = h = pitch = 0;
00083     format = PixelFormat();
00084 }
00085 
00086 void Surface::init(uint16 width, uint16 height, uint16 newPitch, void *newPixels, const PixelFormat &f) {
00087     w = width;
00088     h = height;
00089     pitch = newPitch;
00090     pixels = newPixels;
00091     format = f;
00092 }
00093 
00094 void Surface::copyFrom(const Surface &surf) {
00095     create(surf.w, surf.h, surf.format);
00096     if (surf.pitch == pitch) {
00097         memcpy(pixels, surf.pixels, h * pitch);
00098     } else {
00099         const byte *src = (const byte *)surf.pixels;
00100         byte *dst = (byte *)pixels;
00101         for (int y = h; y > 0; --y) {
00102             memcpy(dst, src, w * format.bytesPerPixel);
00103             src += surf.pitch;
00104             dst += pitch;
00105         }
00106     }
00107 }
00108 
00109 Surface Surface::getSubArea(const Common::Rect &area) {
00110     Common::Rect effectiveArea(area);
00111     effectiveArea.clip(w, h);
00112 
00113     Surface subSurface;
00114     subSurface.w = effectiveArea.width();
00115     subSurface.h = effectiveArea.height();
00116     subSurface.pitch = pitch;
00117     subSurface.pixels = getBasePtr(area.left, area.top);
00118     subSurface.format = format;
00119     return subSurface;
00120 }
00121 
00122 const Surface Surface::getSubArea(const Common::Rect &area) const {
00123     Common::Rect effectiveArea(area);
00124     effectiveArea.clip(w, h);
00125 
00126     Surface subSurface;
00127     subSurface.w = effectiveArea.width();
00128     subSurface.h = effectiveArea.height();
00129     subSurface.pitch = pitch;
00130     // We need to cast the const away here because a Surface always has a
00131     // pointer to modifiable pixel data.
00132     subSurface.pixels = const_cast<void *>(getBasePtr(area.left, area.top));
00133     subSurface.format = format;
00134     return subSurface;
00135 }
00136 
00137 void Surface::copyRectToSurface(const void *buffer, int srcPitch, int destX, int destY, int width, int height) {
00138     assert(buffer);
00139 
00140     assert(destX >= 0 && destX < w);
00141     assert(destY >= 0 && destY < h);
00142     assert(height > 0 && destY + height <= h);
00143     assert(width > 0 && destX + width <= w);
00144 
00145     // Copy buffer data to internal buffer
00146     const byte *src = (const byte *)buffer;
00147     byte *dst = (byte *)getBasePtr(destX, destY);
00148     for (int i = 0; i < height; i++) {
00149         memcpy(dst, src, width * format.bytesPerPixel);
00150         src += srcPitch;
00151         dst += pitch;
00152     }
00153 }
00154 
00155 void Surface::copyRectToSurface(const Graphics::Surface &srcSurface, int destX, int destY, const Common::Rect subRect) {
00156     assert(srcSurface.format == format);
00157 
00158     copyRectToSurface(srcSurface.getBasePtr(subRect.left, subRect.top), srcSurface.pitch, destX, destY, subRect.width(), subRect.height());
00159 }
00160 
00161 void Surface::hLine(int x, int y, int x2, uint32 color) {
00162     // Clipping
00163     if (y < 0 || y >= h)
00164         return;
00165 
00166     if (x2 < x)
00167         SWAP(x2, x);
00168 
00169     if (x < 0)
00170         x = 0;
00171     if (x2 >= w)
00172         x2 = w - 1;
00173 
00174     if (x2 < x)
00175         return;
00176 
00177     if (format.bytesPerPixel == 1) {
00178         byte *ptr = (byte *)getBasePtr(x, y);
00179         memset(ptr, (byte)color, x2 - x + 1);
00180     } else if (format.bytesPerPixel == 2) {
00181         uint16 *ptr = (uint16 *)getBasePtr(x, y);
00182         Common::fill(ptr, ptr + (x2 - x + 1), (uint16)color);
00183     } else if (format.bytesPerPixel == 4) {
00184         uint32 *ptr = (uint32 *)getBasePtr(x, y);
00185         Common::fill(ptr, ptr + (x2 - x + 1), color);
00186     } else {
00187         error("Surface::hLine: bytesPerPixel must be 1, 2, or 4");
00188     }
00189 }
00190 
00191 void Surface::vLine(int x, int y, int y2, uint32 color) {
00192     // Clipping
00193     if (x < 0 || x >= w)
00194         return;
00195 
00196     if (y2 < y)
00197         SWAP(y2, y);
00198 
00199     if (y < 0)
00200         y = 0;
00201     if (y2 >= h)
00202         y2 = h - 1;
00203 
00204     if (format.bytesPerPixel == 1) {
00205         byte *ptr = (byte *)getBasePtr(x, y);
00206         while (y++ <= y2) {
00207             *ptr = (byte)color;
00208             ptr += pitch;
00209         }
00210     } else if (format.bytesPerPixel == 2) {
00211         uint16 *ptr = (uint16 *)getBasePtr(x, y);
00212         while (y++ <= y2) {
00213             *ptr = (uint16)color;
00214             ptr += pitch / 2;
00215         }
00216 
00217     } else if (format.bytesPerPixel == 4) {
00218         uint32 *ptr = (uint32 *)getBasePtr(x, y);
00219         while (y++ <= y2) {
00220             *ptr = color;
00221             ptr += pitch / 4;
00222         }
00223     } else {
00224         error("Surface::vLine: bytesPerPixel must be 1, 2, or 4");
00225     }
00226 }
00227 
00228 void Surface::fillRect(Common::Rect r, uint32 color) {
00229     r.clip(w, h);
00230 
00231     if (!r.isValidRect())
00232         return;
00233 
00234     int width = r.width();
00235     int lineLen = width;
00236     int height = r.height();
00237     bool useMemset = true;
00238 
00239     if (format.bytesPerPixel == 2) {
00240         lineLen *= 2;
00241         if ((uint16)color != ((color & 0xff) | (color & 0xff) << 8))
00242             useMemset = false;
00243     } else if (format.bytesPerPixel == 4) {
00244         useMemset = false;
00245     } else if (format.bytesPerPixel != 1) {
00246         error("Surface::fillRect: bytesPerPixel must be 1, 2, or 4");
00247     }
00248 
00249     if (useMemset) {
00250         byte *ptr = (byte *)getBasePtr(r.left, r.top);
00251         while (height--) {
00252             memset(ptr, (byte)color, lineLen);
00253             ptr += pitch;
00254         }
00255     } else {
00256         if (format.bytesPerPixel == 2) {
00257             uint16 *ptr = (uint16 *)getBasePtr(r.left, r.top);
00258             while (height--) {
00259                 Common::fill(ptr, ptr + width, (uint16)color);
00260                 ptr += pitch / 2;
00261             }
00262         } else {
00263             uint32 *ptr = (uint32 *)getBasePtr(r.left, r.top);
00264             while (height--) {
00265                 Common::fill(ptr, ptr + width, color);
00266                 ptr += pitch / 4;
00267             }
00268         }
00269     }
00270 }
00271 
00272 void Surface::frameRect(const Common::Rect &r, uint32 color) {
00273     hLine(r.left, r.top, r.right - 1, color);
00274     hLine(r.left, r.bottom - 1, r.right - 1, color);
00275     vLine(r.left, r.top, r.bottom - 1, color);
00276     vLine(r.right - 1, r.top, r.bottom - 1, color);
00277 }
00278 
00279 void Surface::move(int dx, int dy, int height) {
00280     // Short circuit check - do we have to do anything anyway?
00281     if ((dx == 0 && dy == 0) || height <= 0)
00282         return;
00283 
00284     if (format.bytesPerPixel != 1 && format.bytesPerPixel != 2 && format.bytesPerPixel != 4)
00285         error("Surface::move: bytesPerPixel must be 1, 2, or 4");
00286 
00287     byte *src, *dst;
00288     int x, y;
00289 
00290     // vertical movement
00291     if (dy > 0) {
00292         // move down - copy from bottom to top
00293         dst = (byte *)pixels + (height - 1) * pitch;
00294         src = dst - dy * pitch;
00295         for (y = dy; y < height; y++) {
00296             memcpy(dst, src, pitch);
00297             src -= pitch;
00298             dst -= pitch;
00299         }
00300     } else if (dy < 0) {
00301         // move up - copy from top to bottom
00302         dst = (byte *)pixels;
00303         src = dst - dy * pitch;
00304         for (y = -dy; y < height; y++) {
00305             memcpy(dst, src, pitch);
00306             src += pitch;
00307             dst += pitch;
00308         }
00309     }
00310 
00311     // horizontal movement
00312     if (dx > 0) {
00313         // move right - copy from right to left
00314         dst = (byte *)pixels + (pitch - format.bytesPerPixel);
00315         src = dst - (dx * format.bytesPerPixel);
00316         for (y = 0; y < height; y++) {
00317             for (x = dx; x < w; x++) {
00318                 if (format.bytesPerPixel == 1) {
00319                     *dst-- = *src--;
00320                 } else if (format.bytesPerPixel == 2) {
00321                     *(uint16 *)dst = *(const uint16 *)src;
00322                     src -= 2;
00323                     dst -= 2;
00324                 } else if (format.bytesPerPixel == 4) {
00325                     *(uint32 *)dst = *(const uint32 *)src;
00326                     src -= 4;
00327                     dst -= 4;
00328                 }
00329             }
00330             src += pitch + (pitch - dx * format.bytesPerPixel);
00331             dst += pitch + (pitch - dx * format.bytesPerPixel);
00332         }
00333     } else if (dx < 0)  {
00334         // move left - copy from left to right
00335         dst = (byte *)pixels;
00336         src = dst - (dx * format.bytesPerPixel);
00337         for (y = 0; y < height; y++) {
00338             for (x = -dx; x < w; x++) {
00339                 if (format.bytesPerPixel == 1) {
00340                     *dst++ = *src++;
00341                 } else if (format.bytesPerPixel == 2) {
00342                     *(uint16 *)dst = *(const uint16 *)src;
00343                     src += 2;
00344                     dst += 2;
00345                 } else if (format.bytesPerPixel == 4) {
00346                     *(uint32 *)dst = *(const uint32 *)src;
00347                     src += 4;
00348                     dst += 4;
00349                 }
00350             }
00351             src += pitch - (pitch + dx * format.bytesPerPixel);
00352             dst += pitch - (pitch + dx * format.bytesPerPixel);
00353         }
00354     }
00355 }
00356 
00357 void Surface::convertToInPlace(const PixelFormat &dstFormat, const byte *palette) {
00358     // Do not convert to the same format and ignore empty surfaces.
00359     if (format == dstFormat || pixels == 0) {
00360         return;
00361     }
00362 
00363     if (format.bytesPerPixel == 0 || format.bytesPerPixel > 4)
00364         error("Surface::convertToInPlace(): Can only convert from 1Bpp, 2Bpp, 3Bpp, and 4Bpp");
00365 
00366     if (dstFormat.bytesPerPixel != 2 && dstFormat.bytesPerPixel != 4)
00367         error("Surface::convertToInPlace(): Can only convert to 2Bpp and 4Bpp");
00368 
00369     // In case the surface data needs more space allocate it.
00370     if (dstFormat.bytesPerPixel > format.bytesPerPixel) {
00371         void *const newPixels = realloc(pixels, w * h * dstFormat.bytesPerPixel);
00372         if (!newPixels) {
00373             error("Surface::convertToInPlace(): Out of memory");
00374         }
00375         pixels = newPixels;
00376     }
00377 
00378     // We take advantage of the fact that pitch is always w * format.bytesPerPixel.
00379     // This is assured by the logic of Surface::create.
00380 
00381     // We need to handle 1 Bpp surfaces special here.
00382     if (format.bytesPerPixel == 1) {
00383         assert(palette);
00384 
00385         for (int y = h; y > 0; --y) {
00386             const byte *srcRow = (const byte *)pixels + y * pitch - 1;
00387             byte *dstRow = (byte *)pixels + y * w * dstFormat.bytesPerPixel - dstFormat.bytesPerPixel;
00388 
00389             for (int x = 0; x < w; x++) {
00390                 byte index = *srcRow--;
00391                 byte r = palette[index * 3];
00392                 byte g = palette[index * 3 + 1];
00393                 byte b = palette[index * 3 + 2];
00394 
00395                 uint32 color = dstFormat.RGBToColor(r, g, b);
00396 
00397                 if (dstFormat.bytesPerPixel == 2)
00398                     *((uint16 *)dstRow) = color;
00399                 else
00400                     *((uint32 *)dstRow) = color;
00401 
00402                 dstRow -= dstFormat.bytesPerPixel;
00403             }
00404         }
00405     } else {
00406         crossBlit((byte *)pixels, (const byte *)pixels, w * dstFormat.bytesPerPixel, pitch, w, h, dstFormat, format);
00407     }
00408 
00409     // In case the surface data got smaller, free up some memory.
00410     if (dstFormat.bytesPerPixel < format.bytesPerPixel) {
00411         void *const newPixels = realloc(pixels, w * h * dstFormat.bytesPerPixel);
00412         if (!newPixels) {
00413             error("Surface::convertToInPlace(): Freeing memory failed");
00414         }
00415         pixels = newPixels;
00416     }
00417 
00418     // Update the surface specific data.
00419     format = dstFormat;
00420     pitch = w * dstFormat.bytesPerPixel;
00421 }
00422 
00423 Graphics::Surface *Surface::convertTo(const PixelFormat &dstFormat, const byte *palette) const {
00424     assert(pixels);
00425 
00426     Graphics::Surface *surface = new Graphics::Surface();
00427 
00428     // If the target format is the same, just copy
00429     if (format == dstFormat) {
00430         surface->copyFrom(*this);
00431         return surface;
00432     }
00433 
00434     if (format.bytesPerPixel == 0 || format.bytesPerPixel > 4)
00435         error("Surface::convertTo(): Can only convert from 1Bpp, 2Bpp, 3Bpp, and 4Bpp");
00436 
00437     if (dstFormat.bytesPerPixel != 2 && dstFormat.bytesPerPixel != 4)
00438         error("Surface::convertTo(): Can only convert to 2Bpp and 4Bpp");
00439 
00440     surface->create(w, h, dstFormat);
00441 
00442     if (format.bytesPerPixel == 1) {
00443         // Converting from paletted to high color
00444         assert(palette);
00445 
00446         for (int y = 0; y < h; y++) {
00447             const byte *srcRow = (const byte *)getBasePtr(0, y);
00448             byte *dstRow = (byte *)surface->getBasePtr(0, y);
00449 
00450             for (int x = 0; x < w; x++) {
00451                 byte index = *srcRow++;
00452                 byte r = palette[index * 3];
00453                 byte g = palette[index * 3 + 1];
00454                 byte b = palette[index * 3 + 2];
00455 
00456                 uint32 color = dstFormat.RGBToColor(r, g, b);
00457 
00458                 if (dstFormat.bytesPerPixel == 2)
00459                     *((uint16 *)dstRow) = color;
00460                 else
00461                     *((uint32 *)dstRow) = color;
00462 
00463                 dstRow += dstFormat.bytesPerPixel;
00464             }
00465         }
00466     } else {
00467         // Converting from high color to high color
00468         for (int y = 0; y < h; y++) {
00469             const byte *srcRow = (const byte *)getBasePtr(0, y);
00470             byte *dstRow = (byte *)surface->getBasePtr(0, y);
00471 
00472             for (int x = 0; x < w; x++) {
00473                 uint32 srcColor;
00474                 if (format.bytesPerPixel == 2)
00475                     srcColor = READ_UINT16(srcRow);
00476                 else if (format.bytesPerPixel == 3)
00477                     srcColor = READ_UINT24(srcRow);
00478                 else
00479                     srcColor = READ_UINT32(srcRow);
00480 
00481                 srcRow += format.bytesPerPixel;
00482 
00483                 // Convert that color to the new format
00484                 byte r, g, b, a;
00485                 format.colorToARGB(srcColor, a, r, g, b);
00486                 uint32 color = dstFormat.ARGBToColor(a, r, g, b);
00487 
00488                 if (dstFormat.bytesPerPixel == 2)
00489                     *((uint16 *)dstRow) = color;
00490                 else
00491                     *((uint32 *)dstRow) = color;
00492 
00493                 dstRow += dstFormat.bytesPerPixel;
00494             }
00495         }
00496     }
00497 
00498     return surface;
00499 }
00500 
00501 FloodFill::FloodFill(Graphics::Surface *surface, uint32 oldColor, uint32 fillColor, bool maskMode) {
00502     _surface = surface;
00503     _oldColor = oldColor;
00504     _fillColor = fillColor;
00505     _w = surface->w;
00506     _h = surface->h;
00507 
00508     _mask = nullptr;
00509     _maskMode = maskMode;
00510 
00511     if (_maskMode) {
00512         _mask = new Graphics::Surface();
00513         _mask->create(_w, _h, Graphics::PixelFormat::createFormatCLUT8()); // Uses calloc()
00514     }
00515 
00516     _visited = (byte *)calloc(_w * _h, 1);
00517 }
00518 
00519 FloodFill::~FloodFill() {
00520     while(!_queue.empty()) {
00521         Common::Point *p = _queue.front();
00522 
00523         delete p;
00524         _queue.pop_front();
00525     }
00526 
00527     free(_visited);
00528 
00529     if (_mask)
00530         delete _mask;
00531 }
00532 
00533 void FloodFill::addSeed(int x, int y) {
00534     if (x >= 0 && x < _w && y >= 0 && y < _h) {
00535         if (!_visited[y * _w + x]) {
00536             _visited[y * _w + x] = 1;
00537             void *src = _surface->getBasePtr(x, y);
00538             void *dst;
00539             bool changed = false;
00540 
00541             if (_maskMode)
00542                 dst = _mask->getBasePtr(x, y);
00543             else
00544                 dst = src;
00545 
00546             if (_surface->format.bytesPerPixel == 1) {
00547                 if (*((byte *)src) == _oldColor) {
00548                     *((byte *)dst) = _maskMode ? 255 : _fillColor;
00549                     changed = true;
00550                 }
00551             } else if (_surface->format.bytesPerPixel == 2) {
00552                 if (READ_UINT16(src) == _oldColor) {
00553                     if (!_maskMode)
00554                         WRITE_UINT16(src, _fillColor);
00555                     else
00556                         *((byte *)dst) = 255;
00557 
00558                     changed = true;
00559                 }
00560             } else if (_surface->format.bytesPerPixel == 4) {
00561                 if (READ_UINT32(src) == _oldColor) {
00562                     if (!_maskMode)
00563                         WRITE_UINT32(src, _fillColor);
00564                     else
00565                         *((byte *)dst) = 255;
00566 
00567                     changed = true;
00568                 }
00569             } else {
00570                 error("Unsupported bpp in FloodFill");
00571             }
00572 
00573             if (changed) {
00574                 Common::Point *pt = new Common::Point(x, y);
00575 
00576                 _queue.push_back(pt);
00577             }
00578         }
00579     }
00580 }
00581 
00582 void FloodFill::fill() {
00583     while (!_queue.empty()) {
00584         Common::Point *p = _queue.front();
00585         _queue.pop_front();
00586         addSeed(p->x    , p->y - 1);
00587         addSeed(p->x - 1, p->y    );
00588         addSeed(p->x    , p->y + 1);
00589         addSeed(p->x + 1, p->y    );
00590 
00591         delete p;
00592     }
00593 }
00594 
00595 void FloodFill::fillMask() {
00596     _maskMode = true;
00597 
00598     if (!_mask) {
00599         _mask = new Graphics::Surface();
00600         _mask->create(_w, _h, Graphics::PixelFormat::createFormatCLUT8()); // Uses calloc()
00601     }
00602 
00603     fill();
00604 }
00605 
00606 } // End of namespace Graphics


Generated on Sat Mar 23 2019 05:02:15 for ResidualVM by doxygen 1.7.1
curved edge   curved edge