void Bitmap::stretchBlt(const IntRect &destRect, const Bitmap &source, const IntRect &sourceRect, int opacity) { GUARD_DISPOSED; GUARD_MEGA; opacity = clamp(opacity, 0, 255); if (opacity == 0) return; if (source.megaSurface()) { /* Don't do transparent blits for now */ if (opacity < 255) source.ensureNonMega(); SDL_Surface *srcSurf = source.megaSurface(); int bpp; Uint32 rMask, gMask, bMask, aMask; SDL_PixelFormatEnumToMasks(SDL_PIXELFORMAT_ABGR8888, &bpp, &rMask, &gMask, &bMask, &aMask); SDL_Surface *blitTemp = SDL_CreateRGBSurface(0, destRect.w, destRect.h, bpp, rMask, gMask, bMask, aMask); SDL_Rect srcRect; srcRect.x = sourceRect.x; srcRect.y = sourceRect.y; srcRect.w = sourceRect.w; srcRect.h = sourceRect.h; SDL_Rect dstRect; dstRect.x = dstRect.y = 0; dstRect.w = blitTemp->w; dstRect.h = blitTemp->h; // FXIME: This is supposed to be a scaled blit, put SDL2 for some reason // makes the source surface unusable after BlitScaled() is called. Investigate! SDL_BlitSurface(srcSurf, &srcRect, blitTemp, &dstRect); TEX::bind(p->gl.tex); TEX::uploadSubImage(destRect.x, destRect.y, destRect.w, destRect.h, blitTemp->pixels, GL_RGBA); SDL_FreeSurface(blitTemp); modified(); return; } if (opacity == 255 && !p->touchesTaintedArea(destRect)) { /* Fast blit */ FBO::bind(source.p->gl.fbo, FBO::Read); FBO::bind(p->gl.fbo, FBO::Draw); FBO::blit(sourceRect.x, sourceRect.y, sourceRect.w, sourceRect.h, destRect.x, destRect.y, destRect.w, destRect.h); } else { /* Fragment pipeline */ float normOpacity = (float) opacity / 255.0f; TEXFBO &gpTex = shState->gpTexFBO(destRect.w, destRect.h); FBO::bind(gpTex.fbo, FBO::Draw); FBO::bind(p->gl.fbo, FBO::Read); FBO::blit(destRect.x, destRect.y, 0, 0, destRect.w, destRect.h); FloatRect bltSubRect((float) sourceRect.x / source.width(), (float) sourceRect.y / source.height(), ((float) source.width() / sourceRect.w) * ((float) destRect.w / gpTex.width), ((float) source.height() / sourceRect.h) * ((float) destRect.h / gpTex.height)); BltShader &shader = shState->shaders().blt; shader.bind(); shader.setDestination(gpTex.tex); shader.setSubRect(bltSubRect); shader.setOpacity(normOpacity); Quad &quad = shState->gpQuad(); quad.setTexPosRect(sourceRect, destRect); quad.setColor(Vec4(1, 1, 1, normOpacity)); source.p->bindTexture(shader); p->bindFBO(); p->pushSetViewport(shader); p->blitQuad(quad); p->popViewport(); } p->addTaintedArea(destRect); modified(); }
void Bitmap::stretchBlt(const IntRect &destRect, const Bitmap &source, const IntRect &sourceRect, int opacity) { guardDisposed(); GUARD_MEGA; if (source.isDisposed()) return; opacity = clamp(opacity, 0, 255); if (opacity == 0) return; SDL_Surface *srcSurf = source.megaSurface(); if (srcSurf && shState->config().subImageFix) { /* Blit from software surface, for broken GL drivers */ Vec2i gpTexSize; shState->ensureTexSize(sourceRect.w, sourceRect.h, gpTexSize); shState->bindTex(); GLMeta::subRectImageUpload(srcSurf->w, sourceRect.x, sourceRect.y, 0, 0, sourceRect.w, sourceRect.h, srcSurf, GL_RGBA); GLMeta::subRectImageEnd(); SimpleShader &shader = shState->shaders().simple; shader.bind(); shader.setTranslation(Vec2i()); shader.setTexSize(gpTexSize); p->pushSetViewport(shader); p->bindFBO(); Quad &quad = shState->gpQuad(); quad.setTexRect(FloatRect(0, 0, sourceRect.w, sourceRect.h)); quad.setPosRect(destRect); p->blitQuad(quad); p->popViewport(); p->addTaintedArea(destRect); p->onModified(); return; } else if (srcSurf) { /* Blit from software surface */ /* Don't do transparent blits for now */ if (opacity < 255) source.ensureNonMega(); SDL_Rect srcRect = sourceRect; SDL_Rect dstRect = destRect; SDL_Rect btmRect = { 0, 0, width(), height() }; SDL_Rect bltRect; if (SDL_IntersectRect(&btmRect, &dstRect, &bltRect) != SDL_TRUE) return; int bpp; Uint32 rMask, gMask, bMask, aMask; SDL_PixelFormatEnumToMasks(SDL_PIXELFORMAT_ABGR8888, &bpp, &rMask, &gMask, &bMask, &aMask); SDL_Surface *blitTemp = SDL_CreateRGBSurface(0, destRect.w, destRect.h, bpp, rMask, gMask, bMask, aMask); SDL_BlitScaled(srcSurf, &srcRect, blitTemp, 0); TEX::bind(p->gl.tex); if (bltRect.w == dstRect.w && bltRect.h == dstRect.h) { /* Dest rectangle lies within bounding box */ TEX::uploadSubImage(destRect.x, destRect.y, destRect.w, destRect.h, blitTemp->pixels, GL_RGBA); } else { /* Clipped blit */ GLMeta::subRectImageUpload(blitTemp->w, bltRect.x - dstRect.x, bltRect.y - dstRect.y, bltRect.x, bltRect.y, bltRect.w, bltRect.h, blitTemp, GL_RGBA); GLMeta::subRectImageEnd(); } SDL_FreeSurface(blitTemp); p->onModified(); return; } if (opacity == 255 && !p->touchesTaintedArea(destRect)) { /* Fast blit */ GLMeta::blitBegin(p->gl); GLMeta::blitSource(source.p->gl); GLMeta::blitRectangle(sourceRect, destRect); GLMeta::blitEnd(); } else { /* Fragment pipeline */ float normOpacity = (float) opacity / 255.0f; TEXFBO &gpTex = shState->gpTexFBO(destRect.w, destRect.h); GLMeta::blitBegin(gpTex); GLMeta::blitSource(p->gl); GLMeta::blitRectangle(destRect, Vec2i()); GLMeta::blitEnd(); FloatRect bltSubRect((float) sourceRect.x / source.width(), (float) sourceRect.y / source.height(), ((float) source.width() / sourceRect.w) * ((float) destRect.w / gpTex.width), ((float) source.height() / sourceRect.h) * ((float) destRect.h / gpTex.height)); BltShader &shader = shState->shaders().blt; shader.bind(); shader.setDestination(gpTex.tex); shader.setSubRect(bltSubRect); shader.setOpacity(normOpacity); Quad &quad = shState->gpQuad(); quad.setTexPosRect(sourceRect, destRect); quad.setColor(Vec4(1, 1, 1, normOpacity)); source.p->bindTexture(shader); p->bindFBO(); p->pushSetViewport(shader); p->blitQuad(quad); p->popViewport(); } p->addTaintedArea(destRect); p->onModified(); }