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 }