NSBitmapImageRep initWithFocusedViewRect: ... est déprécié. Par quoi le remplacer?

Bonjour

Mon générateur de Mandelbrot emploie la méthode :
NSBitmapImageRep *bitmapB = [[NSBitmapImageRep alloc]initWithFocusedViewRect:cadre]; //oui, du bon vieux Objectiv-C
Cette méthode ne fonctionne plus sous MacOSX 11

J'ai essayé :
NSBitmapImageRep *bitmapBip = [[NSBitmapImageRep alloc]initWithBitmapDataPlanes:NULL
pixelsWide:self.bounds.size.width
pixelsHigh:self.bounds.size.width
bitsPerSample:8
samplesPerPixel:4
hasAlpha:NO
isPlanar:NO
colorSpaceName:NSDeviceRGBColorSpace
bitmapFormat:NSBitmapFormatFloatingPointSamples
bytesPerRow: 4 * self.bounds.size.width
bitsPerPixel:32];

ou encore :

NSImage *imageB = [[[NSImage alloc]initWithSize:NSMakeSize(self.bounds.size.width, self.bounds.size.height)]retain];
[imageB lockFocus];
NSBitmapImageRep *bitmapB = [[[NSBitmapImageRep alloc]initWithData:imageB.TIFFRepresentation]retain];

qui ne fonctionnent pas mieux.

Auriez-vous une idée?

Merci par avance...

Mots clés:

Réponses

  • As-tu essayé cacheDisplayInRect:toBitmapImageRep: ?
    (C'est dans NSView)

  • @klog a dit :
    As-tu essayé cacheDisplayInRect:toBitmapImageRep: ?
    (C'est dans NSView)

    Exact, ce qu'indique Apple codeworkshop.net/objc-diff/sdkdiffs/macos/10.14/AppKit.html

  • CéroceCéroce Membre, Modérateur

    Tu fais le rendu à l'écran ? C'est très lent.

  • Merci pour vos posts. J'ai du mal à initialiser le NSBitmapImageRep, quelle que soit la formule. Il faut l'initialiser avant d'employer cacheDisplayInRect:toBitmapImageRep:

    En console monBitmap.description donne (null) dans tous les cas, et d'ailleurs rien ne s'affiche.

    Après Céroce, c'est peut-être plus lent, mais il m'a semblé que programmer un filtre CIImage n'était pas la solution pour programmer une Mandelbrot.

  • CéroceCéroce Membre, Modérateur

    @Herve a dit :
    Après Céroce, c'est peut-être plus lent, mais il m'a semblé que programmer un filtre CIImage n'était pas la solution pour programmer une Mandelbrot.

    Effectivement, utiliser Core Image serait difficile, mais ce n'est pas ce que je propose.
    Tu peux écrire directement dans la bitmap. Demande si tu ne vois pas ce que je veux dire.

  • HerveHerve Membre
    décembre 2020 modifié #7

    C'est bien ce que je pensais faire : l'image affichée est crée à partir du BitmapImage après les calculs.
    Une méthode calcule la couleur des points (un tableau d'une classe spéciale ne comportant que les flottants x/y/r/g/b), le bimap crée une image "virtuelle" à partir de ces flottants et est transformé en une image NSImage qui, elle est affichée.
    Le problème est que je n'arrive pas à initialiser le NSBitmapImageRep. Il m'en faut un initialisé à la bonne taille, c'est tout. Comment fait-on cela aujourd'hui? (au besoin je mets un jpeg qui ne sert à rien dans le logiciel, le NSBitmapImageRep est créé à partir de ce "data" et on modifie la taille et les couleurs ensuite??? Pas très élégant!)

    Ceci
    NSBitmapImageRep *bitmapB = [self bitmapImageRepForCachingDisplayInRect:[self visibleRect]];
    fait carrément planter l'appli.

  • CéroceCéroce Membre, Modérateur

    J'ai fait un essai:

    #import "CEBitmapGenerator.h"
    
    typedef union {
        UInt32 whole;
        struct {
            UInt8 r;
            UInt8 g;
            UInt8 b;
            UInt8 a;
        } rgba;
    } Pixel;
    
    @interface CEBitmapGenerator () {
        Pixel *_bitmap;
    }
    
    @property (readonly) NSBitmapImageRep *imageRep;
    
    @end
    
    @implementation CEBitmapGenerator
    
    - (instancetype)initWithWidth:(UInt16)width height:(UInt16)height
    {
        self = [super init];
        if (self) {
            _width = width;
            _height = height;
    
            _bitmap = malloc(width * height * sizeof(Pixel));
            _imageRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:(unsigned char * _Nullable * _Nullable)&_bitmap
                                                                pixelsWide:width
                                                                pixelsHigh:height
                                                             bitsPerSample:8
                                                           samplesPerPixel:4
                                                                  hasAlpha:YES
                                                                  isPlanar:NO
                                                            colorSpaceName:NSCalibratedRGBColorSpace
                                                               bytesPerRow:width * 4
                                                              bitsPerPixel:32];
        }
        return self;
    }
    
    -(void) generate {
        Pixel *pixelPtr = _bitmap;
        UInt16 third = _width/3;
        UInt16 twoThirds = 2*_width/3;
        UInt16 x, y;
        for(y = 0; y < _height; y++) {
            for(x = 0; x < _width; x++) {
                if(x < third) {
                    pixelPtr->whole = 0x0000FFFF;
                } else if(x < twoThirds) {
                    pixelPtr->whole = 0xFFFFFFFF;
                } else {
                    pixelPtr->whole = 0xFF0000FF;
                }
    
                pixelPtr++;
            }
        }
    }
    
    -(NSImage *) image {
        NSImage *image = [[NSImage alloc] initWithSize:CGSizeMake(self.width, self.height)];
        [image addRepresentation:self.imageRep];
        return image;
    }
    
    -(void)dealloc {
        free(_bitmap);
    }
    
    @end
    

    Mais ça fonctionne mal.
    Le problème est que les pixels ne sont pas organisés dans l'ordre RGBA.

    En fait, il y a souvent des ennuis avec NSBitmapImageRep et NSImage. Personnellement, je travaille toujours avec CGBitmapContext. Jamais de surprises avec Core Graphics.

  • J'essaie cela ce WE. Merci Céroce!

  • CéroceCéroce Membre, Modérateur

    Tu peux trouver dans quel ordre sont les pixels, mais ça risque de ne marcher que chez toi. À voir si c'est un problème… si tu diffuses l'appli ou pas.

    Sinon, utilise un CGBitmapContext. Tu pourras le transformer facilement en CGImage, et initialiser une NSImage avec la CGImage. L'avantage du CGBitmapContext est que tu peux définir le format des pixels de façon précise. (par exemple dans l'ordre RGBX).

  • @Céroce a dit :

    Sinon, utilise un CGBitmapContext. Tu pourras le transformer facilement en CGImage, et initialiser une NSImage avec la CGImage. L'avantage du CGBitmapContext est que tu peux définir le format des pixels de façon précise. (par exemple dans l'ordre RGBX).

    Il y a moyen de faire ça directement avec Metal, histoire de préparer l'avenir ?

  • CéroceCéroce Membre, Modérateur

    @Draken a dit :
    Il y a moyen de faire ça directement avec Metal, histoire de préparer l'avenir ?

    J'envisage deux problèmes à travailler avec le GPU:

    • il me semble qu'en zoomant beaucoup, la précision des Float32 ne sera plus suffisante. Je n'ai pas d'expérience sur le sujet.
    • les algorithmes récursifs y sont inefficaces.

    C'est certainement faisable, mais il faut adapter l'algorithme.

    L'algorithme naïf n'est déjà pas assez performant, et sur Wikipédia, ils en proposent un autre que je n'ai jamais essayé.

  • @Céroce a dit :

    @Draken a dit :
    Il y a moyen de faire ça directement avec Metal, histoire de préparer l'avenir ?

    J'envisage deux problèmes à travailler avec le GPU:

    • il me semble qu'en zoomant beaucoup, la précision des Float32 ne sera plus suffisante. Je n'ai pas d'expérience sur le sujet.
    • les algorithmes récursifs y sont inefficaces.

    C'est certainement faisable, mais il faut adapter l'algorithme.

    L'algorithme naïf n'est déjà pas assez performant, et sur Wikipédia, ils en proposent un autre que je n'ai jamais essayé.

    Il y a exemple sur GitHub de Mandelbrot et metal

    ici: https://github.com/haawa799/Metal-Mandelbrot-Fractal

  • Bonjour

    Merci pour tous vos posts.

    En fait, en faisant une nouvelle application (avec le ramasse miette etc.) et l'ancien code avec cette seule modification :
    NSBitmapImageRep *bitmapBip = [self bitmapImageRepForCachingDisplayInRect:[self visibleRect]];

    cela s'est remis à marcher très bien. L'ancien code était-il trop compilé??? L'ancienne devait avoir ... (elle datait de MaxOS 10.10)

    Comme quoi! Merci en tous les cas pour votre site et vos réponses. Et Joyeux Noël bien sûr!

Connectez-vous ou Inscrivez-vous pour répondre.