在屏幕上快速绘制自定义格式的图像

Quickly draw custom format image on screen

本文关键字:自定义 格式 图像 绘制 屏幕      更新时间:2023-10-16

我正在为我的游戏和其他应用程序开发自己的图像格式,我想让这些图像成为图像查看器。问题是,当我总是试图渲染更大的图像(1920x1080)时,它至少需要10秒,然后应用程序停止响应几秒钟,一切都消失了。

class Renderer{
    //...
    inline void GetRGB(unsigned int color,unsigned char &r,unsigned char &g,unsigned char &b){
        r=((color>>16)&0xff); g=((color>>8)&0xff); b=((color)&0xff);
    }
    inline bool IsValidPos(unsigned int x,unsigned int y){
        if((x>=0&&x<width)&&(y>=0&&y<height))return true;
        else return false;
    }
    inline void GetPoint(unsigned int x,unsigned int y,unsigned int &color){
        if(IsValidPos(x,y))
            color=photostream[y*height+x];
    }
    void DrawPoint(unsigned int& x,unsigned int& y){
        unsigned char r,g,b;
        unsigned int col;
        GetPoint(x,y,col);
        //GetRGB(col,r,g,b);
        SetPixel(hDC,x,y,col);
    }
};
void RenderSHMP(ImageSHMP<unsigned int>& img,Renderer *renderer){
    ImageSHMP<unsigned int> tmpImg(img.GetX(),img.GetY()); //create new image, this one will be shown
    for(unsigned int x=0;x<tmpImg.GetX();x++)
        for(unsigned int y=0;y<tmpImg.GetY();y++)
            tmpImg.SetPoint(x,y,img.GetPoint(x,y)); //copy all data
    if(img.GetX()>WIDTH||img.GetY()>HEIGHT){ //resize image if it is bigger than window resolution
        double scale=1.0;
        scale=static_cast<double>(WIDTH)/static_cast<double>(img.GetX());
        tmpImg.Scale(scale);
    }
    //code above takes max. 0,2 seconds to execute
    unsigned int col=0;
    for(unsigned int x=0;x<tmpImg.GetX();x++)
        for(unsigned int y=0;y<tmpImg.GetY();y++){
            col=tmpImg.GetPoint(x,y);
            renderer->SetPoint(x,y,col);    //sets color in memory for later use
            renderer->DrawPoint(x,y);
        }
}

ImageSHMP包含类型为T的简单缓冲区,该缓冲区等于宽度*高度。所有数据都保存在这个缓冲区中,例如点[20,30]位于[30*height+20]。如何使它绘制得更快,这样窗口就不会处于"无响应"状态?我的意思是像简单的Windows图像查看器一样快


最终代码:

void RenderSHMP(ImageSHMP<unsigned int>& img,Renderer *renderer){
    ImageSHMP<unsigned int> tmpImg(img.GetX(),img.GetY());
    for(unsigned int x=0;x<tmpImg.GetX();x++)
        for(unsigned int y=0;y<tmpImg.GetY();y++)
            tmpImg.SetPoint(x,y,img.GetPoint(x,y));
    if(img.GetX()>WIDTH||img.GetY()>HEIGHT){
        double scale=1.0;
        scale=static_cast<double>(WIDTH)/static_cast<double>(img.GetX());
        tmpImg.Scale(scale);
    }
    int w=tmpImg.GetX(),h=tmpImg.GetY();
    unsigned char r=0,g=0,b=0;
    BITMAPINFO bmi;
    HBITMAP hbm;
    unsigned char *bytes=new unsigned char[w*h*3];
    HDC hdc;
    renderer->GetHDC(hdc);
    unsigned int ctr=0;
    ZeroMemory(&bmi, sizeof(bmi));
    bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader);
    bmi.bmiHeader.biWidth = w;
    bmi.bmiHeader.biHeight = h;
    bmi.bmiHeader.biPlanes = 1;
    bmi.bmiHeader.biBitCount = 24;
    bmi.bmiHeader.biCompression = BI_RGB;
    hbm = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS,(void **)&bytes, NULL, 0);
    HDC compatibleDC=CreateCompatibleDC(hdc);
    HBITMAP hbmp = CreateCompatibleBitmap  (hdc, w, h);
    for(int y=int(h)-1;y>=0;--y){
        for(int x=0;x<int(w);++x){
            renderer->GetRGB(tmpImg.GetPoint(x,y),r,g,b);
            bytes[ctr++]=b;
            bytes[ctr++]=g;
            bytes[ctr++]=r;
        }
    }
    SetDIBits (hdc, hbmp, 0, h, bytes,&bmi, DIB_RGB_COLORS);
    hbmp=(HBITMAP)SelectObject (compatibleDC, hbmp);
    BitBlt(hdc, 0, 0, w, h, compatibleDC, 0, 0, SRCCOPY);
    DeleteObject(SelectObject(compatibleDC, hbmp));
    DeleteDC(compatibleDC);
    delete [] bytes;
}

调用SetPixel 2M次需要一些时间。我认为你最好将图像大小调整为位图,然后将其绘制到屏幕上。例如使用CreateDIBSection,然后通过ppvBits填充,并使用BitBlit()绘制到屏幕本身。

简单的Windows图像查看器就能做到。

图像查看器不会逐像素绘制图像。您需要在一个系统调用中绘制整个图像。为此,您需要将图像解压缩到操作系统可以绘制的一些表示中,然后将解压缩后的图像绘制到屏幕上。在一个电话里。

在windows平台上,您可以使用SetDIBitsToDevice和StretchDIBits功能。只要正确填写BITMAPINFO,这些函数就可以从内存阵列中在窗口上绘制像素。