blob: 3c2f509651a47fe4160d27e1fd9f0e654cb353b7 [file] [log] [blame]
Constantin Kaplinsky729598c2006-05-25 05:12:25 +00001/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
2 *
3 * This is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This software is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this software; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
16 * USA.
17 */
18
19#include <rfb_win32/DIBSectionBuffer.h>
20#include <rfb_win32/DeviceContext.h>
21#include <rfb_win32/BitmapInfo.h>
22#include <rfb/LogWriter.h>
23
24using namespace rfb;
25using namespace win32;
26
27static LogWriter vlog("DIBSectionBuffer");
28
29
30DIBSectionBuffer::DIBSectionBuffer(HWND window_)
Peter Åstrandee3f46a2008-12-10 10:29:06 +000031 : bitmap(0), window(window_), device(0) {
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000032 memset(&format, 0, sizeof(format));
33 memset(palette, 0, sizeof(palette));
34}
35
36DIBSectionBuffer::DIBSectionBuffer(HDC device_)
37 : bitmap(0), window(0), device(device_) {
38 memset(&format, 0, sizeof(format));
39 memset(palette, 0, sizeof(palette));
40}
41
42DIBSectionBuffer::~DIBSectionBuffer() {
43 if (bitmap)
44 DeleteObject(bitmap);
45}
46
47
48void DIBSectionBuffer::setPF(const PixelFormat& pf) {
49 if (memcmp(&getPF(), &pf, sizeof(pf)) == 0) {
50 vlog.debug("pixel format unchanged by setPF()");
51 return;
52 }
53 format = pf;
54 recreateBuffer();
55 if ((pf.bpp <= 8) && pf.trueColour) {
56 vlog.info("creating %d-bit TrueColour palette", pf.depth);
57 for (int i=0; i < (1<<(pf.depth)); i++) {
58 palette[i].b = ((((i >> pf.blueShift) & pf.blueMax) * 65535) + pf.blueMax/2) / pf.blueMax;
59 palette[i].g = ((((i >> pf.greenShift) & pf.greenMax) * 65535) + pf.greenMax/2) / pf.greenMax;
60 palette[i].r = ((((i >> pf.redShift) & pf.redMax) * 65535) + pf.redMax/2) / pf.redMax;
61 }
62 refreshPalette();
63 }
64}
65
66void DIBSectionBuffer::setSize(int w, int h) {
67 if (width_ == w && height_ == h) {
68 vlog.debug("size unchanged by setSize()");
69 return;
70 }
71 width_ = w;
72 height_ = h;
73 recreateBuffer();
74}
75
76
77// * copyPaletteToDIB MUST NEVER be called on a truecolour DIB! *
78
79void copyPaletteToDIB(Colour palette[256], HDC wndDC, HBITMAP dib) {
80 BitmapDC dibDC(wndDC, dib);
81 RGBQUAD rgb[256];
82 for (unsigned int i=0;i<256;i++) {
83 rgb[i].rgbRed = palette[i].r >> 8;
84 rgb[i].rgbGreen = palette[i].g >> 8;
85 rgb[i].rgbBlue = palette[i].b >> 8;
86 }
87 if (!SetDIBColorTable(dibDC, 0, 256, (RGBQUAD*) rgb))
88 throw rdr::SystemException("unable to SetDIBColorTable", GetLastError());
89}
90
91inline void initMaxAndShift(DWORD mask, int* max, int* shift) {
92 for ((*shift) = 0; (mask & 1) == 0; (*shift)++) mask >>= 1;
93 (*max) = (rdr::U16)mask;
94}
95
96void DIBSectionBuffer::recreateBuffer() {
97 HBITMAP new_bitmap = 0;
98 rdr::U8* new_data = 0;
99
100 if (width_ && height_ && (format.depth != 0)) {
101 BitmapInfo bi;
102 memset(&bi, 0, sizeof(bi));
103 // *** wrong?
104 UINT iUsage = format.trueColour ? DIB_RGB_COLORS : DIB_PAL_COLORS;
105 // ***
106 bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
107 bi.bmiHeader.biBitCount = format.bpp;
108 bi.bmiHeader.biSizeImage = (format.bpp / 8) * width_ * height_;
109 bi.bmiHeader.biPlanes = 1;
110 bi.bmiHeader.biWidth = width_;
111 bi.bmiHeader.biHeight = -height_;
112 bi.bmiHeader.biCompression = (format.bpp > 8) ? BI_BITFIELDS : BI_RGB;
113 bi.mask.red = format.redMax << format.redShift;
114 bi.mask.green = format.greenMax << format.greenShift;
115 bi.mask.blue = format.blueMax << format.blueShift;
116
117 // Create a DIBSection to draw into
118 if (device)
119 new_bitmap = ::CreateDIBSection(device, (BITMAPINFO*)&bi.bmiHeader, iUsage,
120 (void**)&new_data, NULL, 0);
121 else
122 new_bitmap = ::CreateDIBSection(WindowDC(window), (BITMAPINFO*)&bi.bmiHeader, iUsage,
123 (void**)&new_data, NULL, 0);
124
125 if (!new_bitmap) {
126 int err = GetLastError();
127 throw rdr::SystemException("unable to create DIB section", err);
128 }
129
130 vlog.debug("recreateBuffer()");
131 } else {
132 vlog.debug("one of area or format not set");
133 }
134
135 if (new_bitmap && bitmap) {
136 vlog.debug("preserving bitmap contents");
137
138 // Copy the contents across
139 if (device) {
140 if (format.bpp <= 8)
141 copyPaletteToDIB(palette, device, new_bitmap);
142 BitmapDC src_dev(device, bitmap);
143 BitmapDC dest_dev(device, new_bitmap);
144 BitBlt(dest_dev, 0, 0, width_, height_, src_dev, 0, 0, SRCCOPY);
145 } else {
146 WindowDC wndDC(window);
147 if (format.bpp <= 8)
148 copyPaletteToDIB(palette, wndDC, new_bitmap);
149 BitmapDC src_dev(wndDC, bitmap);
150 BitmapDC dest_dev(wndDC, new_bitmap);
151 BitBlt(dest_dev, 0, 0, width_, height_, src_dev, 0, 0, SRCCOPY);
152 }
153 }
154
155 if (bitmap) {
156 // Delete the old bitmap
157 DeleteObject(bitmap);
158 bitmap = 0;
159 data = 0;
160 }
161
162 if (new_bitmap) {
163 // Set up the new bitmap
164 bitmap = new_bitmap;
165 data = new_data;
166
167 // Determine the *actual* DIBSection format
168 DIBSECTION ds;
169 if (!GetObject(bitmap, sizeof(ds), &ds))
170 throw rdr::SystemException("GetObject", GetLastError());
171
172 // Correct the "stride" of the DIB
173 // *** This code DWORD aligns each row - is that right???
174 stride = width_;
175 int bytesPerRow = stride * format.bpp/8;
176 if (bytesPerRow % 4) {
177 bytesPerRow += 4 - (bytesPerRow % 4);
178 stride = (bytesPerRow * 8) / format.bpp;
179 vlog.info("adjusting DIB stride: %d to %d", width_, stride);
180 }
181
182 // Calculate the PixelFormat for the DIB
183 format.bigEndian = 0;
184 format.bpp = format.depth = ds.dsBm.bmBitsPixel;
185 format.trueColour = format.trueColour || format.bpp > 8;
186 if (format.bpp > 8) {
187
188 // Get the truecolour format used by the DIBSection
189 initMaxAndShift(ds.dsBitfields[0], &format.redMax, &format.redShift);
190 initMaxAndShift(ds.dsBitfields[1], &format.greenMax, &format.greenShift);
191 initMaxAndShift(ds.dsBitfields[2], &format.blueMax, &format.blueShift);
192
193 // Calculate the effective depth
194 format.depth = 0;
195 Pixel bits = ds.dsBitfields[0] | ds.dsBitfields[1] | ds.dsBitfields[2];
196 while (bits) {
197 format.depth++;
198 bits = bits >> 1;
199 }
200 if (format.depth > format.bpp)
201 throw Exception("Bad DIBSection format (depth exceeds bpp)");
202 } else {
203 // Set the DIBSection's palette
204 refreshPalette();
205 }
206
207 }
208}
209
210void DIBSectionBuffer::refreshPalette() {
211 if (format.bpp > 8) {
212 vlog.error("refresh palette called for truecolor DIB");
213 return;
214 }
215 vlog.debug("refreshing palette");
216 if (device)
217 copyPaletteToDIB(palette, device, bitmap);
218 else
219 copyPaletteToDIB(palette, WindowDC(window), bitmap);
220}
221
222