diff --git a/graphics/java/android/graphics/ColorSpace.java b/graphics/java/android/graphics/ColorSpace.java index 2427dec169d6f..4c669b8c34f59 100644 --- a/graphics/java/android/graphics/ColorSpace.java +++ b/graphics/java/android/graphics/ColorSpace.java @@ -212,9 +212,9 @@ public abstract class ColorSpace { new Rgb.TransferParameters(1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.04045, 2.4); private static final Rgb.TransferParameters BT2020_HLG_TRANSFER_PARAMETERS = new Rgb.TransferParameters(2.0f, 2.0f, 1 / 0.17883277f, - 0.28466892f, 0.5599107f, 0.0f, -3.0f, true); + 0.28466892f, 0.5599107f, -11 / 12.0f, -3.0f, true); private static final Rgb.TransferParameters BT2020_PQ_TRANSFER_PARAMETERS = - new Rgb.TransferParameters(107 / 128.0f, 1.0f, 32 / 2523.0f, + new Rgb.TransferParameters(-107 / 128.0f, 1.0f, 32 / 2523.0f, 2413 / 128.0f, -2392 / 128.0f, 8192 / 1305.0f, -2.0f, true); // See static initialization block next to #get(Named) diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index dd6c2bc326fda..eeed2267d0c39 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -553,6 +553,7 @@ cc_defaults { "RootRenderNode.cpp", "SkiaCanvas.cpp", "SkiaInterpolator.cpp", + "Tonemapper.cpp", "VectorDrawable.cpp", ], @@ -611,7 +612,6 @@ cc_defaults { "ProfileData.cpp", "ProfileDataContainer.cpp", "Readback.cpp", - "Tonemapper.cpp", "TreeInfo.cpp", "WebViewFunctorManager.cpp", "protos/graphicsstats.proto", diff --git a/libs/hwui/CanvasTransform.h b/libs/hwui/CanvasTransform.h index c46a2d3699748..291f4cf7193bc 100644 --- a/libs/hwui/CanvasTransform.h +++ b/libs/hwui/CanvasTransform.h @@ -45,4 +45,4 @@ bool transformPaint(ColorTransform transform, SkPaint* paint, BitmapPalette pale SkColor transformColor(ColorTransform transform, SkColor color); SkColor transformColorInverse(ColorTransform transform, SkColor color); -} // namespace android::uirenderer; \ No newline at end of file +} // namespace android::uirenderer diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp index 3f7c4f005f7c1..430e69e1ed3fc 100644 --- a/libs/hwui/RecordingCanvas.cpp +++ b/libs/hwui/RecordingCanvas.cpp @@ -43,6 +43,7 @@ #include "SkRegion.h" #include "SkTextBlob.h" #include "SkVertices.h" +#include "Tonemapper.h" #include "VectorDrawable.h" #include "include/gpu/GpuTypes.h" // from Skia #include "include/gpu/GrDirectContext.h" @@ -344,7 +345,9 @@ struct DrawImage final : Op { SkPaint paint; BitmapPalette palette; void draw(SkCanvas* c, const SkMatrix&) const { - c->drawImage(image.get(), x, y, sampling, &paint); + SkPaint newPaint = paint; + tonemapPaint(image->imageInfo(), c->imageInfo(), -1, newPaint); + c->drawImage(image.get(), x, y, sampling, &newPaint); } }; struct DrawImageRect final : Op { @@ -366,7 +369,9 @@ struct DrawImageRect final : Op { SkCanvas::SrcRectConstraint constraint; BitmapPalette palette; void draw(SkCanvas* c, const SkMatrix&) const { - c->drawImageRect(image.get(), src, dst, sampling, &paint, constraint); + SkPaint newPaint = paint; + tonemapPaint(image->imageInfo(), c->imageInfo(), -1, newPaint); + c->drawImageRect(image.get(), src, dst, sampling, &newPaint, constraint); } }; struct DrawImageLattice final : Op { @@ -399,8 +404,10 @@ struct DrawImageLattice final : Op { auto flags = (0 == fs) ? nullptr : pod( this, (xs + ys) * sizeof(int) + fs * sizeof(SkColor)); - c->drawImageLattice(image.get(), {xdivs, ydivs, flags, xs, ys, &src, colors}, dst, - filter, &paint); + SkPaint newPaint = paint; + tonemapPaint(image->imageInfo(), c->imageInfo(), -1, newPaint); + c->drawImageLattice(image.get(), {xdivs, ydivs, flags, xs, ys, &src, colors}, dst, filter, + &newPaint); } }; diff --git a/libs/hwui/Tonemapper.cpp b/libs/hwui/Tonemapper.cpp index a7e76b6311400..0d39f0e332986 100644 --- a/libs/hwui/Tonemapper.cpp +++ b/libs/hwui/Tonemapper.cpp @@ -18,7 +18,10 @@ #include #include +// libshaders only exists on Android devices +#ifdef __ANDROID__ #include +#endif #include "utils/Color.h" @@ -26,6 +29,8 @@ namespace android::uirenderer { namespace { +// custom tonemapping only exists on Android devices +#ifdef __ANDROID__ class ColorFilterRuntimeEffectBuilder : public SkRuntimeEffectBuilder { public: explicit ColorFilterRuntimeEffectBuilder(sk_sp effect) @@ -59,20 +64,21 @@ static sk_sp createLinearEffectColorFilter(const shaders::LinearE return effectBuilder.makeColorFilter(); } -static bool extractTransfer(ui::Dataspace dataspace) { - return dataspace & HAL_DATASPACE_TRANSFER_MASK; +static ui::Dataspace extractTransfer(ui::Dataspace dataspace) { + return static_cast(dataspace & HAL_DATASPACE_TRANSFER_MASK); } static bool isHdrDataspace(ui::Dataspace dataspace) { const auto transfer = extractTransfer(dataspace); - return transfer == HAL_DATASPACE_TRANSFER_ST2084 || transfer == HAL_DATASPACE_TRANSFER_HLG; + return transfer == ui::Dataspace::TRANSFER_ST2084 || transfer == ui::Dataspace::TRANSFER_HLG; } static ui::Dataspace getDataspace(const SkImageInfo& image) { return static_cast( ColorSpaceToADataSpace(image.colorSpace(), image.colorType())); } +#endif } // namespace @@ -80,6 +86,8 @@ static ui::Dataspace getDataspace(const SkImageInfo& image) { // shader and tag it on the supplied paint. void tonemapPaint(const SkImageInfo& source, const SkImageInfo& destination, float maxLuminanceNits, SkPaint& paint) { +// custom tonemapping only exists on Android devices +#ifdef __ANDROID__ const auto sourceDataspace = getDataspace(source); const auto destinationDataspace = getDataspace(destination); @@ -102,6 +110,9 @@ void tonemapPaint(const SkImageInfo& source, const SkImageInfo& destination, flo paint.setColorFilter(colorFilter); } } +#else + return; +#endif } } // namespace android::uirenderer diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml index fdba9a6597c6b..80c7a21dc11b0 100644 --- a/tests/HwAccelerationTest/AndroidManifest.xml +++ b/tests/HwAccelerationTest/AndroidManifest.xml @@ -120,6 +120,15 @@ + + + + + + + diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ColorBitmapActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ColorBitmapActivity.java new file mode 100644 index 0000000000000..017de605fe394 --- /dev/null +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ColorBitmapActivity.java @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2023 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. + */ + +package com.android.test.hwui; + +import android.app.Activity; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.ColorSpace; +import android.graphics.HardwareBufferRenderer; +import android.graphics.LinearGradient; +import android.graphics.Paint; +import android.graphics.PixelFormat; +import android.graphics.RenderNode; +import android.graphics.Shader; +import android.hardware.HardwareBuffer; +import android.media.Image; +import android.media.ImageWriter; +import android.os.Bundle; +import android.view.SurfaceHolder; +import android.view.SurfaceView; +import android.view.View; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.Spinner; + +import java.time.Duration; +import java.util.concurrent.Executors; +import java.util.concurrent.FutureTask; + +@SuppressWarnings({"UnusedDeclaration"}) +public class ColorBitmapActivity extends Activity implements SurfaceHolder.Callback, + AdapterView.OnItemSelectedListener { + + private static final int WIDTH = 512; + private static final int HEIGHT = 512; + + private ImageView mImageView; + private SurfaceView mSurfaceView; + private HardwareBuffer mGradientBuffer; + private ImageWriter mImageWriter; + private ColorSpace mColorSpace = ColorSpace.get(ColorSpace.Named.SRGB); + private String[] mColorNames = {"sRGB", "BT2020_HLG", "BT2020_PQ"}; + private String mCurrentColorName = "sRGB"; + + private FutureTask authorGradientBuffer(HardwareBuffer buffer) { + HardwareBufferRenderer renderer = new HardwareBufferRenderer(buffer); + RenderNode node = new RenderNode("content"); + node.setPosition(0, 0, buffer.getWidth(), buffer.getHeight()); + + Canvas canvas = node.beginRecording(); + LinearGradient gradient = new LinearGradient( + 0, 0, buffer.getWidth(), buffer.getHeight(), 0xFF000000, + 0xFFFFFFFF, Shader.TileMode.CLAMP); + Paint paint = new Paint(); + paint.setShader(gradient); + canvas.drawRect(0f, 0f, buffer.getWidth(), buffer.getHeight(), paint); + node.endRecording(); + + renderer.setContentRoot(node); + + ColorSpace colorSpace = ColorSpace.get(ColorSpace.Named.SRGB); + FutureTask resolvedBuffer = new FutureTask<>(() -> buffer); + renderer.obtainRenderRequest() + .setColorSpace(colorSpace) + .draw(Executors.newSingleThreadExecutor(), result -> { + result.getFence().await(Duration.ofSeconds(3)); + resolvedBuffer.run(); + }); + return resolvedBuffer; + } + + private FutureTask getGradientBuffer() { + HardwareBuffer buffer = HardwareBuffer.create( + WIDTH, HEIGHT, PixelFormat.RGBA_8888, 1, + HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE + | HardwareBuffer.USAGE_GPU_COLOR_OUTPUT); + return authorGradientBuffer(buffer); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + try { + + mColorSpace = ColorSpace.get(ColorSpace.Named.SRGB); + + ArrayAdapter adapter = new ArrayAdapter<>( + this, android.R.layout.simple_spinner_item, mColorNames); + + adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + Spinner spinner = new Spinner(this); + spinner.setAdapter(adapter); + spinner.setOnItemSelectedListener(this); + + mGradientBuffer = getGradientBuffer().get(); + + LinearLayout linearLayout = new LinearLayout(this); + linearLayout.setOrientation(LinearLayout.VERTICAL); + + mImageView = new ImageView(this); + + mSurfaceView = new SurfaceView(this); + mSurfaceView.getHolder().addCallback(this); + + linearLayout.addView(spinner, new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.WRAP_CONTENT, + LinearLayout.LayoutParams.WRAP_CONTENT)); + + linearLayout.addView(mImageView, new LinearLayout.LayoutParams(WIDTH, HEIGHT)); + linearLayout.addView(mSurfaceView, new LinearLayout.LayoutParams(WIDTH, HEIGHT)); + + setContentView(linearLayout); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private ColorSpace getFromName(String name) { + if (name.equals("sRGB")) { + return ColorSpace.get(ColorSpace.Named.SRGB); + } else if (name.equals("BT2020_HLG")) { + return ColorSpace.get(ColorSpace.Named.BT2020_HLG); + } else if (name.equals("BT2020_PQ")) { + return ColorSpace.get(ColorSpace.Named.BT2020_PQ); + } + + throw new RuntimeException("Unrecognized Colorspace!"); + } + + private void populateBuffers() { + Bitmap bitmap = Bitmap.wrapHardwareBuffer( + mGradientBuffer, ColorSpace.get(ColorSpace.Named.SRGB)); + Bitmap copy = bitmap.copy(Bitmap.Config.ARGB_8888, false); + copy.setColorSpace(mColorSpace); + mImageView.setImageBitmap(copy); + + try (Image image = mImageWriter.dequeueInputImage()) { + authorGradientBuffer(image.getHardwareBuffer()).get(); + image.setDataSpace(mColorSpace.getDataSpace()); + mImageWriter.queueInputImage(image); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Override + public void surfaceCreated(SurfaceHolder holder) { + + } + + @Override + public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { + mImageWriter = new ImageWriter.Builder(holder.getSurface()) + .setUsage(HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE + | HardwareBuffer.USAGE_GPU_COLOR_OUTPUT + | HardwareBuffer.USAGE_COMPOSER_OVERLAY) + .build(); + populateBuffers(); + } + + @Override + public void surfaceDestroyed(SurfaceHolder holder) { + mImageWriter.close(); + mImageWriter = null; + } + + + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + mCurrentColorName = mColorNames[position]; + mColorSpace = getFromName(mCurrentColorName); + populateBuffers(); + } + + @Override + public void onNothingSelected(AdapterView parent) { + + } +}