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 
  32 static RGBQUAD *byteGrayPalette = NULL;
  33 
  34 extern "C" {
  35 
  36 typedef 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  */
  49 JNIEXPORT void JNICALL
  50 Java_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"