static void android_view_RenderNode_getTransformMatrix(JNIEnv* env,
        jobject clazz, jlong renderNodePtr, jlong outMatrixPtr) {
    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
    SkMatrix* outMatrix = reinterpret_cast<SkMatrix*>(outMatrixPtr);

    renderNode->mutateStagingProperties().updateMatrix();
    const SkMatrix* transformMatrix = renderNode->stagingProperties().getTransformMatrix();

    if (transformMatrix) {
        *outMatrix = *transformMatrix;
    } else {
        outMatrix->setIdentity();
    }
}
        virtual void onPositionUpdated(RenderNode& node, const TreeInfo& info) override {
            if (CC_UNLIKELY(!mWeakRef || !info.updateWindowPositions)) return;

            Matrix4 transform;
            info.damageAccumulator->computeCurrentTransform(&transform);
            const RenderProperties& props = node.properties();
            uirenderer::Rect bounds(props.getWidth(), props.getHeight());
            transform.mapRect(bounds);
            bounds.left -= info.windowInsetLeft;
            bounds.right -= info.windowInsetLeft;
            bounds.top -= info.windowInsetTop;
            bounds.bottom -= info.windowInsetTop;

            if (CC_LIKELY(transform.isPureTranslate())) {
                // snap/round the computed bounds, so they match the rounding behavior
                // of the clear done in SurfaceView#draw().
                bounds.snapToPixelBoundaries();
            } else {
                // Conservatively round out so the punched hole (in the ZOrderOnTop = true case)
                // doesn't extend beyond the other window
                bounds.roundOut();
            }

            incStrong(0);
            auto functor = std::bind(
                std::mem_fn(&SurfaceViewPositionUpdater::doUpdatePositionAsync), this,
                (jlong) info.canvasContext.getFrameNumber(),
                (jint) bounds.left, (jint) bounds.top,
                (jint) bounds.right, (jint) bounds.bottom);

            info.canvasContext.enqueueFrameWork(std::move(functor));
        }
static void android_view_RenderNode_setDisplayList(JNIEnv* env,
        jobject clazz, jlong renderNodePtr, jlong displayListPtr) {
    class RemovedObserver : public TreeObserver {
    public:
        virtual void onMaybeRemovedFromTree(RenderNode* node) override {
            maybeRemovedNodes.insert(sp<RenderNode>(node));
        }
        std::set< sp<RenderNode> > maybeRemovedNodes;
    };

    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
    DisplayList* newData = reinterpret_cast<DisplayList*>(displayListPtr);
    RemovedObserver observer;
    renderNode->setStagingDisplayList(newData, &observer);
    for (auto& node : observer.maybeRemovedNodes) {
        if (node->hasParents()) continue;
        onRenderNodeRemoved(env, node.get());
    }
}
void RenderNode::buildZSortedChildList(const DisplayList::Chunk& chunk,
        std::vector<ZDrawRenderNodeOpPair>& zTranslatedNodes) {
#if !HWUI_NEW_OPS
    if (chunk.beginChildIndex == chunk.endChildIndex) return;

    for (unsigned int i = chunk.beginChildIndex; i < chunk.endChildIndex; i++) {
        DrawRenderNodeOp* childOp = mDisplayList->getChildren()[i];
        RenderNode* child = childOp->renderNode;
        float childZ = child->properties().getZ();

        if (!MathUtils::isZero(childZ) && chunk.reorderChildren) {
            zTranslatedNodes.push_back(ZDrawRenderNodeOpPair(childZ, childOp));
            childOp->skipInOrderDraw = true;
        } else if (!child->properties().getProjectBackwards()) {
            // regular, in order drawing DisplayList
            childOp->skipInOrderDraw = false;
        }
    }

    // Z sort any 3d children (stable-ness makes z compare fall back to standard drawing order)
    std::stable_sort(zTranslatedNodes.begin(), zTranslatedNodes.end());
#endif
}
void RenderNode::prepareSubTree(TreeInfo& info, bool functorsNeedLayer, DisplayList* subtree) {
    if (subtree) {
        TextureCache& cache = Caches::getInstance().textureCache;
        info.out.hasFunctors |= subtree->getFunctors().size();
        for (auto&& bitmapResource : subtree->getBitmapResources()) {
            void* ownerToken = &info.canvasContext;
            info.prepareTextures = cache.prefetchAndMarkInUse(ownerToken, bitmapResource);
        }
        for (auto&& op : subtree->getChildren()) {
            RenderNode* childNode = op->renderNode;
#if HWUI_NEW_OPS
            info.damageAccumulator->pushTransform(&op->localMatrix);
            bool childFunctorsNeedLayer = functorsNeedLayer; // TODO! || op->mRecordedWithPotentialStencilClip;
#else
            info.damageAccumulator->pushTransform(&op->localMatrix);
            bool childFunctorsNeedLayer = functorsNeedLayer
                    // Recorded with non-rect clip, or canvas-rotated by parent
                    || op->mRecordedWithPotentialStencilClip;
#endif
            childNode->prepareTreeImpl(info, childFunctorsNeedLayer);
            info.damageAccumulator->popTransform();
        }
    }
}
static jboolean android_view_RenderNode_hasIdentityMatrix(JNIEnv* env,
        jobject clazz, jlong renderNodePtr) {
    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
    renderNode->mutateStagingProperties().updateMatrix();
    return !renderNode->stagingProperties().hasTransformMatrix();
}
static jboolean android_view_RenderNode_isPivotExplicitlySet(JNIEnv* env,
        jobject clazz, jlong renderNodePtr) {
    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
    return renderNode->stagingProperties().isPivotExplicitlySet();
}
static jfloat android_view_RenderNode_getRotationY(JNIEnv* env,
        jobject clazz, jlong renderNodePtr) {
    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
    return renderNode->stagingProperties().getRotationY();
}
static jboolean android_view_RenderNode_getClipToOutline(JNIEnv* env,
        jobject clazz, jlong renderNodePtr) {
    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
    return renderNode->stagingProperties().getOutline().getShouldClip();
}
static void android_view_RenderNode_destroyRenderNode(JNIEnv* env,
        jobject clazz, jlong renderNodePtr) {
    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
    renderNode->decStrong(0);
}
static void android_view_RenderNode_endAllAnimators(JNIEnv* env, jobject clazz,
        jlong renderNodePtr) {
    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
    renderNode->animators().endAllStagingAnimators();
}
static void android_view_RenderNode_output(JNIEnv* env,
        jobject clazz, jlong renderNodePtr) {
    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
    renderNode->output();
}
static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject,
        jlong renderNodePtr, jobject surfaceview) {
    class SurfaceViewPositionUpdater : public RenderNode::PositionListener {
    public:
        SurfaceViewPositionUpdater(JNIEnv* env, jobject surfaceview) {
            env->GetJavaVM(&mVm);
            mWeakRef = env->NewWeakGlobalRef(surfaceview);
        }

        virtual ~SurfaceViewPositionUpdater() {
            jnienv()->DeleteWeakGlobalRef(mWeakRef);
            mWeakRef = nullptr;
        }

        virtual void onPositionUpdated(RenderNode& node, const TreeInfo& info) override {
            if (CC_UNLIKELY(!mWeakRef || !info.updateWindowPositions)) return;

            Matrix4 transform;
            info.damageAccumulator->computeCurrentTransform(&transform);
            const RenderProperties& props = node.properties();
            uirenderer::Rect bounds(props.getWidth(), props.getHeight());
            transform.mapRect(bounds);
            bounds.left -= info.windowInsetLeft;
            bounds.right -= info.windowInsetLeft;
            bounds.top -= info.windowInsetTop;
            bounds.bottom -= info.windowInsetTop;

            if (CC_LIKELY(transform.isPureTranslate())) {
                // snap/round the computed bounds, so they match the rounding behavior
                // of the clear done in SurfaceView#draw().
                bounds.snapToPixelBoundaries();
            } else {
                // Conservatively round out so the punched hole (in the ZOrderOnTop = true case)
                // doesn't extend beyond the other window
                bounds.roundOut();
            }

            incStrong(0);
            auto functor = std::bind(
                std::mem_fn(&SurfaceViewPositionUpdater::doUpdatePositionAsync), this,
                (jlong) info.canvasContext.getFrameNumber(),
                (jint) bounds.left, (jint) bounds.top,
                (jint) bounds.right, (jint) bounds.bottom);

            info.canvasContext.enqueueFrameWork(std::move(functor));
        }

        virtual void onPositionLost(RenderNode& node, const TreeInfo* info) override {
            if (CC_UNLIKELY(!mWeakRef || (info && !info->updateWindowPositions))) return;

            ATRACE_NAME("SurfaceView position lost");
            JNIEnv* env = jnienv();
            jobject localref = env->NewLocalRef(mWeakRef);
            if (CC_UNLIKELY(!localref)) {
                jnienv()->DeleteWeakGlobalRef(mWeakRef);
                mWeakRef = nullptr;
                return;
            }

            env->CallVoidMethod(localref, gSurfaceViewPositionLostMethod,
                    info ? info->canvasContext.getFrameNumber() : 0);
            env->DeleteLocalRef(localref);
        }

    private:
        JNIEnv* jnienv() {
            JNIEnv* env;
            if (mVm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
                LOG_ALWAYS_FATAL("Failed to get JNIEnv for JavaVM: %p", mVm);
            }
            return env;
        }

        void doUpdatePositionAsync(jlong frameNumber, jint left, jint top,
                jint right, jint bottom) {
            ATRACE_NAME("Update SurfaceView position");

            JNIEnv* env = jnienv();
            jobject localref = env->NewLocalRef(mWeakRef);
            if (CC_UNLIKELY(!localref)) {
                env->DeleteWeakGlobalRef(mWeakRef);
                mWeakRef = nullptr;
            } else {
                env->CallVoidMethod(localref, gSurfaceViewPositionUpdateMethod,
                        frameNumber, left, top, right, bottom);
                env->DeleteLocalRef(localref);
            }

            // We need to release ourselves here
            decStrong(0);
        }

        JavaVM* mVm;
        jobject mWeakRef;
    };

    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
    renderNode->setPositionListener(new SurfaceViewPositionUpdater(env, surfaceview));
}
void RenderNode::issueOperationsOf3dChildren(ChildrenSelectMode mode,
        const Matrix4& initialTransform, const std::vector<ZDrawRenderNodeOpPair>& zTranslatedNodes,
        OpenGLRenderer& renderer, T& handler) {
    const int size = zTranslatedNodes.size();
    if (size == 0
            || (mode == ChildrenSelectMode::NegativeZChildren && zTranslatedNodes[0].key > 0.0f)
            || (mode == ChildrenSelectMode::PositiveZChildren && zTranslatedNodes[size - 1].key < 0.0f)) {
        // no 3d children to draw
        return;
    }

    // Apply the base transform of the parent of the 3d children. This isolates
    // 3d children of the current chunk from transformations made in previous chunks.
    int rootRestoreTo = renderer.save(SaveFlags::Matrix);
    renderer.setGlobalMatrix(initialTransform);

    /**
     * Draw shadows and (potential) casters mostly in order, but allow the shadows of casters
     * with very similar Z heights to draw together.
     *
     * This way, if Views A & B have the same Z height and are both casting shadows, the shadows are
     * underneath both, and neither's shadow is drawn on top of the other.
     */
    const size_t nonNegativeIndex = findNonNegativeIndex(zTranslatedNodes);
    size_t drawIndex, shadowIndex, endIndex;
    if (mode == ChildrenSelectMode::NegativeZChildren) {
        drawIndex = 0;
        endIndex = nonNegativeIndex;
        shadowIndex = endIndex; // draw no shadows
    } else {
        drawIndex = nonNegativeIndex;
        endIndex = size;
        shadowIndex = drawIndex; // potentially draw shadow for each pos Z child
    }

    DISPLAY_LIST_LOGD("%*s%d %s 3d children:", (handler.level() + 1) * 2, "",
            endIndex - drawIndex, mode == kNegativeZChildren ? "negative" : "positive");

    float lastCasterZ = 0.0f;
    while (shadowIndex < endIndex || drawIndex < endIndex) {
        if (shadowIndex < endIndex) {
            DrawRenderNodeOp* casterOp = zTranslatedNodes[shadowIndex].value;
            RenderNode* caster = casterOp->renderNode;
            const float casterZ = zTranslatedNodes[shadowIndex].key;
            // attempt to render the shadow if the caster about to be drawn is its caster,
            // OR if its caster's Z value is similar to the previous potential caster
            if (shadowIndex == drawIndex || casterZ - lastCasterZ < SHADOW_DELTA) {
                caster->issueDrawShadowOperation(casterOp->localMatrix, handler);

                lastCasterZ = casterZ; // must do this even if current caster not casting a shadow
                shadowIndex++;
                continue;
            }
        }

        // only the actual child DL draw needs to be in save/restore,
        // since it modifies the renderer's matrix
        int restoreTo = renderer.save(SaveFlags::Matrix);

        DrawRenderNodeOp* childOp = zTranslatedNodes[drawIndex].value;

        renderer.concatMatrix(childOp->localMatrix);
        childOp->skipInOrderDraw = false; // this is horrible, I'm so sorry everyone
        handler(childOp, renderer.getSaveCount() - 1, properties().getClipToBounds());
        childOp->skipInOrderDraw = true;

        renderer.restoreToCount(restoreTo);
        drawIndex++;
    }
    renderer.restoreToCount(rootRestoreTo);
}
static jfloat android_view_RenderNode_getPivotY(JNIEnv* env,
        jobject clazz, jlong renderNodePtr) {
    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
    renderNode->mutateStagingProperties().updateMatrix();
    return renderNode->stagingProperties().getPivotY();
}
static void android_view_RenderNode_addAnimator(JNIEnv* env, jobject clazz,
        jlong renderNodePtr, jlong animatorPtr) {
    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
    RenderPropertyAnimator* animator = reinterpret_cast<RenderPropertyAnimator*>(animatorPtr);
    renderNode->addAnimator(animator);
}
Example #17
0
int main() {
    createTestEnvironment();

    // create the native surface
    const int width = gDisplay.w;
    const int height = gDisplay.h;
    sp<SurfaceControl> control = createWindow(width, height);
    sp<Surface> surface = control->getSurface();

    RenderNode* rootNode = new RenderNode();
    rootNode->incStrong(0);
    rootNode->mutateStagingProperties().setLeftTopRightBottom(0, 0, width, height);
    rootNode->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
    rootNode->mutateStagingProperties().setClipToBounds(false);
    rootNode->setPropertyFieldsDirty(RenderNode::GENERIC);

    ContextFactory factory;
    RenderProxy* proxy = new RenderProxy(false, rootNode, &factory);
    proxy->loadSystemProperties();
    proxy->initialize(surface);
    float lightX = width / 2.0;
    proxy->setup(width, height, (Vector3){lightX, dp(-200.0f), dp(800.0f)},
            dp(800.0f), 255 * 0.075, 255 * 0.15);

    android::uirenderer::Rect DUMMY;

    std::vector< sp<RenderNode> > cards;

    DisplayListRenderer* renderer = startRecording(rootNode);
    renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
    renderer->insertReorderBarrier(true);

    for (int x = dp(16); x < (width - dp(116)); x += dp(116)) {
        for (int y = dp(16); y < (height - dp(116)); y += dp(116)) {
            sp<RenderNode> card = createCard(x, y, dp(100), dp(100));
            renderer->drawRenderNode(card.get(), DUMMY, 0);
            cards.push_back(card);
        }
    }

    renderer->insertReorderBarrier(false);
    endRecording(renderer, rootNode);

    for (int i = 0; i < 150; i++) {
        ATRACE_NAME("UI-Draw Frame");
        for (size_t ci = 0; ci < cards.size(); ci++) {
            cards[ci]->mutateStagingProperties().setTranslationX(i);
            cards[ci]->mutateStagingProperties().setTranslationY(i);
            cards[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
        }
        nsecs_t frameTimeNs = systemTime(CLOCK_MONOTONIC);
        proxy->syncAndDrawFrame(frameTimeNs, 0, gDisplay.density);
        usleep(12000);
    }

    sleep(5);

    delete proxy;
    rootNode->decStrong(0);

    printf("Success!\n");
    return 0;
}
static jint android_view_RenderNode_getDebugSize(JNIEnv* env,
        jobject clazz, jlong renderNodePtr) {
    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
    return renderNode->getDebugSize();
}
static jboolean android_view_RenderNode_hasOverlappingRendering(JNIEnv* env,
        jobject clazz, jlong renderNodePtr) {
    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
    return renderNode->stagingProperties().hasOverlappingRendering();
}
static void android_view_RenderNode_setDisplayListData(JNIEnv* env,
        jobject clazz, jlong renderNodePtr, jlong newDataPtr) {
    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
    DisplayListData* newData = reinterpret_cast<DisplayListData*>(newDataPtr);
    renderNode->setStagingDisplayList(newData);
}
    virtual void redraw( void ) {
        static bool first=true;
        static RenderNode rn;

        int i;
        double tbalance;
        GeoLoadManager::ResultT region;
        if (_internalRoot == NullFC)
        {
            initialize();
            showAll();
        }
        _cart->getSFMatrix()->setValue(_navigator.getMatrix());    
        updateHighlight();
        _win->activate();
        _win->frameInit();
        if(first)
        {
            loadManager=new GeoLoadManager(useFaceDistribution);
            first=false;
            rn.determinePerformance(_win);
        }
        if(_win->getPort().size() < (serverCount+1))
        {
            ViewportPtr vp,ovp=_win->getPort(0);
            addRefCP(ovp);
            addRefCP(ovp);
            TileCameraDecoratorPtr deco;
            for(int i=_win->getPort().size()-1;i<serverCount;i++)
            {
                cout << "Add new" << endl;
                loadManager->addRenderNode(rn,i);
                if(simulateRendering)
                {
                    beginEditCP(_win);
                    deco=TileCameraDecorator::create();
                    beginEditCP(deco);
                    deco->setFullWidth ( _win->getWidth() );
                    deco->setFullHeight( _win->getHeight() );
                    deco->setDecoratee( ovp->getCamera() );
                    vp=Viewport::create();
                    beginEditCP(vp);
                    vp->setRoot      ( ovp->getRoot()       );

                    /*
                    SkyBackgroundPtr sky = SkyBackground::create();
                    beginEditCP(sky);
                    sky->setSphereRes(16);
                    
                    sky->getMFSkyColor()->addValue(Color3f(0, 0, .2));
                    sky->getMFSkyAngle()->addValue(Pi / 2);
                    sky->getMFSkyColor()->addValue(Color3f(.6, .6, 1)); 
                    
                    sky->getMFGroundColor()->addValue(Color3f(0, .3, 1));
                    sky->getMFGroundAngle()->addValue(Pi / 2);
                    sky->getMFGroundColor()->addValue(Color3f(1, .3, 0));

                    endEditCP(sky);
                    vp->setBackground( sky );
                    */

                    vp->setBackground( ovp->getBackground() );
                    vp->getMFForegrounds()->setValues( ovp->getForegrounds() );
                    vp->setCamera(deco);
                    _win->addPort(vp);
                    endEditCP(_win);
                    endEditCP(vp);
                    endEditCP(deco);
                }
            }
        }
        tbalance = -getSystemTime();
        loadManager->update(_win->getPort()[0]->getRoot());
        loadManager->balance(_win->getPort()[0],false,region);
        tbalance += getSystemTime();
        if(simulateRendering)
        {
            ViewportPtr vp;
            TileCameraDecoratorPtr deco;
            for(i=0;i<region.size();i+=4)
            {
#if 1
                cout << "Region: " << i << " ";
                cout << region[i+0] << " ";
                cout << region[i+1] << " ";
                cout << region[i+2] << " ";
                cout << region[i+3] << endl;
                if(region[i+0] >= region[i+2]) {
                    cout << "!!!" << endl;
                    region[i+2]++;
                }
                if(region[i+1] >= region[i+3]) {
                    cout << "!!!" << endl;
                    region[i+3]++;
                }
#endif
                vp=_win->getPort()[i/4+1];
                deco=TileCameraDecoratorPtr::dcast(vp->getCamera());
                beginEditCP(deco);
                beginEditCP(vp);
                vp->setSize(region[i+0],
                            region[i+1],
                            region[i+2],
                            region[i+3]);
                deco->setSize(region[i+0]/(float)_win->getWidth(),
                              region[i+1]/(float)_win->getHeight(),
                              region[i+2]/(float)_win->getWidth(),
                              region[i+3]/(float)_win->getHeight());
                endEditCP(deco);
                endEditCP(vp);
            }
        }
        Time t,tmin,tmax;
        for(i=0;i<_win->getPort().size();++i)
        {
            t=-getSystemTime();
            _action->setWindow( _win.getCPtr() );
            _win->getPort(i)->render( _action );
            glFlush();
            t+=getSystemTime();
            if(i==0)
                continue;
            if(i==1)
            {
                tmin=tmax=t;
            }
            else
            {
                if(t<tmin) tmin=t;
                if(t>tmax) tmax=t;
            }
        }
        if(!cache)
            printf("speed %5d %10.6f %10.6f %10.6f\n",_win->getPort().size()-1,
                   tmin,
                   tmax,
                   tbalance);
        glPushAttrib(GL_ALL_ATTRIB_BITS);
        glDisable(GL_SCISSOR_TEST);
        glViewport(0,0,
                   _win->getWidth(),
                   _win->getHeight());
        if(viewVolume)
            loadManager->drawVolumes(_win);
        glPushMatrix();
        glLoadIdentity();
        glMatrixMode(GL_PROJECTION);
        glPushMatrix();
        glLoadIdentity();
        gluOrtho2D(0,_win->getWidth(),
                   0,_win->getHeight());
        glDisable(GL_DEPTH_TEST);
        glEnable(GL_COLOR_MATERIAL);
        for(i=0;i<region.size();i+=4)
        {
#if 0
            cout << "Region: ";
            cout << region[i+0] << " ";
            cout << region[i+1] << " ";
            cout << region[i+2] << " ";
            cout << region[i+3] << endl;
#endif
            glBegin(GL_LINE_LOOP);
            glColor3f(1, 1, 0);
            glVertex3f(region[i+0],region[i+1],0);
            glVertex3f(region[i+2],region[i+1],0);
            glVertex3f(region[i+2],region[i+3],0);
            glVertex3f(region[i+0],region[i+3],0);
            glEnd();
        }
        glDisable(GL_COLOR_MATERIAL);
        glEnable(GL_DEPTH_TEST);
        glPopMatrix();
        glMatrixMode(GL_MODELVIEW);
        glPopMatrix();
        glPopAttrib();

        if(doSave)
        {
            Int32 w,h;
            w=_win->getPort(0)->getPixelWidth();
            h=_win->getPort(0)->getPixelHeight();
            Image image(Image::OSG_RGB_PF,
                        w,h,1,
                        1,1,0.0,
                        NULL,true);
            ImageFileType *imgTransType=ImageFileHandler::the().getFileType("JPEG");
            char filename[256];
            if(imgTransType==NULL)
            {
                cerr << "Unknown image trans type" << endl;
                return;
            }
            sprintf(filename,"%s_%d.jpg",dumpImage,dumpImageNr++);
            // read buffer data into image
            glPixelStorei(GL_PACK_ALIGNMENT,1);
            glReadPixels(0,0,w,h,
                         GL_RGB,GL_UNSIGNED_BYTE,
                         image.getData());
            imgTransType->write(image,filename);
        }
        _win->swap();
        _win->frameExit();
    }