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 }