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