blob: 026e5ed42a85622b5ecd38fca3cfe9049cd6b4b2 [file] [log] [blame]
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +00001/* Copyright (C) 2002-2004 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#include <rfb/LogWriter.h>
19
20#include <rfb_win32/DIBSectionBuffer.h>
21#include <rfb_win32/Win32Util.h>
22
23
24using namespace rfb;
25using namespace win32;
26
27static LogWriter vlog("DIBSection");
28
29
30DIBSectionBuffer::DIBSectionBuffer(HWND window_)
31 : bitmap(0), device(0), window(window_) {
32 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) {
Peter Åstrandc81a6522004-12-30 11:32:08 +000056 vlog.debug("creating %d-bit TrueColor palette", pf.depth);
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +000057 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 vlog.debug("area=%d, bpp=%d", width_ * height_, format.bpp);
118
119 // Create a DIBSection to draw into
120 if (device)
121 new_bitmap = ::CreateDIBSection(device, (BITMAPINFO*)&bi.bmiHeader, iUsage,
122 (void**)&new_data, NULL, 0);
123 else
124 new_bitmap = ::CreateDIBSection(WindowDC(window), (BITMAPINFO*)&bi.bmiHeader, iUsage,
125 (void**)&new_data, NULL, 0);
126
127 if (!new_bitmap) {
128 int err = GetLastError();
129 throw rdr::SystemException("unable to create DIB section", err);
130 }
131
132 vlog.debug("recreateBuffer()");
133 } else {
134 vlog.debug("one of area or format not set");
135 }
136
137 if (new_bitmap && bitmap) {
138 vlog.debug("preserving bitmap contents");
139
140 // Copy the contents across
141 if (device) {
142 if (format.bpp <= 8)
143 copyPaletteToDIB(palette, device, new_bitmap);
144 BitmapDC src_dev(device, bitmap);
145 BitmapDC dest_dev(device, new_bitmap);
146 BitBlt(dest_dev, 0, 0, width_, height_, src_dev, 0, 0, SRCCOPY);
147 } else {
148 WindowDC wndDC(window);
149 if (format.bpp <= 8)
150 copyPaletteToDIB(palette, wndDC, new_bitmap);
151 BitmapDC src_dev(wndDC, bitmap);
152 BitmapDC dest_dev(wndDC, new_bitmap);
153 BitBlt(dest_dev, 0, 0, width_, height_, src_dev, 0, 0, SRCCOPY);
154 }
155 }
156
157 if (bitmap) {
158 // Delete the old bitmap
159 DeleteObject(bitmap);
160 bitmap = 0;
161 data = 0;
162 }
163
164 if (new_bitmap) {
165 // Set up the new bitmap
166 bitmap = new_bitmap;
167 data = new_data;
168
169 // Determine the *actual* DIBSection format
170 DIBSECTION ds;
171 if (!GetObject(bitmap, sizeof(ds), &ds))
172 throw rdr::SystemException("GetObject", GetLastError());
173
174 // Correct the "stride" of the DIB
175 // *** This code DWORD aligns each row - is that right???
176 stride = width_;
177 int bytesPerRow = stride * format.bpp/8;
178 if (bytesPerRow % 4) {
179 bytesPerRow += 4 - (bytesPerRow % 4);
180 stride = (bytesPerRow * 8) / format.bpp;
181 vlog.info("adjusting DIB stride: %d to %d", width_, stride);
182 }
183
184 // Calculate the PixelFormat for the DIB
185 format.bigEndian = 0;
186 format.bpp = format.depth = ds.dsBm.bmBitsPixel;
187 format.trueColour = format.trueColour || format.bpp > 8;
188 if (format.bpp > 8) {
189
190 // Get the truecolour format used by the DIBSection
191 initMaxAndShift(ds.dsBitfields[0], &format.redMax, &format.redShift);
192 initMaxAndShift(ds.dsBitfields[1], &format.greenMax, &format.greenShift);
193 initMaxAndShift(ds.dsBitfields[2], &format.blueMax, &format.blueShift);
194
195 // Calculate the effective depth
196 format.depth = 0;
197 Pixel bits = ds.dsBitfields[0] | ds.dsBitfields[1] | ds.dsBitfields[2];
198 while (bits) {
199 format.depth++;
200 bits = bits >> 1;
201 }
202 } else {
203 // Set the DIBSection's palette
204 refreshPalette();
205 }
206 }
207}
208
209void DIBSectionBuffer::refreshPalette() {
210 if (format.bpp > 8) {
Peter Åstrandc81a6522004-12-30 11:32:08 +0000211 vlog.error("refresh palette called for truecolor DIB");
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000212 return;
213 }
214 vlog.debug("refreshing palette");
215 if (device)
216 copyPaletteToDIB(palette, device, bitmap);
217 else
218 copyPaletteToDIB(palette, WindowDC(window), bitmap);
219}
220
221