1/*
2 * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26#include "awt.h"
27#include <sun_java2d_windows_GDIBlitLoops.h>
28#include "gdefs.h"
29#include "Trace.h"
30#include "GDIWindowSurfaceData.h"
31
32static RGBQUAD *byteGrayPalette = NULL;
33
34extern "C" {
35
36typedef struct tagBitmapheader  {
37    BITMAPINFOHEADER bmiHeader;
38    union {
39        DWORD           dwMasks[3];
40        RGBQUAD         palette[256];
41    } colors;
42} BmiType;
43
44/*
45 * Class:     sun_java2d_windows_GDIBlitLoops
46 * Method:    nativeBlit
47 * Signature: (Lsun/java2d/SurfaceData;Lsun/java2d/SurfaceData;IIIIIIZ)V
48 */
49JNIEXPORT void JNICALL
50Java_sun_java2d_windows_GDIBlitLoops_nativeBlit
51    (JNIEnv *env, jobject joSelf,
52     jobject srcData, jobject dstData,
53     jobject clip,
54     jint srcx, jint srcy,
55     jint dstx, jint dsty,
56     jint width, jint height,
57     jint rmask, jint gmask, jint bmask,
58     jboolean needLut)
59{
60    J2dTraceLn(J2D_TRACE_INFO, "GDIBlitLoops_nativeBlit");
61
62    SurfaceDataRasInfo srcInfo;
63    SurfaceDataOps *srcOps = SurfaceData_GetOps(env, srcData);
64    GDIWinSDOps *dstOps = GDIWindowSurfaceData_GetOps(env, dstData);
65    jint lockFlags;
66
67    srcInfo.bounds.x1 = srcx;
68    srcInfo.bounds.y1 = srcy;
69    srcInfo.bounds.x2 = srcx + width;
70    srcInfo.bounds.y2 = srcy + height;
71    if (needLut) {
72        lockFlags = (SD_LOCK_READ | SD_LOCK_LUT);
73    } else {
74        lockFlags = SD_LOCK_READ;
75    }
76    // This method is used among other things for on-screen copyArea, in which
77    // case the source and destination surfaces are the same. It is important
78    // to first lock the source and then get the hDC for the destination
79    // surface because the same per-thread hDC will be used for both
80    // and we need to have the correct clip set to the hDC
81    // used with the SetDIBitsToDevice call.
82    if (srcOps->Lock(env, srcOps, &srcInfo, lockFlags) != SD_SUCCESS) {
83        return;
84    }
85
86    SurfaceDataBounds dstBounds = {dstx, dsty, dstx + width, dsty + height};
87    // Intersect the source and dest rects. Note that the source blit bounds
88    // will be adjusted to the surfaces's bounds if needed.
89    SurfaceData_IntersectBlitBounds(&(srcInfo.bounds), &dstBounds,
90                                    dstx - srcx, dsty - srcy);
91
92    srcx = srcInfo.bounds.x1;
93    srcy = srcInfo.bounds.y1;
94    dstx = dstBounds.x1;
95    dsty = dstBounds.y1;
96    width = srcInfo.bounds.x2 - srcInfo.bounds.x1;
97    height = srcInfo.bounds.y2 - srcInfo.bounds.y1;
98
99    if (width > 0 && height > 0)
100    {
101        BmiType bmi;
102        // REMIND: A performance tweak here would be to make some of this
103        // data static.  For example, we could have one structure that is
104        // always used for ByteGray copies and we only change dynamic data
105        // in the structure with every new copy.  Also, we could store
106        // structures with Ops or with the Java objects so that surfaces
107        // could retain their own DIB info and we would not need to
108        // recreate it every time.
109
110        // GetRasInfo implicitly calls GetPrimitiveArrayCritical
111        // and since GetDC uses JNI it needs to be called first.
112        HDC hDC = dstOps->GetDC(env, dstOps, 0, NULL, clip, NULL, 0);
113        if (hDC == NULL) {
114            SurfaceData_InvokeUnlock(env, srcOps, &srcInfo);
115            return;
116        }
117        srcOps->GetRasInfo(env, srcOps, &srcInfo);
118        if (srcInfo.rasBase == NULL) {
119            dstOps->ReleaseDC(env, dstOps, hDC);
120            SurfaceData_InvokeUnlock(env, srcOps, &srcInfo);
121            return;
122        }
123        void *rasBase = ((char *)srcInfo.rasBase) + srcInfo.scanStride * srcy +
124                        srcInfo.pixelStride * srcx;
125
126        // If scanlines are DWORD-aligned (scanStride is a multiple of 4),
127        // then we can do the work much faster.  This is due to a constraint
128        // in the way DIBs are structured and parsed by GDI
129        jboolean fastBlt = ((srcInfo.scanStride & 0x03) == 0);
130
131        bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader);
132        bmi.bmiHeader.biWidth = srcInfo.scanStride/srcInfo.pixelStride;
133        // fastBlt copies whole image in one call; else copy line-by-line
134        LONG dwHeight = srcInfo.bounds.y2 - srcInfo.bounds.y1;
135        bmi.bmiHeader.biHeight = (fastBlt) ? -dwHeight : -1;
136        bmi.bmiHeader.biPlanes = 1;
137        bmi.bmiHeader.biBitCount = (WORD)srcInfo.pixelStride * 8;
138        // 1,3,4 byte use BI_RGB, 2 byte use BI_BITFIELD...
139        // 4 byte _can_ use BI_BITFIELD, but this seems to cause a performance
140        // penalty.  Since we only ever have one format (xrgb) for 32-bit
141        // images that enter this function, just use BI_RGB.
142        // Could do BI_RGB for 2-byte 555 format, but no perceived
143        // performance benefit.
144        bmi.bmiHeader.biCompression = (srcInfo.pixelStride != 2)
145                ? BI_RGB : BI_BITFIELDS;
146        bmi.bmiHeader.biSizeImage = (bmi.bmiHeader.biWidth * dwHeight *
147                                     srcInfo.pixelStride);
148        bmi.bmiHeader.biXPelsPerMeter = 0;
149        bmi.bmiHeader.biYPelsPerMeter = 0;
150        bmi.bmiHeader.biClrUsed = 0;
151        bmi.bmiHeader.biClrImportant = 0;
152        if (srcInfo.pixelStride == 1) {
153            // Copy palette info into bitmap for 8-bit image
154            if (needLut) {
155                memcpy(bmi.colors.palette, srcInfo.lutBase, srcInfo.lutSize * sizeof(RGBQUAD));
156                if (srcInfo.lutSize != 256) {
157                    bmi.bmiHeader.biClrUsed = srcInfo.lutSize;
158                }
159            } else {
160                // If no LUT needed, must be ByteGray src.  If we have not
161                // yet created the byteGrayPalette, create it now and copy
162                // it into our temporary bmi structure.
163                // REMIND: byteGrayPalette is a leak since we do not have
164                // a mechanism to free it up.  This should be fine, since it
165                // is only 256 bytes for any process and only gets malloc'd
166                // when using ByteGray surfaces.  Eventually, we should use
167                // the new Disposer mechanism to delete this native memory.
168                if (byteGrayPalette == NULL) {
169                    // assert (256 * sizeof(RGBQUAD)) <= SIZE_MAX
170                    byteGrayPalette = (RGBQUAD *)safe_Malloc(256 * sizeof(RGBQUAD));
171                    for (int i = 0; i < 256; ++i) {
172                        byteGrayPalette[i].rgbRed = i;
173                        byteGrayPalette[i].rgbGreen = i;
174                        byteGrayPalette[i].rgbBlue = i;
175                    }
176                }
177                memcpy(bmi.colors.palette, byteGrayPalette, 256 * sizeof(RGBQUAD));
178            }
179        } else if (srcInfo.pixelStride == 2) {
180            // For 16-bit case, init the masks for the pixel depth
181            bmi.colors.dwMasks[0] = rmask;
182            bmi.colors.dwMasks[1] = gmask;
183            bmi.colors.dwMasks[2] = bmask;
184        }
185
186        if (fastBlt) {
187            // Window could go away at any time, leaving bits on the screen
188            // from this GDI call, so make sure window still exists
189            if (::IsWindowVisible(dstOps->window)) {
190                // Could also call StretchDIBits.  Testing showed slight
191                // performance advantage of SetDIBits instead, so since we
192                // have no need of scaling, might as well use SetDIBits.
193                SetDIBitsToDevice(hDC, dstx, dsty, width, height,
194                    0, 0, 0, height, rasBase,
195                    (BITMAPINFO*)&bmi, DIB_RGB_COLORS);
196            }
197        } else {
198            // Source scanlines not DWORD-aligned - copy each scanline individually
199            for (int i = 0; i < height; i += 1) {
200                if (::IsWindowVisible(dstOps->window)) {
201                    SetDIBitsToDevice(hDC, dstx, dsty+i, width, 1,
202                        0, 0, 0, 1, rasBase,
203                        (BITMAPINFO*)&bmi, DIB_RGB_COLORS);
204                    rasBase = (void*)((char*)rasBase + srcInfo.scanStride);
205                } else {
206                    break;
207                }
208            }
209        }
210        dstOps->ReleaseDC(env, dstOps, hDC);
211        SurfaceData_InvokeRelease(env, srcOps, &srcInfo);
212    }
213    SurfaceData_InvokeUnlock(env, srcOps, &srcInfo);
214
215    return;
216}
217
218} // extern "C"
219