Condividi tramite


Immagine stride

Quando un'immagine video viene archiviata in memoria, il buffer di memoria potrebbe contenere byte di riempimento aggiuntivi dopo ogni riga di pixel. I byte di spaziatura interna influiscono sulla modalità di archiviazione dell'immagine in memoria, ma non influiscono sulla modalità di visualizzazione dell'immagine.

Lo stride è il numero di byte da una riga di pixel in memoria alla riga successiva di pixel in memoria. Stride è chiamato anche pitch. Se sono presenti byte di riempimento, lo stride è più ampio della larghezza dell'immagine, come illustrato nella figura seguente.

diagram showing an image plus padding.

Due buffer che contengono fotogrammi video con dimensioni uguali possono avere due passi diversi. Se si elabora un'immagine video, è necessario prendere in considerazione lo stride.

Inoltre, esistono due modi in cui un'immagine può essere disposta in memoria. In un'immagine dall'alto verso il basso , la prima riga di pixel nell'immagine viene visualizzata per prima in memoria. In un'immagine in basso , l'ultima riga di pixel viene visualizzata per prima in memoria. La figura seguente mostra la differenza tra un'immagine dall'alto verso il basso e un'immagine in basso.

diagram showing top-down and bottom-up images.

Un'immagine in basso ha uno stride negativo, perché stride è definito come numero di byte necessario spostare verso il basso una riga di pixel, rispetto all'immagine visualizzata. Le immagini YUV devono essere sempre dall'alto verso il basso e qualsiasi immagine contenuta in una superficie Direct3D deve essere in alto verso il basso. Le immagini RGB nella memoria di sistema sono in genere in basso.

Le trasformazioni video in particolare devono gestire i buffer con progressi non corrispondenti, perché il buffer di input potrebbe non corrispondere al buffer di output. Si supponga, ad esempio, di voler convertire un'immagine di origine e scrivere il risultato in un'immagine di destinazione. Si supponga che entrambe le immagini abbiano la stessa larghezza e altezza, ma potrebbero non avere lo stesso formato di pixel o lo stesso stride di immagine.

Il codice di esempio seguente illustra un approccio generalizzato per la scrittura di questo tipo di funzione. Questo non è un esempio funzionante completo, perché astrae molti dei dettagli specifici.

void ProcessVideoImage(
    BYTE*       pDestScanLine0,     
    LONG        lDestStride,        
    const BYTE* pSrcScanLine0,      
    LONG        lSrcStride,         
    DWORD       dwWidthInPixels,     
    DWORD       dwHeightInPixels
    )
{
    for (DWORD y = 0; y < dwHeightInPixels; y++)
    {
        SOURCE_PIXEL_TYPE *pSrcPixel = (SOURCE_PIXEL_TYPE*)pSrcScanLine0;
        DEST_PIXEL_TYPE *pDestPixel = (DEST_PIXEL_TYPE*)pDestScanLine0;

        for (DWORD x = 0; x < dwWidthInPixels; x +=2)
        {
            pDestPixel[x] = TransformPixelValue(pSrcPixel[x]);
        }
        pDestScanLine0 += lDestStride;
        pSrcScanLine0 += lSrcStride;
    }
}

Questa funzione accetta sei parametri:

  • Puntatore all'inizio della riga di analisi 0 nell'immagine di destinazione.
  • Stride dell'immagine di destinazione.
  • Puntatore all'inizio della riga di analisi 0 nell'immagine di origine.
  • Stride dell'immagine di origine.
  • Larghezza dell'immagine in pixel.
  • Altezza dell'immagine in pixel.

L'idea generale consiste nell'elaborare una riga alla volta, iterando su ogni pixel nella riga. Si supponga che SOURCE_PIXEL_TYPE e DEST_PIXEL_TYPE siano strutture che rappresentano rispettivamente il layout pixel per le immagini di origine e di destinazione. Ad esempio, RGB a 32 bit usa Struttura RGBQUAD . Non tutti i formati pixel hanno una struttura predefinita. Il cast del puntatore di matrice al tipo di struttura consente di accedere ai componenti RGB o YUV di ogni pixel. All'inizio di ogni riga, la funzione archivia un puntatore alla riga. Alla fine della riga, incrementa il puntatore in base alla larghezza dello stride dell'immagine, che sposta il puntatore alla riga successiva.

Questo esempio chiama una funzione ipotetica denominata TransformPixelValue per ogni pixel. Può trattarsi di qualsiasi funzione che calcola un pixel di destinazione da un pixel di origine. Naturalmente, i dettagli esatti dipenderanno dall'attività specifica. Ad esempio, se si dispone di un formato YUV planare, è necessario accedere ai piani cromatici in modo indipendente dal piano luma; con video interlacciato, potrebbe essere necessario elaborare i campi separatamente; e così via.

Per un esempio più concreto, il codice seguente converte un'immagine RGB a 32 bit in un'immagine AYUV. Si accede ai pixel RGB usando una struttura RGBQUAD e si accede ai pixel AYUV usando una struttura DXVA2_AYUVSample8.

//-------------------------------------------------------------------
// Name: RGB32_To_AYUV
// Description: Converts an image from RGB32 to AYUV
//-------------------------------------------------------------------
void RGB32_To_AYUV(
    BYTE*       pDest,
    LONG        lDestStride,
    const BYTE* pSrc,
    LONG        lSrcStride,
    DWORD       dwWidthInPixels,
    DWORD       dwHeightInPixels
    )
{
    for (DWORD y = 0; y < dwHeightInPixels; y++)
    {
        RGBQUAD             *pSrcPixel = (RGBQUAD*)pSrc;
        DXVA2_AYUVSample8   *pDestPixel = (DXVA2_AYUVSample8*)pDest;
        
        for (DWORD x = 0; x < dwWidthInPixels; x++)
        {
            pDestPixel[x].Alpha = 0x80;
            pDestPixel[x].Y = RGBtoY(pSrcPixel[x]);   
            pDestPixel[x].Cb = RGBtoU(pSrcPixel[x]);   
            pDestPixel[x].Cr = RGBtoV(pSrcPixel[x]);   
        }
        pDest += lDestStride;
        pSrc += lSrcStride;
    }
}

L'esempio seguente converte un'immagine RGB a 32 bit in un'immagine YV12. Questo esempio illustra come gestire un formato YUV planare. YV12 è un formato planare 4:2:0. In questo esempio la funzione mantiene tre puntatori separati per i tre piani nell'immagine di destinazione. Tuttavia, l'approccio di base è lo stesso dell'esempio precedente.

void RGB32_To_YV12(
    BYTE*       pDest,
    LONG        lDestStride,
    const BYTE* pSrc,
    LONG        lSrcStride,
    DWORD       dwWidthInPixels,
    DWORD       dwHeightInPixels
    )
{
    assert(dwWidthInPixels % 2 == 0);
    assert(dwHeightInPixels % 2 == 0);

    const BYTE *pSrcRow = pSrc;
    
    BYTE *pDestY = pDest;

    // Calculate the offsets for the V and U planes.

    // In YV12, each chroma plane has half the stride and half the height  
    // as the Y plane.
    BYTE *pDestV = pDest + (lDestStride * dwHeightInPixels);
    BYTE *pDestU = pDest + 
                   (lDestStride * dwHeightInPixels) + 
                   ((lDestStride * dwHeightInPixels) / 4);

    // Convert the Y plane.
    for (DWORD y = 0; y < dwHeightInPixels; y++)
    {
        RGBQUAD *pSrcPixel = (RGBQUAD*)pSrcRow;
        
        for (DWORD x = 0; x < dwWidthInPixels; x++)
        {
            pDestY[x] = RGBtoY(pSrcPixel[x]);    // Y0
        }
        pDestY += lDestStride;
        pSrcRow += lSrcStride;
    }

    // Convert the V and U planes.

    // YV12 is a 4:2:0 format, so each chroma sample is derived from four 
    // RGB pixels.
    pSrcRow = pSrc;
    for (DWORD y = 0; y < dwHeightInPixels; y += 2)
    {
        RGBQUAD *pSrcPixel = (RGBQUAD*)pSrcRow;
        RGBQUAD *pNextSrcRow = (RGBQUAD*)(pSrcRow + lSrcStride);

        BYTE *pbV = pDestV;
        BYTE *pbU = pDestU;

        for (DWORD x = 0; x < dwWidthInPixels; x += 2)
        {
            // Use a simple average to downsample the chroma.

            *pbV++ = ( RGBtoV(pSrcPixel[x]) +
                       RGBtoV(pSrcPixel[x + 1]) +       
                       RGBtoV(pNextSrcRow[x]) +         
                       RGBtoV(pNextSrcRow[x + 1]) ) / 4;        

            *pbU++ = ( RGBtoU(pSrcPixel[x]) +
                       RGBtoU(pSrcPixel[x + 1]) +       
                       RGBtoU(pNextSrcRow[x]) +         
                       RGBtoU(pNextSrcRow[x + 1]) ) / 4;    
        }
        pDestV += lDestStride / 2;
        pDestU += lDestStride / 2;
        
        // Skip two lines on the source image.
        pSrcRow += (lSrcStride * 2);
    }
}

In tutti questi esempi si presuppone che l'applicazione abbia già determinato l'immagine stride. A volte è possibile ottenere queste informazioni dal buffer multimediale. In caso contrario, è necessario calcolarlo in base al formato video. Per altre informazioni sul calcolo dello stride dell'immagine e sull'uso dei buffer multimediali per il video, vedere Buffer video non compressi.

Tipi di supporti video

Tipi di supporti