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::flipVertical(const Common::Rect &r) {
00358     const int width = r.width() * format.bytesPerPixel;
00359     byte *temp = new byte[width];
00360     for (int y = r.top; y < r.bottom / 2; y++) {
00361         byte *row1 = (byte *)getBasePtr(r.left, y);
00362         byte *row2 = (byte *)getBasePtr(r.left, r.bottom - y - 1);
00363 
00364         memcpy(temp, row1, width);
00365         memcpy(row1, row2, width);
00366         memcpy(row2, temp, width);
00367     }
00368     delete[] temp;
00369 }
00370 
00371 void Surface::convertToInPlace(const PixelFormat &dstFormat, const byte *palette) {
00372     // Do not convert to the same format and ignore empty surfaces.
00373     if (format == dstFormat || pixels == 0) {
00374         return;
00375     }
00376 
00377     if (format.bytesPerPixel == 0 || format.bytesPerPixel > 4)
00378         error("Surface::convertToInPlace(): Can only convert from 1Bpp, 2Bpp, 3Bpp, and 4Bpp");
00379 
00380     if (dstFormat.bytesPerPixel != 2 && dstFormat.bytesPerPixel != 4)
00381         error("Surface::convertToInPlace(): Can only convert to 2Bpp and 4Bpp");
00382 
00383     // In case the surface data needs more space allocate it.
00384     if (dstFormat.bytesPerPixel > format.bytesPerPixel) {
00385         void *const newPixels = realloc(pixels, w * h * dstFormat.bytesPerPixel);
00386         if (!newPixels) {
00387             error("Surface::convertToInPlace(): Out of memory");
00388         }
00389         pixels = newPixels;
00390     }
00391 
00392     // We take advantage of the fact that pitch is always w * format.bytesPerPixel.
00393     // This is assured by the logic of Surface::create.
00394 
00395     // We need to handle 1 Bpp surfaces special here.
00396     if (format.bytesPerPixel == 1) {
00397         assert(palette);
00398 
00399         for (int y = h; y > 0; --y) {
00400             const byte *srcRow = (const byte *)pixels + y * pitch - 1;
00401             byte *dstRow = (byte *)pixels + y * w * dstFormat.bytesPerPixel - dstFormat.bytesPerPixel;
00402 
00403             for (int x = 0; x < w; x++) {
00404                 byte index = *srcRow--;
00405                 byte r = palette[index * 3];
00406                 byte g = palette[index * 3 + 1];
00407                 byte b = palette[index * 3 + 2];
00408 
00409                 uint32 color = dstFormat.RGBToColor(r, g, b);
00410 
00411                 if (dstFormat.bytesPerPixel == 2)
00412                     *((uint16 *)dstRow) = color;
00413                 else
00414                     *((uint32 *)dstRow) = color;
00415 
00416                 dstRow -= dstFormat.bytesPerPixel;
00417             }
00418         }
00419     } else {
00420         crossBlit((byte *)pixels, (const byte *)pixels, w * dstFormat.bytesPerPixel, pitch, w, h, dstFormat, format);
00421     }
00422 
00423     // In case the surface data got smaller, free up some memory.
00424     if (dstFormat.bytesPerPixel < format.bytesPerPixel) {
00425         void *const newPixels = realloc(pixels, w * h * dstFormat.bytesPerPixel);
00426         if (!newPixels) {
00427             error("Surface::convertToInPlace(): Freeing memory failed");
00428         }
00429         pixels = newPixels;
00430     }
00431 
00432     // Update the surface specific data.
00433     format = dstFormat;
00434     pitch = w * dstFormat.bytesPerPixel;
00435 }
00436 
00437 Graphics::Surface *Surface::convertTo(const PixelFormat &dstFormat, const byte *palette) const {
00438     assert(pixels);
00439 
00440     Graphics::Surface *surface = new Graphics::Surface();
00441 
00442     // If the target format is the same, just copy
00443     if (format == dstFormat) {
00444         surface->copyFrom(*this);
00445         return surface;
00446     }
00447 
00448     if (format.bytesPerPixel == 0 || format.bytesPerPixel > 4)
00449         error("Surface::convertTo(): Can only convert from 1Bpp, 2Bpp, 3Bpp, and 4Bpp");
00450 
00451     if (dstFormat.bytesPerPixel < 2 || dstFormat.bytesPerPixel > 4)
00452         error("Surface::convertTo(): Can only convert to 2Bpp, 3Bpp and 4Bpp");
00453 
00454     surface->create(w, h, dstFormat);
00455 
00456     if (format.bytesPerPixel == 1) {
00457         // Converting from paletted to high color
00458         assert(palette);
00459 
00460         for (int y = 0; y < h; y++) {
00461             const byte *srcRow = (const byte *)getBasePtr(0, y);
00462             byte *dstRow = (byte *)surface->getBasePtr(0, y);
00463 
00464             for (int x = 0; x < w; x++) {
00465                 byte index = *srcRow++;
00466                 byte r = palette[index * 3];
00467                 byte g = palette[index * 3 + 1];
00468                 byte b = palette[index * 3 + 2];
00469 
00470                 uint32 color = dstFormat.RGBToColor(r, g, b);
00471 
00472                 if (dstFormat.bytesPerPixel == 2)
00473                     *((uint16 *)dstRow) = color;
00474                 else if (dstFormat.bytesPerPixel == 3)
00475                     WRITE_UINT24(dstRow, color);
00476                 else
00477                     *((uint32 *)dstRow) = color;
00478 
00479                 dstRow += dstFormat.bytesPerPixel;
00480             }
00481         }
00482     } else {
00483         // Converting from high color to high color
00484         for (int y = 0; y < h; y++) {
00485             const byte *srcRow = (const byte *)getBasePtr(0, y);
00486             byte *dstRow = (byte *)surface->getBasePtr(0, y);
00487 
00488             for (int x = 0; x < w; x++) {
00489                 uint32 srcColor;
00490                 if (format.bytesPerPixel == 2)
00491                     srcColor = READ_UINT16(srcRow);
00492                 else if (format.bytesPerPixel == 3)
00493                     srcColor = READ_UINT24(srcRow);
00494                 else
00495                     srcColor = READ_UINT32(srcRow);
00496 
00497                 srcRow += format.bytesPerPixel;
00498 
00499                 // Convert that color to the new format
00500                 byte r, g, b, a;
00501                 format.colorToARGB(srcColor, a, r, g, b);
00502                 uint32 color = dstFormat.ARGBToColor(a, r, g, b);
00503 
00504                 if (dstFormat.bytesPerPixel == 2)
00505                     *((uint16 *)dstRow) = color;
00506                 else if (dstFormat.bytesPerPixel == 3)
00507                     WRITE_UINT24(dstRow, color);
00508                 else
00509                     *((uint32 *)dstRow) = color;
00510 
00511                 dstRow += dstFormat.bytesPerPixel;
00512             }
00513         }
00514     }
00515 
00516     return surface;
00517 }
00518 
00519 FloodFill::FloodFill(Graphics::Surface *surface, uint32 oldColor, uint32 fillColor, bool maskMode) {
00520     _surface = surface;
00521     _oldColor = oldColor;
00522     _fillColor = fillColor;
00523     _w = surface->w;
00524     _h = surface->h;
00525 
00526     _mask = nullptr;
00527     _maskMode = maskMode;
00528 
00529     if (_maskMode) {
00530         _mask = new Graphics::Surface();
00531         _mask->create(_w, _h, Graphics::PixelFormat::createFormatCLUT8()); // Uses calloc()
00532     }
00533 
00534     _visited = (byte *)calloc(_w * _h, 1);
00535 }
00536 
00537 FloodFill::~FloodFill() {
00538     while(!_queue.empty()) {
00539         Common::Point *p = _queue.front();
00540 
00541         delete p;
00542         _queue.pop_front();
00543     }
00544 
00545     free(_visited);
00546 
00547     if (_mask) {
00548         _mask->free();
00549         delete _mask;
00550     }
00551 }
00552 
00553 void FloodFill::addSeed(int x, int y) {
00554     if (x >= 0 && x < _w && y >= 0 && y < _h) {
00555         if (!_visited[y * _w + x]) {
00556             _visited[y * _w + x] = 1;
00557             void *src = _surface->getBasePtr(x, y);
00558             void *dst;
00559             bool changed = false;
00560 
00561             if (_maskMode)
00562                 dst = _mask->getBasePtr(x, y);
00563             else
00564                 dst = src;
00565 
00566             if (_surface->format.bytesPerPixel == 1) {
00567                 if (*((byte *)src) == _oldColor) {
00568                     *((byte *)dst) = _maskMode ? 255 : _fillColor;
00569                     changed = true;
00570                 }
00571             } else if (_surface->format.bytesPerPixel == 2) {
00572                 if (READ_UINT16(src) == _oldColor) {
00573                     if (!_maskMode)
00574                         WRITE_UINT16(src, _fillColor);
00575                     else
00576                         *((byte *)dst) = 255;
00577 
00578                     changed = true;
00579                 }
00580             } else if (_surface->format.bytesPerPixel == 4) {
00581                 if (READ_UINT32(src) == _oldColor) {
00582                     if (!_maskMode)
00583                         WRITE_UINT32(src, _fillColor);
00584                     else
00585                         *((byte *)dst) = 255;
00586 
00587                     changed = true;
00588                 }
00589             } else {
00590                 error("Unsupported bpp in FloodFill");
00591             }
00592 
00593             if (changed) {
00594                 Common::Point *pt = new Common::Point(x, y);
00595 
00596                 _queue.push_back(pt);
00597             }
00598         }
00599     }
00600 }
00601 
00602 void FloodFill::fill() {
00603     while (!_queue.empty()) {
00604         Common::Point *p = _queue.front();
00605         _queue.pop_front();
00606         addSeed(p->x    , p->y - 1);
00607         addSeed(p->x - 1, p->y    );
00608         addSeed(p->x    , p->y + 1);
00609         addSeed(p->x + 1, p->y    );
00610 
00611         delete p;
00612     }
00613 }
00614 
00615 void FloodFill::fillMask() {
00616     _maskMode = true;
00617 
00618     if (!_mask) {
00619         _mask = new Graphics::Surface();
00620         _mask->create(_w, _h, Graphics::PixelFormat::createFormatCLUT8()); // Uses calloc()
00621     }
00622 
00623     fill();
00624 }
00625 
00626 } // End of namespace Graphics


Generated on Sat May 23 2020 05:00:42 for ResidualVM by doxygen 1.7.1
curved edge   curved edge