/* * Copyright 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include "android_runtime/android_view_Surface.h" #include "core_jni_helpers.h" #include "jni.h" namespace android { jfieldID gNativeContextFieldId; /** * Class to store information needed by the Layoutlib renderer */ class JNILayoutlibRendererContext : public RefBase { public: ~JNILayoutlibRendererContext() override { if (mBufferConsumer != nullptr) { mBufferConsumer.clear(); } } void setBufferConsumer(const sp& consumer) { mBufferConsumer = consumer; } IGraphicBufferConsumer* getBufferConsumer() { return mBufferConsumer.get(); } private: sp mBufferConsumer; }; static jobject android_view_LayoutlibRenderer_createSurface(JNIEnv* env, jobject thiz) { sp gbProducer; sp gbConsumer; BufferQueue::createBufferQueue(&gbProducer, &gbConsumer); // Save the IGraphicBufferConsumer in the context so that it can be reused for buffer creation sp newCtx = sp::make(); newCtx->setBufferConsumer(gbConsumer); auto* const currentCtx = reinterpret_cast( env->GetLongField(thiz, gNativeContextFieldId)); if (newCtx != nullptr) { // Create a strong reference to the new context to avoid it being destroyed newCtx->incStrong((void*)android_view_LayoutlibRenderer_createSurface); } if (currentCtx != nullptr) { // Delete the reference to the previous context as it is not needed and can be destroyed currentCtx->decStrong((void*)android_view_LayoutlibRenderer_createSurface); } env->SetLongField(thiz, gNativeContextFieldId, reinterpret_cast(newCtx.get())); return android_view_Surface_createFromIGraphicBufferProducer(env, gbProducer); } static jobject android_view_LayoutlibRenderer_createBuffer(JNIEnv* env, jobject thiz, jint width, jint height) { auto* ctx = reinterpret_cast( env->GetLongField(thiz, gNativeContextFieldId)); if (ctx == nullptr) { jniThrowException(env, "java/lang/IllegalStateException", "No surface has been created"); return nullptr; } IGraphicBufferConsumer* bufferConsumer = ctx->getBufferConsumer(); bufferConsumer->setDefaultBufferSize(width, height); auto* bufferItem = new BufferItem(); bufferConsumer->acquireBuffer(bufferItem, 0); sp buffer = bufferItem->mGraphicBuffer; delete bufferItem; int bytesPerPixel = 4; uint32_t dataSize = buffer->getStride() * buffer->getHeight() * bytesPerPixel; void* pData = nullptr; buffer->lockAsync(0, Rect::EMPTY_RECT, &pData, 0); jobject byteBuffer = env->NewDirectByteBuffer(pData, dataSize); return byteBuffer; } static const JNINativeMethod gMethods[] = { {"nativeCreateSurface", "()Landroid/view/Surface;", (void*)android_view_LayoutlibRenderer_createSurface}, {"nativeCreateBuffer", "(II)Ljava/nio/ByteBuffer;", (void*)android_view_LayoutlibRenderer_createBuffer}, }; int register_android_view_LayoutlibRenderer(JNIEnv* env) { jclass layoutlibRendererClass = FindClassOrDie(env, "android/view/LayoutlibRenderer"); gNativeContextFieldId = GetFieldIDOrDie(env, layoutlibRendererClass, "mNativeContext", "J"); return RegisterMethodsOrDie(env, "android/view/LayoutlibRenderer", gMethods, NELEM(gMethods)); } } // namespace android