void GuiTabBookCtrl::onRender(Point2I offset, const RectI &updateRect) { RectI tabRect = mTabRect; tabRect.point += offset; RectI pageRect = mPageRect; pageRect.point += offset; // We're so nice we'll store the old modulation before we clear it for our rendering! :) ColorI oldModulation; GFX->getDrawUtil()->getBitmapModulation( &oldModulation ); // Wipe it out GFX->getDrawUtil()->clearBitmapModulation(); Parent::onRender(offset, updateRect); // Clip to tab area RectI savedClipRect = GFX->getClipRect(); RectI clippedTabRect = tabRect; clippedTabRect.intersect( savedClipRect ); GFX->setClipRect( clippedTabRect ); // Render our tabs renderTabs( offset, tabRect ); // Restore Rect. GFX->setClipRect( savedClipRect ); // Restore old modulation GFX->getDrawUtil()->setBitmapModulation( oldModulation ); }
void Image::copyUnProcessedChannels(const RectI& roi, const ImagePremultiplicationEnum outputPremult, const ImagePremultiplicationEnum originalImagePremult, const std::bitset<4> processChannels, const ImagePtr& originalImage, bool ignorePremult) { int numComp = getComponents().getNumComponents(); if (numComp == 0) { return; } if ( (numComp == 1) && processChannels[3] ) { // 1 component is alpha return; } else if ( (numComp == 2) && processChannels[0] && processChannels[1] ) { return; } else if ( (numComp == 3) && processChannels[0] && processChannels[1] && processChannels[2] ) { return; } else if ( (numComp == 4) && processChannels[0] && processChannels[1] && processChannels[2] && processChannels[3] ) { return; } if ( originalImage && ( getMipMapLevel() != originalImage->getMipMapLevel() ) ) { qDebug() << "WARNING: attempting to call copyUnProcessedChannels on images with different mipMapLevel"; return; } QWriteLocker k(&_entryLock); assert( !originalImage || getBitDepth() == originalImage->getBitDepth() ); RectI intersected; roi.intersect(_bounds, &intersected); bool premult = (outputPremult == eImagePremultiplicationPremultiplied); bool originalPremult = (originalImagePremult == eImagePremultiplicationPremultiplied); switch ( getBitDepth() ) { case eImageBitDepthByte: copyUnProcessedChannelsForDepth<unsigned char, 255>(premult, roi, processChannels, originalImage, originalPremult, ignorePremult); break; case eImageBitDepthShort: copyUnProcessedChannelsForDepth<unsigned short, 65535>(premult, roi, processChannels, originalImage, originalPremult, ignorePremult); break; case eImageBitDepthFloat: copyUnProcessedChannelsForDepth<float, 1>(premult, roi, processChannels, originalImage, originalPremult, ignorePremult); break; default: return; } }
void GFXD3D9Device::setClipRect( const RectI &inRect ) { // Clip the rect against the renderable size. Point2I size = mCurrentRT->getSize(); RectI maxRect(Point2I(0,0), size); RectI rect = inRect; rect.intersect(maxRect); mClipRect = rect; F32 l = F32( mClipRect.point.x ); F32 r = F32( mClipRect.point.x + mClipRect.extent.x ); F32 b = F32( mClipRect.point.y + mClipRect.extent.y ); F32 t = F32( mClipRect.point.y ); // Set up projection matrix, static Point4F pt; pt.set(2.0f / (r - l), 0.0f, 0.0f, 0.0f); mTempMatrix.setColumn(0, pt); pt.set(0.0f, 2.0f/(t - b), 0.0f, 0.0f); mTempMatrix.setColumn(1, pt); pt.set(0.0f, 0.0f, 1.0f, 0.0f); mTempMatrix.setColumn(2, pt); pt.set((l+r)/(l-r), (t+b)/(b-t), 1.0f, 1.0f); mTempMatrix.setColumn(3, pt); setProjectionMatrix( mTempMatrix ); // Set up world/view matrix mTempMatrix.identity(); setViewMatrix( mTempMatrix ); setWorldMatrix( mTempMatrix ); setViewport( mClipRect ); }
void Gui::debugImage(const Image* image, const RectI& roi, const QString & filename ) { if (image->getBitDepth() != eImageBitDepthFloat) { qDebug() << "Debug image only works on float images."; return; } RectI renderWindow; RectI bounds = image->getBounds(); if ( roi.isNull() ) { renderWindow = bounds; } else { if ( !roi.intersect(bounds, &renderWindow) ) { qDebug() << "The RoI does not interesect the bounds of the image."; return; } } QImage output(renderWindow.width(), renderWindow.height(), QImage::Format_ARGB32); const Color::Lut* lut = Color::LutManager::sRGBLut(); lut->validate(); Image::ReadAccess acc = image->getReadRights(); const float* from = (const float*)acc.pixelAt( renderWindow.left(), renderWindow.bottom() ); assert(from); int srcNComps = (int)image->getComponentsCount(); int srcRowElements = srcNComps * bounds.width(); for ( int y = renderWindow.height() - 1; y >= 0; --y, from += ( srcRowElements - srcNComps * renderWindow.width() ) ) { QRgb* dstPixels = (QRgb*)output.scanLine(y); assert(dstPixels); unsigned error_r = 0x80; unsigned error_g = 0x80; unsigned error_b = 0x80; for (int x = 0; x < renderWindow.width(); ++x, from += srcNComps, ++dstPixels) { float r, g, b, a; switch (srcNComps) { case 1: r = g = b = *from; a = 1; break; case 2: r = *from; g = *(from + 1); b = 0; a = 1; break; case 3: r = *from; g = *(from + 1); b = *(from + 2); a = 1; break; case 4: r = *from; g = *(from + 1); b = *(from + 2); a = *(from + 3); break; default: assert(false); return; } error_r = (error_r & 0xff) + lut->toColorSpaceUint8xxFromLinearFloatFast(r); error_g = (error_g & 0xff) + lut->toColorSpaceUint8xxFromLinearFloatFast(g); error_b = (error_b & 0xff) + lut->toColorSpaceUint8xxFromLinearFloatFast(b); assert(error_r < 0x10000 && error_g < 0x10000 && error_b < 0x10000); *dstPixels = qRgba( U8(error_r >> 8), U8(error_g >> 8), U8(error_b >> 8), U8(a * 255) ); } } U64 hashKey = image->getHashKey(); QString hashKeyStr = QString::number(hashKey); QString realFileName = filename.isEmpty() ? QString( hashKeyStr + QString::fromUtf8(".png") ) : filename; #ifdef DEBUG qDebug() << "Writing image: " << realFileName; renderWindow.debug(); #endif output.save(realFileName); } // Gui::debugImage
void GuiInspectorField::onRender( Point2I offset, const RectI &updateRect ) { RectI ctrlRect(offset, getExtent()); // Render fillcolor... if ( mProfile->mOpaque ) GFX->getDrawUtil()->drawRectFill(ctrlRect, mProfile->mFillColor); // Render caption... if ( mCaption && mCaption[0] ) { // Backup current ClipRect RectI clipBackup = GFX->getClipRect(); RectI clipRect = updateRect; // The rect within this control in which our caption must fit. RectI rect( offset + mCaptionRect.point + mProfile->mTextOffset, mCaptionRect.extent + Point2I(1,1) - Point2I(5,0) ); // Now clipRect is the amount of our caption rect that is actually visible. bool hit = clipRect.intersect( rect ); if ( hit ) { GFX->setClipRect( clipRect ); GFXDrawUtil *drawer = GFX->getDrawUtil(); // Backup modulation color ColorI currColor; drawer->getBitmapModulation( &currColor ); // Draw caption background... if( !isActive() ) GFX->getDrawUtil()->drawRectFill( clipRect, mProfile->mFillColorNA ); else if ( mHighlighted ) GFX->getDrawUtil()->drawRectFill( clipRect, mProfile->mFillColorHL ); // Draw caption text... drawer->setBitmapModulation( !isActive() ? mProfile->mFontColorNA : mHighlighted ? mProfile->mFontColorHL : mProfile->mFontColor ); // Clip text with '...' if too long to fit String clippedText( mCaption ); clipText( clippedText, clipRect.extent.x ); renderJustifiedText( offset + mProfile->mTextOffset, getExtent(), clippedText ); // Restore modulation color drawer->setBitmapModulation( currColor ); // Restore previous ClipRect GFX->setClipRect( clipBackup ); } } // Render Children... renderChildControls(offset, updateRect); // Render border... if ( mProfile->mBorder ) renderBorder(ctrlRect, mProfile); // Render divider... Point2I worldPnt = mEditCtrlRect.point + offset; GFX->getDrawUtil()->drawLine( worldPnt.x - 5, worldPnt.y, worldPnt.x - 5, worldPnt.y + getHeight(), !isActive() ? mProfile->mBorderColorNA : mHighlighted ? mProfile->mBorderColorHL : mProfile->mBorderColor ); }
void Image::copyUnProcessedChannels(const RectI& roi, const ImagePremultiplicationEnum outputPremult, const ImagePremultiplicationEnum originalImagePremult, const std::bitset<4> processChannels, const ImagePtr& originalImage, bool ignorePremult, const OSGLContextPtr& glContext) { int numComp = getComponents().getNumComponents(); if (numComp == 0) { return; } if ( (numComp == 1) && processChannels[3] ) { // 1 component is alpha return; } else if ( (numComp == 2) && processChannels[0] && processChannels[1] ) { return; } else if ( (numComp == 3) && processChannels[0] && processChannels[1] && processChannels[2] ) { return; } else if ( (numComp == 4) && processChannels[0] && processChannels[1] && processChannels[2] && processChannels[3] ) { return; } if ( originalImage && ( getMipMapLevel() != originalImage->getMipMapLevel() ) ) { qDebug() << "WARNING: attempting to call copyUnProcessedChannels on images with different mipMapLevel"; return; } QWriteLocker k(&_entryLock); assert( !originalImage || getBitDepth() == originalImage->getBitDepth() ); RectI srcRoi; roi.intersect(_bounds, &srcRoi); if (getStorageMode() == eStorageModeGLTex) { assert(glContext); if (glContext->isGPUContext()) { copyUnProcessedChannelsGL<GL_GPU>(roi, outputPremult, originalImagePremult, processChannels, originalImage, ignorePremult, glContext, _bounds, srcRoi, getGLTextureTarget(), getGLTextureID(), originalImage->getGLTextureID()); } else { copyUnProcessedChannelsGL<GL_CPU>(roi, outputPremult, originalImagePremult, processChannels, originalImage, ignorePremult, glContext, _bounds, srcRoi, getGLTextureTarget(), getGLTextureID(), originalImage->getGLTextureID()); } return; } bool premult = (outputPremult == eImagePremultiplicationPremultiplied); bool originalPremult = (originalImagePremult == eImagePremultiplicationPremultiplied); switch ( getBitDepth() ) { case eImageBitDepthByte: copyUnProcessedChannelsForDepth<unsigned char, 255>(premult, roi, processChannels, originalImage, originalPremult, ignorePremult); break; case eImageBitDepthShort: copyUnProcessedChannelsForDepth<unsigned short, 65535>(premult, roi, processChannels, originalImage, originalPremult, ignorePremult); break; case eImageBitDepthFloat: copyUnProcessedChannelsForDepth<float, 1>(premult, roi, processChannels, originalImage, originalPremult, ignorePremult); break; default: return; } } // copyUnProcessedChannels
bool GuiSplitContainer::layoutControls( RectI &clientRect ) { if ( size() < 2 ) return false; GuiContainer *panelOne = dynamic_cast<GuiContainer*>( at(0) ); GuiContainer *panelTwo = dynamic_cast<GuiContainer*>( at(1) ); // AssertFatal( panelOne && panelTwo, "GuiSplitContainer::layoutControl - Missing/Invalid Subordinate Controls! Split contained controls must derive from GuiContainer!" ); RectI panelOneRect = RectI( clientRect.point, Point2I( 0, 0 ) ); RectI panelTwoRect; RectI splitRect; solvePanelConstraints( getSplitPoint(), panelOne, panelTwo, clientRect ); switch( mOrientation ) { case Horizontal: panelOneRect.extent = Point2I( clientRect.extent.x, getSplitPoint().y ); panelTwoRect = panelOneRect; panelTwoRect.intersect( clientRect ); panelTwoRect.point.y = panelOneRect.extent.y; panelTwoRect.extent.y = clientRect.extent.y - panelOneRect.extent.y; // Generate new Splitter Rectangle splitRect = panelTwoRect; splitRect.extent.y = 0; splitRect.inset( 0, -mSplitterSize ); panelOneRect.extent.y -= mSplitterSize; panelTwoRect.point.y += mSplitterSize; panelTwoRect.extent.y -= mSplitterSize; break; case Vertical: panelOneRect.extent = Point2I( getSplitPoint().x, clientRect.extent.y ); panelTwoRect = panelOneRect; panelTwoRect.intersect( clientRect ); panelTwoRect.point.x = panelOneRect.extent.x; panelTwoRect.extent.x = clientRect.extent.x - panelOneRect.extent.x; // Generate new Splitter Rectangle splitRect = panelTwoRect; splitRect.extent.x = 0; splitRect.inset( -mSplitterSize, 0 ); panelOneRect.extent.x -= mSplitterSize; panelTwoRect.point.x += mSplitterSize; panelTwoRect.extent.x -= mSplitterSize; break; } // Update Split Rect mSplitRect = splitRect; // Dock Appropriately if( !( mFixedPanel == FirstPanel && !panelOne->isVisible() ) ) dockControl( panelOne, panelOne->getDocking(), panelOneRect ); if( !( mFixedPanel == FirstPanel && !panelTwo->isVisible() ) ) dockControl( panelTwo, panelOne->getDocking(), panelTwoRect ); // return false; }
/* * @brief This is called by LibMV to retrieve an image either for reference or as search frame. */ mv::FrameAccessor::Key TrackerFrameAccessor::GetImage(int /*clip*/, int frame, mv::FrameAccessor::InputMode input_mode, int downscale, // Downscale by 2^downscale. const mv::Region* region, // Get full image if NULL. const mv::FrameAccessor::Transform* /*transform*/, // May be NULL. mv::FloatImage** destination) { // Since libmv only uses MONO images for now we have only optimized for this case, remove and handle properly // other case(s) when they get integrated into libmv. assert(input_mode == mv::FrameAccessor::MONO); FrameAccessorCacheKey key; key.frame = frame; key.mipMapLevel = downscale; key.mode = input_mode; /* Check if a frame exists in the cache with matching key and bounds enclosing the given region */ RectI roi; if (region) { convertLibMVRegionToRectI(*region, _imp->formatHeight, &roi); QMutexLocker k(&_imp->cacheMutex); std::pair<FrameAccessorCache::iterator, FrameAccessorCache::iterator> range = _imp->cache.equal_range(key); for (FrameAccessorCache::iterator it = range.first; it != range.second; ++it) { if ( (roi.x1 >= it->second.bounds.x1) && (roi.x2 <= it->second.bounds.x2) && ( roi.y1 >= it->second.bounds.y1) && ( roi.y2 <= it->second.bounds.y2) ) { #ifdef TRACE_LIB_MV qDebug() << QThread::currentThread() << "FrameAccessor::GetImage():" << "Found cached image at frame" << frame << "with RoI x1=" << region->min(0) << "y1=" << region->max(1) << "x2=" << region->max(0) << "y2=" << region->min(1); #endif // LibMV is kinda dumb on this we must necessarily copy the data either via CopyFrom or the // assignment constructor: // EDIT: fixed libmv *destination = it->second.image.get(); //destination->CopyFrom<float>(*it->second.image); ++it->second.referenceCount; return (mv::FrameAccessor::Key)it->second.image.get(); } } } EffectInstancePtr effect; if (_imp->trackerInput) { effect = _imp->trackerInput->getEffectInstance(); } if (!effect) { return (mv::FrameAccessor::Key)0; } // Not in accessor cache, call renderRoI RenderScale scale; scale.y = scale.x = Image::getScaleFromMipMapLevel( (unsigned int)downscale ); RectD precomputedRoD; if (!region) { bool isProjectFormat; StatusEnum stat = effect->getRegionOfDefinition_public(_imp->trackerInput->getHashValue(), frame, scale, ViewIdx(0), &precomputedRoD, &isProjectFormat); if (stat == eStatusFailed) { return (mv::FrameAccessor::Key)0; } double par = effect->getAspectRatio(-1); precomputedRoD.toPixelEnclosing( (unsigned int)downscale, par, &roi ); } std::list<ImageComponents> components; components.push_back( ImageComponents::getRGBComponents() ); NodePtr node = _imp->context->getNode(); const bool isRenderUserInteraction = true; const bool isSequentialRender = false; AbortableRenderInfoPtr abortInfo = AbortableRenderInfo::create(false, 0); AbortableThread* isAbortable = dynamic_cast<AbortableThread*>( QThread::currentThread() ); if (isAbortable) { isAbortable->setAbortInfo( isRenderUserInteraction, abortInfo, node->getEffectInstance() ); } ParallelRenderArgsSetter::CtorArgsPtr tlsArgs(new ParallelRenderArgsSetter::CtorArgs); tlsArgs->time = frame; tlsArgs->view = ViewIdx(0); tlsArgs->isRenderUserInteraction = isRenderUserInteraction; tlsArgs->isSequential = isSequentialRender; tlsArgs->abortInfo = abortInfo; tlsArgs->treeRoot = node; tlsArgs->textureIndex = 0; tlsArgs->timeline = node->getApp()->getTimeLine(); tlsArgs->activeRotoPaintNode = NodePtr(); tlsArgs->activeRotoDrawableItem = RotoDrawableItemPtr(); tlsArgs->isDoingRotoNeatRender = false; tlsArgs->isAnalysis = true; tlsArgs->draftMode = false; tlsArgs->stats = RenderStatsPtr(); ParallelRenderArgsSetter frameRenderArgs(tlsArgs); // Stats EffectInstance::RenderRoIArgs args( frame, scale, downscale, ViewIdx(0), false, roi, precomputedRoD, components, eImageBitDepthFloat, true, _imp->context->getNode()->getEffectInstance(), eStorageModeRAM /*returnOpenGLTex*/, frame); std::map<ImageComponents, ImagePtr> planes; EffectInstance::RenderRoIRetCode stat = effect->renderRoI(args, &planes); if ( (stat != EffectInstance::eRenderRoIRetCodeOk) || planes.empty() ) { #ifdef TRACE_LIB_MV qDebug() << QThread::currentThread() << "FrameAccessor::GetImage():" << "Failed to call renderRoI on input at frame" << frame << "with RoI x1=" << roi.x1 << "y1=" << roi.y1 << "x2=" << roi.x2 << "y2=" << roi.y2; #endif return (mv::FrameAccessor::Key)0; } assert( !planes.empty() ); const ImagePtr& sourceImage = planes.begin()->second; RectI sourceBounds = sourceImage->getBounds(); RectI intersectedRoI; if ( !roi.intersect(sourceBounds, &intersectedRoI) ) { #ifdef TRACE_LIB_MV qDebug() << QThread::currentThread() << "FrameAccessor::GetImage():" << "RoI does not intersect the source image bounds (RoI x1=" << roi.x1 << "y1=" << roi.y1 << "x2=" << roi.x2 << "y2=" << roi.y2 << ")"; #endif return (mv::FrameAccessor::Key)0; } #ifdef TRACE_LIB_MV qDebug() << QThread::currentThread() << "FrameAccessor::GetImage():" << "renderRoi (frame" << frame << ") OK (BOUNDS= x1=" << sourceBounds.x1 << "y1=" << sourceBounds.y1 << "x2=" << sourceBounds.x2 << "y2=" << sourceBounds.y2 << ") (ROI = " << roi.x1 << "y1=" << roi.y1 << "x2=" << roi.x2 << "y2=" << roi.y2 << ")"; #endif /* Copy the Natron image to the LivMV float image */ FrameAccessorCacheEntry entry; entry.image.reset( new MvFloatImage( intersectedRoI.height(), intersectedRoI.width() ) ); entry.bounds = intersectedRoI; entry.referenceCount = 1; natronImageToLibMvFloatImage(_imp->enabledChannels, sourceImage.get(), intersectedRoI, *entry.image); // we ignore the transform parameter and do it in natronImageToLibMvFloatImage instead *destination = entry.image.get(); //destination->CopyFrom<float>(*entry.image); //insert into the cache { QMutexLocker k(&_imp->cacheMutex); _imp->cache.insert( std::make_pair(key, entry) ); } #ifdef TRACE_LIB_MV qDebug() << QThread::currentThread() << "FrameAccessor::GetImage():" << "Rendered frame" << frame << "with RoI x1=" << intersectedRoI.x1 << "y1=" << intersectedRoI.y1 << "x2=" << intersectedRoI.x2 << "y2=" << intersectedRoI.y2; #endif return (mv::FrameAccessor::Key)entry.image.get(); } // TrackerFrameAccessor::GetImage
void ImageTilesState::getMinimalRectsToRenderFromTilesState(const RectI& roi, const TileStateHeader& stateMap, std::list<RectI>* rectsToRender) { if (stateMap.state->tiles.empty()) { return; } RectI roiRoundedToTileSize = roi; roiRoundedToTileSize.roundToTileSize(stateMap.tileSizeX, stateMap.tileSizeY); RectI bboxM = getMinimalBboxToRenderFromTilesState(roi, stateMap); if (bboxM.isNull()) { return; } bboxM.roundToTileSize(stateMap.tileSizeX, stateMap.tileSizeY); // optimization by Fred, Jan 31, 2014 // // Now that we have the smallest enclosing bounding box, // let's try to find rectangles for the bottom, the top, // the left and the right part. // This happens quite often, for example when zooming out // (in this case the area to compute is formed of A, B, C and D, // and X is already rendered), or when panning (in this case the area // is just two rectangles, e.g. A and C, and the rectangles B, D and // X are already rendered). // The rectangles A, B, C and D from the following drawing are just // zeroes, and X contains zeroes and ones. // // BBBBBBBBBBBBBB // BBBBBBBBBBBBBB // CXXXXXXXXXXDDD // CXXXXXXXXXXDDD // CXXXXXXXXXXDDD // CXXXXXXXXXXDDD // AAAAAAAAAAAAAA // First, find if there's an "A" rectangle, and push it to the result //find bottom RectI bboxX = bboxM; RectI bboxA = bboxX; bboxA.y2 = bboxA.y1; for (int y = bboxX.y1; y < bboxX.y2; y += stateMap.tileSizeY) { bool hasRenderedTileOnLine = false; for (int x = bboxX.x1; x < bboxX.x2; x += stateMap.tileSizeX) { const TileState* tile = stateMap.getTileAt(x, y); if (tile->status != eTileStatusNotRendered) { hasRenderedTileOnLine = true; break; } } if (hasRenderedTileOnLine) { break; } else { bboxX.y1 += stateMap.tileSizeY; bboxA.y2 = bboxX.y1; } } if ( !bboxA.isNull() ) { // empty boxes should not be pushed // Ensure the bbox lies in the RoI since we rounded to tile size earlier RectI bboxAIntersected; bboxA.intersect(roi, &bboxAIntersected); rectsToRender->push_back(bboxAIntersected); } // Now, find the "B" rectangle //find top RectI bboxB = bboxX; bboxB.y1 = bboxB.y2; for (int y = bboxX.y2 - stateMap.tileSizeY; y >= bboxX.y1; y -= stateMap.tileSizeY) { bool hasRenderedTileOnLine = false; for (int x = bboxX.x1; x < bboxX.x2; x += stateMap.tileSizeX) { const TileState* tile = stateMap.getTileAt(x, y); if (tile->status != eTileStatusNotRendered) { hasRenderedTileOnLine = true; break; } } if (hasRenderedTileOnLine) { break; } else { bboxX.y2 -= stateMap.tileSizeY; bboxB.y1 = bboxX.y2; } } if ( !bboxB.isNull() ) { // empty boxes should not be pushed // Ensure the bbox lies in the RoI since we rounded to tile size earlier RectI bboxBIntersected; bboxB.intersect(roi, &bboxBIntersected); rectsToRender->push_back(bboxBIntersected); } //find left RectI bboxC = bboxX; bboxC.x2 = bboxC.x1; if ( bboxX.y1 < bboxX.y2 ) { for (int x = bboxX.x1; x < bboxX.x2; x += stateMap.tileSizeX) { bool hasRenderedTileOnCol = false; for (int y = bboxX.y1; y < bboxX.y2; y += stateMap.tileSizeY) { const TileState* tile = stateMap.getTileAt(x, y); if (tile->status != eTileStatusNotRendered) { hasRenderedTileOnCol = true; break; } } if (hasRenderedTileOnCol) { break; } else { bboxX.x1 += stateMap.tileSizeX; bboxC.x2 = bboxX.x1; } } } if ( !bboxC.isNull() ) { // empty boxes should not be pushed // Ensure the bbox lies in the RoI since we rounded to tile size earlier RectI bboxCIntersected; bboxC.intersect(roi, &bboxCIntersected); rectsToRender->push_back(bboxCIntersected); } //find right RectI bboxD = bboxX; bboxD.x1 = bboxD.x2; if ( bboxX.y1 < bboxX.y2 ) { for (int x = bboxX.x2 - stateMap.tileSizeX; x >= bboxX.x1; x -= stateMap.tileSizeX) { bool hasRenderedTileOnCol = false; for (int y = bboxX.y1; y < bboxX.y2; y += stateMap.tileSizeY) { const TileState* tile = stateMap.getTileAt(x, y); if (tile->status != eTileStatusNotRendered) { hasRenderedTileOnCol = true; break; } } if (hasRenderedTileOnCol) { break; } else { bboxX.x2 -= stateMap.tileSizeX; bboxD.x1 = bboxX.x2; } } } if ( !bboxD.isNull() ) { // empty boxes should not be pushed // Ensure the bbox lies in the RoI since we rounded to tile size earlier RectI bboxDIntersected; bboxD.intersect(roi, &bboxDIntersected); rectsToRender->push_back(bboxDIntersected); } assert( bboxA.bottom() == bboxM.bottom() ); assert( bboxA.left() == bboxM.left() ); assert( bboxA.right() == bboxM.right() ); assert( bboxA.top() == bboxX.bottom() ); assert( bboxB.top() == bboxM.top() ); assert( bboxB.left() == bboxM.left() ); assert( bboxB.right() == bboxM.right() ); assert( bboxB.bottom() == bboxX.top() ); assert( bboxC.top() == bboxX.top() ); assert( bboxC.left() == bboxM.left() ); assert( bboxC.right() == bboxX.left() ); assert( bboxC.bottom() == bboxX.bottom() ); assert( bboxD.top() == bboxX.top() ); assert( bboxD.left() == bboxX.right() ); assert( bboxD.right() == bboxM.right() ); assert( bboxD.bottom() == bboxX.bottom() ); // get the bounding box of what's left (the X rectangle in the drawing above) bboxX = getMinimalBboxToRenderFromTilesState(bboxX, stateMap); if ( !bboxX.isNull() ) { // empty boxes should not be pushed // Ensure the bbox lies in the RoI since we rounded to tile size earlier RectI bboxXIntersected; bboxX.intersect(roi, &bboxXIntersected); rectsToRender->push_back(bboxXIntersected); } } // getMinimalRectsToRenderFromTilesState
RectI ImageTilesState::getMinimalBboxToRenderFromTilesState(const RectI& roi, const TileStateHeader& stateMap) { if (stateMap.state->tiles.empty()) { return RectI(); } const RectI& imageBoundsRoundedToTileSize = stateMap.state->boundsRoundedToTileSize; const RectI& imageBoundsNotRounded = stateMap.state->bounds; assert(imageBoundsRoundedToTileSize.contains(roi)); RectI roiRoundedToTileSize = roi; roiRoundedToTileSize.roundToTileSize(stateMap.tileSizeX, stateMap.tileSizeY); // Search for rendered lines from bottom to top for (int y = roiRoundedToTileSize.y1; y < roiRoundedToTileSize.y2; y += stateMap.tileSizeY) { bool hasTileUnrenderedOnLine = false; for (int x = roiRoundedToTileSize.x1; x < roiRoundedToTileSize.x2; x += stateMap.tileSizeX) { const TileState* tile = stateMap.getTileAt(x, y); if (tile->status == eTileStatusNotRendered) { hasTileUnrenderedOnLine = true; break; } } if (!hasTileUnrenderedOnLine) { roiRoundedToTileSize.y1 += stateMap.tileSizeY; } else { break; } } // Search for rendered lines from top to bottom for (int y = roiRoundedToTileSize.y2 - stateMap.tileSizeY; y >= roiRoundedToTileSize.y1; y -= stateMap.tileSizeY) { bool hasTileUnrenderedOnLine = false; for (int x = roiRoundedToTileSize.x1; x < roiRoundedToTileSize.x2; x += stateMap.tileSizeX) { const TileState* tile = stateMap.getTileAt(x, y); if (tile->status == eTileStatusNotRendered) { hasTileUnrenderedOnLine = true; break; } } if (!hasTileUnrenderedOnLine) { roiRoundedToTileSize.y2 -= stateMap.tileSizeY; } else { break; } } // Avoid making roiRoundedToTileSize.width() iterations for nothing if (roiRoundedToTileSize.isNull()) { return roiRoundedToTileSize; } // Search for rendered columns from left to right for (int x = roiRoundedToTileSize.x1; x < roiRoundedToTileSize.x2; x += stateMap.tileSizeX) { bool hasTileUnrenderedOnCol = false; for (int y = roiRoundedToTileSize.y1; y < roiRoundedToTileSize.y2; y += stateMap.tileSizeY) { const TileState* tile = stateMap.getTileAt(x, y); if (tile->status == eTileStatusNotRendered) { hasTileUnrenderedOnCol = true; break; } } if (!hasTileUnrenderedOnCol) { roiRoundedToTileSize.x1 += stateMap.tileSizeX; } else { break; } } // Avoid making roiRoundedToTileSize.width() iterations for nothing if (roiRoundedToTileSize.isNull()) { return roiRoundedToTileSize; } // Search for rendered columns from right to left for (int x = roiRoundedToTileSize.x2 - stateMap.tileSizeX; x >= roiRoundedToTileSize.x1; x -= stateMap.tileSizeX) { bool hasTileUnrenderedOnCol = false; for (int y = roiRoundedToTileSize.y1; y < roiRoundedToTileSize.y2; y += stateMap.tileSizeY) { const TileState* tile = stateMap.getTileAt(x, y); if (tile->status == eTileStatusNotRendered) { hasTileUnrenderedOnCol = true; break; } } if (!hasTileUnrenderedOnCol) { roiRoundedToTileSize.x2 -= stateMap.tileSizeX; } else { break; } } // Intersect the result to the actual image bounds (because the tiles are rounded to tile size) RectI ret; roiRoundedToTileSize.intersect(imageBoundsNotRounded, &ret); return ret; } // getMinimalBboxToRenderFromTilesState
void Image::applyMaskMix(const RectI& roi, const Image* maskImg, const Image* originalImg, bool masked, bool maskInvert, float mix, const OSGLContextPtr& glContext) { ///!masked && mix == 1 has nothing to do if ( !masked && (mix == 1) ) { return; } QWriteLocker k(&_entryLock); boost::shared_ptr<QReadLocker> originalLock; boost::shared_ptr<QReadLocker> maskLock; if (originalImg) { originalLock.reset( new QReadLocker(&originalImg->_entryLock) ); } if (maskImg) { maskLock.reset( new QReadLocker(&maskImg->_entryLock) ); } RectI realRoI; roi.intersect(_bounds, &realRoI); assert( !originalImg || getBitDepth() == originalImg->getBitDepth() ); assert( !masked || !maskImg || maskImg->getComponents() == ImageComponents::getAlphaComponents() ); if (getStorageMode() == eStorageModeGLTex) { assert(glContext); assert(originalImg->getStorageMode() == eStorageModeGLTex); boost::shared_ptr<GLShader> shader = glContext->getOrCreateDefaultShader(OSGLContext::eDefaultGLShaderCopyUnprocessedChannels); assert(shader); GLuint fboID = glContext->getFBOId(); glBindFramebuffer(GL_FRAMEBUFFER, fboID); int target = getGLTextureTarget(); glEnable(target); glActiveTexture(GL_TEXTURE0); glBindTexture( target, getGLTextureID() ); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, getGLTextureID(), 0 /*LoD*/); glCheckFramebufferError(); glActiveTexture(GL_TEXTURE1); glBindTexture( target, originalImg->getGLTextureID() ); glActiveTexture(GL_TEXTURE2); glBindTexture(target, maskImg ? maskImg->getGLTextureID() : 0); glViewport( realRoI.x1 - _bounds.x1, realRoI.y1 - _bounds.y1, realRoI.width(), realRoI.height() ); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho( realRoI.x1, realRoI.x2, realRoI.y1, realRoI.y2, -10.0 * (realRoI.y2 - realRoI.y1), 10.0 * (realRoI.y2 - realRoI.y1) ); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glCheckError(); // Compute the texture coordinates to match the srcRoi Point srcTexCoords[4], vertexCoords[4]; vertexCoords[0].x = realRoI.x1; vertexCoords[0].y = realRoI.y1; srcTexCoords[0].x = (realRoI.x1 - _bounds.x1) / (double)_bounds.width(); srcTexCoords[0].y = (realRoI.y1 - _bounds.y1) / (double)_bounds.height(); vertexCoords[1].x = realRoI.x2; vertexCoords[1].y = realRoI.y1; srcTexCoords[1].x = (realRoI.x2 - _bounds.x1) / (double)_bounds.width(); srcTexCoords[1].y = (realRoI.y1 - _bounds.y1) / (double)_bounds.height(); vertexCoords[2].x = realRoI.x2; vertexCoords[2].y = realRoI.y2; srcTexCoords[2].x = (realRoI.x2 - _bounds.x1) / (double)_bounds.width(); srcTexCoords[2].y = (realRoI.y2 - _bounds.y1) / (double)_bounds.height(); vertexCoords[3].x = realRoI.x1; vertexCoords[3].y = realRoI.y2; srcTexCoords[3].x = (realRoI.x1 - _bounds.x1) / (double)_bounds.width(); srcTexCoords[3].y = (realRoI.y2 - _bounds.y1) / (double)_bounds.height(); shader->bind(); shader->setUniform("originalImageTex", 1); shader->setUniform("maskImageTex", 2); shader->setUniform("outputImageTex", 0); shader->setUniform("mixValue", mix); shader->setUniform("maskEnabled", maskImg ? 1 : 0); glBegin(GL_POLYGON); for (int i = 0; i < 4; ++i) { glTexCoord2d(srcTexCoords[i].x, srcTexCoords[i].y); glVertex2d(vertexCoords[i].x, vertexCoords[i].y); } glEnd(); shader->unbind(); glBindTexture(target, 0); glActiveTexture(GL_TEXTURE1); glBindTexture(target, 0); glActiveTexture(GL_TEXTURE0); glBindTexture(target, 0); glCheckError(); return; } int srcNComps = originalImg ? (int)originalImg->getComponentsCount() : 0; //assert(0 < srcNComps && srcNComps <= 4); switch (srcNComps) { //case 0: // applyMaskMixForSrcComponents<0>(realRoI, maskImg, originalImg, masked, maskInvert, mix); // break; case 1: applyMaskMixForSrcComponents<1>(realRoI, maskImg, originalImg, masked, maskInvert, mix); break; case 2: applyMaskMixForSrcComponents<2>(realRoI, maskImg, originalImg, masked, maskInvert, mix); break; case 3: applyMaskMixForSrcComponents<3>(realRoI, maskImg, originalImg, masked, maskInvert, mix); break; case 4: applyMaskMixForSrcComponents<4>(realRoI, maskImg, originalImg, masked, maskInvert, mix); break; default: break; } } // applyMaskMix