how can i win speed on SetPixel\GetPixel?

heres my image.h:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
#include <iostream>
#include <string>
#include <string.h>
#include <windows.h>
#include <math.h>
#include <vector>
#include <gdiplus.h>


using namespace Gdiplus;
using namespace std;

std::string getGdiplusStatusMessage(Status status)
{
    std::string msg = "";

    switch ( status )
    {
    case Ok:
    msg = "Ok: Indicates that the method call was successful.";
    break;
    case GenericError:
    msg = "GenericError: Indicates that there was an error on the method call, which is identified as something other than those defined by the other elements of this enumeration.";
    break;
    case InvalidParameter:
    msg = "InvalidParameter: Indicates that one of the arguments passed to the method was not valid.";
    break;
    case OutOfMemory:
    msg = "OutOfMemory: Indicates that the operating system is out of memory and could not allocate memory to process the method call.";
    break;
    case ObjectBusy:
    msg = "ObjectBusy: Indicates that one of the arguments specified in the API call is already in use in another thread.";
    break;
    case InsufficientBuffer:
    msg = "InsufficientBuffer: Indicates that a buffer specified as an argument in the API call is not large enough to hold the data to be received.";
    break;
    case NotImplemented:
    msg = "NotImplemented: Indicates that the method is not implemented.";
    break;
    case Win32Error:
    msg = "Win32Error: Indicates that the method generated a Win32 error.";
    break;
    case WrongState:
    msg = "WrongState: Indicates that the object is in an invalid state to satisfy the API call. For example, calling Pen::GetColor from a pen that is not a single, solid color results in a WrongState status.";
    break;
    case Aborted:
    msg = "Aborted: Indicates that the method was aborted.";
    break;
    case FileNotFound:
    msg = "FileNotFound: Indicates that the specified image file or metafile cannot be found.";
    break;
    case ValueOverflow:
    msg = "ValueOverflow: Indicates that the method performed an arithmetic operation that produced a numeric overflow.";
    break;
    case AccessDenied:
    msg = "AccessDenied: Indicates that a write operation is not allowed on the specified file.";
    break;
    case UnknownImageFormat:
    msg = "UnknownImageFormat: Indicates that the specified image file format is not known.";
    break;
    case FontFamilyNotFound:
    msg = "FontFamilyNotFound: Indicates that the specified font family cannot be found. Either the font family name is incorrect or the font family is not installed.";
    break;
    case FontStyleNotFound:
    msg = "FontStyleNotFound: Indicates that the specified style is not available for the specified font family.";
    break;
    case NotTrueTypeFont:
    msg = "NotTrueTypeFont: Indicates that the font retrieved from an HDC or LOGFONT is not a TrueType font and cannot be used with GDI+.";
    break;
    case UnsupportedGdiplusVersion:
    msg = "UnsupportedGdiplusVersion: Indicates that the version of GDI+ that is installed on the system is incompatible with the version with which the application was compiled.";
    break;
    case GdiplusNotInitialized:
    msg = "GdiplusNotInitialized: Indicates that the GDI+ API is not in an initialized state. To function, all GDI+ objects require that GDI+ be in an initialized state. Initialize GDI+ by calling GdiplusStartup.";
    break;
    case PropertyNotFound:
    msg = "PropertyNotFound: Indicates that the specified property does not exist in the image.";
    break;
    case PropertyNotSupported:
    msg = "PropertyNotSupported: Indicates that the specified property is not supported by the format of the image and, therefore, cannot be set.";
    break;
    #if (GDIPVER >= 0x0110)
    case ProfileNotFound:
    msg = "ProfileNotFound: Indicates that the color profile required to save an image in CMYK format was not found.";
    break;
    #endif //(GDIPVER >= 0x0110)
    default :
    msg = "Invalid status: Indicates an unknown status was returned.";
    break;
    }
    return msg;
}

std::wstring towstring(const std::string& v)
{
    std::wstring out(v.size()+1,L'\0');

    int size = MultiByteToWideChar(CP_UTF8, 0, v.c_str(), -1, &out[0], out.size());

    out.resize(size-1);
    return out;
}

class image
{
    public:
    BITMAP bitmap;
    HBITMAP hBitmap=NULL;
    HDC hdcimage=CreateCompatibleDC(NULL);
    HBITMAP oldBitmap=NULL;
    ULONG_PTR m_gdiplusToken=NULL;
    Gdiplus::GdiplusStartupInput gdiplusStartupInput;
    Image *img;
    Graphics *graphic;

public:
    operator HDC()
    {
        return hdcimage;
    }


    operator HBITMAP()
    {
        return hBitmap;
    }

    COLORREF GetPixel(int PosX, int PosY)
    {
        return ::GetPixel(hdcimage, PosX, PosY);
    }

    void SetPixel(int PosX, int PosY, COLORREF Color)
    {
        ::SetPixel(hdcimage,PosX,PosY,Color);
    }

    image()
    {
        if(hBitmap==NULL)
            Gdiplus::GdiplusStartup(&m_gdiplusToken, &gdiplusStartupInput, NULL);
        hBitmap=CreateCompatibleBitmap(hdcimage,1,1);
        oldBitmap=(HBITMAP)SelectObject(hdcimage, hBitmap);
    }

    image(int Width, int Height)
    {
        if(hBitmap==NULL)
            Gdiplus::GdiplusStartup(&m_gdiplusToken, &gdiplusStartupInput, NULL);
        hBitmap=CreateCompatibleBitmap(hdcimage,Width,Height);
        oldBitmap=(HBITMAP)SelectObject(hdcimage, hBitmap);
    }

    void Dispose()
    {
        //clear all objects for avoid memory leaks:
        Gdiplus::GdiplusShutdown(m_gdiplusToken);
        SelectObject(hdcimage, oldBitmap);
        DeleteDC(hdcimage);
        DeleteObject(hBitmap);
    }

    ~image()
    {
        Dispose();
    }

    void FromFile(string strFile)
    {
        if(hBitmap==NULL)
            Gdiplus::GdiplusStartup(&m_gdiplusToken, &gdiplusStartupInput, NULL);
         img=new Image(towstring(strFile).c_str());
        if(img->GetLastStatus()>0)
            MessageBox( NULL,getGdiplusStatusMessage( img->GetLastStatus() ).c_str(),"error message",MB_OK);
        hBitmap=CreateBitmap(img->GetWidth(),img->GetHeight(),1,32,NULL);
        GetObject(hBitmap, sizeof(bitmap), &bitmap);
        SelectObject(hdcimage, hBitmap);
        graphic=new Graphics(hdcimage);
        graphic->DrawImage(img, 0, 0, img->GetWidth(), img->GetHeight());


    }

    void DrawImage(HDC DestinationHDC, int PosX, int PosY)
    {
        Gdiplus::Graphics graphics2(DestinationHDC);
        graphics2.DrawImage(img, PosX, PosY, img->GetWidth(), img->GetHeight());
    }
};

my main.cpp code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
#include "image.h"


 const double PI = 3.14159265358979;
image img;
struct point
{
    float x,y,z;
} ;

struct angle
{
    float x,y,z;
} ;

float GetLineLength(point p1,point p2)
{
	return sqrt( (p1.x-p2.x)* (p1.x-p2.x) + (p1.y-p2.y)* (p1.y-p2.y) + (p1.z-p2.z)* (p1.z-p2.z));
}

//Get Points Line:
point lineto(point fp,point p,float length)
{
	point res;
    float L=GetLineLength(fp,p);
    res.x=fp.x+length*(p.x-fp.x)/L;
    res.y=fp.y+length*(p.y-fp.y)/L;
    res.z=fp.z+length*(p.z-fp.z)/L;
    return res;
}
vector<point> GetPointsLine(point origin,point destination)
{
    point t=origin;
    vector<point> coordenates;
	float dst=GetLineLength(origin,destination);
	for (int i=0;i<=dst;i++)
    {
        t=lineto(t,destination,1);
        coordenates.push_back(t);
    }
    return coordenates;
}

point perspective(const point &p,const point &eyepoint)
{
    float   w=1+(p.z/eyepoint.z);
    return {(p.x-eyepoint.x)/w+eyepoint.x,
    (p.y-eyepoint.y)/w+eyepoint.y,
    (p.z-eyepoint.z)/w+eyepoint.z};
}

//Draw a Line:
void DrawLine(HDC WindowHDC,point origin,point destination)
{

    //for convert 3D to 2D we must:
    //have Focal Distance, in these case is 100
    //2D.X = 3D.X * FocalDistance / 3D.Z
    //2D.Y = 3D.Y * FocalDistance / 3D.Z
    float FocalDistance =100;

    //Getting the Points of a line:
    vector<point> coordenates;

    //origin.z=-origin.z;
    //destination.z=-destination.z;
    coordenates = GetPointsLine(origin,destination);
    point eyepoint={250,150,300};
    //now we draw the line with a color and convert the 3D to 2D:
	for (point LinePoints:coordenates)
    {
    	point p=perspective(LinePoints,eyepoint);
    	COLORREF color= RGB(255,0,0);
    	SetPixel(WindowHDC,p.x,p.y,color);
    }
}

//Draw image pixel a pixel:
void DrawImage(HDC WindowHDC,point TopLeft,point TopRight,point BottomLeft,point BottomRight,HDC image)
{

    //for convert 3D to 2D we must:
    //have Focal Distance, in these case is 100
    //2D.X = 3D.X * FocalDistance / 3D.Z
    //2D.Y = 3D.Y * FocalDistance / 3D.Z
    //float FocalDistance =100;

    //Getting the Points of a line:
    vector<point> LeftLine;
    LeftLine = GetPointsLine(TopLeft,BottomLeft);
    vector<point> RgihtLine;
    RgihtLine = GetPointsLine(TopRight,BottomRight);

    point eyepoint={250,150,300};
    //now we draw the line with a color and convert the 3D to 2D:


	for(int PosX=0; PosX<LeftLine.size(); PosX++)
	{
	    vector<point> PixelLine;
	    PixelLine=GetPointsLine(LeftLine[PosX], RgihtLine[PosX]);

        for (int PosY=0; PosY<PixelLine.size(); PosY++)
        {

            point Point=perspective(PixelLine[PosY],eyepoint);
            COLORREF color= ::GetPixel(image,PosY,PosX);
            SetPixel(WindowHDC,Point.x,Point.y,color);

        }
	}
}


//Convert Degrees to Radians:
//formula: Radians = Angle*PI/180
angle ConvertDegreesToRadians(angle Rotation)
{

    double deg2Rad;
    deg2Rad = PI / 180;
    angle ConvertDegrees;
    ConvertDegrees.x = Rotation.x * deg2Rad;
    ConvertDegrees.y = Rotation.y * deg2Rad;
    ConvertDegrees.z = Rotation.z * deg2Rad;
    return ConvertDegrees;
}

//Rotation points using Angle, Rotation Point and scale:
point RotationPoints(point pt, angle Angle, point pivot={0,0,0},point scale={1,1,1})
{
    angle radians= ConvertDegreesToRadians(Angle);//Radians = Angle*PI/180
    Angle.x =radians.x;
    Angle.y =radians.y;
    Angle.z =radians.z;

    //Subtrat the Rotation Center:
    point p={pt.x-pivot.x,pt.y-pivot.y,pt.z-pivot.z};

    //(best using new variables for don't confuse some results)
    //calculate the rotation:
    point rot,temp;
    temp={(p.y)*cos(Angle.x)+(-p.z)*sin(Angle.x),(p.z)*cos(Angle.x)+(p.y)*sin(Angle.x)};
    rot.y=temp.x;rot.z=temp.y;
    p.y = rot.y;p.z = rot.z;
    temp={(p.z)*cos(Angle.y)+(-p.x)*sin(Angle.y),(p.x)*cos(Angle.y)+(p.z)*sin(Angle.y)};
    rot.z=temp.x;rot.x=temp.y;
    p.x=rot.x;
    temp={(p.x)*cos(Angle.z)+(-p.y)*sin(Angle.z),(p.y)*cos(Angle.z)+(p.x)*sin(Angle.z)};
    rot.x=temp.x;rot.y=temp.y;

    //scale the new point and
    //add the Rotation Center:
    return {(scale.x*rot.x+pivot.x),(scale.y*rot.y+pivot.y),(scale.z*rot.z+pivot.z)};
}



int main()
{
     //getting the HDC Console Window:
    HDC WindowHDC=GetDC(GetConsoleWindow());

    point TopLeft={100, 200,0};
    point TopRight={200,200,0};
    point BottomLeft={100,300,0};
    point BottomRight={200,300,0};

    point RotationPoint={250,150,0};//center of rectangle
    float anglex=0;
    float angley=0;
    float anglez=0;

    img.FromFile("C:\\Users\\Joaquim\\Pictures\\1595973613452.jpg");
	do
    {
        //TopLeft:
        point TopLeftRotated=RotationPoints(TopLeft,{anglex,angley,anglez},RotationPoint);



        //TopRight:
        point TopRightRotated=RotationPoints(TopRight,{anglex,angley,anglez},RotationPoint);


        //BottomLeft:
        point BottomLeftRotated=RotationPoints(BottomLeft,{anglex,angley,anglez},RotationPoint);


        //BottomRight:
        point BottomRightRotated=RotationPoints(BottomRight,{anglex,angley,anglez},RotationPoint);


        DrawImage(WindowHDC,TopLeftRotated,TopRightRotated,BottomLeftRotated, BottomRightRotated,img.hdcimage);


        img.DrawImage(WindowHDC,800,0);
        angley+=1;

        Sleep(5);
        RECT a;
        a.left=0;
        a.right=500;
        a.top=0;
        a.bottom=400;
        FillRect(WindowHDC,&a, CreateSolidBrush(RGB(0,0,0)));
    }while(!(GetKeyState(VK_ESCAPE) & 0x8000));//press escape for exit
    cout<<"Press return to end . . ."<<endl;
    cin.get();

}

the DrawImage() function is heating my CPU, because i use GetPixel() and SetPixel().... i get the results that i need:
https://imgur.com/X8nSsKo
but, like i said, is very slow :(
so how can i win much more speed?
imagine if i need draw a cube(6 faces) or 10 'cubes'(maybe other 3D shapes)... the CPU can't do it :(
you don't draw a cube one pixel at a time.

- you can shade it all the same color (per polygon), and that is near instant with the library.
- you can apply a texture (image) to a rectangle/face, and that is near instant.
- you can modify a few pixels directly, eg put a few small circles to make 'dice' of your cube, fast enough (though better to draw a small filled circle with their fast geometry drawing tools)

- memory operations or tight loops are not 'bad' on a bitmap if you do it directly. a partial example:

Bitmap b(blah);
Gdiplus::Rect sq(0, 0, w, h);
Gdiplus::BitmapData data;

b.LockBits(&sq,
Gdiplus::ImageLockModeWrite, PixelFormat32bppARGB, &data);
uint8_t * cp = (uint8_t*)(data.Scan0);

for(int i = 0; i < w*h*4; i+=4) //go over all pixels 4 is for an image with alpha/4 byte pixels
cp is the pixel bytes, eg if its RGBA format [i] is red, [i+1] is green, ... etc
///warning, I expect you to be familiar with modifying an image as a raw RGB array here.
///if not, time to learn, which you should do with one image / rectangle, not 3-d, to get going.
///note that some packages draw bottom up, some top down, etc. If something is mirrored or flipped, you just change the loop accessing.

the above loop is still 'slow' in terms of graphics and should be multi-threaded if the image is very large. If it is small, you can do it single thread. Just depends on w & h of the image. Casting out to ints (4 bytes here) to modify one pixel at a time is faster than component wise, so cast to what you need to manipulate efficiently.
Last edited on
getGdiplusStatusMessage() has no business being in your .h file. If your .h file is included in more than one .cpp file, this will cause multiply defined linker symbols.
"the above loop is still 'slow' in terms of graphics and should be multi-threaded if the image is very large."
so the best is use the DIB's?

- everytime that i change a pixel on data( "Gdiplus::BitmapData data;") i re-must 'LockBits'?
- i need speak about anotherthing: the image is drawed pixel a pixel, but why i get some block pixels(vertical lines) or some points(depending on rotation angle)?


@AbstractionAnon (6762): "getGdiplusStatusMessage() has no business being in your .h file. If your .h file is included in more than one .cpp file, this will cause multiply defined linker symbols."
same go to class's and others.... how can i avoid these mistake?
Last edited on
getGdiplusStatusMessage() has no business being in your .h file. If your .h file is included in more than one .cpp file, this will cause multiply defined linker symbols."
how can i avoid these mistake?

Put it in a .cpp file. Either main.cpp, or if you wish, put it in a .cpp file by itself.
now i did a new function using new threads:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
float GetLineLength(point p1,point p2)
{
	return sqrt( (p1.x-p2.x)* (p1.x-p2.x) + (p1.y-p2.y)* (p1.y-p2.y) + (p1.z-p2.z)* (p1.z-p2.z));
}

//Get Points Line:
point lineto(point fp,point p,float length)
{
	point res;
    float L=GetLineLength(fp,p);
    res.x=fp.x+length*(p.x-fp.x)/L;
    res.y=fp.y+length*(p.y-fp.y)/L;
    res.z=fp.z+length*(p.z-fp.z)/L;
    return res;
}
vector<point> GetPointsLine(point origin,point destination)
{
    point t=origin;
    vector<point> coordenates;
	float dst=GetLineLength(origin,destination);
	for (int i=0;i<=dst;i++)
    {
        t=lineto(t,destination,1);
        coordenates.push_back(t);
    }
    return coordenates;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
void DrawImagePoint(HDC HDCDestination,HDC HDCOrigin, point TopLeft,point TopRight,point BottomLeft,point BottomRight)
{
    //Getting the Points of a line:
    vector<point> LeftLine;
    auto future = std::async(GetPointsLine,TopLeft,BottomLeft);
    LeftLine = future.get();

    vector<point> RgihtLine;
    future = std::async(GetPointsLine,TopRight,BottomRight);
    RgihtLine = future.get();


    for(int PosX=0; PosX<LeftLine.size(); PosX++)
    {
        vector<point> PixelLine;
        future = std::async(GetPointsLine,LeftLine[PosX], RgihtLine[PosX]);
        PixelLine = future.get();

        for (int PosY=0; PosY<PixelLine.size(); PosY++)
        {
            point Point=PixelLine[PosY];

            COLORREF color;
            auto future2 = std::async(::GetPixel,HDCOrigin,PosY+800,PosX);
            color = future2.get();

            //::SetPixel(hdcDestination,PosY,PosX,clr);
            std::thread thread_obj1(::SetPixel, HDCOrigin,PosY,PosX,color);
            thread_obj1.join();

        }
    }
}

but i need more speed :(
what you can advice me more?

Have you done any profiling? If you use Visual Studio it has a build in profiler.
If you use GCC it comes with a profiler called grof

GetPixel / SetPixel might not be the problem.

Inside your nested for loops you create and destroy tons(hundreds / thousands ?) of threads.
This might cost lots of time.
thmm: i can go back to the older code.... but how can i win time speed?
the older code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
float GetLineLength(point p1,point p2)
{
	return sqrt( (p1.x-p2.x)* (p1.x-p2.x) + (p1.y-p2.y)* (p1.y-p2.y) + (p1.z-p2.z)* (p1.z-p2.z));
}

//Get Points Line:
point lineto(point fp,point p,float length)
{
	point res;
    float L=GetLineLength(fp,p);
    res.x=fp.x+length*(p.x-fp.x)/L;
    res.y=fp.y+length*(p.y-fp.y)/L;
    res.z=fp.z+length*(p.z-fp.z)/L;
    return res;
}
vector<point> GetPointsLine(point origin,point destination)
{
    point t=origin;
    vector<point> coordenates;
	float dst=GetLineLength(origin,destination);
	for (int i=0;i<=dst;i++)
    {
        t=lineto(t,destination,1);
        coordenates.push_back(t);
    }
    return coordenates;
}

point perspective(const point &p,const point &eyepoint)
{
    float   w=1+(p.z/eyepoint.z);
    return {(p.x-eyepoint.x)/w+eyepoint.x,
    (p.y-eyepoint.y)/w+eyepoint.y,
    (p.z-eyepoint.z)/w+eyepoint.z};
}

//Draw image pixel a pixel:
void DrawImagePoint(HDC HDCDestination,HDC HDCOrigin, point TopLeft,point TopRight,point BottomLeft,point BottomRight)
{
    //Getting the Points of a line:
    vector<point> LeftLine;
    LeftLine = GetPointsLine(TopLeft,BottomLeft);

    vector<point> RgihtLine;
    RgihtLine = GetPointsLine(TopRight,BottomRight);


    for(int PosX=0; PosX<LeftLine.size(); PosX++)
    {
        vector<point> PixelLine;
        PixelLine=GetPointsLine(LeftLine[PosX], RgihtLine[PosX]);

        for (int PosY=0; PosY<PixelLine.size(); PosY++)
        {
            point Point=PixelLine[PosY];

            COLORREF color;
            color=::GetPixel(HDCOrigin,PosY+800,PosX);
            ::SetPixel( HDCDestination,PosY,PosX,color);
        }
    }
}

how can i win much more speed?

"Inside your nested for loops you create and destroy tons(hundreds / thousands ?) of threads."
tested now... thanks for correct me.

"If you use GCC it comes with a profiler called grof"
i use GCC, but i don't know 'grof'.. what means?
how can i win much more speed?

First you need to find out where the problem(bottleneck is) is

gprof is the name of the profiler.
https://riptutorial.com/cplusplus/example/19028/profiling-with-gcc-and-gprof

Last edited on
It can be the GetLineLength() or lineto() functions, because they use divisions. But tell me more for otimizate the functions
Have you tried jonnin's advice with LockBits() ?
thmm (590): i have used, but no luck..
i can use DIB's.. i will do another thread\question.
thanks for all
Topic archived. No new replies allowed.