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