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

managed_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 "graphics/managed_surface.h"
00024 #include "common/algorithm.h"
00025 #include "common/textconsole.h"
00026 
00027 namespace Graphics {
00028 
00029 const int SCALE_THRESHOLD = 0x100;
00030 
00031 ManagedSurface::ManagedSurface() :
00032         w(_innerSurface.w), h(_innerSurface.h), pitch(_innerSurface.pitch), format(_innerSurface.format),
00033         _disposeAfterUse(DisposeAfterUse::NO), _owner(nullptr) {
00034 }
00035 
00036 ManagedSurface::ManagedSurface(ManagedSurface &surf) :
00037         w(_innerSurface.w), h(_innerSurface.h), pitch(_innerSurface.pitch), format(_innerSurface.format),
00038         _disposeAfterUse(DisposeAfterUse::NO), _owner(nullptr) {
00039     *this = surf;
00040 }
00041 
00042 ManagedSurface::ManagedSurface(int width, int height) :
00043         w(_innerSurface.w), h(_innerSurface.h), pitch(_innerSurface.pitch), format(_innerSurface.format),
00044         _disposeAfterUse(DisposeAfterUse::NO), _owner(nullptr) {
00045     create(width, height);
00046 }
00047 
00048 ManagedSurface::ManagedSurface(int width, int height, const Graphics::PixelFormat &pixelFormat) :
00049         w(_innerSurface.w), h(_innerSurface.h), pitch(_innerSurface.pitch), format(_innerSurface.format),
00050         _disposeAfterUse(DisposeAfterUse::NO), _owner(nullptr) {
00051     create(width, height, pixelFormat);
00052 }
00053 
00054 ManagedSurface::ManagedSurface(ManagedSurface &surf, const Common::Rect &bounds) :
00055         w(_innerSurface.w), h(_innerSurface.h), pitch(_innerSurface.pitch), format(_innerSurface.format),
00056         _disposeAfterUse(DisposeAfterUse::NO), _owner(nullptr) {
00057     create(surf, bounds);
00058 }
00059 
00060 ManagedSurface::~ManagedSurface() {
00061     free();
00062 }
00063 
00064 ManagedSurface &ManagedSurface::operator=(ManagedSurface &surf) {
00065     // Free any current surface
00066     free();
00067 
00068     if (surf._disposeAfterUse == DisposeAfterUse::YES) {
00069         // Create a new surface and copy the pixels from the source surface
00070         create(surf.w, surf.h, surf.format);
00071         Common::copy((const byte *)surf.getPixels(), (const byte *)surf.getPixels() +
00072             surf.w * surf.h * surf.format.bytesPerPixel, (byte *)this->getPixels());
00073     } else {
00074         // Source isn't managed, so simply copy its fields
00075         _owner = surf._owner;
00076         _offsetFromOwner = surf._offsetFromOwner;
00077         void *srcPixels = surf._innerSurface.getPixels();
00078         _innerSurface.setPixels(srcPixels);
00079         _innerSurface.w = surf.w;
00080         _innerSurface.h = surf.h;
00081         _innerSurface.pitch = surf.pitch;
00082         this->format = surf.format;
00083     }
00084 
00085     return *this;
00086 }
00087 
00088 void ManagedSurface::setPixels(void *newPixels) {
00089     free();
00090     _innerSurface.setPixels(newPixels);
00091 }
00092 
00093 void ManagedSurface::create(uint16 width, uint16 height) {
00094     create(width, height, PixelFormat::createFormatCLUT8());
00095 }
00096 
00097 void ManagedSurface::create(uint16 width, uint16 height, const PixelFormat &pixelFormat) {
00098     free();
00099     _innerSurface.create(width, height, pixelFormat);
00100 
00101     _disposeAfterUse = DisposeAfterUse::YES;
00102     markAllDirty();
00103 }
00104 
00105 void ManagedSurface::create(ManagedSurface &surf, const Common::Rect &bounds) {
00106     free();
00107 
00108     _offsetFromOwner = Common::Point(bounds.left, bounds.top);
00109     _innerSurface.setPixels(surf.getBasePtr(bounds.left, bounds.top));
00110     _innerSurface.pitch = surf.pitch;
00111     _innerSurface.format = surf.format;
00112     _innerSurface.w = bounds.width();
00113     _innerSurface.h = bounds.height();
00114     _owner = &surf;
00115     _disposeAfterUse = DisposeAfterUse::NO;
00116 }
00117 
00118 void ManagedSurface::free() {
00119     if (_disposeAfterUse == DisposeAfterUse::YES)
00120         _innerSurface.free();
00121 
00122     _disposeAfterUse = DisposeAfterUse::NO;
00123     _owner = nullptr;
00124     _offsetFromOwner = Common::Point(0, 0);
00125 }
00126 
00127 bool ManagedSurface::clip(Common::Rect &srcBounds, Common::Rect &destBounds) {
00128     if (destBounds.left >= this->w || destBounds.top >= this->h ||
00129             destBounds.right <= 0 || destBounds.bottom <= 0)
00130         return false;
00131 
00132     // Clip the bounds if necessary to fit on-screen
00133     if (destBounds.right > this->w) {
00134         srcBounds.right -= destBounds.right - this->w;
00135         destBounds.right = this->w;
00136     }
00137 
00138     if (destBounds.bottom > this->h) {
00139         srcBounds.bottom -= destBounds.bottom - this->h;
00140         destBounds.bottom = this->h;
00141     }
00142 
00143     if (destBounds.top < 0) {
00144         srcBounds.top += -destBounds.top;
00145         destBounds.top = 0;
00146     }
00147 
00148     if (destBounds.left < 0) {
00149         srcBounds.left += -destBounds.left;
00150         destBounds.left = 0;
00151     }
00152 
00153     return true;
00154 }
00155 
00156 void ManagedSurface::blitFrom(const Surface &src) {
00157     blitFrom(src, Common::Rect(0, 0, src.w, src.h), Common::Point(0, 0));
00158 }
00159 
00160 void ManagedSurface::blitFrom(const Surface &src, const Common::Point &destPos) {
00161     blitFrom(src, Common::Rect(0, 0, src.w, src.h), destPos);
00162 }
00163 
00164 void ManagedSurface::blitFrom(const Surface &src, const Common::Rect &srcRect,
00165         const Common::Point &destPos) {
00166     Common::Rect srcBounds = srcRect;
00167     Common::Rect destBounds(destPos.x, destPos.y, destPos.x + srcRect.width(),
00168         destPos.y + srcRect.height());
00169     uint destPixel;
00170     byte rSrc, gSrc, bSrc, aSrc;
00171     byte rDest, gDest, bDest;
00172     double alpha;
00173 
00174     if (!srcRect.isValidRect() || !clip(srcBounds, destBounds))
00175         return;
00176 
00177     if (format != src.format) {
00178         // When the pixel format differs, both source an dest must be
00179         // 2 or 4 bytes per pixel
00180         assert(format.bytesPerPixel == 2 || format.bytesPerPixel == 4);
00181         assert(src.format.bytesPerPixel == 2 || src.format.bytesPerPixel == 4);
00182     }
00183 
00184     for (int y = 0; y < srcBounds.height(); ++y) {
00185         const byte *srcP = (const byte *)src.getBasePtr(srcBounds.left, srcBounds.top + y);
00186         byte *destP = (byte *)getBasePtr(destBounds.left, destBounds.top + y);
00187 
00188         if (src.format == format) {
00189             // Matching surface formats, so we can do a straight copy
00190             Common::copy(srcP, srcP + srcBounds.width() * format.bytesPerPixel, destP);
00191         } else {
00192             for (int x = 0; x < srcBounds.width(); ++x,
00193                     srcP += src.format.bytesPerPixel,
00194                     destP += format.bytesPerPixel) {
00195                 src.format.colorToARGB(src.format.bytesPerPixel == 2 ? *(const uint16 *)srcP : *(const uint32 *)srcP,
00196                     aSrc, rSrc, gSrc, bSrc);
00197                 format.colorToRGB(format.bytesPerPixel == 2 ? *(const uint16 *)destP : *(const uint32 *)destP,
00198                     rDest, gDest, bDest);
00199 
00200                 if (aSrc == 0) {
00201                     // Completely transparent, so skip
00202                     continue;
00203                 } else if (aSrc == 0xff) {
00204                     // Completely opaque, so copy RGB values over
00205                     rDest = rSrc;
00206                     gDest = gSrc;
00207                     bDest = bSrc;
00208                 } else {
00209                     // Partially transparent, so calculate new pixel colors
00210                     alpha = (double)aSrc / 255.0;
00211                     rDest = static_cast<byte>((rSrc * alpha) + (rDest * (1.0 - alpha)));
00212                     gDest = static_cast<byte>((gSrc * alpha) + (gDest * (1.0 - alpha)));
00213                     bDest = static_cast<byte>((bSrc * alpha) + (bDest * (1.0 - alpha)));
00214                 }
00215 
00216                 destPixel = format.ARGBToColor(0xff, rDest, gDest, bDest);
00217                 if (format.bytesPerPixel == 2)
00218                     *(uint16 *)destP = destPixel;
00219                 else
00220                     *(uint32 *)destP = destPixel;
00221             }
00222         }
00223     }
00224 
00225     addDirtyRect(Common::Rect(0, 0, this->w, this->h));
00226 }
00227 
00228 void ManagedSurface::transBlitFrom(const Surface &src, uint transColor, bool flipped, uint overrideColor) {
00229     transBlitFrom(src, Common::Rect(0, 0, src.w, src.h), Common::Rect(0, 0, this->w, this->h),
00230         transColor, false, overrideColor);
00231 }
00232 
00233 void ManagedSurface::transBlitFrom(const Surface &src, const Common::Point &destPos,
00234         uint transColor, bool flipped, uint overrideColor) {
00235     transBlitFrom(src, Common::Rect(0, 0, src.w, src.h), Common::Rect(destPos.x, destPos.y,
00236         destPos.x + src.w, destPos.y + src.h), transColor, false, overrideColor);
00237 }
00238 
00239 void ManagedSurface::transBlitFrom(const Surface &src, const Common::Rect &srcRect,
00240         const Common::Point &destPos, uint transColor, bool flipped, uint overrideColor) {
00241     transBlitFrom(src, srcRect, Common::Rect(destPos.x, destPos.y,
00242         destPos.x + src.w, destPos.y + src.h), transColor, false, overrideColor);
00243 }
00244 
00245 template<typename TSRC, typename TDEST>
00246 void transBlit(const Surface &src, const Common::Rect &srcRect, Surface &dest, const Common::Rect &destRect, TSRC transColor, bool flipped, uint overrideColor) {
00247     int scaleX = SCALE_THRESHOLD * srcRect.width() / destRect.width();
00248     int scaleY = SCALE_THRESHOLD * srcRect.height() / destRect.height();
00249     const Graphics::PixelFormat &srcFormat = src.format;
00250     const Graphics::PixelFormat &destFormat = dest.format;
00251     byte aSrc, rSrc, gSrc, bSrc;
00252     byte rDest, gDest, bDest;
00253     double alpha;
00254 
00255     // Loop through drawing output lines
00256     for (int destY = destRect.top, scaleYCtr = 0; destY < destRect.bottom; ++destY, scaleYCtr += scaleY) {
00257         if (destY < 0 || destY >= dest.h)
00258             continue;
00259         const TSRC *srcLine = (const TSRC *)src.getBasePtr(srcRect.left, scaleYCtr / SCALE_THRESHOLD + srcRect.top);
00260         TDEST *destLine = (TDEST *)dest.getBasePtr(destRect.left, destY);
00261 
00262         // Loop through drawing the pixels of the row
00263         for (int destX = destRect.left, xCtr = 0, scaleXCtr = 0; destX < destRect.right; ++destX, ++xCtr, scaleXCtr += scaleX) {
00264             if (destX < 0 || destX >= dest.w)
00265                 continue;
00266 
00267             TSRC srcVal = srcLine[flipped ? src.w - scaleXCtr / SCALE_THRESHOLD - 1 : scaleXCtr / SCALE_THRESHOLD];
00268             if (srcVal == transColor)
00269                 continue;
00270 
00271             if (srcFormat == destFormat) {
00272                 // Matching formats, so we can do a straight copy
00273                 destLine[xCtr] = overrideColor ? overrideColor : srcVal;
00274             } else {
00275                 // Otherwise we have to manually decode and re-encode each pixel
00276                 srcFormat.colorToARGB(*srcLine, aSrc, rSrc, gSrc, bSrc);
00277                 destFormat.colorToRGB(destLine[xCtr], rDest, gDest, bDest);
00278 
00279                 if (aSrc == 0) {
00280                     // Completely transparent, so skip
00281                     continue;
00282                 } else if (aSrc == 0xff) {
00283                     // Completely opaque, so copy RGB values over
00284                     rDest = rSrc;
00285                     gDest = gSrc;
00286                     bDest = bSrc;
00287                 } else {
00288                     // Partially transparent, so calculate new pixel colors
00289                     alpha = (double)aSrc / 255.0;
00290                     rDest = static_cast<byte>((rSrc * alpha) + (rDest * (1.0 - alpha)));
00291                     gDest = static_cast<byte>((gSrc * alpha) + (gDest * (1.0 - alpha)));
00292                     bDest = static_cast<byte>((bSrc * alpha) + (bDest * (1.0 - alpha)));
00293                 }
00294 
00295                 destLine[xCtr] = destFormat.ARGBToColor(0xff, rDest, gDest, bDest);
00296             }
00297         }
00298     }
00299 }
00300 
00301 #define HANDLE_BLIT(SRC_BYTES, DEST_BYTES, SRC_TYPE, DEST_TYPE) \
00302     if (src.format.bytesPerPixel == SRC_BYTES && format.bytesPerPixel == DEST_BYTES) \
00303         transBlit<SRC_TYPE, DEST_TYPE>(src, srcRect, _innerSurface, destRect, transColor, flipped, overrideColor); \
00304     else
00305 
00306 void ManagedSurface::transBlitFrom(const Surface &src, const Common::Rect &srcRect,
00307     const Common::Rect &destRect, uint transColor, bool flipped, uint overrideColor) {
00308     if (src.w == 0 || src.h == 0 || destRect.width() == 0 || destRect.height() == 0)
00309         return;
00310 
00311     HANDLE_BLIT(1, 1, byte, byte)
00312     HANDLE_BLIT(2, 2, uint16, uint16)
00313     HANDLE_BLIT(4, 4, uint32, uint32)
00314     HANDLE_BLIT(2, 4, uint16, uint32)
00315     HANDLE_BLIT(4, 2, uint32, uint16)
00316         error("Surface::transBlitFrom: bytesPerPixel must be 1, 2, or 4");
00317 
00318     // Mark the affected area
00319     addDirtyRect(destRect);
00320 }
00321 
00322 #undef HANDLE_BLIT
00323 
00324 void ManagedSurface::markAllDirty() {
00325     addDirtyRect(Common::Rect(0, 0, this->w, this->h));
00326 }
00327 
00328 void ManagedSurface::addDirtyRect(const Common::Rect &r) {
00329     if (_owner) {
00330         Common::Rect bounds = r;
00331         bounds.clip(Common::Rect(0, 0, this->w, this->h));
00332         bounds.translate(_offsetFromOwner.x, _offsetFromOwner.y);
00333         _owner->addDirtyRect(bounds);
00334     }
00335 }
00336 
00337 void ManagedSurface::clear(uint color) {
00338     if (!empty())
00339         fillRect(getBounds(), color);
00340 }
00341 
00342 } // End of namespace Graphics


Generated on Sat Mar 16 2019 05:01:45 for ResidualVM by doxygen 1.7.1
curved edge   curved edge