1 /*
   2  * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
   3  */
   4 #include "config.h"
   5 
   6 #include "BufferImageJava.h"
   7 #include <wtf/text/CString.h>
   8 #include "GraphicsContext.h"
   9 #include "ImageBuffer.h"
  10 #include "ImageData.h"
  11 #include "MIMETypeRegistry.h"
  12 #include "NotImplemented.h"
  13 
  14 #include "PlatformContextJava.h"
  15 #include "GraphicsContext.h"
  16 #include "IntRect.h" //XXX: recheck
  17 #include "ImageBufferData.h"
  18 
  19 
  20 
  21 namespace WebCore {
  22 
  23 ImageBufferData::ImageBufferData(
  24     const FloatSize& size,
  25     ImageBuffer &rq_holder,
  26     float resolutionScale
  27 ) : m_rq_holder(rq_holder)
  28 {
  29     JNIEnv* env = WebCore_GetJavaEnv();
  30 
  31     static jmethodID midCreateImage = env->GetMethodID(
  32         PG_GetGraphicsManagerClass(env),
  33         "createRTImage",
  34         "(II)Lcom/sun/webkit/graphics/WCImage;");
  35     ASSERT(midCreateImage);
  36 
  37     m_image = RQRef::create(JLObject(env->CallObjectMethod(
  38         PL_GetGraphicsManager(env),
  39         midCreateImage,
  40         (jint) (size.width() * resolutionScale),
  41         (jint) (size.height() * resolutionScale)
  42     )));
  43     CheckAndClearException(env);
  44 }
  45 
  46 JLObject ImageBufferData::getWCImage() const
  47 {
  48     return m_image->cloneLocalCopy();
  49 }
  50 
  51 unsigned char *ImageBufferData::data() const
  52 {
  53     JNIEnv* env = WebCore_GetJavaEnv();
  54 
  55     //RenderQueue need to be processed before pixel buffer extraction.
  56     //For that purpose it has to be in actual state.
  57     m_rq_holder.context()->platformContext()->rq().flushBuffer();
  58 
  59     static jmethodID midGetBGRABytes = env->GetMethodID(
  60         PG_GetImageClass(env),
  61         "getPixelBuffer",
  62         "()Ljava/nio/ByteBuffer;");
  63     ASSERT(midGetBGRABytes);
  64 
  65     JLObject byteBuffer(env->CallObjectMethod(getWCImage(), midGetBGRABytes));
  66     CheckAndClearException(env);
  67 
  68     return byteBuffer
  69         ? (unsigned char *) env->GetDirectBufferAddress(byteBuffer)
  70         : NULL;
  71 }
  72 
  73 void ImageBufferData::update()
  74 {
  75     JNIEnv* env = WebCore_GetJavaEnv();
  76 
  77     static jmethodID midUpdateByteBuffer = env->GetMethodID(
  78         PG_GetImageClass(env),
  79         "drawPixelBuffer",
  80         "()V");
  81     ASSERT(midUpdateByteBuffer);
  82 
  83     env->CallObjectMethod(getWCImage(), midUpdateByteBuffer);
  84     CheckAndClearException(env);
  85 }
  86 
  87 ImageBuffer::ImageBuffer(
  88     const FloatSize& size,
  89     float resolutionScale,
  90     ColorSpace,
  91     RenderingMode,
  92     bool& success
  93 )
  94     : m_data(size, *this, resolutionScale)
  95     , m_resolutionScale(resolutionScale)
  96     , m_logicalSize(size)
  97 {
  98     // RT-10059: ImageBufferData construction may fail if the requested
  99     // image size is too large. In that case we exit immediately,
 100     // automatically reporting the failure to ImageBuffer::create().
 101     if (!m_data.m_image) {
 102         return;
 103     }
 104 
 105     float scaledWidth = ceilf(resolutionScale * size.width());
 106     float scaledHeight = ceilf(resolutionScale * size.height());
 107 
 108     // FIXME: Should we automatically use a lower resolution? //XXX: copy-paste from ImageBufferCG.cpp
 109     if (!FloatSize(scaledWidth, scaledHeight).isExpressibleAsIntSize())
 110         return;
 111 
 112     m_size = IntSize(scaledWidth, scaledHeight);
 113 
 114     JNIEnv* env = WebCore_GetJavaEnv();
 115     static jmethodID midCreateBufferedContextRQ = env->GetMethodID(
 116         PG_GetGraphicsManagerClass(env),
 117         "createBufferedContextRQ",
 118         "(Lcom/sun/webkit/graphics/WCImage;)Lcom/sun/webkit/graphics/WCRenderQueue;");
 119     ASSERT(midCreateBufferedContextRQ);
 120 
 121     JLObject wcRenderQueue(env->CallObjectMethod(
 122         PL_GetGraphicsManager(env),
 123         midCreateBufferedContextRQ,
 124         (jobject)m_data.getWCImage()));
 125     ASSERT(wcRenderQueue);
 126     CheckAndClearException(env);
 127 
 128     m_data.m_context = std::make_unique<GraphicsContext>(new PlatformContextJava(wcRenderQueue, true));
 129     success = true;
 130 }
 131 
 132 ImageBuffer::~ImageBuffer()
 133 {
 134 }
 135 
 136 /*
 137 size_t ImageBuffer::dataSize() const
 138 {
 139     return m_size.width() * m_size.height() * 4;
 140 }
 141 */
 142 
 143 GraphicsContext* ImageBuffer::context() const
 144 {
 145     return m_data.m_context.get();
 146 }
 147 
 148 RefPtr<Image> ImageBuffer::copyImage(BackingStoreCopy copyBehavior, ScaleBehavior scaleBehavior) const
 149 {
 150     //utatodo: seems [copyBehavior] is the rest of [drawsUsingCopy]
 151     return BufferImage::create(
 152         m_data.m_image,
 153         m_data.m_context->platformContext()->rq_ref(),
 154         m_size.width(), m_size.height());
 155 }
 156 
 157 BackingStoreCopy ImageBuffer::fastCopyImageMode()
 158 {
 159     return CopyBackingStore; // todo tav revise
 160 }
 161 
 162 void ImageBuffer::platformTransformColorSpace(const Vector<int> &lookUpTable)
 163 {
 164     notImplemented();
 165 /*
 166     uint8* rowData = reinterpret_cast<uint8*>(m_data.m_bitmap.Bits());
 167     unsigned bytesPerRow = m_data.m_bitmap.BytesPerRow();
 168     unsigned rows = m_size.height();
 169     unsigned columns = m_size.width();
 170     for (unsigned y = 0; y < rows; y++) {
 171         uint8* pixel = rowData;
 172         for (unsigned x = 0; x < columns; x++) {
 173             // lookUpTable doesn't seem to support a LUT for each color channel
 174             // separately (judging from the other ports). We don't need to
 175             // convert from/to pre-multiplied color space since BBitmap storage
 176             // is not pre-multiplied.
 177             pixel[0] = lookUpTable[pixel[0]];
 178             pixel[1] = lookUpTable[pixel[1]];
 179             pixel[2] = lookUpTable[pixel[2]];
 180             // alpha stays unmodified.
 181             pixel += 4;
 182         }
 183         rowData += bytesPerRow;
 184     }
 185 */
 186 }
 187 
 188 PassRefPtr<Uint8ClampedArray> getImageData(
 189     const Multiply multiplied,
 190     const ImageBufferData &idata,
 191     const IntRect& rect,
 192     const IntSize& size)
 193 {
 194     // This code was adapted from the CG implementation
 195 
 196     float area = 4.0f * rect.width() * rect.height();
 197     if (area > static_cast<float>(std::numeric_limits<int>::max()))
 198         return 0;
 199 
 200     RefPtr<Uint8ClampedArray> result = Uint8ClampedArray::createUninitialized(rect.width() * rect.height() * 4);
 201     unsigned char* data = result->data();
 202 
 203     if (rect.x() < 0 || rect.y() < 0
 204             || rect.maxX() > size.width() || rect.maxY() > size.height())
 205         result->zeroFill();
 206 
 207     int originx = rect.x();
 208     int destx = 0;
 209     if (originx < 0) {
 210         destx = -originx;
 211         originx = 0;
 212     }
 213     int endx = rect.maxX();
 214     if (endx > size.width())
 215         endx = size.width();
 216     int width = endx - originx;
 217 
 218     int originy = rect.y();
 219     int desty = 0;
 220     if (originy < 0) {
 221         desty = -originy;
 222         originy = 0;
 223     }
 224     int endy = rect.maxY();
 225     if (endy > size.height())
 226         endy = size.height();
 227     int height = endy - originy;
 228 
 229     if (width <= 0 || height <= 0)
 230         return result;
 231 
 232     unsigned dstBytesPerRow = 4 * rect.width();
 233     unsigned char* dstRows = data + desty * dstBytesPerRow + destx * 4;
 234 
 235     unsigned srcBytesPerRow = 4 * size.width();
 236     unsigned char* srcRows =
 237             idata.data() + originy * srcBytesPerRow + originx * 4;
 238 
 239     for (int y = 0; y < height; ++y) {
 240         unsigned char *pd = dstRows;
 241         unsigned char *ps = srcRows;
 242         for (int x = 0; x < width; x++) {
 243             unsigned char alpha = ps[3];
 244             if (multiplied == Unmultiplied && alpha && alpha!=255) {
 245                 // Unmultiply and convert BGRA to RGBA
 246                 pd[0] = (ps[2] * 255) / alpha;
 247                 pd[1] = (ps[1] * 255) / alpha;
 248                 pd[2] = (ps[0] * 255) / alpha;
 249                 pd[3] = alpha;
 250             } else {
 251                 // Convert BGRA to RGBA
 252                 pd[0] = ps[2];
 253                 pd[1] = ps[1];
 254                 pd[2] = ps[0];
 255                 pd[3] = alpha;
 256             }
 257             pd += 4;
 258             ps += 4;
 259         }
 260         srcRows += srcBytesPerRow;
 261         dstRows += dstBytesPerRow;
 262     }
 263 
 264 
 265     return result;
 266 }
 267 
 268 PassRefPtr<Uint8ClampedArray> ImageBuffer::getUnmultipliedImageData(const IntRect& rect, CoordinateSystem) const
 269 {
 270     return getImageData(Unmultiplied, m_data, rect, m_size);
 271 }
 272 
 273 PassRefPtr<Uint8ClampedArray> ImageBuffer::getPremultipliedImageData(const IntRect& rect, CoordinateSystem) const
 274 {
 275     return getImageData(Premultiplied, m_data, rect, m_size);
 276 }
 277 
 278 void ImageBuffer::putByteArray(
 279     Multiply multiplied,
 280     Uint8ClampedArray* source,
 281     const IntSize& sourceSize,
 282     const IntRect& sourceRect,
 283     const IntPoint& destPoint,
 284     CoordinateSystem)
 285 {
 286     // This code was adapted from the CG implementation
 287 
 288     ASSERT(sourceRect.width() > 0);
 289     ASSERT(sourceRect.height() > 0);
 290 
 291     int originx = sourceRect.x();
 292     int destx = destPoint.x() + sourceRect.x();
 293     ASSERT(destx >= 0);
 294     ASSERT(destx < m_size.width());
 295     ASSERT(originx >= 0);
 296     ASSERT(originx <= sourceRect.maxX());
 297 
 298     int endx = destPoint.x() + sourceRect.maxX();
 299     ASSERT(endx <= m_size.width());
 300     int width = endx - destx;
 301 
 302     int originy = sourceRect.y();
 303     int desty = destPoint.y() + sourceRect.y();
 304     ASSERT(desty >= 0);
 305     ASSERT(desty < m_size.height());
 306     ASSERT(originy >= 0);
 307     ASSERT(originy <= sourceRect.maxY());
 308 
 309     int endy = destPoint.y() + sourceRect.maxY();
 310     ASSERT(endy <= m_size.height());
 311     int height = endy - desty;
 312 
 313     if (width <= 0 || height <= 0)
 314         return;
 315 
 316     unsigned srcBytesPerRow = 4 * sourceSize.width();
 317     unsigned char* srcRows =
 318             source->data() + originy * srcBytesPerRow + originx * 4;
 319     unsigned dstBytesPerRow = 4 * m_size.width();
 320     unsigned char* dstRows =
 321             m_data.data() + desty * dstBytesPerRow + destx * 4;
 322 
 323     for (int y = 0; y < height; ++y) {
 324         unsigned char *pd = dstRows;
 325         unsigned char *ps = srcRows;
 326         for (int x = 0; x < width; x++) {
 327             int alpha = ps[3]; //have to be [int] for right multiply casting
 328             if (multiplied == Unmultiplied && alpha != 255) {
 329                 // Premultiply and convert RGBA to BGRA
 330                 pd[0] = static_cast<unsigned char>((ps[2] * alpha + 254) / 255);
 331                 pd[1] = static_cast<unsigned char>((ps[1] * alpha + 254) / 255);
 332                 pd[2] = static_cast<unsigned char>((ps[0] * alpha + 254) / 255);
 333                 pd[3] = static_cast<unsigned char>(alpha);
 334             } else {
 335                 // Convert RGBA to BGRA
 336                 pd[0] = ps[2];
 337                 pd[1] = ps[1];
 338                 pd[2] = ps[0];
 339                 pd[3] = alpha;
 340             }
 341             pd += 4;
 342             ps += 4;
 343         }
 344         dstRows += dstBytesPerRow;
 345         srcRows += srcBytesPerRow;
 346     }
 347 
 348     m_data.update();
 349 }
 350 
 351 void ImageBuffer::clip(GraphicsContext*, const FloatRect&) const
 352 {
 353     notImplemented();
 354 }
 355 
 356 void ImageBuffer::draw(
 357     GraphicsContext* context,
 358     ColorSpace styleColorSpace,
 359     const FloatRect& destRect,
 360     const FloatRect& srcRect,
 361     CompositeOperator op,
 362     BlendMode bm,
 363     bool useLowQualityScale)
 364 {
 365     RefPtr<Image> imageCopy = copyImage();
 366     context->drawImage(
 367         imageCopy.get(),
 368         styleColorSpace,
 369         destRect,
 370         srcRect,
 371         ImagePaintingOptions(
 372             op,
 373             bm,
 374             DoNotRespectImageOrientation,
 375             useLowQualityScale)
 376         );
 377 }
 378 
 379 void ImageBuffer::drawPattern(
 380     GraphicsContext* context,
 381     const FloatRect& srcRect,
 382     const AffineTransform& patternTransform,
 383     const FloatPoint& phase,
 384     ColorSpace styleColorSpace,
 385     CompositeOperator op,
 386     const FloatRect& destRect,
 387     BlendMode bm) // todo tav new param
 388 {
 389     RefPtr<Image> imageCopy = copyImage();
 390     imageCopy->drawPattern(
 391         context,
 392         srcRect,
 393         patternTransform,
 394         phase,
 395         styleColorSpace,
 396         op,
 397         destRect);
 398 }
 399 
 400 String ImageBuffer::toDataURL(const String& mimeType, const double* quality, CoordinateSystem) const
 401 {
 402     if (MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType)) {
 403         //RenderQueue need to be processed before pixel buffer extraction.
 404         //For that purpose it has to be in actual state.
 405         context()->platformContext()->rq().flushBuffer();
 406 
 407         JNIEnv* env = WebCore_GetJavaEnv();
 408 
 409         static jmethodID midToDataURL = env->GetMethodID(
 410                 PG_GetImageClass(env),
 411                 "toDataURL",
 412                 "(Ljava/lang/String;)Ljava/lang/String;");
 413         ASSERT(midToDataURL);
 414 
 415         JLString data((jstring) env->CallObjectMethod(
 416                 m_data.getWCImage(),
 417                 midToDataURL,
 418                 (jstring) JLString(mimeType.toJavaString(env))));
 419 
 420         CheckAndClearException(env);
 421         if (data) {
 422             return String(env, data);
 423         }
 424     }
 425     return "data:,";
 426 }
 427 
 428 }